From d6b917faf4e172f44cebc14344f09ae3d5ef4d33 Mon Sep 17 00:00:00 2001 From: Kosta Petan Date: Mon, 28 Aug 2023 20:57:56 +0200 Subject: [PATCH] Add service to enable github issues workflow (#1) * big bang gitub workflows * add missing settings in local.settings.json * config refactor * fix devlead plan response * swap cosmos to table storage for metadata storage * unify config via options * azd-ify WIP * add qdrant bicep WIP * working azd provision setup * consolidate SK version in projects * replace localhost :) * add fqdn to options * httpclient fixes * add managed identity to the function and assign contrib role * qdrant endpoint setting * add container instances cleanup code + wait on termination to upload to Github * formatting fixes * add tables in bicep * local getting started WIP * add azure setup instructions * add the load-waf bits * docs WIP --------- Co-authored-by: Kosta Petan --- .devcontainer/Dockerfile | 6 +- .devcontainer/devcontainer.json | 11 +- .gitignore | 9 +- azure.yaml | 8 + cli/Models/DevLeadPlanResponse.cs | 2 +- cli/Program.cs | 4 +- cli/SandboxSkill.cs | 6 - cli/cli.csproj | 4 +- cli/config/KernelConfigExtensions.cs | 27 - docs/github-flow-architecture.md | 1 + docs/github-flow-getting-started.md | 90 ++ docs/github-flow.md | 1 + docs/images/github-sk-dev-team.png | Bin 0 -> 309649 bytes infra/abbreviations.json | 11 + infra/app/sk-func.bicep | 45 + .../database/postgresql/flexibleserver.bicep | 64 + infra/core/database/qdrant/qdrant-aca.bicep | 72 + infra/core/host/appservice-appsettings.bicep | 16 + infra/core/host/appservice.bicep | 119 ++ infra/core/host/appserviceplan.bicep | 21 + infra/core/host/container-app-upsert.bicep | 104 ++ infra/core/host/container-app.bicep | 161 +++ .../host/container-apps-environment.bicep | 40 + infra/core/host/container-apps.bicep | 37 + infra/core/host/container-registry.bicep | 82 ++ infra/core/host/functions.bicep | 87 ++ .../applicationinsights-dashboard.bicep | 1235 +++++++++++++++++ infra/core/monitor/applicationinsights.bicep | 30 + infra/core/monitor/loganalytics.bicep | 21 + infra/core/monitor/monitoring.bicep | 33 + infra/core/storage/storage-account.bicep | 76 + infra/main.bicep | 165 +++ infra/main.parameters.json | 42 + .../Activities/IssueActivities.cs | 76 + .../Activities/MetadataActivities.cs | 34 + .../Activities/PullRequestActivities.cs | 240 ++++ sk-azfunc-server/ExecuteFunctionEndpoint.cs | 75 - sk-azfunc-server/Models/AddToPRRequest.cs | 10 + sk-azfunc-server/Models/CloseIssueRequest.cs | 7 + .../Models/ContainerInstanceMetadata.cs | 12 + .../Models/DevLeadPlanResponse.cs | 17 + sk-azfunc-server/Models/ErrorResponse.cs | 11 - .../Models/ExecuteFunctionRequest.cs | 23 - .../Models/ExecuteFunctionResponse.cs | 11 - sk-azfunc-server/Models/GHCommitRequest.cs | 12 + sk-azfunc-server/Models/GHNewBranch.cs | 10 + sk-azfunc-server/Models/IssueMetadata.cs | 19 + .../Models/IssueOrchestrationRequest.cs | 8 + sk-azfunc-server/Models/NewIssueRequest.cs | 6 + sk-azfunc-server/Models/NewIssueResponse.cs | 5 + sk-azfunc-server/Models/RunAndSaveRequest.cs | 6 + .../Models/RunInSandboxRequest.cs | 8 + sk-azfunc-server/Models/SaveOutputRequest.cs | 12 + sk-azfunc-server/Models/SkillRequest.cs | 9 + sk-azfunc-server/Models/SkillResponse.cs | 5 + .../Orchestrators/IssueOrchestration.cs | 75 + .../Orchestrators/SubIssueOrchestration.cs | 154 ++ sk-azfunc-server/Program.cs | 103 +- sk-azfunc-server/README.md | 82 +- sk-azfunc-server/Services/GithubService.cs | 36 + .../Services/WebHookEventProcessor.cs | 118 ++ sk-azfunc-server/config/AzureOptions.cs | 9 + sk-azfunc-server/config/GithubOptions.cs | 6 + .../config/KernelConfigExtensions.cs | 29 - sk-azfunc-server/config/KernelSettings.cs | 91 -- sk-azfunc-server/config/OpenAIOptions.cs | 9 + sk-azfunc-server/config/QdrantOptions.cs | 5 + .../config/appsettings-notyet.json | 6 - .../config/appsettings.json.azure-example | 7 - .../config/appsettings.json.openai-example | 7 - sk-azfunc-server/host.json | 7 + sk-azfunc-server/local.settings.json | 26 +- sk-azfunc-server/local.settings.template.json | 52 + .../sk-csharp-azure-functions.csproj | 25 +- skills/DevLead.cs | 46 +- skills/skills.csproj | 2 +- util/seed-memory/Program.cs | 23 +- util/seed-memory/config/KernelSettings.cs | 4 + .../config/appsettings.template.json | 9 + util/seed-memory/seed-memory.csproj | 4 +- 80 files changed, 3712 insertions(+), 469 deletions(-) create mode 100644 azure.yaml delete mode 100644 cli/config/KernelConfigExtensions.cs create mode 100644 docs/github-flow-architecture.md create mode 100644 docs/github-flow-getting-started.md create mode 100644 docs/github-flow.md create mode 100644 docs/images/github-sk-dev-team.png create mode 100644 infra/abbreviations.json create mode 100644 infra/app/sk-func.bicep create mode 100644 infra/core/database/postgresql/flexibleserver.bicep create mode 100644 infra/core/database/qdrant/qdrant-aca.bicep create mode 100644 infra/core/host/appservice-appsettings.bicep create mode 100644 infra/core/host/appservice.bicep create mode 100644 infra/core/host/appserviceplan.bicep create mode 100644 infra/core/host/container-app-upsert.bicep create mode 100644 infra/core/host/container-app.bicep create mode 100644 infra/core/host/container-apps-environment.bicep create mode 100644 infra/core/host/container-apps.bicep create mode 100644 infra/core/host/container-registry.bicep create mode 100644 infra/core/host/functions.bicep create mode 100644 infra/core/monitor/applicationinsights-dashboard.bicep create mode 100644 infra/core/monitor/applicationinsights.bicep create mode 100644 infra/core/monitor/loganalytics.bicep create mode 100644 infra/core/monitor/monitoring.bicep create mode 100644 infra/core/storage/storage-account.bicep create mode 100644 infra/main.bicep create mode 100644 infra/main.parameters.json create mode 100644 sk-azfunc-server/Activities/IssueActivities.cs create mode 100644 sk-azfunc-server/Activities/MetadataActivities.cs create mode 100644 sk-azfunc-server/Activities/PullRequestActivities.cs delete mode 100644 sk-azfunc-server/ExecuteFunctionEndpoint.cs create mode 100644 sk-azfunc-server/Models/AddToPRRequest.cs create mode 100644 sk-azfunc-server/Models/CloseIssueRequest.cs create mode 100644 sk-azfunc-server/Models/ContainerInstanceMetadata.cs create mode 100644 sk-azfunc-server/Models/DevLeadPlanResponse.cs delete mode 100644 sk-azfunc-server/Models/ErrorResponse.cs delete mode 100644 sk-azfunc-server/Models/ExecuteFunctionRequest.cs delete mode 100644 sk-azfunc-server/Models/ExecuteFunctionResponse.cs create mode 100644 sk-azfunc-server/Models/GHCommitRequest.cs create mode 100644 sk-azfunc-server/Models/GHNewBranch.cs create mode 100644 sk-azfunc-server/Models/IssueMetadata.cs create mode 100644 sk-azfunc-server/Models/IssueOrchestrationRequest.cs create mode 100644 sk-azfunc-server/Models/NewIssueRequest.cs create mode 100644 sk-azfunc-server/Models/NewIssueResponse.cs create mode 100644 sk-azfunc-server/Models/RunAndSaveRequest.cs create mode 100644 sk-azfunc-server/Models/RunInSandboxRequest.cs create mode 100644 sk-azfunc-server/Models/SaveOutputRequest.cs create mode 100644 sk-azfunc-server/Models/SkillRequest.cs create mode 100644 sk-azfunc-server/Models/SkillResponse.cs create mode 100644 sk-azfunc-server/Orchestrators/IssueOrchestration.cs create mode 100644 sk-azfunc-server/Orchestrators/SubIssueOrchestration.cs create mode 100644 sk-azfunc-server/Services/GithubService.cs create mode 100644 sk-azfunc-server/Services/WebHookEventProcessor.cs create mode 100644 sk-azfunc-server/config/AzureOptions.cs create mode 100644 sk-azfunc-server/config/GithubOptions.cs delete mode 100644 sk-azfunc-server/config/KernelConfigExtensions.cs delete mode 100644 sk-azfunc-server/config/KernelSettings.cs create mode 100644 sk-azfunc-server/config/OpenAIOptions.cs create mode 100644 sk-azfunc-server/config/QdrantOptions.cs delete mode 100644 sk-azfunc-server/config/appsettings-notyet.json delete mode 100644 sk-azfunc-server/config/appsettings.json.azure-example delete mode 100644 sk-azfunc-server/config/appsettings.json.openai-example create mode 100644 sk-azfunc-server/local.settings.template.json create mode 100644 util/seed-memory/config/appsettings.template.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a221106f594..bbaf9543a1b 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,7 @@ FROM mcr.microsoft.com/devcontainers/dotnet:0-7.0 # Install the xz-utils package -RUN apt-get update && apt-get install -y xz-utils nodejs +RUN apt-get update && apt-get install -y xz-utils nodejs npm -RUN curl -fsSL https://aka.ms/install-azd.sh | bash \ No newline at end of file +RUN curl -fsSL https://aka.ms/install-azd.sh | bash + +RUN npm i -g azure-functions-core-tools@4 --unsafe-perm true \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f1fb5cdc9ea..61446950c64 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,7 +9,8 @@ "features": { "ghcr.io/devcontainers/features/azure-cli:1": {}, "ghcr.io/devcontainers/features/common-utils:2": {}, - "ghcr.io/devcontainers/features/docker-in-docker:2": {} + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/azure/azure-dev/azd:latest": {} }, "postCreateCommand": "bash .devcontainer/startup.sh", "hostRequirements": { @@ -39,7 +40,13 @@ "ms-dotnettools.csdevkit", "Azurite.azurite", "ms-dotnettools.csharp", - "ms-semantic-kernel.semantic-kernel" + "ms-semantic-kernel.semantic-kernel", + "GitHub.copilot-chat", + "GitHub.vscode-github-actions", + "ms-azuretools.azure-dev", + "ms-azuretools.vscode-azurefunctions", + "ms-azuretools.vscode-bicep", + "ms-dotnettools.vscode-dotnet-runtime" ] } } diff --git a/.gitignore b/.gitignore index 85fa5e4e205..af040acbd78 100644 --- a/.gitignore +++ b/.gitignore @@ -482,7 +482,9 @@ $RECYCLE.BIN/ # Vim temporary swap files *.swp - +__azurite** +__blob** +__queue** # SQLite workflows DB elsa.sqlite.* @@ -490,4 +492,7 @@ elsa.sqlite.* .env # ignore local elsa-core src -elsa-core/ \ No newline at end of file +elsa-core/ +sk-azfunc-server/local.settings.json +.azure +temp \ No newline at end of file diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 00000000000..cf9b6d9db7c --- /dev/null +++ b/azure.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +name: sk-dev-team +services: + sk-func: + project: ./sk-azfunc-server + language: dotnet + host: function \ No newline at end of file diff --git a/cli/Models/DevLeadPlanResponse.cs b/cli/Models/DevLeadPlanResponse.cs index a68eacf91ce..3c036d7e9c5 100644 --- a/cli/Models/DevLeadPlanResponse.cs +++ b/cli/Models/DevLeadPlanResponse.cs @@ -3,7 +3,7 @@ public class Subtask { public string subtask { get; set; } - public string LLM_prompt { get; set; } + public string prompt { get; set; } } public class Step diff --git a/cli/Program.cs b/cli/Program.cs index 977cce0ff7f..19b6fd99365 100644 --- a/cli/Program.cs +++ b/cli/Program.cs @@ -92,7 +92,7 @@ public static async Task ChainFunctions(string file, int maxRetry) { try { - implementationResult = await CallFunction(nameof(Developer), Developer.Implement, subtask.LLM_prompt, maxRetry); + implementationResult = await CallFunction(nameof(Developer), Developer.Implement, subtask.prompt, maxRetry); break; } catch (Exception ex) @@ -137,7 +137,7 @@ public static async Task CallFunction(string skillName, string functionNam .AddConsole() .AddDebug(); }); - var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient("http://qdrant", 1536, port: 6333)); + var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient("http://qdrant:6333", 1536)); var embedingGeneration = new AzureTextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey); var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration); diff --git a/cli/SandboxSkill.cs b/cli/SandboxSkill.cs index 0cc9f310d49..a44782a13a0 100644 --- a/cli/SandboxSkill.cs +++ b/cli/SandboxSkill.cs @@ -3,17 +3,11 @@ public class SandboxSkill { - [SKFunction("Run a script in Alpine sandbox")] - [SKFunctionInput(Description = "The script to be executed")] - [SKFunctionName("RunInAlpine")] public async Task RunInAlpineAsync(string input) { return await RunInContainer(input, "alpine"); } - [SKFunction("Run a script in dotnet alpine sandbox")] - [SKFunctionInput(Description = "The script to be executed")] - [SKFunctionName("RunInDotnetAlpine")] public async Task RunInDotnetAlpineAsync(string input) { return await RunInContainer(input, "mcr.microsoft.com/dotnet/sdk:7.0"); diff --git a/cli/cli.csproj b/cli/cli.csproj index d795b4e68be..5b4aea6bc2f 100644 --- a/cli/cli.csproj +++ b/cli/cli.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/cli/config/KernelConfigExtensions.cs b/cli/config/KernelConfigExtensions.cs deleted file mode 100644 index 6d1d9f2c399..00000000000 --- a/cli/config/KernelConfigExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.SemanticKernel; - -internal static class KernelConfigExtensions -{ - /// - /// Adds a text completion service to the list. It can be either an OpenAI or Azure OpenAI backend service. - /// - /// - /// - /// - internal static void AddCompletionBackend(this KernelConfig kernelConfig, KernelSettings kernelSettings) - { - switch (kernelSettings.ServiceType.ToUpperInvariant()) - { - case KernelSettings.AzureOpenAI: - kernelConfig.AddAzureChatCompletionService(kernelSettings.DeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey); - break; - - case KernelSettings.OpenAI: - kernelConfig.AddOpenAITextCompletionService(modelId: kernelSettings.DeploymentOrModelId, apiKey: kernelSettings.ApiKey, orgId: kernelSettings.OrgId, serviceId: kernelSettings.ServiceId); - break; - - default: - throw new ArgumentException($"Invalid service type value: {kernelSettings.ServiceType}"); - } - } -} diff --git a/docs/github-flow-architecture.md b/docs/github-flow-architecture.md new file mode 100644 index 00000000000..04be0b78ab6 --- /dev/null +++ b/docs/github-flow-architecture.md @@ -0,0 +1 @@ +# Azure components diff --git a/docs/github-flow-getting-started.md b/docs/github-flow-getting-started.md new file mode 100644 index 00000000000..e2c67c986d6 --- /dev/null +++ b/docs/github-flow-getting-started.md @@ -0,0 +1,90 @@ +## Prerequisites + +- Access to gpt3.5-turbo or preferably gpt4 - [Get access here](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview#how-do-i-get-access-to-azure-openai) +- [Setup a Github app](#how-do-i-setup-the-github-app) +- [Install the Github app](https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app) +- [Create labels for the dev team skills](#which-labels-should-i-create) + +### How do I setup the Github app? + +- [Register a Github app](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app). +- Setup the following permissions + - Repository + - Contents - read and write + - Issues - read and write + - Metadata - read only + - Pull requests - read and write +- Subscribe to the following events: + - Issues + - Issue comment +- Allow this app to be installed by any user or organization +- Add a dummy value for the webhook url, we'll come back to this setting +- After the app is created, generate a private key, we'll use it later for authentication to Github from the app + +### Which labels should I create? + +In order for us to know which skill and persona we need to talk with, we are using Labels in Github Issues + +The default bunch of skills and personnas are as follows: +- PM.Readme +- PM.BootstrapProject +- Do.It +- DevLead.Plan +- Developer.Implement + +Once you start adding your own skills, just remember to add the corresponding Label! + +## How do I run this locally? + +Codespaces are preset for this repo. + +Create a codespace and once the codespace is created, make sure to fill in the `local.settings.json` file. + +There is a `local.settings.template.json` you can copy and fill in, containing comments on the different config values. + +Hit F5 and go to the Ports tab in your codespace, make sure you make the `:7071` port publically visible. [How to share port?](https://docs.github.com/en/codespaces/developing-in-codespaces/forwarding-ports-in-your-codespace?tool=vscode#sharing-a-port-1) + +Copy the local address (it will look something like https://foo-bar-7071.preview.app.github.dev) and append `/api/github/webhooks` at the end. Using this value, update the Github App's webhook URL and you are ready to go! + +Before you go and have the best of times, there is one last thing left to do [load the WAF into the vector DB](#load-the-waf-into-qdrant) + + + +## How do I deploy this to Azure? + +This repo is setup to use [azd](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview) to work with the Azure bits. `azd` is installed in the codespace. + +Let's start by logging in to Azure using +```bash +azd auth login +``` + +After we've logged in, we need to create a new environment and setup the OpenAI and GithubApp config. + +```bash +azd env new dev +azd env set -e dev GH_APP_ID replace_with_gh_app_id +azd env set -e dev GH_APP_INST_ID replace_with_inst_id +azd env set -e dev GH_APP_KEY replace_with_gh_app_key +azd env set -e dev OAI_DEPLOYMENT_ID replace_with_deployment_id +azd env set -e dev OAI_EMBEDDING_ID replace_with_embedding_id +azd env set -e dev OAI_ENDPOINT replace_with_oai_endpoint +azd env set -e dev OAI_KEY replace_with_oai_key +azd env set -e dev OAI_SERVICE_ID replace_with_oai_service_id +azd env set -e dev OAI_SERVICE_TYPE AzureOpenAI +``` + +Now that we have all that setup, the only thing left to do is run + +``` +azd up -e dev +``` + +and wait for the azure components to be provisioned and the app deployed. + +As the last step, we also need to [load the WAF into the vector DB](#load-the-waf-into-qdrant) + +### Load the WAF into Qdrant. + +If you are running the app locally, we have [Qdrant](https://qdrant.tech/) setup in the Codespace and if you are running in Azure, Qdrant is deployed to ACA. +The loader is a project in the `util` folder, called `seed-memory`. We need to fill in the `appsettings.json` file in the `config` folder with the OpenAI details and the Qdrant endpoint, then just run the loader with `dotnet run` and you are ready to go. \ No newline at end of file diff --git a/docs/github-flow.md b/docs/github-flow.md new file mode 100644 index 00000000000..5be43fddaa6 --- /dev/null +++ b/docs/github-flow.md @@ -0,0 +1 @@ +![](/docs/images/github-sk-dev-team.png) \ No newline at end of file diff --git a/docs/images/github-sk-dev-team.png b/docs/images/github-sk-dev-team.png new file mode 100644 index 0000000000000000000000000000000000000000..c70e4d87b58b58e87b5640bb4d0f6b31797055fa GIT binary patch literal 309649 zcmeFZcT`i`8z!t=kN~2wPy{I!M5HMSNCy=Vks?a(A|MbzKzi>Y0wMy^QIXzDLQNE; zONUTGi1bc`P!j^*M(>?(X3d)UdB0iTS~F|>M-MssoW1wE-}1cAyUz|&S5>5^WuZNE z=n%cq-8&kG4$;;hIs`#Oj(}e@W|WP=e}|nk6mK0W>^wVv=n&^2r8{zu+>Dm6u5UF5 zhN-_ZEq&0DlfM?%6;dZ})<1Mo;$>I~BHPR4+@?Zt_>gCq;j5-N<_P}8>=Ldf7U4}J zbve0fCV%aq&!DP0Q_h$ix#_xDH7jOgZjSZb8YW@8YINIWd~DRR)MUudZ@EFBoU(q0 z4pUJ6*2U?&CiOuyc;}Nr{OZkb@BRLRP|ohlKh)%OLj8ZcFEFt57Zd@-@=J+{H2-Fl zx1i{A|7KFq%Nc&0uydHqr@twMUxSg;vHUmlmrZ{SCKC)x`tYwNLus1wyNmO4SpK(Y zA@z)PFX1W%%6q%voOD_X@4x{kH!3P zW&g&*{;`-p7W2nq{#eYvk-#5|`C~EvMqmEKm_HWt$723i%)gPqAB*|l*kW3=zn67M zhc|QhO5)_dss1jh^@&NxXJ^_BxiiCCcf0ivrKvLdN22}Kd{$Z{))iMbv#9s_cU7c~ z>shZ7cO#=d>_MRlrhqg5`s*S<$sv&z+`q|U`Qs_U0NhW4e}*?%?5eGxo3VLOdE;$y z=YRJNLXaiLwb<1mWqX?Xxc9^STbCjFoybAE!GPk5GWf~gWZ~K_17fVHPGm4P8C|kSbDjxX zv0I8aZZ_QCb)DYoOp%aJbJn(rS0IRGE{=-9ja&y@Io~Obq7PjLeXK*(yR;g;5?shr+C5LoTf9+Z{w?0PanI6-ZE%b* zcX8Ipy_Msu6={WeF~9OrxP$H?_r(jHxkR5SEGk0adRPBr7#tCC{On0J)SAnHy{<1_ z#@>Vv<<`}@vbwdkcfmzw9udPfc#|`aoW3j?ZacVd%1eJp?Vr5>&)YPrsl1ImJax{x zjBhxMASJ$P&X;EP@MLSN(PLo)AE-8l{8e7_bLE!sgew<5hvj;jP0*amc+U|%7 z@dp(9x_w_AS{Xn~Pqb?M}xnh2n(uF6RVJWQstNuy;mm zvKsX7@|E3z6jr6N3>OTrieFw zk$^F#h&x+vujFWtz2w-Cf?KBwQJbduPS)vcXrpr0w&xex6A3L zjSbcpL56c-?neBHAeAz~jfVsMM@(JCSL!Qw_Ix${m-fTc8fiTDKMcs|%gpsAP4?T^ z7qOtGqqM`4)L6PL`mIg`v5}?+S6405nr~reB0E!1bVD<%ULrH$b4d67+#FoZ^yr+3 z_FxFU6t^$28J?@Ji_urK5nc1~nr)26ZjY?*crC@3d{Yh8#NHjT#12dlC%5vBCK*6o zzH^B-&JSXHK76g|ym>=J$9Z@&j=g}&G$}N(OsP!&sbI~r|MrI1;G=?)9X;Mg4r-38 ztfQg^ozF%PlVYbLDQidrsYh!{U~10D4Q;+WgOm2pp{chxT*F?6I{v_dw+O!eVYNG0QSF8Sae5H< zHPJgNj0wkrR-e;r=Rc9Rr6;M1wItme=kVIKf_b#$#CG+mi5l)M_D-_Sd>dnvULb_% z-%64m?kF23;r_G8p-nS7iVXY8?UR6-OA7xuE_hmbls zy24oTo_qo*vj5B5TFoB3TQ9$meaAjW81?3wKJSs>OlhPFP{y8-L4EkB5OM^L+c?3K!(L5ZQT=rKDRDBNLJ zL@|ZURg!prK(EX2dPcA=W7{`72IOX{;+48iqqa_^8Of(n1!k#*WtuZp8}GR%-?i_n z?~IzyBupR)N8l_hb=z^M#zusXui0vwGT%w~4AvvX`k1ob~nI-ML8!shKbqtYz1IA?8`SY9mnNcD7UVHFpZPM)I0IW8p7j3X#GfY`5N9R$fn* zIf%J8?YeBwlMo455wE3b-&ds6UA0f0O9~TM5km<$)+B;QoXO~SXpwBe!*WB!1xpFn z3lNOz)dmdDRbjay-j!|7b(!V1Tn4-ZK6`t0Opl;u^bBm!oy0@i4SYvsLIi*b!V~7Zi-AUAvFQ_IdJuoL`?YZ2cpIBQryH6d?HiL zMZ(4<&#uoWt=E@y)1*e_l2te9$Ms3UC@G1aYwdz#*HbHZgF#v8`s|1<#?aIgThe<5Nedo}Gi7G}?z1rFn#mHTJB-`5UZwhK_L8Ri zx$b+byEY|bKHOT~TPyst3R_jxs*ZJ=l%{H^l}Ki{Ru$Xa{mfI;ckmUc?++GX{UkjjP0+;Ym3_+qm@b`>5KVgcV;_rSeRH2=hFB0h{mLb5JcthhG>mF z_Qy(8962pS-_ZD%H*C<)ubO3)30w9hew-W4aXQNGVH2)@JFRx&Yg<}!*L6q|d4Bt& z%OYD@=M6Ih&#sUB6pa#l&kHIYO>iO{Bk{N$POYJh7Lf>J&tR^}7DEqhtPf`Uv5nkN zm`b%mQR4wng?r@LC((wzI1sqI_6RvUx7*&O5C2JVX`pzDElT>h{jRj51<*ECy^mt{wmMz8K6x%W>cv3uza)ZoeUwR0k~OR@M(8Nx#46r9AH zR_0zLWs%luCyJT1!TX-(HrmL_Evd1GtMc`Gr@r%D5qpduTSRzVw`F`R{WzYVHCe4@ zWiz+fZqr^sqAWs`w`h3lof|P0!E2L`XcKbSR@MLdf$n)!{MZ>Z^bHvV;aD82y?Q6| zoep%fI0gO`f8b;|i;HYJth=&TDQ(UJ9p5&b~!aGT$PyK4Y!wIVKe zQBau@`h{J;_EVY;VrPR6k`3NhNrhF9cdc6d`E`<_Y&yta=&0ox^S`AkQJI$E(mbXM zwjbck@-lZ8{PS|95sX3P9gjTGAV${xx4YS0&0lx*SM)cGw0z-FORc<+nvvdc1vswh z$74A9+^jLvEY?d+lRs_L+2NZ=`!`$Z%wgM4q%>z|N#J#CJ!b1~Z@%DT9$mLmgNk_K zmyGde$s|~R=7t{Y4KBH%nOPpXmt$VNej)R|qxi~$5X!48jN%t5)3xoA)BJQ}j5Y}? zg0Y!xnCLLQa0Qj-`Mq!zrElB)LyYcuSA~-E&7H#Q$0F#p4B;K5G^;vnUXwIH*Co3X zQoK0&>h+nM8zUIrG;O0b>eZIU=0}}vm8+Aiov^;EHj@Q~!yTVRar;GXD-l~0!-HdC zTMDmU$)q_xn{L9udjIO&F*JDIZp=%tlpDpd!z~+T(E-I|_}JA>F_U<_R8n0U`PrCU zjXZ8_QM-u`zAU%8_aZvBYnG#2|8~pZ?cboU)r~-;S=*F85BPiR9t+IJI-od-%~5Y4 zB3JkGb-HC?Uk#diX(Gn#@xUj>f-Ck_hLJC{apdAPEItY&3g2m6pid*v)*Jcm`t$%y zm%HFbYKSsxT|S;eT*={8du7gKm~EJ4vWvhq(fg7t4ZH_$uYP_gsJe-rkwYJt5S7s$ z*{Y<40(+@O%jU1lix7^T7+9IUEo)k)+-MU%F;tg&1+ICv zPBATvXgNL%uoq*LB1=;rWy?&gCWFF}H?$6`^`yJ^f- zK2{Fj(De!>xd}6G;UOcU_+BslRzNtYq_}$?1cb3c3jzoTSkD&oYwIhayuqE)i{DmP zQA+xVZsqE@3t7wX@u9=41(9%rT>SQwla?7rA zq|^aK`bTynwrXDA9pt{5UTixHW+4{04@LD}( zU9O0B5$P@i;hS-~8HzB6xib#<$u3Q#Ob#CI&=Q%1u&lWTl9^r!cD;eDCEO#z>s5V@ zSJ~Ak;PVBA5sgngYnR8fSUl2F{KKST=<0ol@yd_m5-jp-eHz((qxP~4b$3Q@Tprz? zR&6#MdUoA*@_NR;i^F%9BzSwP$!}~4_a~l&1{D&DQ<`S%wa?V^du&!i@MFv(l5xYU z#kshoG4Y*#Ui-!1SSQOC8^;5XE)8dT2>Xi(z$fP)mS~p7z)s^{v2x_$g|o*)dNR8} zd|^0%Zu<8MSo+2|m$Y%O!V6`vdDfBbQKJ$rW82NEQM1P38=Dd(SmER$SIo_FrF3V+ zDZ$9aePUOeq~c1i1ktX-C;auYzGxa7-8Tu#8g$YlfY~}+(eyq8g*&BRbfG()eaCIx z@QWbzl(sZr7`#D~t!7={gn3?etpE$b?BX;Ty}BrVM0Abt@}2RO*vii3N+i+L!)rbn z{l@(V)1z}uMZWLs!Q2jl( zSS_6A)TnPgME1M*NG(sEFU`ZS;tv^0qm&RI>5rK;7+iAy3qu`WeOd7(zmrBi}+ zXJl4f4F;r}c9+-Q`Bcwe!9@TF_Rb~-scCkJDadv?b8zX>V5;|#M;(Nv?ijP2V5WCV zJWqXMmb4c0SwvHdee>`YF1Ky_JoM=5BWp>B9+0c$&S3WD2>PQ75;@M?@GyWVx$nP& z+HA>4mm+U?OII!jysNQ^54q~dc+r+v`y6W(O{I$=JejQ?>tz)q<*F`yYq#{Zx5&wVxrFUwtpn%! za~?LFjmPb64b86e@)DM}`zj{d{BbG90JC0Wz4IHv@hTNSG!Ol>WQss~uO`?x+6o{| z8JppDGpfmdcCXOdX_!B5@GMqF#L%fR7vCJg!2xK=r$=k2GILq)m&q-I&`DUplr)Vs zKkC5lW}?w{DYp2CVnGx^F28s&T60knpj{n{X!clq;(pHdQs_bC+Ty7GG1rpJk~UdX z%cWXPMCVp0V7-;Ti4$JMNZkxg_BBj)B}i#bcJ6I-_Si4d^B!(lYPN5Ru!qQ52#b2J zNX@>`ac%s>inO+YZXRT<8WLwmKEQDt2zwYIW`JAYY0?jV#8hT?s*hQ69lpI>u%+37 zMQjlr0F=-~>SuTP<(4_`8p%X8xQ>;skq+<|k(74YsRPXwXIyK!uJ`225Ue2f@%YY6 zPNFE`n}NP(DXW%o-T3;uLPCapqU|yW?n9x1;^JDy7A0-nb)g1Y18HbG11uo=CSqM> zUNJD@&kWlZFBX^l!h3cfUpbHwF(7u40iacCX)Y%(b8 zLPJ@|1K*YPmOLFnm<_Io(?1`HBQf=qkw)pieC0bkx<2e2Q!&|lVbi*Ae5aXE)aXG< z8G5|LQpXsjf;PA|lVAql{ywQTNkox9Hu7ibW)r4yX^hwJ?_>E9w)FKDW@X>)7{A*R zpNx;i7s*J?%!}(MS=$44hOmL8>$MOq^O3SM@Y!71h#M0ypVt4h513?Lgy|bh+wE4VO=Xav{CInIsSIk>!qOC_8 zDxbo11BS9}`3WM>&hbZ1%DiB9uiLV5 zZfjx{$dZSO=$mvPo4@uI)nW?^4~XU}8snPrS95?-=vAug8;K~5(Z4|Qo=f*B!G_pq zg}}KX5}WNsW(NRwOYv2{o^`wmI?6zn7Hb)YZ2$VGA9+IcFr?mHb3VU&u7eF z%m~ht6SsW_7GCpBC1XuSciks5uV0{ps=Jn|1ayYf(x46PpAO)-2o>q$L2M zpl#*6r#(i#3Kv^ga@)Wj=dRlG$m2WZd)7<#d4hYURkZ3IG&wxdcL3l;oDRf%j}6 z|K6@w-?CjrXKpkZAJ3ZK^DM@kFq;x39maKlITkZT>n&tz_x2P^`W%?xl?B3d#un$k zq3g?yrJiYqS0qwf7fHC_wn46|9pMSmE8vZ zlZ5M@^!L#blIYl$&JrMeVn*#Ouodn3s}1Ii;bV*Wz0d42(+a1nti+WVFG=mzQN z_i;fXqA*d0?lpTkHuE4hu?A$F@+__6M}c7HKO#dfcJ&}s^4O}~Mo}jV-zcm>5HpAQ z(uv(vf#>u1JrB70WCwDJ!-nDU3YIynWm3e%Yc>%Z2f%f_Kz={I)nv+Kg!|BR7-M&~ zd3lGhJ)KU-wM(x1{vB&p#6KAR4}p`rmAIeOYS+?X*JkU*vR^0cZB=wGzERj!#$acm z+}rg9%=Y;2l#kr0C{5cjq!)m#`iZ8j&IH-&tqcO_4XVD34bnR|q<5AG@66!3A2G>> zW*{JjC0Hh#CNA;OT|tU^J*g#*gnAaf<}^rgxs3!;f22 z3C*v>SSqSMvmD#qF6){EHNwWAcTlOCj#G5bX*C0fT{6GQyNAg-bFBPS_g%6*vEYVk-Oewcn|Ufv#Uy0?0f;--eKs8}jn*5P&Qm{nPRPXx;uV z6LoYPV*j7L03f~nzijq@X8gyQ{Kp{q$9?_@82=M2{l{hgK_mb34Q78F$R7vt$ASD; z`%C_~%s(#kkIVdT`Qkr8;!lwHKP^be`co-*ON+4k&KFSNW#=g&8GPM~5s79uz+5sg7d+wMtjt<;b7%%HaFa;yd<2 zO0`aW`s9Fu9~hJmva`)$%}Afgjrtz9Z2Cv46<^}?Hmdb7(c3sY4SjlPUTze8-p+DAN;+?LHr(= z&&R0(<#Z$Yx&(TRXEQHNxH|~ zFPDx4trqjrTyCdd0K=Eiz>QEYXoDH~c-mM}u7M*7J8lmoQ-Z;Z|MiOqlvM=F=JM9- zkVryTf+GU>xb3TIC6(E+te zITeL4P}72Q@K2DpcR^QoI^dc=UHt2JU?z|0b8N+c_%;M^VEfthVint|pVN5+2KsP5 zNF3514_bL|^w~k4ADkGexS*dTO9o;=+uJX{l4U>Of3p)~0EzrxPKKPj42BT-M(weo z+|b1l^=5g{*ulv*;-@Gs3II{K1-?~GPX~`u*(R6LTm)Z_Cz(8adnf(afppTt;Iio< z;HL0b_-X%>VES>{!|-3nZk{{^4N}QJSHZ6cg~P|D^})_nFDZoCuj#x5!@H8APW{i; zq!D`m$O8kt>uqx?+Zc=qY$Q)P(Q;cBDso)Y=_C-(f8m?ixHuu02Wv|CypW_jtP7P8 zu-JBm1t)&3ELf>i_OI6gb8QBr-2mEHC}n(JNu$pm-|1rKxhEwUgguN1x|Hi&}w5qt>#>J zG`M2RvA3nz;05c4s;jt8;STg+=DoEl1rX0a2V`ei7>$?EV1<2V<5yv|W0g4h9;a^5CSuIMA|!z)K?({wxQ}2*qen-aGg{<%0$0 z%1^t$dml)sB%~jHR&Hp-Bi_E+UET--D_YbUs*IT zj+CZ0i-yPnQ0*FYlK6E_W)4{J%w8I2=-(eBWMQFmcR~BeeuRiW5tw=bZfxX?za)?W zL0y>pFzB`Or*#2$_W=CC#+CN9I-ppo{C#9NXgoPH!tzTLz!!?F9^r|Sl>;k9H*(Yb z)Lo#qB408uhsm;mPSJ9wZ2e{F!A+YNwmcMEVEX(I%4}nQneL?@zDrn|-TNCTu;DH3 zlg5j?(R@7marrb{8t^wJ-Hg^-rSLYyImE9cbI*5 z9X0HON)U638+0EfRl3e36AV++8j`UT2z{KCMM%GXR@N`s3ap2#;^Qj29ak74QqsP9K|tSl?BQwX|nU1`59KOEklG7F{v=B@$}=7nep4)Q#D1tXi_Cl z1HI$^ZTZI|mV7Mx9{XyJOLIJR^Se~|*OEK-;quz$VSblnp>cGF|2FywF5n%E*5c;k zIrF*NzJHekM?F7tNMND(X|s$>oDiE;iffXTuq(ipGlR{BAUf*6jH$xwUqhcAcn|8+ z(L9_cA7n4e4UN~$v@ghW1WApAvh$wnFv{kUSDv9GCA)Fl^j$mR?!8Ux%`E0*88aZE z*yO3xy5f6xP^{EqSdr+?<~bP4m}0|K!=?(|S&TxNH|62)`g`Kvj?nYUo|N_DPVx5$%IPS-{qiLc__^;BLc%}&A0?jv80G}kV>igqBQQ)Ay5`~&#pz&} zbd160&V4@w+|{wm=O4;`zH|6`U~#IIGwbNxRJ(^AUv)EUc>{`h5Kbcr@q%1!3!*Zw z!vu?76K40KjT+Kovl3;N%B>V^r72B6Dk$Cl<@2`ell_>wDzVxnh%$=r3e+dDJ7@VC!ivJ>)^%U?Hr)vLgYo@{Hu2_ z-B0Drr)hca51jOYr}hDE-qDoPPnM4oQr~ndR^-I9EYQCzIz{&a)CN3Q!kFnznal<4 z;9SB6ue^p!EwsdboAUIOWIoWJ{d&csRl*&x5sq;nzHO?usZp9g4AGi0w|goZio$qkm1kp!RhB` z4Ei=;hHygZ4hEtANriG0#3po#vn3kxRSzs{ey|nCVN04j*CF~P*4NI+pqx&om-(i| zYg6;b3b#uOEiH;vMoVHd_0+owzWcnssnWc|PEVqP`f0$HH()8V_3}HD>ta>>mT`WQ zd&`3|Q%$Tz@#~_7X&uWh?78EWf2FJ$FQFp)|GC6gU$8bDjJ? zg4GevO}>BRaQi{jzR$x9p@1LQ8|kFbVM@LO$-FT)Rfp(keFhqlxQnwCyj%B@Jhh%v zRxKZwf9A>L-l`I;zof6Z?g9tWRW@?>M;?+!^hxMHdjU{x3A-9OdJa~-E7xvkJ7S35 zLhj$J$(5|VSutnwhTfHf-^IFle4c6<@DxOObdj5FPZ-a&*$X4IJeAi*det9>4 zSg38vT&n!@{6eF|ll@eD#nymiyVeyxLu%pP`qV4TlI~OYc1#F9Vms|;mfzKLt=;`( z&6Q!El^F8b{_zBZ)!H1i>bckhiIq(WyJ#A^>Zi;0?ygZwd*6ANy%{q2AHGk2?w<30 zs?Evf%}x+&5ZR3sU;1o8VaE%1oOlI^7)#ZC8C$Ka=Ul+QLpK(kv8;4qtaoN&_>zFv zgQUSxrNyZjL+TBeHf@3Pw$+2tAGioshli;iincXr)=VFGMAeQ2iKNkelN0mZB&F83 zJC{(xN-GuPRvF%bC62_cyumRQQA5Ep+%mUyVf#$2J+s-D4^`9i=%~9LN2WLS1`CF` zoNzjVGCrQ8S1*|tQXs99YBEt7sMTt}!Dv=jV9jO&3DKQL1bEw=Pxw)SZkB9hUpVm0PYY^(uiE&z*~P(ZRVqG;dF=Bl5)BynS18U4kX?X7SeR^4#ma zKgOE6j0Uu<=*zjdA*=exs1elK#}gw9)s&_UJ@-70$_>4(a$x*B z^4$TkO~mmwD+1_DQ)5|UH%hx1hiJl*cd~Xqqus&m% zHMcWo#3~HCSw3BadlNkXJ|<>#A+4X)T?l@+L@lknSUnq(VIOT&CPwz zK`h@iO6ObxaYng@-TC5VhiKc3>t>6Dvh325`%F; z{W(v@GB!e9m*@?e?eO-LCT>3H(<<>t?`Hjk$+_n_gXdN zd6L;P4T7gl^E2<=Xam;V6TWgf6>G3bbktK`x}tXo6*M2I#UxuR!nS+K^ughUD1Yk&5Oy>RI0F4i$QX`8qDc1LZ+vMAy#eO?{GD!Gg67rwGr+*OmivHIEA5hDK z7?$%ES9#AS%2ZAHW1MEifJxrz1=!~eBYv%A9ckCRSM#X5G77=`@wRo<^)TRLbF@p+ z&fOpOd~(XFySx;RGsfBT&0jL-WjLnSJ$1x%;9iB(;AZ^u!A}DtntA=X1J-q-xI-(& zD^B!sT6pqaK(Y0w!D&oL7%iX`S%(d<}?iR6Jft9-Tae5zH)LMEYXJ|2R zrtXCCdG=;gyw~P-#W-t_W{5pf^v+h*^ZC*n9@|`EYYjz2=0~p)&V$VxgV;orfP@Hs z(0GewFJE$C*VK|63~d|+arFEpm2(GaD8MzPAaY&`Z?~d8738cZUm(H}P^^^)nUwn( zc0Wsj^b`Bg#^R-40=Vkv&lIFg<_C5#ykYM04|<$uI_yb%-~@bEIQU=B)orkmllyJf zUc)DB`Vs{fYia{ur;3uPuDE*1=;}XsWQ&X{*7onDVm)F_>d(y~i{J0VXv?*@x<;WJ zQvGw|M>%2pB^^_(mRIIG*mfsg+;CsA))_48Fcy`iW$5~dGP-IWc;CxCc(3~Lz+)kw zy#e)N6lZ?(x0;TJ-1~1?wwy)RKh6{$?PuhJEVBMsKL+(=IV)jO!k0uqjv;AZ@_WDV0}7%cNf;;F56wjmAIJR>{$jT9$Ge$dx%dhMLC z8v5|^J?cc=Yj+c5)woDo7=jN%6wp0$m3JRp{ys;Xe^nVcG)| zeiBd&zrqwK|4X0`4l;X9)k}eX=AfNk=INu9hcN)hR(R7S9HNLj4T4^4>D9}We7q(W zP_*&ufwC)k_UfAcIRl#B2t0Cc_y>9+<$`>HKrXxwMdpf%owFD!(t4%I+w9`wQoA&= z`3Z>+_SyI}Oc1_>=Bkn#YE`aLPIwu-?&3?_3zFREK6G1VjJG0}jhD$TKDv%+u31w3 zQ}%tGZh6mTK}eofGQY-o`!^+nSFjHc%+=SofBl$7g%;1Ub1~;q%<*;M{EWO?En-gQ z!sWgo8)|r2tahXD@?XuugORKB_3-)4pz`Itpm8r8XD%%<_j8dYY5co`)uU;y1~I_q z-zcQV?Zp;3ZjN1wa8sVCD>3rT+jENj)RldSXcJI;ZoiWG7@))c|A2kpIG3_&P%-cg z1y(+-<+D7lQ=OSqP)+EyDoj3tbg!h>bv}#U%QOV0Q=#vR%~iI|!L6Um(ap_zKkrO3 ziiQo_9~(Aw3@koCHy{)1!$=YPlybD+r@c>0kx_{)x9Xe27Z}yv4%tf1LLFlWGF%&j zR^^#=UNp1US~TBTC`()4s?(ODKOMBeR8Aju{`@pMA79K^EsOXjeZ32Nw`}U7I=1_p zSz}Gx{6ou)t<1tXo=Tn9 zXFl8v0F|?WQUa7B?f`g&30;Ly!Y%+EbRkx{J^>jdGfY}q(oiEy>2B$jT=BaFQ!Lht z5({Ft(^mm#`~(0X(aO4F$Py!c#f&J}i_6oFA%Ie742?A{S?1ELJWRxx0Z;M zQAQsvb=cp2B5~=GQM3mGskNj5MJ4GvQP1>gw_z(}0L~Ctj19dJ*Hpl9?bZG+ZolpJ zmZZI#{T|U@BU{IAxlWe1KGz$`+j=aa`<}JPWsbNg@ylqrp{E9{0OnNLkd&iOy3;$| zw}YZR8?nVD2G2R=beI^DasUAd=3;UUB!rB-FRB{OL2L>UB##S_MP%Z2vtMhUe;%p? z-JEDtUi*^SMWMv2#z($stHff|?nCaBn=X3D z?R9GMu70VxP<}08V=rxLsOKSZ?{CVb&lvBu?UI8Gw{xZpX>D-U=p zhOICq(|)_8u11?Qht^7C36Ob-{b+svc`LwO$v<#&d*DpaexK*IWl_k|PmS&`ovxuHPgv(c-8@h|lX20U4% zs|qCbvhp|Ms*7?+vc?TQxLCF0OHvmFol|*SXX-TjUuNvkPQ6uR7cB5r@UXY<9O5fF zX0(9FK1kZeuu^&_HMpOW1e5o*RmsFqpZscHjs3{)!}-iXn&-v`Y81}QEZcYDy~X7A z?N?0Xba39kyvvfsk421K?pf*Qw^jt+D1$S=WDiT-Wm!D%#1T&^O+DeMd>Ayo&#=0- zUp=whv%k*QJ?Nd7oQ?qfFLn8kG&biO&V1U7{a3Kpb+5w!+_S=vX8aA>1(OCTnYiX1 zI?*L*A;aU@UrX^%E7&?GUH;mrm|xyk+K=pOV1HRH;|a%_jhLkle9Wj5&GJHrM2tJ9 z)1KF^&HFgsU*XyOl2$UfOl)2{loNK7VV0Q^`U3*EmOLLfg6wo2Z-G&D^5u8|qh24r zok>JcPQVO4%nS~#)1^kcbhZyA^jh(5M<0$KW)ivy(4C`jm zn6Oam@jLxdvUv>NJ@(NOn}xjm;)A_g!9)`7B}izu1LU2Q5$7VN(HOfpKp(YN$|)Zu zcxSj+oe?Xj0~mkbNL$9u_JL>6_^##1qZk*dw6{S)kJeUtqn_N~mk%KaHX&XEKJY2N zQ5=#-e6c?ALIH|)O|Sr2(J=op??KMMPdG00h^I1Ua23SHzcysdtpN zPX3_fWvj`(%VBdYWk}DuABD3=1Xh20On8q9x$q9SqZ-|qW(-}0TIP{CIgNpjQfzdwUaIby!DtSaT&9#^lHqF9i zzI1<*X>;LD1xtZmEE;E_1=ZRp(ArYZ^BS@`5Q*M_NVxtHiL6&ZaT_^;3!4G^R3FC! z3UhC%Of?UaURj^tNUPcWc;%-^oPkJ$eCw`3DPiYKT%hnrI~2X%Lw$~}?WOt|x?S9lWb;fiiLHij>n~xFhAVjqr2A%Vg%jZ{=(; zUV(V7EK!{qHIIYgwsqA#D}>132D~q7k1Y`$EGSJiEkP#O0|5tqtRi~hd(0^5n0S|X zAqKkOT087>z?h0^CRP0%p!k6fp>so9KY{=yRJm9U0#x;z>!!av4&MdnGAE2=q}FB# zIF+woc^&6_%>mb_t1B|=y}as$#H=j~DyQxutn8`j>T}68$JDOpPdTq=#b^}WLit5T zA2C%_!#~@2(4bwIy&wIRS9_`+x8IUzE-?*&O3ZrA4q*^hafMHA=(8{71^Rk+;>^3v z7WuBY$rzxexMRntRF|Op>2U6^~4x|Vai@{X$U50IxxFgr<%U!_OR6W}>79&n7 z=}Oz@CCh9@eYsa)f_lEF-auBAyhc-hiM~EEWuw-mD3hI+j6=mw4MpRgeCdx+vd*c$ zytoxn<6+NzUUes5rFCN)FT?a{!jR{9z4%X8a<7oHQ|@yeMKq;ppj1cGgvRTvAn(CzZ?$nEdzXS z*`s{DZ_h?TVmaDBt6tyOI^gwAF*yS+ia~bZUV5quR?WtzEK21Y3uCC>ymu?KC~Vf3@Tw&S7gA(}Pj03N;> zoup6gzE`%14cSU8B9q=)K0NoB;nL<OFj*S6XA45LQpfs^EBMNx@D&jY`pQz7&8!$N$ zMZU?NfK!^KxQPT%I#Tf3sXM=nwv?jhSbe7t!uXBgDFNeqZ?%sI#4!*MXU>MG+2v7F z-lj73#&}z{h6?4AM*RwbYPoWxoTH)1*@erve16P7x>y&ypcWk9E9IhBighW=s1dL& zEu`MyYEQH54AXPJcz{0|0R8|pN4;G`pX+9_?)p_VP~Ac8ZR-mdtG@k)c=AKyi13(k z6j@-{t}8kJ$7^Eg)ZnD$RMXKx<&anN4XMUAsdKxV@!b1w9?|4XWxf)nw3X@Qgk`Tn zgXp=w`T(o>d>OdDd|K!1XQ6nP%_73uLi#7Y&-I@F5d7V5lJ`l*JA`#z{@Mu|y`~Q# zJOD8X!4JUD^{l<#%nvJ4cD3^!O0jMGby5vXR(X$VMV#r93^HQujIJmbe_IGgrz)X z#R*a$l6#4CLiV#hzzVQ@t^r@=GywFDafqK2wWyS{Yfq3b9ZTfVO~aJ1o8@D+n1T!| zoH9nEC6cmzV~FE5lWWcMP9wB6qLx)W)(sD~ATnnq1$@an2$wVm5*wmrLo_2?j}$F( zy36MF@b31q!!oXPZ7k+?&86WtVDYDOf7Xv=5Ba-+6el9SmWv2VFJVGA-Jl>7H!#Qy z`Bgz^r1}O5M*$otl01tR`_pbWq?(k~SWf1qQ8p*6dCd8f{Hz{vhb*DW#P9-A9!#6* zk+wB9qL!hC;R(qEw9b~-dcZ=>f)TV82T5_9P*78C+A2iXaWp51Z6p609NDr-AP zj&;La+-BWE+i@Iw{J7NYJYMFXoZWl3g~z>T@kDrR9*!GCZh`B~C|-<+SFU&Tdy$Iz z_6Fg5_d<=FxttRzm)p~L%(UD0UA#UODSXrlbFY0MpR;)gV7WjuMMOw^FmpFsLy%J4LSL>EeBIhSR;?XzJafG9576Q`j_o zIwx%df3-%OuuE(R#j_n0{JOW|`COwm?^<+OWcl5_JZVDGJt1;ZsuNHl_J^+r6z8H) zT}H1#hIB^y`lA<42PGKV50RRA;W;Qmopzm|(Ir&&Kr4Wy_euAE!0cb?S-Az-g_Z7n z!p&v+&`D*t^X(8nS3t9f`Pyka<3~(m0NEvXkZWw3rNxFz-z_ao7GH^OZofObw=unG zpi7Q4l%kRKKCVZ_M&VZsV zs6{@sF0my`CrHkWT(K%J&gW;~;1RgqRL9R@OTLAQ(#ZQDrBhH@yRC7%ii_&Y>$hXG zLEs!z@trIe<9v#mRDO_Posla02#PX!9}kRSO*ilmMeB)TN2rJx;n_k7KuD@7*9ndx zAVymDN9+Cq3?_;oM!Mwd^*7yzidetBF{KU4woLzT+4gfj=TcF#n)0$x-O;`!>5P&a zcH|eEsrFF#Xkf7U7Pawwb6zurs`0SCX94oNTjeuLsk`rQT|qo)zlifDm-%?{1r{Tt zCLg8wYaG`>d%wkYmJ)ZKHJ}Jp0R!IpWA$%q}?$5>4yIe7rNZ^zORZ&+bQ= zb}`qBSW^x4kQvXg&(30A)0TKpyVeF4U+EsnZc`>6a2C*kOq6V)_}yS7&F%`DzOcj1 zdF;gcWm|FHMw@l>wwzxcMIRzf7D5E;unXGo?ZLy=6GrN|h`JV&BT znT0eU^OPwvq@qG(&KybRnat;U)>_j3oKO4g&-ZuEd7X29`ya2hJkPqH`@ZJ;y586Q z06nj(yK!`ID-#|lTe;S!EZK3E-F3Pc-H z-8TpQBY{q&>I5gmKRYS!w0juC_%(NM^}TORpB*;8cvZ7izi5i$^`OnV*vbh_X(JOc zF0)qkhQ+p9mb_`fHgB7hckDjkLu_YNyn4b_-?l56qV|qYRIf_E zRPwAZVtg(BnN*HKAMKM}mOJ2-^wovErh4YZace^Xj}P@V7KEYb4JlYEB{R@e{zr!C zJw1z%VhggZW)3D6*k8F?Lv6AtlD(c&^Zb@mj(jTZS@v%Jv-|d`_?O&C(JguQW#jR1 zZNoLTGUw~IESXU!Wb{@oR4r3zyHYq>?&ch)i8a%{E&DL-WaBhLjDhXNg!tM*JzJIF z@=RNgDbJ%F5|(f2(dm;rN(5t%B}{I7Y@GO*kSyPsZ<>!}@fixx3*kFF4vdyNH2yeX zxc(}5{uGNcsrq_KhW5SotHY}aRMnr1I+~kOuBxqzVHr9WI!K34`ojYWf6Mp(EslPn z@!`vZ*WBvsL8p42{VGSs^|`(an?sj!&rOde`JV;NbeVq>`pF$&hq zOS7G?RUTv()?F!zFXdg$VN5Jvj2@9Vm|oKBT=1fB^r2S7^{@GhS{<61 zEv3uxV!T$3`(BTjy;UE|5CpQEf!(5@oh(%rkKH(xLWvw?wumi-kJE)_RFJxoEV{;o zaWtO`r4pKK-NFJ)*>46n1|_;7j%*1Xw>Fru<~XplzbBn&N}w-C%K2^D+bdAK#GCoJ z=B|E(qpdZk&qbhaw{+AYb>1b{>R#+{ySSt7HeC z#|YZp%1OS^d?B3R_hxcH#e`SbL5{e`pLoLmAeOX-!9 ze8D=pJ@wC>)NiX9hH*?ywtjn}{$hRMhiOdC_cO2W<{Ir{kGyKz+uq>z#OBN)p{y5+ zoqc9`UbvocPUIh*x;YLF{q-fL?4 zlE*Bm&dn{p_o1`u%*%rHg-2sU3jQidXHBHfGIpyOMm6+6{#@TBqk!-*v18JU6j@%E z!q4RGX6b!BxcBN2?rkCDjz0vrF*=COAb-R}u_SGP@ zy)S6SB|RtrX%^&}CauvBOV4EZrK;;>s=T9of|NhwzpftoT6SY}hT&P_`DJrMMZ2}w zDPoBErW#j7aV7c2B8LusIODr1mj*J3tDl;R^p8*!uv$etV8VpMnrrKxR~J6s59MEJLRRMCv1CrWE~M6!R9bS`GKZsSl^Rr2L`fsGsIS}b`(lh>Yrcz4U%lLt zaCf>-NoPm!A1E*UP#5uASH=liDw0xYTlCH=wuoL77byqSUhY)f5mG-1&(-_rX(0++ z{{IO*tt?9Bo5(8Fq6G_$cb*N}JZQvui5grIvh7D@C6YLP<7X29p=CKML0-l(XX zDWbO!+J~f5vpK%gn>Ho3?S(ChT6&yw_J`yu#yuy>87XE9+Z!$(eH*x1WB#h?S{9nh z?1mHY8zgC=UE90f>^%vTVx*cjAbaP-Ws9fbo)MtbhtM}2`|qM zg{@9ih=(Q%9mK=e;j|v_;Q3tcR+=2r?GgdCorZ)}*(0pewsHvz+pD z$CW(+&e@(*tDif@mk-ZMRjg>mK3(Muhb{KVM7s5Wr^bVkY;OX?r#yllQ88LaH44gX z#}4M*Ty*i3K5+d|h8RJAQ&O$UDF@Nl<)&=T^6zH}h^CfDj`=w8ZLPJ92udlqxZJu@ftmm86<{`q_U*^B$b-#D`$DXQ{kC%GV zm2_HKxs4lmAZtIJ^H zpUCn(7`l4(Jd_Ywxtj)VKWNH1LvdlVV(A+fTJ)Oh9@8Jw3bT+YUXWJne}26*b!80F zvwk9+ouI)VMpMH)J}l{PT4=Va+PNa>)vK5GzF1gH3}#=QcylQA(g(_uYUxQyN*?is z<-Lz}3yj|RsH;4eTxP7X=$lR*U;j?Ee5L)BXZtM&QhUv%7rwH4cWKp|cXqUPGFXdg zr_hbxxx-v**Z!vS0H{3#gK| zILxgFEa?d^c%Ms6?6@zsdc>>% zkdkLt?J8f&A9XkC?Zn%;$Aj%h)@MGMyy18IW|dqLmxN(k*y{GVv&?8c>&bB9Ds@Ad z{9tup(slho0qQ(bYT$boc}Ko!*!;NDLd8Oui}-*DJ9EGvD8oIDld@Wc7RPlK5DOOyEiLtq-f0uSa$MjeOJ3JGsPMr-J~1U zmMsHdpC7LjuFeG1uL!5nOnE*fe3iy+kq=YHi#;^xUB@9rPey1eLUbq zG_~rIVIwr;QGVUcbVG`GLYcz%&?npSj7#NwaTkYRxBTv1s>bWO_yn5l+Zrchoio70 z+lA#1mOe2DF?ndjM{JpKY4JYMh={DmRQU~u+WCkw;u@l@%6S8TAzsEE8=ADBYU_Tv zS0z2{obz*S&EeFV!|7^jcqA8)RnxPXD3a!B$0rkx?&X z2f!zu)Vx5`^>ZK)h(5E}bo-!i(HkaVPS-P{0J11=Z}W0T=4km7u9U`DE zN$LLmsZ?BT;bD){r0i&_z}!@q=<^LhCwy$Y&)JWxN;BzuQ6ssO#Gye3qC1l!C+l-& zD@;sc;xdVA&Kw4VzL3TdkrYSQ<+hkeL>)Z84i)yKsgF`@psMSYVvPtSA!4+ExH@rYM6 zl6yMDL5u^5fL*|cdUI{-7R(q{tLSxYm!U~Tm(tzHx+seb38>;lG`mtQ`dqwB;j=Km z^r7-Z*vWmGnFEX8B9DeLY0J22BAKwjw3k;XleHuT?H;s9o_pw;9GXI!+v$ug?D0`& zqbi5k`q-F0e2W^C^@uu@UH)_`TdydN2zJ9RG?RC8M%P9LvkJ&e0c>(s05&jz7a^G` z=HB@{)xai2t#Ohy*RBrCPK!H&eaaCG`d(?H> z1O>ib{W6SpM`O`GPlDgP`qN zsV{9ui$_*ooUogEul}mPyJGg2h7T9(X$ARdfTtrJ$i@}+h>oGDDWoQ;gak>NBWq1h zhZtX*Ea8fb=IPbG&*{hCdTfRfE{jmE_(3n|s&u?^!Swjv13rOoqza~7l*X=8IC&g+ z53=X3;+#Nk1yz^a+1Fn}*{<8~NDsrLELT~42T3I)fNet}kURXP#|NjMJ^+)bWx9n) z9wLAG74gz{8Ey)>olk?39alNw6RU1~}5T6pC)EKKjh^ni{VS%VbZoEySv~X$Z^_Yqq3#R1n2G+TAV?CU5`n^;7!% z+`XvxTznc!a!r|p&!G?#vr+z8vctGQFEabos@#+Z&g?6neBYa`-m4H1BHv;+zsz#y zN?(?Do?Ekt+>wRxGA&HdOqH(YLd(@v%k2w+%KPR`T7$)LYf!<@{dz1Qtfzx*k(@1( zXoB2x_$c72`d!4^+;O>@`SsqVqwiBY{EeQ4xjy{yaI-gx9I3}2?j->--?S)an@*>l zNm;!E8SciK{#AxZ3fhVK z=&p+*AA;@CJStiKY~vA|@z1^5tN!z*qHmS^dt^<{N=dQ4cG*Btk9uBc7Cqall{9bU zrg3Pjqcki3yN*KNW_!YL(n8o%M!-iPRX0X`uQ-*N`y$M&u9Pq) zo^R|M1FWwFptv_WUCwmWPmSMqr%68aF`>xbGleeLWUk086Bv9{cNi*Iv?E5dMNcdM zLR%oa*{jPKmC|aUAf_yFH~byd?B0QKfAgVIuHX-kuLqmI8alsDbrv>63v>I(v)_FM zsDrGIi(o-XIw5;{{RwpXt@GyMPG?-+%+cezS z^@xAmgX?iv<+urW=@7~60>wNo%(g#y z_KK-XRo8vLR_rm-XH#O~yz1fW4>tVVPz#s zcjtz5nx^;6*uByZP!h8F>P8}|dCHtrf=(DULs*rz>E2V~o}lyM#u?g4@8HOgnTEq~swR)6ovIyJHhnI_cyb@(p+US~Z7wy~B`IxY&;h|f2`7xt- z+ir6@YM<=}ku5{;MiL-Z)QuxJBr#H+!N-H%bQU6A<~`3W<)3^hyL%0)zd8MWX8hfB z86s^e39th8K`rZV1T*m}^0!z`FP;0x@n5_KC`3jUZaRS*y5SwDDaaqrhSasfPj=0MC&J_fj@{ql+rvKRy z(;gF5@-lc?pvG~i{k&8a23@P#?`KS^?#6sdd08)WSON5_>8*m+kJk=w9SZabb{xEZ zBypXz3Je0D8r_@cJ=7bYi5?+PE$)H2AKkI|R0z@_2c|BVp;`difWv zhSkTDCiI88Y+LUfQb(0#clYpGS+Tqi;VBbLkc>Az{4J?E-{7^xaZUar!Ix*6PcXd@ zzra?O-mo~9rNp9OVtF5SB21~p4y1xDYjuQ3`U9Xb5Z?ig*L*36!j2=ozR0aWWE#45 z7Q}a9tPIsR{VHmx89k7I15Y1s^pdC@VOcMuG0Pg4tqcMTz=WniHAH&Yyheu}0GTHg z-rJEz2`$I5N`srPC<%-1x&RY)0>Fq$drez~1r~+;}jxxR`Yz6e`i3U69b; zgy5Dxedb0e_gU}ch@_==>4xu+iphf{m%~*lukNZ0kI3`(M;Lb=t%w(55Kvf9yirWZ zg5*#8j9b$3?iCTWEiLclu6jXNM^cr>gCuZ|1{}7CV+}~Fs_HdNl&DE>{h`(RqNF5! zDjB8B%06D|lx+{V+4PTd>+~1OimV3}RQh{G3yhba%%pKb(q-eT4G{|M!+=07gw|7y zT83}0l!|FQ)Fyt0zZx*pzS!uWnIyBm8aA#HoS%Iu%_WpJIi>OO%1eca55=ryW^}5` z{GqvllSOCM>$)CXiF-9Ej+B*I)!4G)ZD;y66RmE$<4vidUTE{6sZ!5mc;~>L&4*Am zwe<(pNuf&Rysh@J;#KNfIbSvc-`Sw)9)ZQzDz&$V`ThGOJJQ3Up`lzym=y77(U2fkEBCxfyO^Is z``|~Hy@fDc&x1o6*Owf0o)4S9+p|8pD=yyo&3gE!bfVSLy{7De+TsICamtdKb}I5U zi(T0s*S^Slh##0Csy?xQY@LD5uFhn0^?XZOzE`L6!$a50FP!>pr<{-gpvC)IZvZSb zP5+_++M3iRf@xo>9%$&uXqO{+-M8!*e&vH}7`DVUgDG**p(60-Sbp4qPV=^g z=wd1h(}!NmYxWSxH9-8j__{SlNd@n?+2TYF=m%HHeQ}u36OtU{}^3>kR$WUT1IR;2Kf(+OR)#SaFYYRv&MK z_0383G&r7@AL(!xa@2t$;uUmSn&2E1=2wDE(|S%6xVrkB5*#Xo64Yx=4>5PGmf?z( zxcT0+b`BOd8NvHA(vk1Gu1_{P)a#2Y6H?S>mXQclEVa4QyLF!DaX+%!o#2zEU7S#G zU-L(AvvZ%3(@KToOsML+u3ql$Ex@(|VQAB8ob}2E^f9JhED!$DY1Nukm_N0UD1-sq zbVO0@g1S%AWEX_&b2mzirb?Qg*mA5d05tpXhL|@U zy9YI?A2gwMe0Ofbwe}*76!5+S!`GJwpu!U}@HYF@fyU>#tn3E~5KrLQia-&y+^p0O zClKyOFbXej4Bb6Y`2Hc^y+pY%k?*9fcXew>5<JuNmm^|6mF(*JMcWUID^JM@`A$F zH2dE4>EuM+kGlc$9LWY&@d*>)eg(CR&@DkLabh;(`Gxz|0uNb*blouibjrMy@XJiE ziiltVeVUfjhWr4<3x&*q2glv=p56GeRx#V7A#T1TveX^R_Ek(NPn3Ja=bGjl=alZ@ zM4e~^XD&pJcY^S=?HSF5zLkoEH3b!>#L!sYUb;iPJ2x_jO_-+6j!KeZD*tphKQ zHKA}05@|qzO)_(A06D4kW6veCoQES|0QPXBGXjxf93Yj{jX&S+*o(+;nlHEX!8E4rw_ho1A=RqwOfX_~*uR@2N!@!_{!&|3$8qn{y0)dLRApsmuCd1z%xJ1Sc4|)vMId5zcvLmn z&&|#nG?ff}fURpkiWNV-DrZAb{r1o(X7iJi2a{yexRZO-0O=xL%%owEtu%v4W~!4_ zdwcRBlYAOfhr)Y~uL3WgUL7Y$g@SELqnS`F;VVuz4?k%6l@~p!Oo{}{iCg8)2NOKw zrlx@L+JM)kTek@B^!kA$JrcykekMIQ5sdx70G}Pc6d}Ggahok~dX?{$uYVx*Y%d|? zZt{~o^!?T_DH1O3W@%_Fzm>y$xtRjRiByB0T;@~5e?k}1L^s}Mx0Q-v7)g6BzW_0D7x%riaCwqy83&t+UYB{)d zPs1#a?N4AP^?MAeOgYfRzb%q<@K_xx=&jbQbP>=jSQ&%&3Ke)OL~&+j@2gGA(UHVN zcj^?-DAD?Xn-hN=X{hL3Fq%he%7MT}q`lH4wGILz54VKI&>8qj$b0A14qHV)Y&|0V zvb2!~qPA};#ix&~mqBeueqiYIi&AUO_ZoC}zSc#kVW8gY1cqZLb>({9ZSNR%;+i_; zr2yI)knVko^y~|mb9k2x^Y)zYf&?T-2a%Im7b9xK`lIu)^sWo9Gz;n<2q-L$e}1~l zX!mQ8waW(1h(S6BcX#Gn6H(#aLz<5qy(o{M1cAopPcQSR;#(kRjmW&coPySrpi7eM zP;&5Ku^bCL&+E;8iSOMGwYzeFzE4z4k_ugDQ*<{O;X;qO7`)iEr}oVmU8bW))j|cK zxZOWsh(_a+L6&6b7q7nyNNXeUOaZ{D{bh$U2+Pv~=`uLvYv?eNyP=PqP5{Q?jTGn^ zyQ(QUo&(Z~Y0?uTfXW5wp)xn@l&!pkI!;`*Vf*DtOLmJsv?(zk^V2$7xI8D2bR%#T zT1(8m+MudDa=k>aZIXuV#MR=Xa?3ihD@~0qT7Ib=$ z`JyYa(_{U0#t8tk$bG~^zfpsQ>nx9KRToID(?O1k)E_!AkjhKk)@f}6woYuW06b=? zl{J@j|ClhRYU#KvCWlAM)!`iL(GXXu zt;dVHND5}GX$od^asU90_9nVvDV3#Ow6!H@H>3!HbeF$azmGUk`q&Z&E+|pNqyGS@g}vP>Zg`8d{sFbpX)+ z+SKx%s&4?ISJZ8jE;)HZvV3_h$K5K)?CntNi+CbSGM~VV$+$p?48#&@MKAth36Zo< z9$1)ZFl@fVZ!Tc8w+$rIJYI`?AATGn?Q*SGIMHtaWo6;IK0<@{43Rpst1$;rxn#gb z`^7f}vprF=Lybi>Lp2qW2D)2Vfa}2u&o+n?kvNTx9Ghvm3hl`YOEE5?ERTrfj zX+^_|R67>|eZHFX`VUex99j~&2SN!P5QUBeq<_%!d=$C^oMHw?ZS^CB<{HSM9Cphw zCs91ep&Wc#hh-}5;7(nQ8_WTo$KlgC!(?*krbf)TSJ|CJwfOY((iJO2TR$eOvb??%G>l#m#ETwzZ_h+xmd z#$aOD=W@qkyryZRX03*_*2>rBFnd#i4j4)2Xh+B z&Pjqfz#<0;RQBxmI!f0s0W%?CXT2yz2M4Rc(FRIIXd!6V%Xqz~Zl2UH56*tk6bR4W zOC9;P`NVrbSg!|-LzzXP=F~#s>CdIat6X|7k&5$)mqk{6Y@r;h^(!tOai>`9J}mKM zaCSlCqG{il+~=HGqeC60$?g`*AI@AEc#^d;hC5K^d$#^}4gCjV`qCb{={J>-qbAlD za2=3mE=7HV?j@M&Qrp9rpKjX*q>f6~wM3>;9cKLV)D1d%FoQ(@V>5`P;6MF9juU}W zrLfIffR3DE__%Q1yUZe!NUA3NVRmj~*f1J`WhTDo?~M~LFlbNMfI%e>^L@7u+d{Io zX)wQM>XA-INs;JfX5pE`FDk4l3#7C&dv%sH$6F#FFqF}3Mo?v&13o=4Ij4kCeaxQ{ z7=?2uh=UU7yyJOlOuE2;21aa4C18-Ol!K+ltDgIIOTrbgDD4ToWWPL_3hv9bNm9e_ zM$X8Y(D-8xYk^_b0|?B3bNuFAPh!N$=3FB>gv=T|HbjYkDAIaSB7qJWdp&)>6pX^j zE^<+(5+6P>(=`*INRUA1(Pdr=e0IbvNxd~Vwl!x@7g$8ev$neZUhm+O%(t4)m`mS4 z1!Nv#85 zw3_fxe>#KX@ass{UEV)a?A<_%HjW76hv?H_Jp@%GKVrzljr8|_9+BqW# zcu3Uw;ISetx)>Oev1;})fXweSJeJWp4v-n}LcppUmbkNTAR6D!vTB2+F1Y%c5((e> z&0qpqoZ?}!bjH9wBN>I)=wjq7kTLm>K4jcy;M4KCo3}iTU^J2Ba7BDP1z1mksxT#F zwXlQtAAljOQu*gj)2YCx`-C@6A(uQq!+7-vo{8gkBd3he*cnHnqmfTUS63t|-QZ%6 zhOKdc1S5Q6_%x^kas}**GzT;DKgnFev;O7qiLFq-2smMu>3f{Z`g zB!tR>P+~AdyaQ}p!WOcCTCHPbynoNs4((4+cXOWB(AT&jYp6!CO0Jr`7 zk5UcO?&93}iLQgxa64TJ+`>r;x78;OotwJArN(SOveckB1@_ifoEl2 z+#_?#GYpa9)CYA0xF#3zGKPLtJoTS(3owsLL+ecC1pK1Moq*c|OA&Y|C;Vry>ijcw z2Xg!v@xAI0L}H>4*R@PgIZx(?Bupt^HL;^O!Ss(+J>k2GuZXmTBj-=`BVt0r%6w6> zQVPc5P$lyHSp_Xdtgnk%y&{<(Vlu9{#~~6!_qU@#cr3PIdu#W)r9OUO+B$?DH8cH* z50$Mj0bN~p=3t3BAVzgSq%WCuJW#YfB5pT>!Cm-tF?s|auLjW&#p5w(R3x&;Nxi`h z?V4B-spLSYqhk8aUm_7cF;IMU#awz0((-`gya&2i8kQtFTE|YtjeI)vVcyGg6&{8; zDAMaJe8Qp?NYBF7rF|z(!Xd9gjm!uyo|7ym3Fe_487PT3)GTBRJUYwggnt>yQyb^= z2Y8Ml!)1L{q{O=jpB_{xvY>^Kpw3AC{b|yiaQL-Cg!ikT#?J~=Y%EfqPYItM)PgFN zZW!aKqY-m$VY4D#NrNouf{C%e=OOr1ubjLWzncXvx(S$6qfaC53Dbn}b04#%D+6?J ziRAlDe@_m?bLCkO9Y%EJX8=md2b|}5nq-KtgA8ZKWKxBWMlAe(V-d-Jt3Eg?Zy(-9 zim3Ei0%0UqB2j4p;~C#;rbSJHaE%3t@G0QeA!6ps>qggq3NB8j3-ig)?SoJEzuRyE zXad+*TkvnUlgJTYkynix5o7&ta2Nd|Fh~}gFu6?Se)yEk-MkwWgb*@Xg_jJU9S-hm zQyDVg#@Z(sLL1>pQd1qd*IA&24C}jP%q3&Q*J{UYkXqjXwm7uJT%tEXIyd;jM|R*X zAX6z++ ziRqQ^jgLv}!-WD+n-Aw&7K6dSOF&wC0y!>ZmM3aW_ z&qqBaP9n-J9e;`}C&AN!w9Wmg$r~_pNJ&FIn{*J{5)1=TBXV_N=n60T4Om(WA{$dg zqa^5 z4jlXC8qWN|UBCO(jMyTXbF%0TWOq`(=iT(rP0nwBvwdMZ&ytG-7>FbKi)U^B4)^J7 zF4&3v%{KHXP9(I*yTzyb-`#^X;Qwya$`x2}raps=pQiw01CIzs)N^B(<15~ z2(Yb@3vJiv|J^8<|5aMSP!sAr)%PS`B0N`k=#C=;9TSoPrho(f`pV00es4U=bk2Hv z1HTpO{IuFXEz1Jf6RirF08%`2uo49_G?bVc0sO#+1%Qx%U?^N)UAp7250^^WipH=5 z_;6G8KZgE?zja8w44ze|qN?}y*=0;VsW86gz9Aa&!4}8BSr`cudmfW>>Ql8jmP{9M=H+|)JeULn>R#o;7G6i=xP3k`ybg5 z{`9Rd+c!uep54xr3W|habyWmn_Fyl6+&_IXl&f(Z{aWPtWF01E`UCG7dfL(OnQADk z3R!8tg_i`(G%e?2mgw_es8Trq%;wOe^S$%S=}g$=q0lK5EiF+zuLR4(zszoJaMB4K z`)aKlF1NDsaN@{-o}c1c`y=L;$RFfr0Et3_e%uW7%mm_grs)vj&fja_?U*ftK#wgq z9x7+7&>N4oq;|Tv9v>`G@ZOExfE(<=JHlmgkC*2x_6a&%AOSiayZ`t_Fg(=|p(@}F z*n^jj>}a&kQ0~Q1%W)4pf!Dl^{lXF_9Bry~A=7bYVC73jPIbXnJg!bYU9K?`5a@Cs zUCn<6#|DX%XEW0~u4IvIu@_MXzc3^Wd)3T=Dg?DOFAe1}z)gywzZ9~$RV$~oI#%_3 zxvuKinsVc8k;T!n0NYPq|9@S03{YED;6WW&ZPuhWK5`Eg>MGQH7L2Q$W)$3 z^?o5!XUx4gM9GF@?ZN3KZyErqt4wF)Czr=vFuWwVH9NXqkQ$+0ibq>fE?R@GvDwkp zJ10%7T$WHH;+H)E7*u&+^izB}KC}>m&=^#x!^diMPNpYeNQw)^!drzqBsBR>2wq(D zV_}$aKTc%((ZG#y%z2X-1)%2YpOO=OxJucuH^}L5@F3R?=Iv^cr)LDhmlq-d#CU87 zUsMDrabk-0**AR{nj16_`B!@p!;U3VArR?mzy!x(HIPw|z zdRElzSuORLafmxK_;l|Uo#-IY?ogJ(rP6UcAx8|B-#HC*2eRwUtgY$CvTJ*nfnkY( z3*Mx@g}xLsmO-9y)_zqAA`7^#_pw(K3-ZtKm-&D1+ueaBM`D5{mTFJLR7Qbk60c3;rG8k|aaM&K8FdiKtz*}L zz02SU#Ny<+BZPR`2rulGDNkaB4$>EA2eywOvEY)3ezVAQ1t;gl@}4dSH6mi)Z#Q-P z66#g5B6C#PUA-mmU(nN675|!>E>Zu1oat8=WA=s^B6jTo24K!K#;Au5Q)St`^bhq{ zei{)dK_i*XbPd0mqXra6JerC>h~@nsUe@?Oc$xo_ot0$@|AC-!8R;NdeXLoZAOH?v zPBFyLJZoQn?_0GYR05cyy7v%i<=?@L-` z6c1*zhf(9oMO1f1N4bw8PN_9^TTcz7-Qc@~lVXHC8Au*?l;W~0S`$f5sj6?I&hAOUaQptgsP)%UV$u`8F&xDXHrvJ=Lpyck9P0%_ zo>TZ4$Yz^0!hjk_Q_`S6^s9ZIzTv@rH(&xYl%Yyoy%l{u#xHi*jXfj;w{`@H2b|LM%r>_S5mF4IzE&fkz8kbSkg@LU^OHL@*A@#JKMmSUArHtw> z_1Sk>9H(R$If#ZZsqM5yiBx49D3zt|sh!Z~2Wx{2$q(M@zcL+@x;p=XQ3L7tatuK- zVx9^$`_+SNYTmh9vUm({QdeqsE+$8@MK(pG@T^2C*#V%Or94z>mTKF<{!Tm@Y=3^Gu zpCVx-e{QMH?R!t68!T^i;KLxCiJ>Dv;mfIbRv*X79H|D#+`tE=Vcd7zG5&+r|7&NK zvuQMSzH)-9wt7a~l$-xPGcj*s_^kX(rUz%qBOW&~wrV_{JA2luXMwWZ*NE;)wm7^8^V@3`Fw)|12O9f=qb~dwF3Vat)d%zLC;L6u zd@LQ@1F^4kL;fdWh&pVzMzJ?<-VlfhA#C1+xm5&5++7)tPJGqkrLk60z(56u^^kvWU`Whe6^ zcT;Vdz&n^_V@SpVJywK74QOcp5s3k#eT*7WDhdSqjog060tB;;qlmN>~A}{9^Fq|7Z!v} zGfc5j5U-x`S0bh8cE;;JWxzfaZcvVf^;GR+)_0-a(^pp5K^o zGc={B>Qan@`xpIZ#$4*grAVShP zAiSu!cs7pQ$as%zU`~NyB6P?Ud@Q%8pen&2r$BD5iCOrgw;-+uSlSygQ;?`dXwClE zR~pMFWEfW%W@xw(jaHPAU>Ls=SB4cFDL-Z;J7Z9d|t*N_WXn2kKs7_74#?U`Qmxp*ELw$$A$*j4P!|L|GuUJ z^B7DdIr|8vV5q%41qz^b)0@KwFq6NQ>$#Hj_Pqcw5-EBGyOt+Y*LMOX39#Q(GVZZnvU3!n3k6Dx9J@ zAVP079D$?B02+k8rl%D1;y^r?BAz?h$S8tBiyiF1 zr9??moXkg~%%XT6k7LaXM$#)2!8Dwg?_4E4rVE-pa%jIeM)to4dEVpcd0%iJ`(DjL zP?pY6xk2n2dq|Z_Z!$E<$oz>TI*kWg7Z~(!*xZ$AK)7P6MX)!dONJ{{}dUSZ$d%I0LW9 z^637XZtRNi<-wImum%PcEWxm@FCWq!#xtdFfBgCyCLW+;G6|(^U-&)*HXYY zUDm=G#H8IR`tr5Wj@UUCL11Aox+$adlT38dFqcEjXU^j1?T5Ks)xyV`F?0+18Y2$} z*7kTc!P=6pDnf=GhJDlp4=#=Oc)A_3gggfSLKH^Nh`I#ct)7?T7S5`gffI;6fTOf2 zP`ZhiSr2QuE#bjEA1glLTs++KP8s;^D@V4^3klPVV-85-{HXxAB*i^g20h}(TTn1P zM9zg3ib8m>)o|bUZ9j757*OMNBHCXf66X0_@!licA`asPGA0vXO|S9;@FMvE{!v&? zBC%7^+;w<00FG?}$S-rS!3L66-2j)$o=s=E>ene+;(>aNho=l2|T;LmYRU!w6nZhJ5ci0Ip_Y zjYS!J8|)12oj>Far%0nqAIuMNUUIofwz@6$}70ffeD3{_y@; zgy5hLB>(8cKl<=rF`xg(>H}VZ!rwMo_jrD5_$HeEuCeLA9Pm4n^?#6=_=hYQA13jy z&h{^)#Y>N2d?%GfNRZfK2fNXJoOh9+gTRevFV1=XYXo2x^|!t5L8IUL?#`sY>n|(c zQvTNK+K>F*z{r}X`gi^kPNe+1Aq|k_cY%?2LGAKzLZ2nkFTs%n-7W|rgs(^v;meak zMn)>~4K{&;_JLSp4jTlJ{=XPY{1bZquM9o;vsGTnE&qx%>Un0rh3L-_muy56injaA4j{L zJNOGn`;WBz|3g}ejaz=ZKdFp5{5KOb{SPkuey^l&cI9uTn#^T>pT}@bz5h3pXIY2r zew$O7PLlbX`JI0V^P9xif0ZzmOPqh#9hJAd`!&Vc!tr0(UH_}}mc0Gl)?}^SV_kzF z=haTCM5AD_!Y;~R^0%H6B#`B_eRqoz9ZuJOOYzs(Z1li?X>+lM_fJ~&`+5CUMjEE_ zCqcIQ?Uw1Ua>>PXVZSO>iuP}Jl7E#2=6v@j1s^@|+nwZJ~{D5D>qLK*vi}!X_ZM3ESDVuRiv;H%VUc(wBk_A-aW1I+ zC^{8q|M_Z^mC>I{GQt3xsXw~sGM(}*&po_MSL6b4Wi)h(_J1jH@Q*Fus!*bTEp+}v zmj9S6e?NWZ8~Ur!{Cy^xZF-P)Q;6eIULvZ)X*j-_vvj;kmxy7zOfErp5Hi6pu1Hbi z-#7^MWf$K!ve@+RJW?m$gVT*ar|xx;I(dEB{a@2E|jDL!Wj5bmv)1aveMkb0i%qOda3ijt(Q6=_<<8kGUstg zYrrxX!SoKX$X>(G1N3C9ng~7)%TNax!#DH)Hb$R>s`!M}WMCY&It%wmRKxW6&IdK3 zuP{ONKK&-z7ke!l@{Rr@|GrR;V6u9;sc@)8q);A!6w2eMACtY42MVc=kP>0jl-vLc z-Pi1m9r9li?QBpfE`4$0#xF&CfLhcG2}?1AL;!^{nvPOpuNmw_00g^mwf8>*2sGe% z4z}07{tS`ud7+LUYjeX#hK>y=bSX)i8+)yi2mv$f{kJ*OEO_4Jx88TZfEn0<9D`rc zffDKnm_h!I0gk(e5P>;@6Bs$v13CE*k(#*Q72Ffv2AljDC-RtaI~1YZ2j!6Pf6F1O zJ^x)m1DLlCMREpq)MrFmIGIpC5gz|ZmWCCU)BFZ~Pr@Eqj)K4E&jk5@P$h~%P!;?< zRrDe8FsX4BdO936Wqz-l9FP7rEJ{X1?p#0)`U)Wb1WUuwepvZPA^T6NME`e@MV|CW zt^P)+-t?`5^U^(L>6uOEMJH6HzF*dX9RFz4)8JDg@y?^E>z~|4FDA&dWfc#g0@{uu zV)dvV^^wK$xV77?M$fy#j4K`DVK zqm@5m6qq~7?#Oi5fBkd}E;SnC6)43%7v1Q1P(sPKA=)ESXLev`+>b1IBD5M4H6({dXdxLX>a|*LBxHs^$x_G_8Br z4_5p*qjfbx%~*8*Dq<@oY6#+#N_g-dr0F79k(=+CqExSaLc8=j4cqN#JhrW4q?%~Z z&^?bI)bhA_)-jP!_(?NZh|XTy56{2$+_Upp{bw2vi*vdGhc;=(me#t5Pes{x7~a{~ z8!S3moL?{4RkxI*he1?v0N}JM^t2#<0QKm>Km}r^WlbjJffW3N1;6ar^}!uqpX3xRo)f4l6fD>YFhZ|3bc&l+*as)# z#MFB{bA(b1DGYN}@1jp;k1!?j!$t`x(H zT^{g0znjZLQP1{pz2sj1cA`RVb8T2Wz1<@3Ju`#8=#5H(i4f^^-q`9SZL1edZkwxp z+201Qd#rZtcM4ek(Rk-n{))+43JvO1@phv(V_7d}KWxmH`$B2Z!ic9I|oo%6(4=uw`OA&DMYIS!Mjm{%jKHF z1f`wkQIKa6eCu$-_OtPvZm*857-hRoaBj%l8L^X5F@pk#Vq!gv8F<`6r^`S7h)Lrb z&{Z}yjx_hNtp0QXxxwHIe#@!N3d=68!hi>o!u+A90}@udhnq-5#~b%96%Pg}bXh#F z&;PRdT~=KF)1&SOpQfc413~}kovQ98rplnf(y|!Jz1!DS7 z#S85f8wH&Xs(h;Bw$0~GB;DgINipkPX-?Qs6{tSl+*VC>J#NymMl?u(6G7xZ3PcSG zR>_c$!pn#fg2Xp|m>v@BwthYTDcJpHXZ5G$BZK}*u9rnN*XGifJl?YD-6s*7Z!|R$ z9?yB*)ax>(yMFScz70vVmFQpqUsJl?LYjNn#I$>?W#J`fn4)g{-mdZ?<%)pj;{AQk zmm{U8j^qIeP}0b!xE+b`lv>;eV1jsxP4u2j9JpF(V z7Y&(#V^I}rS1D}yU$t3&vM%j&Url-@zCL1^5L6BEp_kL{0NYc`t9_dfgMNv7DOm&;KMt)jG-7XY_I|eoAr5V{A{LwK4~ss$he^%>&7At6v&+y%TJB zlxozB?uyr;Iu6E1p5nXnM03PFj;G&DNCFwj28#o8V6sZhUA>EiHy1yfD2e7@OA(u{ z-0@8JmBpzVzvQl6V&5g#mx3a63VZCkOpW{#;+?+5v^MB3=0z=VQIk~AsVg|GkLf3z zhf_B?je3SS9ptV%H^(+X0L|OR>icwdau`JJ*g1nZnGW;pvceLZM@3qn6^#Na(perl zUUse`f5?5ch&hNSR;J_HF@G7aj%0J6^&0Vw`!Jn^n?JtX363c2m^#Dd{7G{)vg9Jk zLak7s!l(TC$fB25*}r8L^@y^&%{dl#DHZJm# zaZK)F5jjwxgqeHPbrvXHDw18(H)m5=eEge0wo`q$ZmdwX#bGLsX>Z!ZXJQkk>y$8$ ziKgW%DuFrYzLxkK@r&uVydGRz?85f!AX-$J0V?$ILB^wl0cJZ*2VU!w*G%K(1;{0l z6$YI24+XoWd*Hj-1iA23)7!`kwkPk%g!;2gIjSbEZxV0UB*zk;-;%~R zCsr-RU$ya-MO?1ydc}GG+uMbmWg9DF&n_y7*=5Kc3nsYA0=9KOJ@ogKvFR^=$p7}h zTs?iIns6=`<-X=}-DS$9FZOamZ`&K52Ub4o^UuWN?ZGo2cXFN$ONe_S^8$o6eYr!M z>_yQ<{v9>MVuFZE(9|+$S^oN(cEj3^@7ncyVByLhu4sZ{8d;&g&Iy}<Yz%-yMuw5xo4W~`2DIkNvwFXi#pBH_bajbSvsoqGY5UT!Z3dBL{E5| z_Vu!jwA$RfSy}OwPsf8Rf^6dK+Af)b?Tg`UGtYWq+}XOKEqZ}C0L0V?9UziG)J73| zz1(S~<=Lr%mWxupW49gUKN#MjY4ccuVZYj?az0dIh8X8`7RXc>;!mn+_mysW6Y%-5 zV0>P@h?q&cP)~Qx^@sTY(cRhCD7Gn@`Z^ZZlyov1Eb z>|}HME=`*zwktAjwWPx~MPEjM)3S7;aHNi}A)_kTxINfy+^E3RFV_BY#PeSJRyB-!WHad(5;q-~cV1*QjZ?m+a2>y5#{ z6Uk~Cx&@m@cG0?8ciPm@nD_Cik4?HA2#V!+n&qE~5to;ot#9YvGafdKeP>5rbL~cX z%AMhLmTJ5$rNBqmF+z-4KWCJ@trWE}UeL(&KH@CI&W&nbS*@q;4|D2S91^Ioy~gNI zDJxKg*n@1&mv0XQO3e6;l~_97ZUw_5U|BMf+?oZt_+F+%b7bLjgS)*4<#qSD*o$iy zi*%iaB21qK3XY1OTE5EVa!KF_(jHYp5A?VHVSJ}J)nIddDSOCY7F{zMp8M8l)FnM# z&?Z30_#*h7)z1x0eHIi*DTg3NG5r zCvasDJgaGiz5m?CVe^b!)l*h*R@27lRBoqB#$r(&Z>{R802qnGhEK_^CPk=^)>JIC zX=|jvI{)9;dke2B*RE?^5tWecE&=K85+tQWq@=q`x+SGiK{^x^klrF)($dn+rlh-@ z@4C_RoaY_y@AnUU;~Rr<7(UWKGmtu9Z3Dy zfEs}-O?mz+T^)%KO(K6^yZIEJx~AdRv3ahAU;;hO%i|NT=^DrH;Mh4*tb#VzPED}? zsRja|8sMCAEh_%!*sCEqqNgh3E79jL3_nHE=KDWinSGQVd-aMIY-vPtR{LmOrmO)0 zD)~mWHA4Tw z2+v%YQ9!*|*RJY!w&M7ETKNHnoYaBqDwzi{$Q~y_b`&z*=kNsSyiiG2#drdmWT#_Rj-UmMz_YsI<-jMI>MK#&rQ%37uo!5&Se1|_0U$uFYWjn=o1>E;?*QV-s{N)FU9C<_Gy!wjP#D%4|ymo)8DQopg<^hyd8>rU|$TJyu z)qSJXo*NIt=jgU(>cJBRM60w&LHy#Xt?Ko&nwIb@4%eHR!;6d` zx%(XeE5reun)C7-zhBGRoQjw5)ozUPab>%R)iFr8fJ?a^dUJvwNcU7F2D5;Y32Mp< zp4bEOLw?~Rz?%!IPY8M*g0r!+&3bkd1ib16b;otv(-hJLNHqZ0_9PYoiY45-Q0p~m z4lesaT#m5mi||WGIV{8^%)vT>vT?t{t0~9!9{weZmIx9CJy4f(?hDV}Ry_h~ztl3w z2Yx=200BY95I}=`8Hg#oCZTqPD9HJJ0)G9u?m&O&K0Op@0Q?iGjwh{*^^>69#+m&R z9JP2EouX@0Ha7RcXxS@roVy8KOnffUI9d)$z4>pI`R9TTCPwRB^#gbnhPthb$I3D`eSJ{Ds z?*YM^3-^RT02K95@Uns6-JkEeXw6C%Pd?chZQ<~K-R(_%*|76JN@BiGwI@^(?bnRD zbB~D*_f-IbJre`ZRc&D)#`hqL2!kL>ayR#%#pZX~wT8^Q`)C+K zC=(p*ib%VBo|Qzrdh8j--Pu^KaGztvH{_lccnc0RHmUpddmoCs1tDw|QL>ktwD%wA__vq4&pD-6~PgJDYfbuOv14dv}$r}n^ zch7ZgxFDMubSC>oKqFyf-#T49TQ4pqCS6G7bPqHk>-w6L{<08PsJ3UBu(XQx5TJoN zKN_i)9gq-f{d`x~PCtL$)@wUuW$Rv!U7x8<>br^Xx%jI&F6Nsvhm;f@$ng3;EI1S- z;PJ2JrvaTC57&-IkS2pNjc-u91j;|*?UW02hO8O$p}uy@peW;(r>5|;k%~OLz50_@ zwh>Xg%40p4;6?|1MrypS>o|kG7&xJFEM8X8wx@P5G~T9kfFgz$-Y$bkO^Zn%#io3g z{KJOW@ILtLC-_zQOT0ty&2`g*Mt$K5&|_hmcAFT8=S;i7>ly+!!SQmFNnZ|38kz2x z0*5BivTTz=aYCs1v=;Y5`MioiWGP*=~LQ4m!gmo zJx*@S8&#sA>!S7&kcqQxkujw6FKj*xb>J;*U2mZ3pGds4uRCl@=YiK9qM+(ao)`n8+uB?qeOq>H` zV%$1@lm@iArGZNB)C2&W>7X>X$@iqa4}Mf$@kQi#%4q{PTCmN5W}^?NQ{UI}@<+(U z)C+td@OeFMK(ir5pc_XHa`d6!5iG`s*%p*$<916KC}ML7Eattj{U#Uq9ru;(A(d}h~@Y*z}wE6dj$1*(*Hi2IDUAj5S-|7*Bx$C-9}8J(+%fw`9;3_IxFa;&R3La9Bg5 zCaBSX#x{jc5nK#~D0%Y?A6L&duJnHT1|Vk-K4mK8wne#@l9N@%ovZ*TO6}Z^aJY<> z*s7dzUexa%dtavpIeJ$@o1m!EvQEbQ1O4Oo3AGO)Y@h7-Qfd=tn?SoD$Pd)kGE3r{ zJtD+d5Gemi_2#o%-Ma4ZxlQFk5{`{ zGodM5Zz?A3Y6`7>M3K0AP8!hIX!Oy?f}IW2qY$7w%_O&5?28QiuVIhlfb9VzcgeX0@alJ>Qo! zX7&=21CsfrNw=3$?3Ma+hbE26#x-5a?IL9EwLg7bXIqKoTB5?pswvGn&oMG+D?{OP z5Jv|eFEHS@B0quJa<2#o0qg_#`&eT*vSAQrO{3`A zEtg_#tCuYcWk3n-Vz~AJQRuq^ev411RUlU(D~Z#grLxz=&tuz#3vN1a!@8vnviCax zN`B5$dQ`sOCng32EsQ)7@2MdVkXIY7OJn>l7FGrNJd~{F2_1iBMGNM=ba8!=hQ;T; z1(2Pp5vk4pM*gLz)<*jOR2;D51T+kVtiJ{5v8!^);!s>A^M_!({jKl_?E6@J`WamF zmAuEJRvAVAMYsx)YGypwzjU$I6cG33wktFMd{acuMXJq>)=Fp$pqyhWo`w#ruNoY; z8Ug;B3bBhh3Xepf}d4`1xJZPZaI%j<3$Ry{Nyz zSzl1VJrub*Sr{y;*_xo3@xM==2%*O>$%YHt7Q$yD>XVTyc9Q(twflK~_R#Nalxb|c z_M1c1*DOpOw>F3(oEILhvCJZHo;D82^5?XxUa`eGhlu(2i@h#ox!(>Oj#m+CmO0k< zSY5~u=6eBKIDE8THu5rG|9*?Wg=)sxi~WG*T; z*FY%AYumMWwK+^wM+xF(!OhfxXPi)@piOKl4o|TJ0xReFIPJTvm9qf{Kkkc`9DJqs z1qaW~%076y2;ffoD^>X^NgTfV2gNPKeqF~C*2M=vIYcZn-w z++Nt@AF0}^0ldI=B5!ZUi)#y6&sBN-i5S}CGFO0!ahdYb?3zL4v)Rj)5y_*lj$Dt; zxG-_KXk;N5Aj)q>BWEK|B8^chhh%al1c*)Ieb>C?zA`&1cgOWA_+E9kmY?J9b3q2w z>juFu<7~ucZa}76qg5oOiFVqmT2_zorB$>|38YUj`{Nn1RwfVN@k2_@4lpwMJP z=#zo`_#6oP-y4*>{SztNK!oRC1I=)rpUu4ybwC3EONM|Wt7X`|89CHF@U`jOe$QYV1Z|CqqaKjWZzOMXGdb_GrUgtmfsH&0e>9r*PDe9MGbOi->|* zZ5Z6?%q2aC^7I5HL{r=NYoCwjI?BY_OliV^5^~@M_>*HvrQAH(wtIUnW}eRx0G(D1 z)zV`p+YM*K#ZV`Qee=}xL~&mDFq5FpV$mz1_6aDgxouw{ys6gws-RnF3qNWi#-rQ# ztOlsB{F>YbGCq!=7u!wvw4j>e@HuyBoBAk{7bIJa)*qFv0+@cG*-Tva{#x@s3{u-) zGDDAu=b5)3SFP2t^Fyfdkw;>puIKUliWNK)Xl7mWi^>@Hvtz_L^SL-T-k32$tVxTKV`FF?i{g5li60 z3T#y_q&9rU7<)$RSH|0n=ewT;Cj3#v~ z>W)#vI&Uf}9r>=Ll`EP|Hz@KGTG7y8@NXcsQ?`c&q8Bd-oJhl-Yp2F$io+_J+#=a} zyIiyj^6=0~`xgbL@!KQ!XAEdewfDq?qd_A-@k!w631B{HTRz)f%E&zGM84;f@R{Pf z*b_jTQ8jpt3N?A&Y@zO)a@-%Kxi)s73z{ef zV0GTUoNi&+(S&u*_~sH+@E>lN&Cuul+x`!0!oq;hI-~{Z4g2utpfdzXf`37rX5_<|6A_s!a`> z<&$|Io&k*ANAw0SCOmQ}S2Nhw9jcGr?%Z&@n$376K;tJgjlO($cixc^-FxSoHFAR; zR?F-nvg6!!_GTBD?;l5?CiUB3pSL`P-E<=o^=3h^nwMpo3NxPUb+HwzKhw7S^5J+m~ifghEtlDWp%~umD zuAC3g>!($JKkdE{wd~z3aQE9!w$3lULj@HbSz5FVs-mxw5P8*HHv)yOJ?-w%zYBBN!IqK@*-Z3}U;kXKnOH>{o0(HAGUy6M zvi*{r9%$jf&pcXf0`gE2YvH?xZ8!uM)LWWibgcEQD21?Ug>8gcX%gEPHBIo3QaJ6Q z3$0!<3o<&lBK=~_-fP}9cbBSE@d(Y3akAAJi!_#MeqjR(?_J9;PUi)@!#PK2+A_6y z?C)Ycy^k$tUk>Fsz{djK@RgVTa(qos|1N_0qqMYvxon|(@4@OaSq94>9<;xC0vnjD z;@;A7{57@&0-IGgBXYs-yeGyR)YdO=p%RLDb%`ymXq^l*N4{h_n|VMvvP zTv%rI|Lh@q1ork}4u1OkOYRapt6}Dc@%`yW{dtK;pl+DkFX16pgVjFeDIR=QH@_8Z zt%>0H&ydRN*JrJyfB)4}>}NG`aV)TZEjTM+A>3H+=ZVM2-KyYu2;Z*zYqv`b1wRDG z@QCiu?Rp#tw({KLAp5&KNYerAKMKI{`a9HnqeGd2EqJa+z_Rf%1-!1BB)6$JG7zF#g}++(!7n{cuWNBaG^K*;X&UmmdIGBLR}~oD*uMXbbDl)`rC( z0SckM^a)%1 zmUq3VpjY@^1DKr(rrM%3``p{O&cywGGwI)m$qB?Q62q*}8y^I{yo6rRz_gs3r-K0r zg%?m}z2Reqn_~1&`nV7Sim`4o3TT^t*ra32<>q+xFW6Lg{&Wt=KhgQc3;B0AjfY7YzT6}4?(t={l~bts8_#PkOOii{=KaVF|i=%5TVA#|qkT&?cZ};9xjY5r6*ZconHX^rBV)*w+=C)g(GWcdL9N za0e5ZPjK)YM3d6b0i%*yH;xQvj@g;1;H)1sP%34&P^6 zCqSK|KTYAbNyiF{&}q<59|ShYSIb$B{U}px>2I;nCSX$2Sch+JnoT!(`3EezX_ zUo-EczPUNaK7c4ctq?+KlN}j&*($6vvf0|WIn0%XcUunH`05?ohKEAn^y(Ty!2ANa z$%ZA7qThXe+>54{^d33l@jBv-xdMu_3CC7OjXKl@7a=m2A(d6gJ+ZFic_)0|!2Y6t zr3C0crd%U*4HX?w|Nj{?@hzfaIrVOC`Z|Y+@gv$0vN&7 zZ3vQQw+k%>YpE zSm@N89t?pdWrKsoUO*>lPG4MtWst2zKL>P*2A}Q@iCSgo5X*Tc@t@2F4*H&N^4S{A zV~uJ6o85P@Ye2FSAyXRjL2>i~+k5DwA|&T+k)5AXs*ghR1i`IydXIsbIbv7 zrJm<|1vGYtZH zOSqMPX#_~^T7~EpkQIdqJb=1>+|y4OK9ABInvhkG{5s_Io?p6YpCdx_X}G>!^a!>| zl8c2!$!-r$@>N4hk~3&WfM7ZRhBeQxXRci=6$+5_tLAs0{<&^7J0%bAWI-q%wBfu^ z_JFvW1o>>cy|x4&`yv3YxLaibvRnt-Gw>t-Hh2~(f%W(eGxLiFqK~8Fdro^NL8L{- z6V?H{Y`^po^uPJH;ypW$JldAnjFqR6Gg$y52JW8I>w0{&9Cb3swqPn$ZSZ+P;t_~< zq1MiiyT7j-25mN+fDUGQ_-Lh{tA~66WpQejXNzz{=Jk)MyO0ITc`3>K2m_PJ))#bW zWl3zm_kX`1b+1LEZcT0VU{HB`dmj?BrMPTJC{)t-;T=#Qe)2s9hPf9!1{ik5?yXD% zz?`>5q~U%E^1$2lqwhJ29@EfTHY4B331Hzb*FEJXMnI*V_2zuZyS!~iH;v>4JhNXk z5B(g37T-R3K19h?ct%t<2&GJh;`3EyzvK~3OXK~FsBnq{~>FjEE+pK6C8fA`K!?oH;ofJHO z+#zST{Z<5=7*H40rIY6N0>ZCPZ^#7&8;>3-Q7+#ro@c+S@R8l$-e+C=M#&Rfvj~X# zP`KsM=M_yVH+8WO1VRxs(U9`Om`8o5+a|H_Ng!afq=tvA&EV%_w6C=+*yN;pS`C*B zO!5L}qjM)iPUQacb6m(s@zDhN!w+f{{*1`BzSEi{;J|~Jqk`kK<&IbE_|SKI_hyp3 zZOx_UHIcpVMP30Paf&=0_z=xMZNNtoK-tBzOn&kfgvp!ueFAhvy=O$q#!GeF|4B(D z&k(FKc1>!YoiBn0_BcOCLHT~3+By$}A?Xfc@b5aRfeUF62tbPr1cAPQpyvSZm))ZE z<}*;F(ezB^!s8!nAwWjULpR@&cRY40;~*Ebk;@9d_uknp{$tn}oSg56f$QsX=x5Bc zr>3s;)YT*Qd%n_nVn~-x!;&RKgg2-GD1v*F>jmq%jHc7hKwrbWgNi45g= zd;1HrNEY&_dMF=Wlf#jH_EX>rP!-8b3C}dkolMTdqK?+Y5;3Zp4`<4AdUkOc>C^~7 zi>k=w7rThdg)%^9HNr#s#=ws%PU14s2l?4bUns+=ap(4eFwrvnWQ(eoK_x*-mvA{0 zgnUTz8_|PLzV3ZqGy4PmG87_5Y;A|5`Wf>bNOdf2m`)DWEWPF`aOo(*Lx}=@lI5)v z`Za%dqdsTtsSQ%)y|6zL)=P$RsDb7KU|^9|Y+|fqIs_*&wwyA~6$D;%zstSMx(a>y z&?@G{ygJ#OV=ZkC*0+3YTH=ZltJi}QqN3d{#sSZyLpj&;;Us)ACS8Zkr$grO$aXI#**QNobgRt?I!z{LtC<~LkQ09n6MJOn2(q^c}qmVJAcR1+aU_$zr$nd2eK*Za!6emCKpNXA8Lk8(5E%YN-C{o7KIwqk@p%xZM%aykX z2yo^Dl(;v$zbjT(8JAEQ??~wDM4o|UqBDHtxXVUvSqTae>u?YbK@IQ&WxOCzGHRF! zj!j~Hi54T4Ea2J0rvitn{;*W8UXM`P=xl4TMfRHRRJgGuO?TCDx;BreRMCQ_V@28|jwVxiEJ8XPo>T|SQT6R%U?qRzZN zt2{LVD6#PB8g%6|>OrvPK+ZW;R%$lzX?m?$=JzZ3-?T7(%*pWev}NueJIUs&W7q^b z7Z>kt>oF_jS%b`jJe!I0nruF+^7-u(=z14dz+n-eiDi_%3CY(JMiDI88`z3>Inob;>s>x^BrGD+_1e)oX{(kc43q|Z^?SdJS<&_gVn|3m{0^eHTZ&VXVT`vbIpfuxh zW9__b<_KgUeyGEpKI~xBizb@MZ|f2ggtcyhj$j6-Jdcox5G$3XRYcU^Aj{Jl~O8?u@C z(c`Mi?1M%h<13j`r6FrBTk}*$>ET~IX&MFf=YUvvO$a&xtG^Qb zq>V4HV$ARQauC!KRV|<02>k+|NegO0KrM1El(qoKSOwZ1Jkn8`yiKd$Q$4n`7x<0n zOMWfKT1;*5%-&bh!3kG8as!>3D|kn>(<@aT32#2!!x7*kzhdAseL&B672gJ*$Pxa{ z07738nQPh^OQ@}i_DUVYeqV&(AetbfH7jI2jUWWRTxjoO9lpOrLvi}^DrVlviMtcs z9;~{X?a^IOnt-mKJWqsl?23(j0a9rnu$m7~wz(nfR(YRD$Ntows>R}&b=FR;Fjg4! z*~VUOk!Y<4!G4ez*k1kuUuq3%xfaLp$}RzLbI z7)`HRU{C6xzw{lWA$}Nw+4K?cidYuV{kmze9y8Tp@v7r({$}dcby0k{pZ0OL?j?7y zWb8V>HaC`3Y5$JmR&x{;40~^u@xrA&dTGl-FN$s}sobI{yo!G;QACGHs%H-*H1D=k zGMJ3212<0JffuF|N3#36U3_>Sf4>9HVGsYLv&%1o=|j+d|D0DbAM!D&JX9<>#Q00u z7}}v(z(hdx5hc2^TMDGOFt~2Z(7PAX*P%Fqd^I zX_8%dg>wq22+MWD((d0va}8F3PqvM-hk-#b4{=)7*J8^8eM!4U*9LkEs23;i`PAc* z*8%YJK3Q-5P6+uIm;cMZh~zIfBpTrXGv%<#pkpgns6OiW1fIerVJ#)+7zq@_WoRB<}oO5}|Nc9>bDrie-QrO~xTa@2?3wQ@o~ z9Ze$hhzNby`?;Mw=0JjmOh={*>B7g@NBs`NH3IgmXnG4RHWUbmomip6S(&G06|HXg zyDw7cD(1{4tBu>y@ayh1&Ap^Qijg^Q%hT-tNX+Dn#+TG)Jf1b~x0C5p%bIL(|C+b$ z7`e}Lq%=?Mo3Yx4CJ*x!9R@GbR?T+(s^8VJUmp8{y^p=x!B}yH)Sx)`K^wkbpBIy* z^fb2buI$7NzMmIOw8hUbnd`QN_xIs!zT`|EXkz8%TlUTyB*szu{lNU9Tl3VWBdWx) zc{ne>XrdwlPkNzt@&KPH8?Cqn-{mn|PX7*;;-^55JnL{Gy9Y#(TT+8BL-??Ii|yTO7mDr8Ob9eeNLpe=`(5qykoX?hkPZMg2k#=UDV z?x>c|Z-LtGQd0@~z8+^+#|;fbRzZ%b{4+b&Yj)J(r=$%DunAlK2VPQvju$xE7wMo$ ztzSegBI>^IyjJ5Px)q-XpX6!1J6L+y?WPX{4C0N?K^JeO%84Rx=DvA+w< z-3;lAb>aJp=1ag%Xv{BN>NF*cy?j!ggM4rlL(DXD4b1F1YuR!9ebS?Kc1}7&B3Fvow)Fn@;y!G8*LWZ-iya=%6Le&3!`a!;Lm1GT|oL zfC018*O4Ten!lFelH)#X>06lZG*BdsIg7X!9s68D*h24>mi0PV6USuTWPnUJXpt(cT=y0w_F z9Z)VU-Ow;pp62`>$$k0lhMw|9Ytj7{Yh=uys+8)cq!;Hrx`B<}nHgJNDegg@sca)U zPCaV~ueL96nXE8)_gXub`fUW=uc{$bzBJJ2)a8Is^9McuK4S)<#nru*XtCNppI84>Bs*=*Mx zD*@u5?U%t%FRc)`9GDxH$zi;MRRi2T&J#|3A~``SuYl(;3Y}NEt%q{3&{=d8I0;}< z%@*5J&&V;Ewe)_;NIEgF(8hG!yi8sv8?;WMP+)$GLS_cU^1=(ZY%KlBN0 z*IKb2m4~hZ3$E6hV?WGu3fNeF!P%@*ESiX?*4BznM55_3Ak*EYuGoH3--~?4mIs8Cx8lXh`CtVh`KzaEAA^pk?lcp7LWb15$T+_T?)vRU3g{mvQju41N^MCBF;9 z?+wm!+)UYr8SAXJwP6t#zZ&BPy1u(APY&%k!;RZLu7R)2;w2bhI9~|sYk`nY!Wn5j zxJHgMzQ9!r@EgBaI~u#99hl{C%S#dU)aGXK158C8vI*zZDsr3H!CLVzukn{pEwagl zl}CICcEH~hzZ5BVLk9%?T2JJQStZQ1QYmbk(}=Z|aleiw`E}OPhHY$-+q_T8nNQM| zS9b0K%`(uUba=YQ*pizULY5lgQMq&XD&|s>vx-RTEvq^_7XEy`5TqwaK_A#7=rf2Y z29+8YaoP}JcdUn^kD;O1!mUiwsZ0+ZR__PCvjx_V373KPDR#j!a`yw>QWY?SK0)7k*8KemhINkXpaZG{4UY^DSt8|LEiy+O0K4LfGt;ln$>zOIaY68sAFQ=E2{%d1YT52~5i;hnS+ zxNjt=BtAW7ct3EsNSQ$M-cIIIg!3Mogw&f?^3;rs?+?6cmoChG%zMlW>qck1j=;C$ z^-Fj6A@gFbQZo_rk>ulKj~=g)f_Lu>>J$hVjbLrSj~Gv{ReSuf&68d4Q)l2eD&o4) zMgFq~Hpm6k>7QbxPhUuCOVVtP!Ln5ENm94R@96RRoH>sgg}pdGXTKysdhSBego`w3 z5tDt3ULDtj*`d9OP%Vc}o6QjELgVuw+H$_dmo>M@UBlX6+@YMm5}2B!AHck6#bD7y zXhfNAl1)54k!u*Y(OU%APs}hwn8YxAQ768xKFJlvYE*ByM@^~m^?7U6=T*14tH#Ez z?p%-TUqKR@B+7U8Dg8HS-%?zLjgCFYl&0p)x8(IfTr9L7M29VWw;U8^!hvUeVA61n z{^aNX<3)x0{&U`Qgp;HbGqsTI5y&l)UZ2BY)+Vru-DK3yaKT_&?k4+s442xt><98C z12udNRrx@$u`+Cn=A+^#TL!Lngdez~U+OhcEUCQPqT{Qno|a5dNnh(;)BT_(LOfB4 zhhIhaEVo*xBG;o_P!C3vi>zVOd2K2@rm{IYBB|xVro8?4OZLuf6vM$=JO|8!m$$=6 z9#~8Ko^1>)YSd}CZ4-xuBb+$<%nTiI;n@8SI(K5s76{wIW_{yIQWxYFh*AcVopvcd|$_+o`OQI3-P1r)w(qE0SVH%NrSC9r*pD&MI*N zr@@@!x$|XsJ77GUZmV`*DTh=K$7>f+@9TG*ba{k_n7Rm<%M9-_GH!7X52P&cVxC12 zax`&ii%77vxDtw~yi2ZCssw$L)_}XHsZY)_Z`;6%G6kvFe0GDA2=IAxLzQJw{kp!p zZ_7D}>WXC2;%l#98Ns1OOepzlJLm0rJ6a- zWfK`-m1Mw_oZ!}V4k%1=>D3z*m*mzpK)dbw_4yZ*=cbOJ+1I@HOW&ABU9e zj)>>M%A7WtrgxT%DGv>OiZ8*Q45Aksmk%A+bC%!|-VBr_DTHwuO0hE>C!2fQVm1uL z_+=|EY|oH>5@6Nu)?qYDP=*rLJrI9cA|Jt7EKqP0hykFwy@({{>14n_=rZWf&bT{W z^_ap-uFXvxo-M->Gb~2C|NZg7M1dC9@{neI$aA0dKAt5}JbKI_mTez!B~n6o7dQ`CSX_M1s?$&-)=@3h;R zFZOpSq~>PvqMY<-z(g7VpIYl?y9XS0Gn!h$h|$ids?!|s&=-~M#P-8|N( zoLQ}BVU}vQpNR@jK8>@LjvKsWJ;}w4JWV3%>5oK^rLy5vzST$NKSlL+>k9E<4~(cd zO$FC6MO5PLOUXr#T6jM}Ef{F)W#~XMv^~0;*yS-7Okme>0 z>`6CC<9nV)mS7ZOKKwE9L%)MBNPXTUuvNOlp01wK(&%d+xl_5y+`yrPIlz{$xEpmf2 zRth@f`9#ivXA~I1Duvf0*EON8*!6dS@x)Qe*gdnKUbo`Xe z=(&&h8x4(`H%^io1;gIamJHBX8QY$wap z`^{ZkJyUptxvSy9EE!Zk_qd$gSWpZDzdK}8-Nt@~I53?tE4VbKP+%{qEgGQJJl088 z-=5544FV<6qAQ-IHvo~#2YN6*;`!=@Eo;O1rm))bG`=ot>F0_GnE3Vh^~E3Rz-U5| z#p+j4EwhI@mo+nN+aCK%VWK`~{BQd8^I^D7%s=d5s6}@go+ZJfh4|A$F?pwNiXW+Y zvY<#UCyk_nHoPO?#FJ`Hku@q*_fTPAprzF}U9yrq2HGl&qyaG~ruIX$?W z2GWXe_{C!qiQ*8ctMX-wf>H3WZ704vD)F@2UeH}DQqZrI9Xgkq^}Nz0X?iUeONY!X ziev>s=J4)a@!~Hz%}U~l-h9DfFiF(43$Wcq3i4#`^(o*tB})vu8_9|Fj-e1!qnUX(`+!VDS_%oC_c_ahLCmr)xCuqHl4QHbe*Ap({?WmTjI$40B+JP&+%f~F?@0QjP5 z7?NR*a>iEp_u}OoR6`hANqC&O9#=dj{_j*Sb0y9mt@Jzk_pw|)tojm^@A(4#(0e}{ zlh<-X_#L9`W2DpGK?2dSE#+I|2ilk(YWksh8bK@+;(a)mr1eR|O+%D;8Q{5?>3{TN zYJZ8Fxpmi<^o-pU;HSSYeMIwFc2K?SV6BH!Rm4MlAu20jX7!AMW$W;&1Qu<&k{^iK zF?S7ihXCeW(t4qQ)O*kwjdGl5D=28pSB`(%{487uYtCKfEAe4=(wNy?FZYF#`BnefXB5 zUO6$A<#hFYEZGKm@EBPe$r}Bn9Y?;+lb3xp^J04McUJ`9ni}4hhocBh*rq!Xfsze1 zvjM27D?VViLRg}ZTTyotvwd|dS!Fis&(N~N?+iiE;G&KSs*9_y#8?uSdHB`kdCmcf z#6i=m-<=)80ZP7903a|JE_Xb9JB+whe{RQst9GwHr5?@uS4z9U)1468DXgz}0b`PF zwwOH+BCwxx=pxnloVz*#-^yw9=tq5G7=>;kyH*W>OIrNgG>5Ej#Dc^?@*-6;_N9*a)39e1T(tv{?id7WWxpIkF#Fo z_VAf1Zki@KZB*eH#8m7i9{^Z+r(qPZ`DU|G7GArIvv1XYx?R_ON^0=c!ma}5_raQG+dIU^vP4#ugmSitK$d%^zySD?WAET?}_VoE`sy22!UDA>}1QLc0j%-_uPD$>!UtE5W* zhR>wH{|+Bb5I&p#hEGJfot;ONg-_Vo2h1N5Q(p^oYg}JV>M2Nw4}G$^x;SN>i-wnG z%LEO#=7cFc8^d(_=v|G;8&BCBf{EXcBoj&aZh+~hG7^dMfr+%VndDepoD?GD)|aRSd%*Z*UPnBd3N0vnAc4sI z-E!b_Sgu}O^&`F7N~aAV9^-(aiJS`|Eh=W};{042%mX8TS!(qHO%9ds7Ai2WV>==_ zRU+z~b?pVu-^hktC5Fw5pWJ;UYB_^98xrf~3pU*@w_0(L#6P2D|IAm-QR=l6r>0C% zxqDbxysu689$t@~E-sSHiMVwa6+dz`D0Klm7kt{MaSnAPC0Y295@SCIliniAQpzE$ zZYV$dEZJsebBa{SO5F#l>WI|cD>QuC;UZma)-mR1A74A^Twf`J(=TPS`1R4hH1O8e z?FcDjBI+0;s={y1$cBn@+)GEZgL4gGiS;XYmyJH*oheFgsb#`HqQ>wV;CwtaqA7&4d@h1TKE1aR%E9DS3lPq@LC}SLm__;*aMYT37&cX}SNW9Av8Jl3Sf z!A*J;G+a^Gc3BUS9|DfQI9hDdnWWCI4z@*QDw}7W3Ni>)GCvMW9rP?fB(16Gv4=*d|`kv!f^1FHAtoaJuYdj4rHahJc@{IF2*nK3atJ&D?!N@Q=v z7htN#9DN8U9Nu7}3J50TjTc8mbwp+UV7+ira!H{3`t=^AAS?bPBF_o^Sn^A$Z!$jA z?}g6#0Y~b{J?g}?RfG#pt`nQ#YKg2_m!lf)D$I^QyQlID)Vlz6sdQ&?0?Ii_=^#a7 zF6D=}u}`j%KbML%(qa>(z<$%rRxfFGfU#JhBDZ&eZ5JdA(Q5I3DhHdp=;rr9#3fH7 zqWUD5ySf%g2c7U=fiu7=FJkoL^=** z8+rhnQ1}IVtq^tdyiHP(@g%8iX6gEZ{(an!5q1H=A<0lN%l`4&rCO9y{9z3734v2>i`@4`T$H7}8>;FKJe za-KF}Sy?351Hq)#2_@3NTjk+z#*~RNn|jlPM#BX0BAS5Fg_fAtX60$7{dD-9u*k_3 zUtGjA!aL9Ui)%Y?`#1jj^d#+x=B+^4VzX<4?X-LuCS`RbA;Tub1Oq%F9ICy741d-A zrJl6r;`+0^LFGiO*4gXTcRYu}7-WJEr5c`|B&|w}feQPREZ%u=yv2*rMTLD}?VSt; zh3E$Mt2Ju825&{UFCcnKLvuC+O+}iprr-JQ$hqC+p@o%hn-jEtsZOX2Y@l|BZh}GX_{R-%=ArY^7F(SI3_-fz zE#CbgP34SCGaxV4se0v@LhH@3K_2#+zw-bV4N{kkj$`6oZm)*Wb48Zw_$;+SL{WPf;L-s9_vBYoi z2~+rx7Q{%c7l$o5-Sj!)n@Lc~A8|#l?C?2K3ZrXHg={(fY$0shtH1Jth z8-?!Aj{)dzf5k_ayABQA&^ zF?Xofn7R*W%zBb(@1p3Hn{;6NyM6mcDWR!4yX?~AuH6twhDh0oS*94Kixs>9OlYs7 zMfQHuusVkLQe+eIr$3_8vyKY5&Xid}8^C|Nw9=pTZS(8T`V%y2e5Cg(Ih~k1_QQE< zz3?$tErOI4?~)P<2~E?b%nT49Tkr;bAeuB+`AKd!ix4or_*qJ{T91s)tXus8GD<4F z1lU=`M_38rA;^D%=dqv^C^S68qY%HoC$`_smA35|I}PyrU3~A{Z5PrQ`k+<>vdG&j zW~FTG1dNRY1LwAv)l1YbN#%1eN31tKKmYR7Xr|VKbD%cE9|N(*lbv#d7&#hLp}3r+ z{W%)7&`{$yi0$4u1_$0jEE!4$a#OD1*gdqjeqRo1l37`cGy*k$|eud zL-d9UKdwgpJb8?4{Eha(KSBs@&_j#ur7gshO=xRds%99TDF&9z+0o{@r%4IV95l(f z%oqBB^5yg774DLMya4z5U$jS)iBND2F-TTDWgiA&6cJ2W>rc^W^;wIJKbee7&JE;h zm8_V~m8fdNJ&J8&X3a!GkfhriLU|VM3KZCMjLI3FU#h!9B9$fd<9mW$ir;?5$@`AfQel}|CKMD zZBZojz@U$>At*^p#$^_XoAs!ZckeCO zgE7Op$k+L6*us1^W0aKmA-P%=fGVhe2k7&Vr0oN1{fW^s-}n6lJx{r%uan?cmm`Bd$&5^#=5)!=uTIOo&-wFGG#))KKz-}h;CuP`X zE(!h^m;(Gz)T(|Wk|v?Kh|$kpKdu-kHkB!bTIC>3P(h8re6orf<5)CWbW0Oa=+&Yu zy*2Z94=}g7m`OHWlDiIOYMp(XDv|M(Rxd4~I)>LY;jM>FWZvYm0FVxxq{c!AIQ+bK z-~S`CKw`^l0!V79(*3-DhZB^tp%0?B1H>H^UazPay}O6=1eml7t$&d=tWS32d17$R z8d#S+>;^RGf8y9vamuuMWq@N1PejYyezJO_>&QnMZs>ZU( zM(~FG%<_~JO%3fJ6cHrzeG->6QD$9pp^IeE(?Js|TY~qoUTyZ`bYE9x1Uu>D&lgn8 z_$`JAibP|5=^{eP4+W8|A=woBR-t}_-U}e$3-E!$r-Vcughsv78FNfuEDlf^`EALX zActRF>|6eBm_c}+h?*bp+c`KQkCQDyWX|do-z8mnLirQgZ;mUHnK5uTTo0@DAav&^ zfzLG0s16N{j*-nA@V<0&>1B0Br}^#Z1%*l`5vidcqtJy0pc*pUOqSdBUgz&8^V%{# z!$)GRuo`l4p8|LAMu*S>^q3j_k?#0}*Xx!%O~}Ke#rM#2qzhrG;p!~^wZU+gf+H+F+iiOW!8eridlVQv(J9xLjK8a0>N5ql zM;INf^cTth4!}GDgZQx#ADw$GjCT$x$*+zk?eCU*kIp^WnSarIfOWIKeJ!D`RsRHp&7?FMM-abvtx<_y>OlJ86aNde}r zCUwCJL`0^;_0fb?c8a^D;{@(fo>EnqQh3`ACr=4gcfL8J8agU6rv^~0nD2;{SifiR zRa?Epzi&K;E?Ia+O>vL|cEZ9X57&lU{qNj2(XaMCeKq=}#}SA00mz*l02|%pP6HBU zqzE!n_IugqZ$o|cbCk$DSfl=tlW(LQZUo#WGcPCE(fcNvIESBsT*Ler)sKJDMv_rY2jwyx8xnq5d$>r-O<>EqOP{0Rc2 z6sZiEGi*~RLPB;KGE-#AOy&rgDnmk~%u|N*`I4UJ_n!Bhzt6kYS*!J|y?V6meSh!! zGhCnRx^Cag)r^PVsCao#rn}Y68R0OA-U2UffB%qUZKg_<>+Y}xE5@;%b4{# z%kIuR;itl7*>|OeEgWM>R92?;dpu`+Orb;(EIt&tJqdl)eC32rS2IhQ-Nebt-4 zgbAo7B&2A?kX6t~_@nBD3GvNiz(!#-C)?)1(*TmFuJ#Z2_X%G#i#vvE`pet0+{r#` zIs1VlClyjSUy1su4R&w~XI@u%N+QX;^I({KZZGl&J!pF-QshqJq!M`P^oiI&Qf>T` zoOli!j?w-Lh1b3{)qEoDJb%fX3LDI~KH@@#6=QiI)Ix_9$?<TlpWTu51(8A=eYqn}E8E`C4|}QUS!pRTGNv-cWu&wSyk?`5ElpWhDXvX1~WB z{|nt@@xvJGqoLyyB~0y;Gd{8V2!_Y=Gc$Ax!#Md~E}@L%Xsboe_tp5)%Jm%-`QPme z290;au1M@!gV<$K1Mo2qk4o7|>x(#7e>p7ueatmz9$f1a6$&(`Z_LFky1ZNi7(ouqFH{O6*(pGJnxCB(bMoNdgzE)y|;_(C+p5eWdn96 z>Dn4EtBc8w@gXWYs(wxu7wm807!o{r5cN7z;Mx{daG*db`o(0(wep`ds#@LH;<{Im z`I|_8&I)c&&oC>6h~56!<+<2-@>X)0l*rc0Gs(V}@Aeg42%{o=c^Ae$5qD#}HLk!^ zpjMI7`IF{n5F5ZraJav*Hp0>Ci2@Rd1^dl`F#%n1fkF53Y>V z)TY_9Egm5~-xKqAN}MUU)CORL1=da}X z%aP516lm$CYP0Dw_3;_mvp@JUR0MWb;LJ1SL~CR-b0uO;WAEd` z`=-vm!I7BB3}6>v?2K0;Du0hXa{tWF5b=tLUn|n>awl|}zgBwEo7;b3Bh-E!V3)B& zPxt1=7#rqXG`@kQvamo`6S+$+x*A=C4M(e2WM?c$S+xd^gM`dtv=QbomzU+BexZF{ zB2rmC9;|+DhnJ`D3Aj`lkl=ByKVQ+lKA5>43~acdY8&-iRXO5=A3>8p4_D#z@hpW# zgu(#e-;kU3s4Ip zh;EB~!(#EM+0gXdzZR&6mxLYZ=2w=jI!uzZNC3#+T1}_*_2lp2ppARrwvK zS9zP3g!q4>WGfOS$9pX-og;GS8gZvAtW-&RYXjw#`Xpz)1-N3Ps`p8dwcfe!&CJNU zs!{WT3P(C_t=U0D4r8{pqsb37HeFIu{)<$J5bMo)*sak)#g{G%P}SPE-!Gh`8q|-G z>EbmcM*O_gf|()-oOn&lxZkDtbQOo7Cam_j@)=(WBD^eVZ{PK9WcGW{=A0B$H)+ z!AY#U^TfYDILoJu6)_=$FzycJNWfc|8CnM5nz&+|tZ9lL|T7w-rW}w2-<6he4JevkY4}qGUD#<P|$fS|S+o-*tI?c)4&y(_dcju#oGC|b#yme69{gVEx3)X*{w)DsBmh!Iu&fUzz|f!I zzBZ5KgX)`6|L&OrU*%H&Jdo6dtb#(c9^~u%v>XZol*~dr9&{VW=IR(<`C@PWeFnQn zwMriLQ8u?t08fNJg6hL z8DwZUQv7XdFmF(}POtvgS0Ba7*grAKGxu5jo|fb;JVpo+V}K9yCLP+y8wMBZe%}4{ zUODCFOnK7Z*PIx_Ttq$=>>_$cL^N~vRwpcRg{b^z%|5Yzy2X|A*5 z$nnHb6XR9g#v18ezXR$R433Cmp_VT;pFm6Exw&L03Ba-?DC&$63C6_$N@7Na07 z(wHpQKIOhTYe7^knB_21BPDg?oZUow0>SL9n~oz|Ef?mWdV(A+yD{P!J^q##!)t;a zl?C+7qGV9reuj5s5j@APdye9gs~xGoXI9=E_q>l z@V4A1{c^oGCrrEF2>qW!>GPDQ3ZHk>c&7YnpmNl4%=2@nnQGKu${4Zy`5}!&WwBKy zJ%)$rPo5~=D=OYx9<)-|m%h&2*@LN2*j(&-lsk)0T7yiC!plUdXfvcVWy;@~!ow)9 zX;cxwNvTol-3@{9qIkx6uI2>lFe13z5LH$(6&2c|@;eHqJ14a1DYb@gJHYM;ir{Zu=H4g_X2P_C1pUIa4e~G$Q;%vZv(|h8!RKhv z;Ek432leJIxvl(6xU6M@HwmOYEc86=W0tYL*aej+v&FA4Ib@c;+{tJl zREMkETbsyXk1RHzwBXjG5g|cR4G9&oTCCa170`!s`j!@dQ;r6P72%ms6}H}6 zDJ zvG%RAJ+er>!g3WB|8ca53J?(OeEQ&6jj6BKhN`_-4 zt?Hhs+$UxphFgxJe3@-nO>aj+uvB^GBrT{H<*x;g<*b&aF+7%A3CdBwA3eKcbpi#k zM9C3AB(d9!Rp)cTWi8I=&5eYryVD5b`1L80Q~c^Z;vAw`y^0qH{PC#7BcP;516UM2$fYNIKXcqA!P85 z-g}nq2@gldR5dn8V>J)D{vc74qQ-4;qr7M7#;;O0yin#a(8tQy5AZZc3-h0OXBKdG z2_6F;I`GCKJ!ICOec_)@K&JIdZ-1`}=Wq5w6$!LJ>Z?ciYDw|I%)-H?ccz<{CvCvg zHf*s4vkGpk_Y!y-saEt0tTl;j%>s(xHYJ%RvR=hoWB7|qFgbk{8}ht-zUEcF=cXIc zCHgh<%qt?VYZhR1J#1Tr%wX3vFmcYqAG1wJzx>Xt@uvy_irG8Sd`}&bB!TEHlA+wb zHqrp8WIKr;KMU^8Tv(&)z0sGKoNkz#xZ%}x_Pg$=YU2b6Fi?Px#{}Wy&M8lD(G3AY zozQH8`^snM(bru%P9L7@`sqHW-ujVaS~iw6tJyJB+rbHw#Ix(Ea_>Uza@%!N-cHIo zPIh4Dz<6DQlWg-JuFo|$z3FZN5+97v{1y`RbXLWNNqi)zw_Y)9*Eq{VMWOiq^mzZm z{_R@2aI>yAE^Gu#(|HI~C*K7YoZeI=*<*7b=;`KF3L9|7i?!^(c z?=U!F&`;HNq(!)QqrOyP4qE{(Ujdkp_7avvlh7l01sA=-eqbs)kO-{*W2b|6-&&hE z8{sprTx&EH_Jb7FD2_dWeZ4E9SI*mpm6qW$6n3emf*s=)s_r)>W02LPuIekoILtXrP9 z2TGh_WyDmLI86k@ps$R@Ws(#wEV-=qdwC`ysr8oI=Wf_mxOf@+5Bh@Gv1=!^AD+qw zXF5v3jx*rh9v%b$gHp0c{_x2m!hxMu-G*cGrU7SoC6-v_9^!)X^e$275gbdYN_p5! z$~vqY3`agb;ryhnJMUO?+bm~;uFnc*vzd2fvHsr^RybWFRhVRXd*3X{_V&Eld5Qx%XtDtC5 zGY>oX4+BD*^4`w;&0I{kdQfICq?zg%>FGoTV`T2t%c?w(nNxb|nRxFz6{Jpn2Mmj! zhwWQmTPG<>bFMCd@o-2V5c3kRo2wjd$im3{?8%H#`1!3xEq|s!``;|0Ol*7_c5haW z85SqBzd`SiL!M;v!)9{pqBpFSzq|bN&_qYBe&8|Go2}T*wOl%wL3d8}N1`R;zhB6+ zS;0je4Bx=_#<4)vuMqGeuH-C9*RBi$;fX^i1!MraL6e`#V1OYe_oIz<@8HhI(lO8B z60TfVB^hPb#vl=>V#Qw*t-YnPkD$91cKFzNg^k%-Nmp|MY3J$NCrDJfb3Mb?9dW*F zvNxV8FMc*6tCrx4TNGtlT7-dl#3KXrJk0aYyt1)`&`v_(4&W#Os>k*+zK`Ey%TFFT zrRbYk60L~y6C%zuG;_0dIrQ$HK{!|ZHP?u=qc`qfw@f~vaNnG?hWP+61zW1lV8P>JIq z03K^%WENBTwBL(gWAL3?1X$4KEQoM$jTBw)75jOQ*poZ)unkiUS+;#D%q8vvs}vK2db|ra=5rr_?KhMcE5)!nW!T#eW~FtQ3CulJkUqdh8W1%d#7G z<1tfxA>Y}Dj~#N-m1$$D8D$?j6fX(vhyf~KQB{NA$*GcA8GQA&YPX??Pyny=V<5<; z#Vo+hf)uX>GJx0nAXgo07xBdQe^4!tAV^1>_lY*89=aj{K;>i4XpZlf>JARAs)t%o znKuu$yJs9Jte^j~g3))cd(t_6t5KQP`zX+#sXKo~@gAg+sp7TnKFjOpzsguq#%8b0 zS~Ktz4NS=gT2bt|<@ksDCGiLkV|>gu>pGXzC-7pQWD8??UkhHA81R|Sj;&Ykmd8EU zYryBtU5k4M8;*h3TxaX6yItFZ$sxS0zjK7(zPj^Mkk1NU6)Puwdbfs0rBJCXN?B-~ z_i(JUi81AyyFO@3Ow5wbz!l{aWOQF-Zr(`}3V_dXkFr%ZgvK59%Js z0T%meASu>L+ReDbYh*L5K1tXxC(1MQ#`{O= zeMh=afp8j}R-9ybp<%N|9iGIjUM-P!}amT$X^jUtSd#5j( zr)=21cKp_h@qJs7shaUVarA4U0L`~|Z%{dv*LsBB3*BLz3+!nFz0D5?w6WpP8=tOgKS&Xnz-}x7#rgLso#CEc1`~vdZ(akjcCd5 zTtObcmxEOODN{bRFHzsdW?gE4L~^SRQ^beEm`OdSQPuXRh=dX=X!G}q*2v?MdRe}+ z-H#j5j$q7mo%kYp4#&nv=Xgm1*W-_-9_KB$?=MLCG%yPy>PSll>1N6z+lKR_r53~# zgLkHh4G#rMP+1D0^cOX*t(P#>T(GZb4MmvKY@77-BGK4~a&-xAUikn`3vP7rH_mjj}jWJPF;OrkHB!>`zv z<}vwp1u^oU8p$YtrlAGj>^21Qr7MnL@KGB4U=l{>8PuM^hGRzhdA^e?r^cSQ|HC?7 z^~(o+UKVG2qm$sjz&NkW3%?HeDAq3ml{UdngBW}_jjA4Nt1{A?Q- z1(R`K0ZI$|BJ&*yPLy}HGls-x-I=|j2f6Z6p}vaX4@1VImGSgO_DlSpR?@RtH=ss< z`rFZ%>_zRTE^ri?HsL#j`jA9o!*Ra!oPVCK(%K8_G?BwYpMX(4_w^l>$i_=3b$`6; z8u6M`5;3yi2dMf^t;=Eer{7v)jvc=j#`(QW-erwSuK(k!KpE=e>MNa+i6qIlMr!;M zZYe=MZaLYJobx*wEt8cN*~_~Jo&qw{f%G!jZPg#iJ-f*c+3BW}ta-P7lzG_b0g45b zXzK3IqV6jftt%Dro7}2iq0raxExP&9xRj5$90=049~?S*wi`ICHxjb>oa|2HhqqcO zj@j8$(+6M0e+aO5k5;+pt07#9EjWlXX-1$S!Bagm&}lyI+`lx*kMg3el#CH{OFcNxv=YLNM~2j65QuhXVXHq z7SI0d%Riy+b&g9GMb7CuM$b>NuBhn7&xOlSBXv`9lb62mq;1|MY;af>b@+5`8H)*t z@Io*Ng!=VC5w0GrYMhj~R6Ow~mC#gYOiYU0DDC zN)RKiw4dsJ+5Qjx_aZoZ^S_WAL@~|Jq*k@T&92gvi|I})lk`_-P-6Vzu2jg|LhrbJFdajsa>qc`hMt^H`vZ@{rr%kl_+&s(d}H6uwnb!XWyuT(vNEM`>b<=NVFmi!0J;JHPPhCjKa$0luEJd_1DR)YqT9rt^48^}y5n zqQb)G(j(G@&^9cO+}0m|W~xaYuta!g-a7jTL0|;ln1qR_^?MV{Gp^RmR5qpR9ZL@W4b!0~!#=0yP0PYwBjh}pAIo|-#UAJG|Pru{#!W4fHfG_K% z8kkp8T|yt`Fxek(tB$|lxC9Ohi=yF2?_L$lQh9B&dRYS1l<)s41}kI&rE$UuGeWdW z4X=I#Wzx%&;T#nb%mpLaf9LK;dPlycSh zF*N;~>p*Y6$gDSZx&3Ke8dJ8(o4YKW-PtCFSrI5Kv!=5m19hQ?Xgb2|lj!J6gw)2fpj4&*hqZc3n|9M~;1VL%d>@P<3~u zxix#l{_(p*Z+wPXH~o`TqVN=V4>Zdmb^TdTCU0jco4-71t5$aor&D+}6@3@**c^e^ z>xBW-Y+QMKX?@RWTF+E#tswt_tg} zMPgU6Uyr9;H!@8M*CcL`5w)-ajXn;#*{79l^~y5885ag`11mO1BbHB9ao}Z)T!g9- zmV3)mQmaErI?nbQr3GA6aH9X0TU^^R!;37)N6(oYt?Y>HjS;?g{QJb8_sH4ae|SEq zk79o@9vAT6f}{;W@}!5)6ujB2W=SC;A7m6N!I0&5qW*^jI|M$(UScpol{ro5=;fLz zrj?M>%keJQO{yBN$c+@+-rs2vBYsKD!&T(V+Eixli|u5V*(d&*h+3z=_NN>Ov#Ia+ z9cH(TtH6V*DYGpm?$5+$XYRvu62kacvD$BuK~6ipgGeBMNM(s;&yEHY0j4Rtgat9w$Oe;zp<2a`d@ImahuhSo~#?|sEUvC;X@LeW3iQ3G4;G4rx_h)kmC z(1u~SH6iIyC`fwr@_JIF>_UF9zEw-9J}Q)P(DS|o8@b~ zxOVJT2-`nzft@OK@ETzm5RqUowyZr$Lzo6_XLDuS-GIi-^&0Hv1vj1oT#NVOtaInD z>3~~d9EumzJ!!7NnFO>&7`Q-;iwv$Gs^JdItJ4*UF&du~))1OmUxo>rf1ak{eniZn z@zA@?858VXFOSd2Cb>UOiv>IEWaTmY11ub^PmgvP+=|}}Ix2I&@f)Jl<5k1jZK(St zIH`vn{KgZ?FSgt=7k{&Y-pC~-3%W~Hj~2kp3(tHM7LT7HU8@yWjdI3oZsKyyzG>_{ z!uv^nk|H)L4JtsaWp)JlU5h@``S1Ipr}oo=l0H z#O*uv@|i*$)GL=!n1eQe$?2C~0vTrOqB9FYRm`{mXSSc9ut?h6eNS)GzD!}fxDC-d z28-ByLCzvKL+ZA#h7B;F?qoEC9z66qzXC>5qs_v(w;x(b>g1Ze9UB4#=aRpGN^W+V zw11v$owf9PD3eWK9%7_?CgtP`L=P%@N3N|%V-{XN#k)UZ1@u!xor?Dqc88M&eyw#o(V>TsJ(Y95$ z(H_3yA?LZ_;`8hF0qz>NHyOZ!@8ad#0$&(0&@cYwB1pGy#axhG?S!85R=4Zo-@F~n4ei$rRZ3xq= zI=FwD_f^e#FSKpFw4H9)g2f!4{MT>wh|Y!Q87fwfeJ&AbQkPV{G5aK+Ux2*w)e`cM zjuQSum>LskcM6S4Z@=6)thYFK<73-hSG(}RNvcFkHDz6tISFGRqOGG^zvn@ugSJi` zW{NzG{o%*U<=3P3866gHFJop5l^GxK+k_rr=S2u4Q)Cu$b!Q8&_p%@f07zcmPDU%e zAgg)uH4VuEi1yCVdcp;|9W{Up$A-`@)q#GlYVt><)n0gdXkDeW*l%_DZ$>qU#8BEn zlc%pL^0@ApBP4a=OW?#)H(YPNI-aj6BYkh#j;;D)KPXulr^H1Jj8FBOV=1VI z+E&eUx?BC_(oc%u>b)2xLlvLB*SAMv3T+FB)nx`-;JOv@pKn*pCS)9R5k;L*R)Cr? z%(d@6uxKUmjQujc^!d>2z4CkollNbKel9yaMAMrf=SY}@y1jMEyYh%#pF~^$Jt+4| z{)cief)C(r6$maCp=_8}7e6Edl z5V&uC-ahhaDEj=6C7mmx)yqjgJ_oskz}LA`26?0u7@2To94S2zpxWp18aR*|7<%IM zV~6Oa>5obqNb~1l(Iwz-f6*l6eZnzAgw*dUX7fs+=$-v7HB@{?EKkq!^OHFz%Qb_ywB%s!4D3q7W#qF}G9kUSd zY?c*sTaQdd7le7lPTh=CA#r&fC@ZIYYR|JUI!)jKbj3e`A5!8Ar0hN;9#El7PvxHI zHYHdxdp3xZoc54_vxsDyt(WCJJNPEsJ&!A-_ia@m99nOp{lsr-5INp@S@XHyXT12i z{0jTc79CH8YGST=Lh-8jYZ@j_On`3Pe!$B}|MBCi%jz!zQ7o2yMr8CN?tJW?Cd0uy zJMiCfdKO_2$rRDXLq6VVtakFT{nP*Fe9UuY0zxh0%-|d6CklK@=(t?#riVV9`*9^I zOwu--o~y~}2APi+oX=phAf{jJW10Z<>?LHxy^^@7VG9w6zHq60q1|czB<>=v;dDc* zM$nv}APhVm=Rp3>Hl+|qqEQv@t@NEPnX{r~$)9B073~iG4c%*OgkInQPBfWa^7htv zf|TS{GOr{muUwV0-FcKSaZQ@I0)5>ry^tFSy(5$JfdPXliO+5H?a2^d2KpMEScz)h zQX^=h5rOWnZ@t+j{7+i1-+q7t*+_F@1S`A^Zqa&G`FQwV4n)H#mvne<>$ScfZs(HF8l> zgwu<#QxY@s`jh%N;*-(KUOoQ);s?6)$-J0VJ0P#sd%W%lEV|m&(C<&iA3HPyS;xLG z26CbCxtxprf?nlnH;s;D|Js7m=Hoa#(>{N?G(t~&w6DB(ZVrpqyM@Bh^Fse2%_5!f z6cV`;EEaF&ErBbRG1CsJGUj`rPMs7jb2o+o#Pl24EmMW##P?i%>g;BvNy@6(MX1AnS( z%}Y8QA|fR&Qva%>0v>%AVLzQoR@J%NH&6;iQ~Qt$*_`&|)y3=Fqm>->4&OhdjKa7s zQnS_HpjfvuI}PjR@1Q0N`kxum>fczONyfZ2+U{Q;L;JFS%T6B0hAv4#JvNiirjNS5 z%-@-7kF{GBGqd6cLktE`f;xrq$0~Gu%-d%TB9HcDpx#o1rZ9;Zb*zcf!+BY@d|+jM zi1zDaGqP>eSC;jhV{?9aMjQ8kcejnd3QR9YY-}EFa)&F7G6+xi{=+&A@EC=zSc}FU z1qlX52p`Ta%hH6?jxSz-TCOkgOlLjDj5DIYPw8O^KTsW#8~(Tt&Htxu%9n&;(ae*< zL98zB+lkw`Mta*&pK>E3A|c|UQZQZLo>~`8hYje57mV`lW42`N>bQa3NB{`RNdOYJ z&PP&_Rd&6hjPuzbw;X>%?8n>3$B0Ox**64Li+@lc;d!!-G#&ObTz!}W+K`o{Y*2R1Hyfnfip~hKG0PEcP!&yWxoZAYmxCK2H@hBdUnc}Ih zK>E6)*6^+xa-@bA+-ODtyCZ;91R2I<{?sGGm-XB&*SLOTaibSv{f18GuYD?Us^^WlINE%3eewI*Ls`_sCSx{0XsSPozN^yl z{LM|=9kUAel1GN1SE~c&i76K+uA>rcAWS`}bYPw{ZN^xRWoSepSlc>HdOQD1C80Bm9)zGqjbY!(IL{1Gqay>9%Ko5eWga zp^HOZ@7nIzG!&us-Oe}}R9}c78pxfhrMSG+&!1>em2q34y##BM_lYGay55z8bdrM- zAAs4hl=k@BzwnCBQw*eoqVun^>D(Xb_U)8=zV6Dl*V#44Dj=Zvv2^WD(fw2~CLWxF zFlQVF7lu&H!o%DJ6gX}?E`y!mVgB13x1!H=_63Z6JGGWjK5}%A+}w`PMjM{NihLM+ zf|lf$??_Y+Jup&w9G{0-O@N`rv@>2AEsB%1n+cn=YsOpvH`x(q+==S37%7Jni-)uW zz6bpB1Ivlxx?@oCu_FE8!#KTOL`#NIP#W=Le0n6-=>8Q$)`SrZ*hMJOBS2;q&v2pn zD1vm*96W-CO%TO(Vv)+tFWrb9Yp?}39mIR1_1+kDemEy@@-1KShLyUY+Hr5^-@pt> z(6H|BC?$+mDjWHv*SeKS4}O4+B_wm>(`3+RCPD@-h|=PX{ovy?&(53NQ-Un7?D`AT zC~d$MU@B_=C>8z`Ag$jvV5*U;85wk=aRMyP86m|*em}l8#`yl8ipl+|*MO_)L>WqJ z>~lYgbMS9GL~PIpb9+8kiSMCjWbp17K18He50;mY_~k`+MQS#4!HdDSuv3SN!pO*1 zrOW_9mMLd*q2q~gMf&`uvXe*L#jB@bV1m&0zEU-xM=A+E4KIeJfj8gFkPD3%-nuQ# zzqPqI4d{0}x2FL=!o4e~lo8cK0+1AlBz;V6Z(oNo=b}}fH|bX3&)UiF!+|554YJ)_ z2fK6w#r^8bX7wdFt_^Nit&+Ptd`4@aj&Wi~#Tu@Dfje6i%3&nAuHQ2MZ{x451Jq&} zlg$2)4kuTQyS{AXx~;IyYNDTUXTN>Yj43YK^3B%IN52BUH!oC zjnVPR+dpcW=M%>zFa{nM$4U&i{N4zqK6IkbU7_g$rK{o)pQxP{`1eX&6(< zosqysLFWp|>W@{DHu2H@Up2ow8IM(+3!V(nW?-%vJtV^CE6|`DmB~umd(v%{ zEOqJU=VR}Q59hMqk6^CYNBA^lyS~pYN;aC7>fY=R!TZmUhO)xfVqqOm$n_H$m&7F? zJd4qfr0wr4u`sl5P-{Tw>{q$W7Y?TfuL5te(H~uky|xT44y-F;c8mN)?`+RZVm38D z#ZyW8*bkBF=?7A?jSAmgDm92AvLat6Hc>PsfV^;vyulaaw!~PLA(df@&BGjsym1Fg z??OO(UP7bHL4qGR4kXm+t#?2btp`lG4%|67-uPFaR5@G)KtV{z{~(orKyuNdH0>`+ z8Kv2_4}pq1?ZPCvwM*fFFkxsH{$*)fGB&F3A@@c6R%bna=4Brul&cO$;1Sq_`hbKo zli0<+h1U?g8>>EAkAc;jIJ)GYM3qBSJ|AF2J$!25)n#2tKusrPQTB_XVLe24PAC13CLDf3VhI%4ZmIrPSRyTNOJ*6WYQIPT#?6Dg7}Y-I$v8Z84ivsH%@yNjabfS~J=uYbq+}WyNP9=A zJJXY{sJ-7$zkr!Hf|POd%N5yKMkJBOk=dgv`anIW7=Ge4Q6&-8o!LW{KA}JPW0k6a zA&i)iTqHIH%!BH%lDp9-fCD5wltp5FFZrXc=i7ZQy+Z(ys^X4MqZ9a`JS1k~J@jDZ zM@qMxixh&i)nSnhBj*3h3dJ66!OBqn>b_A10wzgYHBkF#YHeuDt8&!$3ACL2Fx7j zFH(iLz2!<9m1Che4)md0Y5QM%?|%6x(+(tOkdo;D(vh#{FB@zy`kyQ^MAat)VkblFZ!|9he6y z7p*CM2ipjFmm6v7^T%Tl$yYHI?wfBcF34kJ9;*1rP?)lYT~B@YvGmjndY%uQ?QbO6 zf@CP>=t;87=m#W9{4|BNADc5!`5GynqKHzA+~Wdd!DyrVsNIr1WfhdArOp>wEhUbh z`25v`bPWoUDp|NC*Mt8IZRKR4+%~v7Fu;)K4p*bfzEFGes|a!tK}(GPwE2e{hzGTB zix$-R#_E|Ba1W@J&g_J$t3x*HIAOw_+FpJ9*M*?mKW4(1Nmcz$B@W>#y+H+_#$ZggB0|3L&#Djl)s3MF z(|Is}6r`OXEd(N9<4lyKdf*BvS8cK#p5?F>3DXfTgfs)xO$6I|ttVr$85CB))0Na$ zo)cF7;d|M$_qALHL#fR5#P6p0{373mD_YwlLb5D976yxto6L}I%B-m=TIOuZK;2?KRc?~}iU}b3;aaDh7^@*}K zfNs}LV6UCxmX(T(IUL}}xq==}5I&Y6e^@1o7!?qTWC z{DYth)M@B*DV1>CM^8aQy=n+4MX&6xy&#`YF*76gcNz9ak$|u;_GJ@wHei^laZJtO z>3p#fdSuB>pvJFoPsw3S|pI#8^S$K6^F2JvegsulAshOL@qD z=Kg=CBghZ>LZcUEo|31*gFXuAjC#%~Bh20^5OIPU$fE1-?5yz!$ZMps8;s2U`~(cE z7EN!JZMm)>8}siUv>x(KuPu#481PI$t>iX1EJD4rT1ab2jlAg}kQd(i|9{i}gV(g# z`8z17CjmsgQ9Jzk?^fGIfpPvYYSnv5@%TOoK^ro;Krd~J`rbBq#`rA4Z^mbDH)6U_ z9bQeZ4_dkIR&pAcH;AfmaOr*7|Bwax;1NOZ4vAg)1;lgqms92zyNDtD*AfkH+V*#g z|Nn3Lf7F_W9z5p0n%4(hXAwID1qb|0`uVWPo}SSN^q^Tb&m;?egL-1e;3D`TuU4>> zP4n1Ly({nEpoP3>6oCk2d)_1eH4mB=B2eu}d*)xpH;<0D>chG?|2qu;9%1kP{Pg`I zhb064)RjLGEaEz-KCRWmK=NVo$$gz>{-WuI071+6{BA`9%#>-4ryvxe_g zk^2SIKpEUmLy^xs|YNX+z*r(GmV8c~G< z$%1=g{$}}HfE-*lUMobKyJvfsO#dvF=g1<3DcIj29!-GwNF*!7#{PMq0w0i%Ze*$f zi9P(E-2WCmdv-`;G!e>5NE5}0Sz!1ZsLz8kwSlt1y{V3=rUEX=C0y-FUAtv(=J)4) z2{;gGMqR6|6oJK5SIqw-gZ<9?>^L;Vw$6PL^lO_Vs~Y_8N!9R!!ynXP zd4ZwP0G`G8wYG&n9Agd40_u#$!=rbg`5fnJ^vGEgBFwH^N3MsT>uSx9g?zMb& z3Vf8oRU492?HP*`<42bh{EP+D73PpVk1r`g?y<`_2m!s09ku9#I9>saF!{W%_$0TxI_}Q}li1sErsH${8$v?>b3&{Ou1!y9wm}7y`{_pr>&o zF4Y?($tOh|8(_%si=o8~T<0u7Fp#4V4yqpxw@GZwYPX0!oaz+i7us=BuEA8*L1qEK zJ=D@ey1bN2<2HR?Txq(t`reA~$eO9) z%l&U;&B{@JJQ!aAPGAK*&+S7rGK?TUVgy%@msG<5uD?L3^@C8W4g_14svO7eX1Rdm zFX0H#fSC{vxr9e{uIKoW@rE~^B!GfdyPKL#3MXP*Q1{9Xs1D2v6YrgaT-t8AB@_ct z&-U4G?;)2cV<@*;5S*&xq?N@f^a&ujm$hrU?dq_WVbkEj+VHpSKQLG%A_^=(Y;wT@ zRNl*Kj>bJ1oc{Q4IBvl`rxYlceDq0Kebiv3mzU(7sqW&nxEcn~gmYPe%owTk4{@36 zqvEf5ajI?CGS$@NqDC)SsGwQMs$m3^OdkAZHzxdP#vVZokhv-)&d_)siR#ze<}3bW ze9UhYPDO)|{)9N(=rw)Yk7{zxsKMzP&}d-ZH0cM-+@&wk4%bPY2%MRRo3E+at}>4^ z$B9JGr(XvG7pPkrXV$v^+5Aao*$30tM*I5~@b*Ja?~d(;3ym;kfQIonta=a4rjmOw zICclY)*9*`H%u`ucgerf4hYD&^q6J1^&#)wgA+ zRuCuv(fs`#(_b$h={$5zywxlyRkH=N;fs1b4jXX%8DR%H=fHS(sm5HAp_z7wG9)n1 zNJDn>%kXpTLq+#|Akx-pk|m3~%dXl19<7y4KWn#FfTM|vU}C$o8&lEebI*r{n>%B0 zLH?Z957Lmy%DS5QeN@4LX~2@>#ka4Tf}&+<=>2m)N9J}NI=yoZH*@> zKnl11vyaN@_I`~gu^#%Nx0D|z5VY?Q0i7u23Ut7$-(oJkh{ds<#UNt1`oqqjMxHu# zotAByS%|_iT!+u9DBSt%;GkR^Ey%v<+F`7f_7hwqFQ%h-IFJFV|n2$G~R z^6M}mbM5w{Py+#a_6Dl|T@(_?0ks2diqs@gE_xnf&8H5)GZuDI| z_u6aQ^D;dn`wkX@%poX=OaKGh^yuE86UT})XoL2YC#PimU|JkzN|-P*ZFK^i#CQC~ z|KVNbnN=}Q{S_^2|6paWqS%deMDQ5c(M*pbm35FHoR*m+yX$4COw6W+vBOfBBSu+L zHG#oDd?>q^E;mv4g|{LqJcpWzBAIdop*Jx zfDcA^h1f+B02I*aA8(82e~#vIj$bei@i;RJfDLe@YnT9C}ojmk(+YfuF%) z{$bz98<2OYV`?&D!hkWYogm+H)Ne8i;PgnZZMa(+4ejf#2P)cwka-mPW50v<^C}`(;^ESD4g^Ld@Ofj44XOzGzhBxSF%>qw1+PA)=|vy6P^lo zk#eK0ffYVT->v$|2XCJm_Z?%a*Qpxu@w{sU!cgs}udi=G5?Ao*HTBa#^sD6%V~N>G z+!%Y3n#D^X9O6euoA!eSQ3UbOyO#@@`nKrB5(um$Tv;_8y}e+!d6ZSJ-4=MW{JDtG zsAdp!oWWplDt|T6eO{9-+klPGYNZmE6gSH1VG$tB3=#c`QQ;349}_b z0|+xz=*UC<8+6e!NY$J}WR^rC-KT&3t(??=62E?^NaN}_GOG+GHSACZ4;p$$%jvv1|Gx3LV>hKUNKK9ZMeklR+ zYx@we5*Z-JIBq2Z1w}Y`BLaJZ1TqOadKp6V%~o5>bw)acS#Lz2o5p#>eboU7Q_id7 zfmXm)iFx;y1*3;*;eY(6+Vac80xM$Zxej5>3(RO?=7WbJ>zYgg9Agg+( z2lF>U5t8!Epnq*5NgTuFZ=rSRQ(6H6ET-GwLDiH<02FGbKVz7=$9HG>0Y` z|C5#Xf&}jL{b$U!los??i(vXzVGJinz&>@i?`c#cmT_l|-&CBcI>b)Vg{%T%#Fh5+ zR`5-I5UsE#aea~g&wf#Uf~Z#b@>(C06a>Q&Gd4&lXWL7-7l>u}tj_xHmn5%Ce|ey& z4r;rnkLvYx6?p?_4^M5-I9>-I#BW3Rs*gyi*>~^PTYivcPi*|z8gxn!;w#>q!rBRC zdr$1<`7uf#@Cp~PQ(-E!25li%5+nx{uHFz)Fgn7$u|de81Rb$l=hwxTV}}*g112{k>8~Y z(sy?g68Kshd9xq?)N4DBeLfz(?Aqz4z1%(^RmIxzeLUEr{U;EES$c!Dc1Aa5y=qKL z2o`$fNB`Ggp?ebv&hj-z_!|a1g@1x$so2g}{dm1aX>&AHBZl5W|0=V>pV(_c;RozI zLL^eL0p*jd!4zrb2sjGE^}X_;vf5B(u~$$(6%WTyYwN+}3Nn`wIQs%W{RvKIa2UI6 z^U#GNJ3-slSo&fS4WmDH`>qez=pU>0`JIuH{PQeb81hES`hNT}z&;=K0v9N9;2-TZ zZ}38u8@}U}NmmPoMhsHEp0UYK-_1Ha5w#?C+6^y)e}R6J9x=>))CR-1AHnV{;@iZe zlo}TJt8TAlm|bU}y4uOtXK@nK5XPp@4Ss_SWkxf<*Ol<7!%ik0?Ep#kG5-LR!HPIT zS^T!v=NFocKJ|)ui1j+AW)nFuc@ktYV7@MS_eY;XKlkVcj9$imnduob3IeRX)35J# zC@A&a1Kq22m~{KTYM=QG{^ftamIj6fp$P3iXRAqUjL@>PGS7ZN?;T&>o#X3U{n#0v zT;ij)<6Codoxu7(_lgW1+s= zAjaI3d3Wd!B)R)gF&M~<`+Y5?7#go4z{c&Rg&p%9_`et{R9|eU=8`Rz_9eK4KROLQ zDI|QBmT?>uq$c~oC0s)H7S0Pv-*vjjH+jO5$)xg_yw{fdnrvMW`tuXpG6f`dtNw}I z`zVT$jr`+}E4Lui1`SFMJd?(Z4WMhz6Mi@iI;#4k6Hg!U&j7iX`&w_#OtYaHUh(&j zSEPRVX10`0z!?yP)HOC}Cx3eqk>QF?$mN*8Nmz3RLnatVEPSiGyH^JF{|Vx~M^9F7 zYowuBLcG8h{b}~;kQKO?YwwZ3ipaxZeZBazGQOffhg)Pp(A zu(kgVNqVVu;3=k>rMge3uLYr8CVH-4bxj8Ya2+0*4Mpdl7agIMc1gVa3RYm?3bu6* z#N7&qCM@QAS6T=-71RoL_ZVIhMOZYbQ$0R#VP+C?&47+P-WKjf5v61j7&cBx)iB_7 zn@kR@3zPjaIY_-@V%?b240Ea-6YJhPc^X%sNF=7Wi(dNP^^W=U$0GDypk)$M#YWZ7 zSN1jRAssIOiIlNq9wS2}WS*(WJY=4~>&-r=&bgn?=X~zt-p~F0?(Lr*+3)G~ zTCeq5>$!&4T5X^ka;ui1l!VKN zr#JkpxRsLSK$PPE{XQ~YDsLZ>TPY4nF`fw*at#s;>}S(W^vnsV-_XQeHGyVokTzjNAd1sqg?dUD5ZqPuIak3SJC z@)hecI;3bBP0H|Ay-zt`qweVJ1NTVoAfRcag4n0{8__prCev+^qP^V;;e1pPf|mBW z+en>fy0(1U5%ya)1MWI;>x{11%shM5CMzK)$YI(0uN+27(?ea_V_UQK?1=n>;z$B3 zrCC_@(n%Cl#yQq{An8o^_TS{|E`kU^lb?`taf47rKnsrH#w6w1k8!fN&eUgL*$0*< z(xOOH7{fDD`QrS;R?a~4U8B$#=9~^g;wx!=l+=OWsK3VQJkMg&q0$oQo^j|2K3RR@jJF4$GFtA<@s6!xCTCwS@SeH;B*w?& z`lq-HN|tl`Ff^F+B#-N!#LyOM*Dgj?*tFHQIVX!bUlQj{-PcFybg9N=VD@3j7ww63 zhY=KimtNsQ8IMI5Z~5I3MG5c-HXm}ky&TIjn-Fq#8k*2sWe>kd%&b@&pI93&QOefi zYg-^rGOYXzSy5fq$&j7mF!Euxc$@fS+F{?V2C4bqs`{QQxk3-%fvo*0YN3kKw}PeN;Jg6Z+Yr8K_RWI%viG$WqUEvH)n}sp9HsCb&twVK34TkL&y19eB5P$s(nV6T*vugJ$7UOS zZ82D!BreVaXC|j$LuITkiOp>K&`t;6z+jE#akJH7G{Qx*I_yfMXMW2CDB>4|(-l3| zFQ!c0E9m%?+?N`BV*`XuhVJ>g+4a(2estK+(@)*KIr;0uT9<)IXiMy1nga(REq4;K ziLCnRDQ%3m!##hnzjmRi{Bt+uipl3G0;yJY5fdil!^i0orq-^4!9xaViq%%1{RJsl z;bg8aRNS&2xvz_~$)%ul9h6SL7iiEtwPrJ2BSBx2lhn`g zz>QvIW7`PK&TX;3lveZHOMWZxU6%ridLZ%JYb9~%9=KDNtg1KWNPsdW%`Qq(Fh(IW1YV2< zUX(GD$6rK#J9I)4Yk&K}OSBU$)_W{tdQM?u9v6r)_udYNnh+yI-{NUtvD;s-zEVoIdLcNg>+oNQx`=rABw&izqvfUkW7Iil6BJ29g-u`;jEsc^CgCNkR|T3&)?|T-4&# zGI~5!cN1sl9Up#h7Qbq6-F7by)1jVc{-F3YQ1zh|3)|+py&{1P{qx27FBr)+J({1g z$;t)8zs<1oC)yIYkK?%y=2^@K6S;C;hI&w~m`7k~G*6d$W49Os-~WUtzlQRUSml2M zRVGFLAXEN|ZHrn5e~@n3=|7Qfz2n>eF58YD_zO9kC;wA9J9)?dNAV^i{}b^xe(du`9+j}KT#-F%F3a%#pMs54sU8MkK#*2`F?Flscm?~ zGXt=0NTKHBrnEOxEM9(6(-!{l2SAnqAfe^c^dV10^vZ?U`!XHu8wdh(5O;EWzGv0G z4eiEEl3irmYOAve4?T(Uc}O}%<2Ka9#@)s`fOE&z+L=B1P;M`S%IgnoViXt-`jo(k zsr}~ga0MRA@=j{Jcz_&1Jy~dMlX(dbIuR72&kedLV?p$E3^f7Bm#6iar#6P}MT)tH zwv73-J7U%DwXZF;Zk8~3irD+pL z-K$v&#{<%nJiTl8x*PrQV;w-}^Rz!oe)wJ2W3}V~%W;cSQV$*n0U>Z z4P%1K#oK2vWzzV!fFAHi)+-rV`tAyKye-8sa;ej*?Q?1uQ1gd!!L)1|=R3P#c8O$R#5nVPQy2-7a znaP;TLyGmzU^olZyVG9Um-TiUhaEHEw@JZG9+QJV04XWN` z@BOen*>D(-wLA45Z4z4&cMl-IKOe{yxO?0+yD{{qfnIfs4#R@dmMf5NeO@;~KRtal{wSG-cdC3=J&{?7rcKN_&V zM)Dtx`d>8pzf6*N+TRcFmZ$F!tWHBQ@2=_=`!PR;a!nqI z|G5~2c+B4vqyLV8%wKHKJckLE6lfou>0*PD!0R@Lu=rnCzlGl6*w?iwf`> z#*7=A@r;e%!q{;b5kv#H!fWQ*9P&1YU60Q9yVZPn7Vn7|=lOs)O@58!{{&6+*Sq>5 z-JBI_d3u?RoZX0+|Hd`);1Oe{LPC`P|54`upOT`>(7%U>?zdckA0_H9EcjoH1tr5S z{uplm3+etx^Wm@H*FGyKa7y`k`N`DR-?qrrGOZtc#ssKsur-81Km0iv;IHfnMX)Cx z6@>g?PiS!hbPulFfz zL&3t|32ci@!KlMLy!bc_>6X3^-herQMxVN!ri1^qmNevSkc$T5RiQR~E&ZwvKIH&m zSI;XzY{XWmdj|GXv}=~>2Gy|&l6Uu1IRLaO34#G?G z%<>ed!@$H5p?YtHH^^>+wMJ!s2mfhaBJWe*gK+zI_l|$3*9J+FK$+)--&a8|frEc6 zZ{1#Li3g8oCW;LG8$@i{(BEzGi zAatkhZaewpO(G%l$%PdjOX(Llt?yI3hC|wfA&!LiVQ<**_juMoQbD@5UOIl^hKM@# zK1PHGZSqX(Bagi50kfLZzk?=^;&xanQ|fRJ$!+i-X1=x^8{8miMZja|C8)f(@!l9| zFlQ+uEUGvD!lilS$@dO$d{5GmT!v@I=36OsVSg6zK3!4KydPVQh^L)Q+>s6f`5=r6$$&(fji3p#S6gha*U(* z=JxrQv#CIaJ&SPkl5~gdu{zrsu(`D$dFI|v_B+~u?PvQPlnfbPAYL+n$UXJycaeJ% z2T{o_^)#C@KvDq2j9KH3-J7xv*@^nO@U$(j5_V7th*A{Qad<=4+++u${3KHF=h&75 zOra~I(}(yy7L<(V0H9su%@8TTozk$lgV(sk;Dg(a3&(r2Dna;0__rV19M=X7PeU$X zq{RR+Y87PaT#Jp!Z^jk+)DyeY1)FzzEZ^b95B@mBFZ~<2Gw|{rt`8!pAn!rPAy0Z3 z5^tR-J~Vm}Vv+Bv%zukuctV5~bzBWkuQpi6bQPT+K)dGgGyRc91gM`FHe7&9 zes@F`+k$dH)Y|5fRl&kONY6U*`{>{;un$7F(?i}Ecq_5^CCLDXbYu6fSv=?XLcV|y z_2&9Z?tvxwyEBx!_Z^P?*|rCu^(WgNT)!e+@2SyCh$2n>YQL!o|CLP3U$Oy&;~%4l ze^)kmpZZZY^p4;Ao3i2WJ6=S9amLSDl%g*IwEZACjoIYFf7u1}GdB7AvAdtsD*56dDpoqA#%J7gL4OS>IRskP_yGSiGIsOWY;r!Vp z#swQN#p5Eza~v_`Bv4y69+#YjG^nPmlb)M`zIYeBEC7En0DB6=^&dUDKi$q?NB(?7 zSnnDc^IeHgYQ96a2oBo26}RNB!_v^yZjf~%3v4&F@^i5tAx4t7Z#vb+TXMQ ze`Xi|QiPiV{0~O)e-)bmy$z zSPfQ(K}#;Tk!XJjLuGri_e*%)glXJ^JFTiF*oZg1m^iHlvqnd49j44hKTvEAZ({@*wx#^%61EvuGn~t+@qwI#fUjn6c<9+t> z)QxIzk}Yt&)I4Whw&OP4;nhfTyuqmgPsZBHMQ(mUf}p3o;djdmF;Kw&CDqzXen#B$ z0E|UDj2l1I(=6GS*k%lgx^gPthar8BJs&hsU|IR(z_>K(a} zXncJ)UaQ|iCAu&M_7t5y&X0FKZ}@Q)U;!mC<-8PYS)8`sarM1I*_DWjK>voyVuObV1 z9Hf(fx6~|kC77-e(rpZQ7Cz@Gtykqo(0W+(lkeusamx3LU+D3S9#m6n`Hk~NX?*N1 zTMK-D9X8=4aN3hg`)^2Qz``|U${65ZxDHIAX5i`-Zit62jhtztYE9+2t_4=fU22<$ zQwKGRv!-S`oJGDTRAG<9Ryi3Pd@3dZR!qh14&JlMg&Cy`S>U@zn?{D;7Pmb+oYS4TAR>r!_VAIOna}h%*6D?@wg6|P|}~!CqzL-nopD5J>R$)!`g!J zgT?P}T&hPZ-G>o#e*FAhVUwo+?pofssTHCDqcU0|ku$7oeXKrT#piP($GbC$v6Z_f z#d@a2N+Y^w;+f>NAsen26}{Z~)d%V+?OHe(tmGkCgPPClS@n?>{}#j_njnBeb5bvi zUX~E)gw6{g>T!?mp(b3uiue{S+$ox*3zQdl{4zsHc<5!ixadQH@0{-quqM9x#?#QKDl$Y zmQ3%O+#-UQ3yGDGVF`x605aH(O z&~g38gxAv~*4G!&nG|O&?!ckZm1a}73)!m=Mz(>Q@ym|gSdi@kE~Mrj2)Iw%0QWCI zS|q3JCD~o~2Ezj`%~W$V;2x_XaI20UXa1^2|D*T+@npi=dhW%56}ls zLcdfvd=P`A*Htk-6-CjKFzxhq?bll|xk1Z$Yl~+BN^><@-lxNIVI4-0z1BYIX@#Kr&hs4NgZV)M$jHw&$u;>o!+7 zxZRezmhq}gD{_nH0DA7LCNyp}O~#qoaTA}ri!(!_9Fo6pSWZYFOitsF+g%5IXU69L znjA$0b0Ce;xhJ^~CKbuoEE_%bsY3M~g;dC-_8 z;5i3BB#ynrnPnssYlY#=isLyRN@C$boWCl&=WUn-mx+%mh7Vl69a_@nzLGL<&9C*r z?z)FCo6F=`x~N-l6SY*h&lb-c@MVgvzya`5`C*-ijTlMOoEA2-y%~Mrw6gHt+~0Bm zumbuG!KZNu!SBj&+?PB7kJ9o>bD$Xzjg<5_Q~LL_-Gj)?(tSD(dLDzZ(X9JNz*X${#2p(5&3C>n_BG z1=upDrGsJelD)SSNQhSrP z^6jO@Kw2;3Q<#U5Fe5c}J8*khi82UcO^ z3V6DaV&i4jT)ZYA0TNA-!ARQ zf}F%Yq;6?5tDjUW;=H#Fq*cY50cZ6_=J05&jf_qKRqsG9SmD!;q!ln#Eu-DrL zj>+ibBeLMf@^9tmr@jf%Dd%g^XamDXy6O*pJ6oO%F4vT11EA6)zukwRa;N=^uqng= z4@s_1LhO=oW*Xz=bkyGF`EN31V`9J?B;_Jcje(OJjIXV$*s&1Rx7WK5zBVR}y&8~W zqN6KMb3BF~nYs=KrsMroW)L}y%L3cdh{lk4r6n?>{ypDuRLV2oy2! z=0=Jb7;6J{GsYFkOWX#fSs9>zeR*=vOP67_==P=c8R&OlC)6LT=Hn~+WmoCp3Z``1 zz8ssC>Y|lutNAVm%NlnGZ(Sa7z=^E7KrL|AwXvQx^SGW0zw6~a&sQ2GRvWC4g0|5M zZ}<5?kmr2qMj(z@=~{`^%2WkU*L?ys1#q<;gaq+R(-N+;QOJrFoVp{DAPqQxga0mRQF0-huIz7D9?I=PWQE8hob1z+rBEUR3?_`u!;jr>EldA zlcYt5@@JDRyTX?@giR^A;HXx0&uUi>xsuzr`&sKQE{m_!tmZO1EEw(KP#5h1?HOJl zW4-0}7K)yE*B;HK%Ckj(@Xxb!*TJVpx_QghTOtkB96}_PVkHrSG`IIg&qJ;`UjQjZ+yMlP-&yUW0CRv4lUrucYd&}N-QLoAS=NG&*RKCw*$lBoM*Ehk zz0x?w={W{Yx@sueC;%N;## z*hkoSAkItx948?Sf2Gvq2Yx)BDTf z(=kQHE^}>{Emu!;r@|0e#24QwQMj*tbr(P9JeJWx8qiZ{gOsI)8r7Y167q*-s~~wL z)B%cs%QD;Y(2uYbXD!%L zUUYNDv}n$>6gp2{#nv%)Px@Rhbi1}9FIF_~T+~up#5s5EPXyiJy^4hDK@?K9MU6J4 zy3J00qAOZRdk0NYc=KoCc)HfUS~_=h_^>Vpv9>Kua18AHdWzn=0M0u2L@(z=ccjcC zA@lBqa-cS}KAn?ThUbMuzaFinUz<@{w;fv3Y!G;{48H6K(EliOmbyW?_TxstAPF(C_fR*%507Ld(m!7mL6DeK<40?9ed z(lHV%r4spKZF+0{dK5iN$vsZ3Uyz=ClQoc((BbYlkK`203C=zZGdy=l9XrPxz4-pn z+KtH7=((k^3eii~Ag4l%2`caVj+v5`ijQ4UVq61g<}^~v0+}F|UDlvc6Pi5yhmlf2 z9nBp=`1Go;2X#}=!>P{93 za{Fh|NLh3pgWgphJC~1Vhum#OwDho<0pbZ>l6N*3boQiFE6uzgjm^{iL_a7=^16L$UJizMcLe%Wy4(7^yZg0qo9I09DNyWI*8sibey(R)zgLTZ zt}!KlXTUJR=N$V(;sRgI^1@cS1jRo#qQ;u03$H-N<#|szaBf=@9k&i-jGBJ*l}yR8 zO3erQqq+?pyLEwW`)|1b90Ni#Ycu&@#VnDYDh}tClPz6Gn)MV}+!c|`oMs)%E|z5k z>y$3k9Q|CoFf_}$ROcIz?HlOwg{>5N2)q;1ugx!%~}>vU-Jq_@-oFweGw`AEaC%MM&hIuabax8vWE9f5jCtuPu-U*pR5dW zE58hAGhl32^vaigS%vYfN=3kLt8?8Sh|R$2n6v6L`i=2&6E}3YDZn4|n@C1ZUkfSg z{@E0V0@LN9fWCWE4WCwKkha zUI}hA3>35P%La~KI^H_P(|ZoKP={T>x@%jl=lp{ywb2Tt^m*ytlQ5hQP-iOwghSWU z@v}ChB`IZJ`>uohW`=TEKi!pS2QVT5^m-rtc+NgpeFXhfO7G~ceCm4-ty#C%g>xp{JjNMdwG=k>Jx>C_ zxB&0Ma5ZsBiiOdaBS2rHV{4DLc;>)ZZTdxJWAxxBH)qSQXaQ~zRZ7=FaHB%wNPd+D z&VD5qkmEedOR<)7HZ{%r%2<}tKQO6zrMb^tb{7!+Okdl2er+JavfIttw#_J%Tw;A% zqNsUlI}~b7z2!?}5MdmvfK7nBjJe6or0w43Ln%jm&LcYEy?C@&TKdp2iTxPHT-V$C zlnW3taG%|a$#aiKL{j#*Dy}TIif_>S!=!>}1B63|5U~SfBI0QqBcRi@t+#@XW)@A? zjIndj#zKqyvabqaw%bjo;mib+GgVM<*5BYxTI#C=+$g79`R@nlF~1+(EP!R2b3=_r z7Z1R#^KB^%-ePIg4$wI5(AvcGa?ko&a!h%J_}t4{KoRTH;7@C>QaX&34@i3J0x6@FLk-+?b~ogl-g;M zmt{!bXHnin;c^TSE0`x_?kg4UE^l|oe>e<4?Otg9#bs(UO7ar%rTV~b zk}m-gN-m$Mvq}oo+{W!voyqlP)%E!MsSHJ6kBp@aWuD&5KN`IvPh6rnePrqPJp|0! z6`bTq{CkSErcm@yD~BF6tpt5)-e$XT3 zg3NZByVuiLCET7iK39{UJ^R{N3fj}YT6|rlH;OBLH&7E{w`QB}Y>@1cF4ZbndtSBJ z-+5-Ro!QBDmO>?Xf3UFpR=JR)Vn&rAH#$|>$wzisFDGr`ilGLI@l$$pW1H!x$u7U{ zd!JMFSlX8+!gk<8q&##;db#9%!9p!fUusCph=+%V=w#x3*@!NTAv{KI=$YfFfxE|r z5PDlN3kEu|>k?eOVbDM-o}SzmCFYy}ZQnkQL9Zz_XxR9U-aMurI%U;AS9|6?7t}=i zY*p*XY*-^M$tiiOpJ!fPUX!%}HFT_ui(3w`c+$Ls_+Y;Y$sx>$304188fpnyeAhDR z0rKlv4i!;x6cS&bzD=8__Et~iD3BB-0mu#i9>~46` zmp+)9yv;0E`I#Q$i_Vt>WjBph*w~6(SLI|i#-8rWVo`2T>0%Cvs!m58Oa%rR*p*3C zB5WAd=O^&!sHWrMx4n+nuAMw~@!h=}hgfgz;0`bQ%BnqiSmLFi?A1lRsFUK7TMgJ2 z35!cF6P0Me1T%=uD80uTo>49?FO&Kc>@Tn#+J55J_Oc6~w^1;~Km+?z^KZ$fJ{S}F zUr;;Ld#oN7Mdq*}XOZRZ^aJ{xtIPB9{m23kDnXF~W7u*UM2UJug-;490$J4_30c1; zejm3x83p{^UN>E=?P;X~{B0F;NO+gZ_QjY4wRpsYe1?C5&6s4EW$=2uISj$ApKPfjv|_ zQpp(wg4>}uq6lF^-}*_%T3=qjTcB=kg54oI8^W!l!TEqo^B{EO3ZegU zr#1$hL5=_K75o*)t;I8Ak8^x$}v2AS&Z-=%j)H5Eze11MAn3ud!~H|yDSt-ez{g1 zwwNs8Hj3c4#Z1P~r~Xn*<+uLo=a*}vRF6ugSB_sIe?c60Ic)jnPME#_&aNCy4cse) zH8W3k5E?W^cfdY)2C^^0)uPCK{k+z?ko70;y2FY+Z-lqLhsj3S|t+#|R=Q zN6H__mXjQR4NJXMws;5T&hi%dEVH)VH%NK(?;b_R5WTwITSoTkI(%4@Q!V){KfwXo z8hZ2IIO3(Z)jR7yRRk(Qdo~)k*YY!x&x~s^WvlG+7BI+q zs=l>ZMB)wvy(B8h9X?0I=mJvYMCBSg!+?IJNi@HyhSSmv2lLUZ$AkJ<`mMN?qC}ls z^V?DnA*47&6B==eNQ>YCR-;2&pVGVRl(0}6I}Rsll6EfVDl{bCd)2{VU!#Qm=+3dW zblI141p1lsp$CsKUw-XrUyO>e%pthSt&tSg8;$9EA!|YGT>#Yt`)eXJRbCs{KRE-P z8Fx*OLAO1$vPPkWw_{CxtSpC=7rOmzziQA~*b!`Y3yzaXl3^eu&BElFK2m_(e17#@ zEus<@pZ})STGLb?bQlqx(}Z7j1v5pDXyBgE>SG0jP;-)6`8NqXc5hQQFFWY??F%Y6 zi0_K(^Dy3aNyBTq&cw=)=LLT7CZ?v*Zam%`l7gXGM_>}Tocaf z8|U;q>paqWid^?aw~Gy~l|_&|zWRVu(kQ$ohEKxi$-21Plj&u}kC|7h-@i5c(sY9} zLy$S>3QflRqUDo}ScNusP!n=rT5Bg^KWo{Pjs37MpuUd`P(Y(NEij)pXHe%H_7kYv`KpGJRlGBb^w>k7 z9%(?MC$bWsoVP&< zgg|MPG{=M_Ng;&NLZlzMUZM`h0NWhI9%n&u#i+s4C$VYMawa%nKfCUY$FAblc4B8zmV19N;{MB>l zm0R`ce)iRVxxPwqQ=cSup{v<$t4l8L(-hr9*-Dau(iP3)@o|LDI6G6C3pkp_8^WK+ z@D_9)_e{?VxTmLSd7|wLz3Tni$9rk^n$Sc@uTOlwyfir{mJ`P(HS~Zp-+9re&~?!y z>rq@TYY$O#L^92qS(ym52@;$Wy{dr|1#qJg zQN?pUAFKF^id_3#P>s5IWp6jMD!wzqa6!BHiB_sqj@HZfN@!~t#zE7*k{gYwdJ=aC z-!d6O57%QpT90A~(*@{CH`GR!Mb{uS!PX!eX?%V^jpL}a4_W*1e$}KcREIIufdl}c zyyJj5eByE+n?y*pD!;1;p#^DFO^HS>g&>zgUUeSi>di%zf9}ivpqD7BWEgG0iC$1k z=#x9z&hzLMo~}%|9eP;k1I4-(S*owEZ;ec3y-zE_kz`!M$XG`fkrkcm*Y^+aKamX_ zZ!KPPe~l}*?JFTVHpkCY6Dn+HvINa_={c>nYARKB@j>-3FaJT^Ueikr%(ENH^!+V0u8bgYeO@3h+WH_RN0Ue`)8ZbW?^deg6?+SScf-jI8Eda!X$x+Eib zd?>-Vi{#AbtO3=BmmLU=r@xfhj=gn!7uq`YNqN94C*kz;aBz_vaiV9w-N2DCo&1N2 z>u#+{r@fw9oJl%=f?(L6e&2$TdGV|>U+VHsC;CZ0aU$0JO$4=UivMN{L&=ZvQdx2R2Xjuch7tE@tz_cGvQ-00!&BEEZ1nSp2&$0LU8TL`U`s61fMxT4@?`*ffB7>oc?-) zyaGB=P#<~DuJ}mntRcs_?6gl$ndMnP7rtIzU3Mt(iE&?F4Fy2KF8M+`{StXS6QK=< z-?kBcU4lXf%x8@Cn8@{KDG)MwIwy!1PS&( z$3A*DSu-8#5*=w)bj@G95=Wj-HZFTe#CjEclKN+ zB8!8akbog3mg6YFJuD{G zH}qa!hr>Q!XxCgf116&2nz)ng*g@CDnTPRFx=Qs=WDy-j(`Kla`z$s-Hc#zS@VmkH zDY383+mGB}rB+IM^7i?C;uZl#t>byC&2sZWAsIOiN^;S>yan#FJ{p0Kq#bJY?vFpK z9+zUXuZ?R_A{?q5jO&E#Fqc(Wmju%=$yCJIx1`I650XF1qf!qT$;|!fLY8 z{D?%IN4hdT@6s10l5A_GT)jdCW)tC8IyW;u-%j#iK^8Ai#UNUXU=^`?)(qdWyh6z! zDv?b#(DgErAA;%219t^_(AK4cBpVhGM*iX3YU?MZMYz>p_Yx7=6S!q7F-_h(lEIS* z!D-0sZ_rvDMhO1Wbp?s;_j+^phyjb*{3ViPAwCICHbg=E%`LW$kfS z?u~sGe8816_u2x@VkXOrLHS6Prqo2i$S%7xoNTou&Yvgl*nXZl;bgVALo8>}W0=xH zDW{s|K~+?@wfBG>E88lWvi47;p7p zpcvZ|;&^RV!2g9wmFgYFN3;TG0x30{A0sn6w10Fvm;M%nZ6)uvp3o>RMwq;}_vJ{E z2Pwibr05{uVv<3`O0i>PLjGYO6fVal5nL}nn2y>CmW0-I^zE*TM7B?XBElqF^GZmv z!{F7qT9S@JpvXC~7qHC^H?*6Z90eUzKqMafh03aUy+N395bU zi^_@mmddW_{pR)#Wf=zj?ez^JlyRo68h+21_L6#QNu*Sg3@X6bXLnG|`O+Tpfq^lw zDUR;t(m#XD+2l*BrA9G7!ub53Dr5DDW)S7;6mmq)YKDTm8b8W>pg-Jv{+DrWG znQj&A-0Xe2#v)_h6FTpBpT=uhf8L+6iz}(KU4dD$Y*?MFV$eo2NwO6IQLV$($?2#R zWbi2r+AMTjF_g$4Wtmz@!F)mPxacn$7(t6+g`de;Bc>kZ45gCXS2UGAj%@KRGvdqlShMpyW1yFqzJ69Sp|K(v;R7r!}*oX>S>OqZyo{6G|&3<$D z$Jv&z#2Q3$xZOKm!p{$73*_86HHz+OJ5M_;)D87e!aBYSuTL?N&U_!EHT9|PjZuej z)1>z8Zyvff4-Ou0yS;Mymg=6koUVLugTU#_mWleCRm`+w&uo=sK{Tch8NOpL+Rk^; zkm%eAn1k-#d!iH?I2V|^fXnPax0YumB6fqgKZ%~#kkms|N$RyU!6c7FL@-;hRsAOw zZb=zch%n+)BC&i1LYjDuGPBCk#bwpZJ`DD@t@*i-JwPmJqhNn09mX3ri< zCs@_Ix@2M&HK-)=H7O`1)a=ZSvr40h+WZ+kTdwK^jGQ=pp^T>en#;5}d1|YaNi7Q| z0szvmfTj1DHMKg~;fINtCDAWsAE-WCW9p{2+)95Ve5<0F5$B_ENO6gtkSdML5z=1B*pbnm*o{f6r0 zcaqPKJbOl#^Xjfn)l*@H0&CqI+Zi%f7siV=TPi2FUSkJ|EO$?UXs=w<+cd(pl|JfK z=SjN#n{ow1QowqCtrlZ)ig!`btW$o?b>FELf)|QHh4((O-{#Sko@-l` z&s5H3Vks$_Im)ZHR#i3xz;#$HE$E3tDzlAu&V6HF&9N9-X&2hD+Wgn*ON+%HlV3Z% zH5>XW+J8q`$z5%$cBdrkpgiBKR*;IrvFqJlwRHz-l;}B>ZtM=`GnW&SI5^74)q5An zD7=S`4vc5d^^Hlq8}pr+tTI1UEkyiTB}J8p{MlF+Y0FUA^?mnxhF;~7nu?hrt_3G# zSqgkQQ3#wz%v|Xd=9Y_qla>($p%;tce1kJX6HnahI)WLwXV5S85|xXrs;;+Zq(0yg z@TF;aPz!ySM;vJZMT$iNXL1Y)Ir1Jpeye(Pd2B>e_?5{%v7A|t@PJ{K6lr>m6lSr4 z>AJ__)}l6D1m+xiai1uf8l(-3N7Ww}l!OpDtk+#P9lcHt!|JaK7EswD?z;YsoVjOm+b;PR?dZjuccU70cRxRcFCswG&c^eJ3esHq2!-d zU(+@w4jvP-xw)nAQq_I`kC&>CI}fBjBR8bm-+o0fPJ;HiZj@Jdek}Rt9VEGxHF#wA zQTeKvhNJ_+zqTtZ^BW;VsHJ-DPnTuj%uK)x zWHcw@9W8cOd6T`Ue99@9j5{zqI;SJgxbMKnq+BqKKe`y79BQ6fSu;GLdokpOHp<@G zbt%XH*nmST9br9F=d<48;T5GIFxU_%LiOaW zIDm7+h}aGMxPH!UeaSx{rE9O*>5`R{wn3INDeB2}g73L^o(Fy zoTYmBU6#CTZQaa+>&Z(d#J9)zO!w2KsK_KtA01?ytu%6sm9II^{ahpU!Lg%EwW5iu zR(TKmgK9D>O6qC2EX)*^taoV???+}YrphYr5(A#G`a5(ygI#$JG~8$T45GZ;Z&B}S z?#6~*Bszx#YUsV~$_?|gC-w*zTBkPBTu=L;+{E7wx@pGR1q!ir1K}E`-62G$MY5`m)2&|w9$Z^E zc(d96;lB6v-9&{NGa8bF&#lCp_zL*#=a_GL@4c4tWIcu#Q;kdFjZ(chQL!S= zx78r>@e9+}ApR>qE%aB@oqc)X#-So1$B#34aTeSVY-)^?ryK6f;VrvuB*szt`bq=F zeZs(~D)_O~^{qGFAEryaakLj=*4`=stP)Y28cBnn*z8yP4r4}^4i8YhLQy>j(M9#V zu%?jSyUE;`Y;Y%7T4W4)?@4wZNA~sh{r2~1>w-{^2@C?0yAyUEd%*e@+jIw}HSBYLxW#vX^TNf7u(<0&nB?gJ1Is4n>Knv>b$oloi_eb}(# zDQLuw_ij5>u{$2NcNaOy-%{Hr*ica~dqFG5{`P4LyKS#yO}fIh%(hlY&-1jOwx&$G zI??~x?(wtsTM*H!ORT8nIR6AqGqNsw%y+9dQQGQ}Ui!VBdnWvdl5f+{Gv`}~AuHKGV#w&+pPu*ibdg2a?7+>i?Zjwg|prdA| zYiV!dHB-;OcAmGWIgG}NTzHEyFjTZ9br*TSqo)WJ!-CtLSI-jnn2uMLk$yXOVx1sh zKX%8+t_i(@$9t}*YS9kOHb~xPmNk@*E9@Dhmw8j;@FY3o z52Z7BG>YY1;oj#B&Slqj>9jo`^Yu1`2$o8HE^R`&Wlh|X+VQP*1Mlt(wcnb)$8&A* zevbY@&W3#9{#4bH7ww3dJqh6`6wtoe&9@s5x3X*^R(?;FAijz zIuT@9m+a2PRloR@(4=omwb{&> za{~v|?+(wHN>5Smd%e@E&iHAp-l~=L4Jobr?}Z+uDntkfyxD$Ht(3~`!2u}qWjom` zrONuX0wQj*5Bs*83lBX%qvet2#K5{-0>Fmq8Ayi*8Saz41WifW_s6>nOkZyC&8Yt>&5(FDbnK!$v40wqy_(>^jDgKuve9h#042#Muh3Y`&SF{L>w zzavKGeB_N9{?-Hej-M_F7p;2-pAuK=HY*V(6xd%axNm)7?YfNSh#Z7msFzNHrQmpQ zimF|r;PW9}aVL%JfQ;bLvT3!_$C3L_#PeOcYD#1f^q8e5!!o*Hw=12}BVl@@gLm(n z%$O-&NzaSOV2{(q(ecvOS)6K7bqazh;+AKEOWXMzHGy#`V zjq18~f7!F`(E{lRqU0%aObuPoU^JEgB}NwTdbt)=6{UVB0N86ZaQyFNB_Q^^@78fI zSSGbNVqH~{=<#l+deFVjVCRz%-%g*}xhu-Kd+rLySy+1z6gE#HG zT8;b}(WHfzc}O{KRFH>DSSWokXa6^$z9Y{ED zKlnUWO$opV30^;a&iResDAKR8-!DXKAWDSwuZ+8$x5L?b5$EP(GNqs+V$!URa!mF5 z!6q}4Brt8JM+TN#b9XWNa=6Cl+=htFn0vcW%r{`yyXp}P3p@ij3efoU)cKhN)^zH9FI>o++XuU35Rzw=q_t_xnH8KGU6 z>Elj76!CQ}3TpujvwT*3<S(=1W!*+IEIcH*#bI7)r-}XCsoP$p(oG^dazvW zZf=1Jz^2WU*II^ZX?CR5bc*C*mI3S94tV^Bpt!`1zsdv|j)1_U$Qiy!I2a%5y= z<2=CEasP1q7LuxfrOntXrez*cRnYNrlYzYTH87W19U8BHRu7af&z^f?7;}P?tXHSi za6`@DySc8S%!6E#hv8{D366^vg&ah~!;7dpk&p!BITtV!WovA(_E3~wUIYCzlS>_N$6O<@Co^v+ma%lYPnd5VXt zFqjOWBfOFc@|)fC$dt$}S+$iGkBmtFOSY3K6e@X9}gJ)j&>YKx7-n4VIP;o6`NPsu6HOYZVHvR-19X+?z{cQa1>USP7 zxcjoGApWodp3^{<)oFCdnsR>{Pj7TXuf^F`Ud)G<$_?ls3g6sO=vAvbmaQ|qArrpw%=Gc z@9{qae`y_@w!CTK&}T*s3G+5Z2#4PM%@KtEt8whVG?&nr{;qG)=wr{37p!mi2Gxrx z?ynytKQuilJXv2;-B|D_%nk!gCv8LA>pW{8y|!Hd8^%XZODkVKw7+_(pr4DBXg~HP z^;zM=>SbJuykQaZyC1{*BGR{nnFo_x`?(qam@2kqMV}ZT^fMAwhkw{ADmbnHs5`E~ zNO^buFDL9Ufwldii_5Cw(D-x%LkG^V&LUk%(#ndfiTwWj5uc?HJI15k>(AT%uS^Xd zPZq{1^xnu6!|jpkl$WiWVhsYjMei^Zz4dM$Ni9LUbve9!#%yiWpF8=ux3KK!jSGZC zmY*Y-5)3gDEP&1%O6@<9`&zO;4o}e4W`y#4j4$7VT{30@sNIO5w$~PT zI6ne^iPY!kG}*vDqZ~=XALy%`gV^z@-h2n&cCpDxhVLD_UbAy|j6cwXY24i0Akw(H zw)fX30Y^(Mbl1-N(<~ARc(v-)Ca>2zB4_t!DuQxS#@>Y!aL^m~g~NRdIABFj@G7G)mV0$RwyTLKP^S zp?4@#ir}1dPq5T8+4W;UjaK(rd5#(J+<-R|Dz_?s&Kldb2zdUO(Im#xrRuz*bY0 zfW$%Bq2_}x$_3F#rg#AIF>2(`2V=p(YtTU*@v11es8gc5((@4 zz0_+SVp9=IQ>~_#mm#SZX$>e+ENX4sF6qMZ$_n?{$l3ykc1dPaFPS;pKO9T-3*BDT zOFG;Ab}T9-`Sc9}J(AmRC-)5NMC`QSxXktE6gYl;8_&(ipD=ytBh|$D{qhOgo^ez9{ z*qk29X+&}cDOG1Rl}#-ppB!a2n*L@am1)i1(-2XNO4RiO_!!iHc{VFW z0~1esIl9VrglJ;O(xt;s(R?^5i|>b)-qy*l@7%5jTK zvPrnJVM}#a78MnB4A-Bq=2Qwi!_H@0Z)%MCp9XNOnJR@mB@Mhw<}`ior_KA{q8F62 zQ9Xy@&6Q79!BT+9g`p*>vRi5%1IDmGM#uGUSxlPcuTqU_{VQQa0YO#br#mTPu)Mmt z2|;=OqH>Rnmjr`UK&}6&`3TH#IJxM%p%71obCg)6xES+bFz`9aa0BqC6ogmp*IzTg` zaD0|XgnE#kK0L36C_=6e6MlF-i1?-22Gq|=!NRSzN#0fCD5E&|_ynmC*nKr-xPD&d zzWx%3?UN0uY1N3V+ZjoHiROf2!(MNe6a_}w<*nn&IYhc=wsYBw!xS3bW_#eDs?3z@ z+1kDB+uER%45qKX9}+Cr?+}3%dDu+Uf02RDq9fJ#6?4qF=hY)@X5Y6sj#^cX;V{_c( z*BiF{;0xW}vyc2s)m~&u!`ux~C7OVy!N$iU`VY^Kjpxu5V^q{70X3`6lu;FD7BOS%nbM z@U@>ft;je)YSZAidxF5%4o=xqgo%P3bhcri2>+2TZJRHyQ^r@fxK8k``cC;n}4Lr^0WT^kY zvRa-%(-0o*C{5S?^8~S3N?r3yF_8q5eC+=wRk9Sc;7mYoFUznAthEKbsOaj1Lr4vzj_#@s)*PEFf=CNUY(t( z+IZAYy<2K_u{>IAk}&hAOXGKv*6OK3mcoi)RJZ*3dDK+{D*vHN z{ka;1nq3qBG>2=Dd=J_GFfeK5MIQQ`Cp0_i)1=eYqD!sgABlksdH0&JpJiK)RdKN2 z@sJk`9At{eo3f0)dZY4YyM3(4v%a5}&$fY_$%^WFG|^wKp+|5dO!uNfTrn>JdXJ-& zL}V!LcW4F^cY*XA88R9&KB4-iwkE94DxWw>mD}MwkATN{p*HXh4u#|#ZA>>h8qe35 zM>!V(+C(i+7B`gmC9AO4b@WqG0V$+sWN}AN)VdtK_H3+$JwS7MxtW$j*eh3uQ{r zkqbrTWFkozOt78u0pf$TH;8=k@=v(DalZzzX>)EkAaQoCHG4(Ee#ikH)zJ5T+Av-e zzlgR7z;LBGjKQ9$Jo>uQ5b3agn<`14;t44)-`bAxu=|D+)1s*BL6Wws9++{?UWZ=S zcVh>Vw$3F2DlQuF{=g0fgQX;}_ulRxcZVZe&NQ-o4l;z8tk6}3)E&9Vl>wAY6I$kJ z_sV7J6rueh9?a?I79})B{BpRN%R}GMAi$bd(=j1voFEE^X7V+=k!rv)`a9m=cmCKbm2 z;QW80g^-uO2e>MBwcOEBz7PLubhoZw@#QKbJfvG<)u^E2A(~EJr+fY4Em+Wp`R4^j z7fi`K1wP{baQ!c=MuTmV#jDnPvjT?d$3SQVrx&(mi&^heHSSIF^SoEGPAv%Jl*mpv zf$HW)V|)6*{aKSpKivlsM36LdH?pyx@3-chH#wZs5-rg=R;QL0Mb z#cmBmVfHTx#AXOoZ9sTVA%pc&ct&85VjP0d4?OzdHO=}#BTU&qUd##J3^opr*+qB4 z9%FyjUqWrcZ-SWX6CGzHc_Y2Hzxps|oF;XRu_wD)kXn#GgMfC2AP!OB=38*v% z!|p&8!rCXk8)Bb4?7lhS&fKYDr-Qu9mns@e*M~G=)K3G-n69h0MR9qR&)20UCjpmY z7N{Gb=n7lBn$E{`t4o}CVu!Ydkmhi@*WyZL@Y*Y zw}as$bcBC4T(v0HGGgKTcbP#(G-Nzt{}X)|L4aSfN;E4_6RWVG(|0JR)-Q20kFnOa zwjz+U8rOMRzS7~VKl_thO zEfk9>U+Mm2%qg? zAcz?cU>Av+D|s|;uTQ8cf6Whkp`-ELgbDXjeYQo=uVFth=|o5J3|$KWkAUFSY%Vn% zYi}rS4ymB464-ttz_StN9=r2w3qNV*)7;dD@^uei?Y9_#ww$d@>K2-sScRkFMhHHaO;kH|xEgAzk#NwJPySZwJ<9Yf#mdVSLAAb%`RLD*T z89p@=!xr~5T964F&K89+kM5Q7QU-(N-@~7oo8n+@={vH_d1ykOfx>$^)0-RQ8d$!p zwXhI{F+jhn$nnc=6acyT>rc*?Sj(sm%ssk}3-u%w?z(KA3_oIL0By<%r!zJx7f>Lpau$|JtK*JrqS7lv8}c zF+Oz#%}uo>MrqatU3>-rLFM)8WUj7M5>V%F`vCwuU&IvT=9CW`3_3vmRzpOli$UOY_&7nQ?=#PAAvM@L z@F@NrqXS*4Hmk6cjQUYn`!GVi!UVhv1_3pV;xZyp-H66EWRau`x6SXtkYxF*FCtb{ zs{!cNPt!!aQy4Y4ZQph_qfP6VCVP~91}2Wtcr014rvvG$qM)8!sJK`4A93#oqG6Pc zjyw|`J(p#k>!>vKOp!`SjJfaQ|`Dg}`lKqA5OjV$jIONEMeL8pfF79u^*+g(yx| zI2c1x=fbvg>c8B@=nk4VpPfTeCL1%dbxv;M3lHNS&eujfPlv4qII)9@o$X%vz0Xm? zY5udV4dK)r1tr1((r-uooLi}f#i}2r!I?0 zu~)b3w7az{XZAku@9v>G%SZ*Il-+FQM*(Mc)5LTx^QUXG0_G_!n6AgGT^rlmbj~4( zQuu7|YLmv~kDgV3{=}vo-R3e?tloVHR2OU*m8N)RZ52MeXEH=XQE2tNB7Tki0#roR z5n3K&)XOr6a~VFjui>=TprdZ!f}b^{5?5pxMAY+3PY%)pInF4wAZu> zv?YriA~+4VZ3ybZBLuHW@JHsc`V1%A3Rq}UFU}^Ebt6%5ac>RS|8NhV+awA2HfMquxOgzJW zKy&gCE=Me?(Bz?kaAC?=*9xf=)4w#{?HnGm=fC%S0=!H{ z&fbSVXQL{??z+bPiEQMC*jT0DY#YCgsuR#!>B=`7`}W0Wa^}!B97L*dn_61r4nZ_E zmhO$O3mByIQTP1dy-J0^}>M|pmDT{fF>6muR(Afky%=cfNd;dHTulU5zW8%mbMQ2%2$bdaJz?0L+6oX|1 z(Che_Qdl1Ii!XGF`5eZ5ucoXg3k~sym+Ea()yTXl0*E=YVGeL@!2|=}VVf*ui~{lm z-t*lJWC4{gOe)PWd7Vc|dByHUoM;a92)^D}d^>6i9KQa+&EoCAoL2N^jG-eI*ZnK9vPrICfoC)%4^phy4sM&+@OcQx> zTh<$m`dJbPhBkR}iFgz^9H}X-k~hH=ZxhL1+Kh1AyoC&B_Br9U7;>Ayg7+*PB1p7H z`d0(hYcb<*9Kq=J!Jx#u6!L*w>qVLxHW?T;|E|mOt7JuA=2-*fl(9QJl|AMkBZIbV zeO~&af2SzCQ|4lN7BlWTeN|5sYl>0*`VC()sN_cP89h-1B8APZ6QC5jQADy0&v-ez)@#Wf zMS?s(e*6gaWv1E=)cgYa$^Ff^(`&$kl8|0T;Ig*^sI|u%4E+TJl7^Eh4%$k#Eo}DR zSm09>X#EexCL&RTRYZ3y+Gvxi?MiU- zA#AFIo`jqpDZYm|5FZ-P>SF< z2fA`e;n|#`c}CKUqK+hKwZ@=!+b4+V6u!Q`sUxd-7=5A4PJGQu+?_1e)1_70{!@2+ zDFlS-(fkTRt^`4KtnQJsdLMOyit_E#e&1#UA^Dl0y^fGHp0 zQ}HqdbCiGMfTj;1P3}(gFmJAsrNG46K=>2>^^(QUV9U5};W9N=Au%myyc8EAbHB=& z$A?5(o;_>j3=t+erO4+u7mq`oWWk@o%9u}7Z=b`@J8@?l9k=QTT7BOCfzeC_;aI!P z!=C;vNOIFZfG&6LzIM!Ed^f zj45<&;^>tFjrQs&%8U~eb(`MjvAc$&{R*(uV~a7rn5m}4e*V~~a1BivUGI&wRl&tT z6IU+8j{nATw&XEgM>b%c9q!>u808n-Le^;=(6Hk{z5rXhNm+wJ4eTvq#+yb?CQJrg z2Sj8hRJXmW9_J6ezny=t?Bj8jXxL~*6+2sr*%ifVPkOx7Q*6E;#obLOGN@BS0n-2O z{?U!KS;LtIv+R^7FWpt^-0;!b=J!y;cBl~DD{N9^3+yrXZHJh)B;;Pqw51@=GHJPm zxoh58>(mm?OKiUMU|R$nq$*8d5d4{n{A%Ds~Hx)Y|KwYiLN)g8&i5mDgOJ0MrKfvpU~+!BKKq#)9WP zcVD@ZNOeHvULu*Fu*3P+97KvrSKEoR@Zn;RsmDwKH41PNxHVztM@zbV9;4|4QLhkR z_%qX3Jz0d+0&WnN@v6Xtd5Cmu0MavAcIM7F@Zl~vb?td$1@;cHn#f?;{^fsSE;@Ya zLu_mTuzOx?;a!DoB-50w3NAt5nlcx9MaqRh-oUH_40t-Xe6>b`-XDlx+$gucgOr>Z znun(@R6@g8wBCS(mBAq5j%NLHxnR%jac&VpPHE!QD?0Wq$MvKJT;9pLa&h#OQ`#Lq zZR=-CDcGmsm7vz(MQ7{#yOUo?Yh3WD_Qw({D)~HBLVkMZ#7r8@Yq3Ht$vss4Vp{1X zfl|WvGbk9yVd$Ct@QgaLSI*p9nU+q;M3v|B93h+=gH-WIm7!m3H}W6KJ-m9zp)0(S zY~0xJ`1jeB{YsIO*~H0W{)iJ5Zg7fC*XUAnrlMfp)q9NK!2^i@LGMc~R%8e!2 zcrO`!kUAbSm(HfgGxi^pyc+O_;_OvJie2rbKC3d#1a+SYdcT^noU}&$7mTZiKVA_s z;S$$Zv3|Tj89RQ7I@%LlHR$Fs8@*(D~!w$Udl} zd9bZnK}B(?9KZ{XXzh<}Gx27=3%OPsj6kye64d&LWu%^msW4xMk@4D&F4# zwyd%q4imQkb*>tSRpYB&U7txi2`P+iEac%0VP}t4$iFUj&kIC1X8;Sz7+^7ih5!>y zi+WoGOx!+3UFiUULudZx2*&>)a6II@m4c|r$C-?s38b?eY$@#&j3f!uy@rX>@x;-L zoj0qhfU#%n{hS!RPW#HFjy0e%#dOoZRICvi*lK(|LJC?Ma{Rc&wUlv~50FH>WIr@v z(uoVnra+TbF>%>a7|V5Jc$fG4FFoFGQ?{XhkVg4Jlb>s;sv^_?&)XuA*6C+{zW!hw zno19wH<0qt=-X2tl;U2c$K55F^V1V2*>n*e%AC+T4hih*%Z*FPP;60Fn%7U>#J&=| zsXNC{H>HwLboAmwyj`0TjN@~8FT0yQoyUzjO0btz0p@%v#J!xm=(#$;t3M4gzdl~$ zHV)p+eb!nDivv|C2f6YsN2HlXEBzB0Q(xNG??Rggg79Af=BNreb=9IBbW5Pvg4&?K zt>I+qx{$;-36V7Q(}w3eV+;U@o&y=m@(+C&^&bcBY8FY_Wngnhfqj{3fm;-XiNQD1pzgv<4|kJ%PhqX zK3<(b)6mjMC0aEg0L|d~r}2f^*v$M~OQQYGLSlt~R8|#R#$!&ys6L8QP~lj%j;$L|-r zdX_tH?X_(nC!le&bC+wMWIiE&ckq#PkMD^E!Yr!f5*TIVGESz&v`o?x$uCN1h*=&) z=w8Q4?U6aY?}O+OG9OY=pRZ3z_zbYvVU^%2;b}+hg55EBANY)5tbi?KzzJ7I zic4t@^lD%U%ga$5%=XC9yZ3Pij<)%wUhUiuHN>&D2=+E`BB2ft9BUIf+_NIIc`LqF zp~IiHl(j35RV`r-vL6gX;z<;v_Y^U^-#EU~fZdMG5XqX!I#wNbdB(D+l_(qml0a=XKs4$$#RA)&V zJ!r@#m{>K*ltX&}`IV7{xE;)LF#JM}{DXT39DOiiHiQOPFbqceAo-zcqFc4iwMYGg zIr6~sVe!1}`KlEtGz(&;^u9}pghjCU!l=&ZIg+xZDar0<0NXRqDOTxcXb2t%vwNT# zG85({NE33`r0o(Md&P&Tz8S@dU& zR-2*4qkKH5P%4K}tx=jVNcvg%Vt!CZ#Fuj1V&!0P`M+xUGyOB@iSO8bSXZ)BGzyFF z=5sYSF~KmgB0NkygQG4}-Q90n_n`;ZFpSmzw@V|X*TE=91m}D&%|ZBI|F;BfQ;xOk z*vLc|JF(c(R~j@W>WUxTv^gLgGO_hJZ1>Ltwo33N5G0kCQ6WJ#*VBG#CT~}S+&T0F z9n&l45D@)%ox0uvao1%~zc&s`#hLqfOf_Z9ENR27oHM8p&_sWoaTj-#lwqwFbD>*# zhyj9_URbslpXQt|uK6LCUdm^H>{d>(NiIAq%~Ej}D}eUQ1jse$2+7^p4{>x3 zPY7zIG%&y9p3bV(7<^=y$O0EO?#A3cY%QEko#`HPb)-MOxVU(&)fD62N=L^ehQagJ z>ySFj3F-bpM6?mFkK2qXuD@=uFsGo0cZeAywDX%Q+i~vz&ry#>8w)wrY2;Ot@CWToj(3!O!7OsSg(f&Jr zdjDtqWX5!Tzis9GYZ2x3$MK7KmGoG3OAYePG1Mpchcr=(2_4991*$U4&N)dxn!fn~ z|M`ome#ap^y9T!|#lbJZ84ItL&`_H?7?(S?**Ck@8=MA{(2A4)Y6D4!at4ZH2KrjC zGf;gd8z{&>M@1h?kq-c|{T1?rLu7cn`6? zYEA!bX<_30Cfx3x5v<8whqS||eEQAVE9c^uM9Zza@<)9+6up~zLRtK0$(PX>NbQVJWVpXx+*w1W>sr}&O@-B6C z`kh`oF6VC2+IsSDq~G<CvjJ$NGaLOPs4gS@56_&XzShk;!To4!bQRre_fi6Fyw&ZI;DCDqH@J7|G` zEs{03XyH3iJGAxC6%1}-fQ(2u8ruFKr0Lk!K?c;sck=F1rG9@Gh%Wc!oLmepc#C~q zAhyZDT$-R8z3;=HP@`++(de#o;b9`=l633Bmk(Ndx@~jx?CeVYogVAoP;P;an#Xv| z1pHnaONkkm0X(UIkViJ%JTy*wLTF*T>lYz=IwH27Z)#E;Uoiq?;9!sQ9m6SdlM`~@ z;abZWaV<~9bdDpg|Dn7FklY!|E#f`I_t^yA9qxh%t96Pr&k+1{vdz$}c!s@B?o@vY z5@}HvWS6q=!*)e#c}on6m|z5Y->;)Y*z10LCyeG}$T07R;7RX9mN$xsP7b1aJJ8lN z7g93UNZg;$o~S)e<^vq2)#p&CMxvF^TB=C$xgS|epK0WaE39Y z^}v#$qAkWcQJ~mxZR34+8Ii(cE$ycT0a8Rhd>4gWYElq9p?q`-l`tkJqhM{BPJ3UM0xCD-2aB^_ zzloS-n4DNiJ&j|dif;xzP!Sa?a;g(n^s2zuZwXK6`kh>l)#TU(K2VRt8IklI;a3cm zus6ar3jGQDugEWr+yTDj?g(MviWe4*hBWHD+?4i@{x-S&^SNxyYflYEPancfk$}p< zSB6iOa$jL}Z?{`qm+CjzrNx;ptJs`0MN<23&Y8sMxh&M%;_z6h6@>!g8pJjOBVJ}x zI_U%;CSdbsie59ArD>YCAYlT#pCitVPhR*I!7rrAF{lrJ=mU5sJto!xkhqG3ws7!{Fx&F7YL=52K8@in+P#Zx=7FiW zUQEb%$MxaZz;Fp-2WG=K*jCg}>a`$^qzal9Sp*_)V!Tu%h0gACWTNObSyj6W-OsjA zoEWWXk=||6ts6WIYKpU{pH5zGb*DaONQZ&<`v9dU)Gho+dQRO}fy<29ju#O#d}R4O z>0z0@Wf*f}=zrCbSVO-tyI%m-{4;RDcH1R$rMR-B(ME_khH`V;?RS7)Hnn*^C;0m+ zqhN_-%ey0`MR>bV>qnAB1t+As*mBb!x`6@_dJ=X6Dq{qKqL2TLCDJ1!e?beH6?4Wo1mkoqTE-JRBgB!`nYf`&d6Ih?FRV6||gc5#QENX0+N zZ?r=T?DcMykLfABG!_)=B`cx>v? zgb*+O++c+hf0>7D6jMi>EP>RLW>GQ54>2z;3w%C55oKxfQZ{drq@J3dl8(NM9n z6LqscQvI_&bvz?<>ZK`rErMb?kWI~8l*lOGfu=OILJvZZyLH?$LjDloGWg=fypsQv zhji}E?c8(j&%<^9l~>rgx&Lk^0HejCRmP1V;vtLX!_{R?uLVLDR-9CgQNLH(zbGD( zIX`BjluB+b{C{erAa|fPimJ1e9i&g7(h?|Y>$29z#!EJJgd}K+_4?ZKiz)sV$p&@~ zrdxVo(4$fK;tz~C5~eBY1#WCBN*Qn%xTb>*zq|7Ho`z2d|*)g5_nK*)sr6wWFXY&5ab3xS?ct|2pV+4 zAP9vuCvX-5L-|UZrwsr5jp$7D+A6aVA})RQ4q#tRpgJoR*e@Y@+qFe$5+bRgQD=Ch z1}ZyIR+W0nX9)s_%d;t&bb#p}*NJR+Mmmz@b-x{33W+yi5jIx$0uMjh{z4>FFd28iQ{_yi~cSKT0Lx`0edYmEa~@_A1odudv!2W4&oV z`gj?$reTA0mH{m6sQZfVdvtKN7-pGSh^hw%72#FvrC4=p3)$(MoVXkjlAhFCj7f|= zod@FOT}{b$EVw541wdY9&lEafso-jujj^Q~a5A!S+mq<(V(s~oEpzcZrgpte|Zz0(QK?ZZ8B-e^jyil<3| ziDl(6;nyk|jBeUjU%zs0^~>Q${Z>7nF#W?hsLy)JQc8S0W|F7}Z9&kZ_#_0cLGcwR zth`?lwvV(vzRHC`r=E)cj~5`IGT%VI#Wf<@QSyOG>VbY^BsCGVO)|H#SD9EfN-!Hg z3xs%n_(=KEM4d&wIL;yP7#e#|BulRy=xVIbI0iW?BRNnMz>9`sgZ{WyCt6dO5izWJI#J)F#4?S{R(*h4NkK34gA}A7awmPb#Wj zZ$};s#SeJbk>mTorvMs$vOMuCR9mG`P?Ve|E%I#=dIsxq3f%mo-h|Yp3iyRiakarT z%pmQ}hHirBDrflhod)UUL%0)SF!3bJQ#%mTu;R5-7p)yD5fGz38 zAN0Drjh9y2%O#L2TP`ciFHn&*{u6|pmWbZ1Gj0D*1Rtqn-KgCE#!twpw6(I%rW{O< zVC6Y8xO;do-Xaw06e>}|Ds+8?)z;i}!ZZo!<+%waG4bdT60`H;LB(T!1=_Rm=3$Yk zM^{6u`%R)QrFs^TjpK>8RQ#-9TNj&M;CLQ4MLc^cFbZIRQtT&V8hy){(Bo~B z5^rvb`x{JH%K66MBLheHvSCHav}^Kz;wfBq34Ck!zQft4+KcEb40BiQ@VUo1%sjN2 zt429PF)@MUd)SCP$2h~Z5m1Puk8!r0$bS>~M)-Zt79~vA$$Bp~ua5n4tM=M6&R@lf zEWiboQ@KSogC_9VTBPx*l1f__@jWOBnyo3B83$XOhw}iRX_W{{UM%o|F$3DOR`t`r zay#lMoBuwAL0ZIPX(f&`XLkqg_ZAJVwzg`Ec}|RV92>U%S3&VFf|^{am}2{*G>poZ z5~lQ?Yn*ZP$(C1NTPukI>u^Hn^@5e_>IW0QAfgjODS{n*+6H8|^U{>)blPTOpdD&1%?&XxJ3Q_sGk%eUHQsF?+`e zyRetUOFESny`*6`6e3?hT_T^%>PWsxl?p5j!s{@sFs51odMs1$+@YgdCK@#+mttC8 z>7JgRUn(jp@@1+(Q?!KdDY~f<#KaP!;a$dSHA&BfjPC;8?Z6_|HOUDSdMr*rk;H3@*v3J#prrqogJE-(0da2;96H>b)@B8|UxS-0A5HuuCBk#33BTf%a6z)`cS82J(DqS!aBYHi)LCr}F?{F(4`Z*y2D~Kn z_TQ|e3L~JvmP_W|;MtolV`}{wO`cI;cLMl{ZsiZzlwXQzFcC0Ws{;$UItCXcFL}fY z5;~t(k-T$;DXxoChcG{(FzJt`l;FSM2rwcfVGsfuPnM`PqHi=M$Z^T&`GBB8H-~Qm z`us=|M}Ux!P_ApW$$$jkc=VeU=oS#%-g6DbT5x``HOA9oF_&pTft~+jGSvFtIz2g4 zBJ|x?>H-J>>mZGB({S}_&Y)Ko6q<}@#^xKdI(nb3Sko(qzL3DOKHk`;V=MGkrXhjm zC_`~yMp6~_qcXj8r&ZZ`c?pL)uUmNBMb|zVgBpAJXzq>_dK4=cqbp z!0B_;Pa3~BeyQ&zr?s09+T2U}i&H^o4iuu%8=(ZIIXa^CekRu(3wDdJ3oUI%|QEZez-z_r0%WYm;<3s%Q;EpQ|Kgv{UzEh5GhU>D~2yk zn1Dx+8<5VltDzA24PNsg-rtYMW#+(Z zDHm+)(tB`Rcy^q{&{N$i#JgB)yD&{V?2iF~kLe?xxl84& z9HZNuxyaa%NcG1Ft*y}c=WDFzG5Kd60A0>Id--&srk~a{E+aYR1*}aQtGbfewpMxG7Y@IN zley5!UbjV`Mvl4@}Fi@v1+vYm-VlA!MlNyXu_49@CMriYKJ0(Y?K`&d1?dP3@ME8>WcK{V9ejFuOd zLqs^1({I@JnO_E$k!1kJ5cDkER@=lMNlfMIo>i8qAIm2kkX?0 zRxJ(#cZ4k#=#s%fE!Jrdk7;^Bppn zD7h{2!i8wF4E}y7ir91(;iW)d((O{JO10XutaMDbLnHFtDu!EsG2}Da_QcGe3>-FX zs$yi~dHi>i*Clpec=$^WqC%;;9som7DK(xL42bxtoGT4_U$Q+iOh_v*I7xE=R%k6~f2i!@d>?_gGXmGK2!o z7Tq0k;{0Ow-W6nx$|XI;Q;Q%uY;!MMQLa9DAqhBd5;WRe|1b|F>4>~?jL8zOsp3mBcDe3ty)%{U+$vD zqVIE)=W?h9by$Mb$ zOx;smL`O8+DL*?kGc)T`p9Wpuwj;vMOW98az^jQ$bwG=W@=kzU%#7&X?eDWu(T1a% z!aMF_M3)SjdD&`5Tp8*^6pC&uax4UiP7AnCmGgZe$R)PihnfRX0)$BjhZeFFiIX_I?xLeE%a&?1L#JpEdTqxx|39XEeDxz^~tDMBM zz%ELx$Mh330y_Zv7sMJ_Ostd*Ns?(7jV2qE=PvopPpraYpW?@y?UozK&T+ zOKLW$BaYh-2Hpld+pp-DetxOoaV+J^t$-)P4F0SNxr!VP^Hneml?~_lTVXt`Y4ujv z{nkRNgASyp{XgpqKI!1r7cSHV9DPA6f8V~WaN)9=aj#OU~uMQ;`MNFBnv64E1CjN<_6;uiS_Zu9@HRvRL4b zwfP=ZNlhQy9%L8f$&TzfW)RqgRXWiP$Gt2NvY&A}nzGFu3WRl>X|NpeAe<|1$?;~C znmxvKQ{Nt1xsf6}5R-33wU<8RI4>P@7-T&e3 zJ;S2hl6GN1)HWcrA}BfMpdeX-lCyy1B$AXUARrkOiGpM#D?xIm$w5)cLCF~rkQ@Y> z+;FP*-t*3!bFOpFp7*=v`!&yOT6ntFT2*)5byuzR!i#0(dEZ%e_Lq=4``VrdqlcS& z1v4u>1M;5Ddz+!6$JQ#0)sFN8St4X}ILH%GL#q_)=Yv-w-F8S)&7+fLUf8#$NPRK$@x zt^7W7CFx|Z9$dbryEl52sXcq1pwRU9uc+M)|8>pwW|UFK2dajMoyymdd_%jaG1lMC zYyIxC1s0U$V(NLdVXku*m*-`X6&_z%xsTBrYH6k{t|rZruB#;_JN9M-f{N0Oo7LHT z*6^zC*bQN#&_lSvh9jxn$kGCcC< zLtR)*zW8B8jD%4|dd-S|fw8)37qwGT>pf*w5bM^84A99-r1WgEcd+bWR9k$Tt4UTV zHT;#Uk&fq_VrMPWv&VurPTH*VQoij>c2;jrs&8d9w>jmXgcW$#-9G4nc}l{A)S_ z>bV0s#@-)h=WDszBMX-KM^dE;{&f) zo4=hGK*NG4jQ4sRYl~)_iE_S~h=6*%t&-OFya0P=o0HF!5(Pen?=2a$TDEJp492|T z20|RS3Z*WyG2!>vtytbzT3==;s>$*f8a%`At~x7pG|1v+(e! ze=vfD`4<@n_-U=x_5_CSzJqhtsmkBcd*Xg;ooYRRoN>1iB`qJzsZMRokj~1&>p^z3 zr{{2#W6>UccSvq6ji2K1K*d!lObiti&8{)3i|^d+=UZyo=I)%>Trj4v`<*90l}>8- zsoYMHNUfQ6Z6Em++p-=0HYW*2dp_8ZoY)PC2)aW5|oN6#30B7dx#B2+gZALqHV}3<;#>wIy!bmVQF7YSO}LVz4ma- zeIU5@c%)>G-_`8sk`6gzdG@GKSZ8dk!Pp3@g%nOMdq-;HzN$28aq;1MP50HCE9N;) zOXoM2{kt~X_u@b(pbReNJl)VbdkN9UgX?iAerOTxvrcQp-$eG|BM^(<1jz)qSt8S={es60=+K zuIcZtO~%+6O$8Xm9w_N*r;;!>g!o2c9w_}I^U1J|4oUV)A& z*Uv{8mfT7Hr7rakE_j9G_NWo@j|#Y6M8+3qvSweq6L*JSVNe+Dx)8;*yE4l0GEsk- zQ0e0G$+#6~H0R9Wn9?}0@qoBxj5=!cN!{36f|ST~O9~ral^BHk&?}u%WAvy81M~Oo z2#q57Zn6+lxApJ)J6Z(>bg9otEw@uCwd4YfU4L5U-|Dx2WtOwkQ+vYVIujxIJ1z2J zSy2RAz%$2k8NX1nRB)I*!Lbl7D1A+(hGVGM%Z|Ixd&_3rY*<>)t({7yxt6D;?0PBZ zigDxdf@8eMqf)fXuSJq%rV(|SjJm-z=LHnva33v`Y3SlXvO&%IcA&tE!s=9TJc|9%`tdL&({XFKm}W(m!P;VjHJWJ1-(drr;;?^as@qN6dbyaTjZChg()M$LSCCG|*rd zqQQ4abIO+%?9YLF`?9b2N7w>qkMJHI5I&871R&(;A}OvY(+_^6W>DdD5sKiAN)Tsd zSB=)vZK06}OLkX>txakYbS6%llEExv%QmXXY$<+qerY<6PrcR%oagq}cS?jy`^{Af=3iKY<#^*Bb`Dw8AhzV|py1%6kDHdZlvMCTH zV-cil`mkM&_Ou8rZg}(d{5^3i*n7Gy683~QQ?OYOlJ?Im=$e!tW-a8+TtmLX>WHT3 zhuGI%`&(6hy_qMwX`SRwWiR`cT$E$VIM*I~R@kB)TwS`;Hy8CzLgm%l^0c-|S)6#f z9ac_uw%*Jguhb>v4(7ADG?sUMH+Sl$b$#ce*J3f9*G_t!ib3L^FYqlAULdyQJ`tYB zU4S7Zx46$EGG2+ZN)6|;(9OOM=_&j5-f&DfEpyBG2V9ytcB|!UQ5_r4;e5Sk*7TXs z%yo4Wa+?(Lo2sI&`xH<=kTDIqLNrieJ|cf(ipCB-uo=VJy0B}}UM4|Ru1SqQMS8m< znvCgk_yrBNxtTkAE6SH~wszvEqGx)t*B@6}%%^VtTv&`eT)Q%|KX$oAl-D*fg(71g zKQ1Kl*5^vuCk|i8)HBRq);M2fSe^WyA2Rb+RyhJ!Q97@TeVWff9aVqaYQ2AJH__v; z|5n^~wX&r}(!%g=2Z2~e@h|>sLJgy?MlE+YyCU^#C%WETj_XuN7!hNmG27V)Fl!s% zCHG{BcUl+^ki0dD!Vtu}OsQ{9uMBPd#s>N)y%n#vMfbT|8fqt9^}`BXlF&MLOuj^A zE-4h`@2+)Lmp+>A^~kq0OJb&2TcBaO9dsO3~T_lWL1>yhFhA zTX#$#Zn9AOAQEu{pW*oWAjSRgwSOp*;8H>?MHBK#-2G^Q7l}0P3#Cm!2rrhA6qN3) z?kjj5EV;92*1hc)$Wa!(%NsxHVsmJ~SE$3ax!$N&;&x4NbAc6LS(|B%;$n&AKwp=b zb+`?gcf@Blt!Vj{2QdDP0Mbx3+QtQIuqh18yg_LenJM5yj?@1%g9L#WD$Y&2vVwdg zpouATF_*Y;^fSW)F;t{Wx2xaV@!K`4)~ zDe|Rkt8yyy*+{S2+Qw&cw!xN!nJM>^$f-)(%EI*isks1hFKyod56o|pLDquz91c>u zP>mjKTtO4eW!~!B3hJ0{p=oMPYdwAn=RS!5{JIdh!4FZQCK4Y{ARwW-t|K+a9$#Wt z)Itu5M@L`6K}dqfoQsC^}nY6U*JDk*D;r-`3~RayX0_hKy=IyRRV*RgXYNU zk~v?vg6$%`rPI;3ir-^S-1WPUmp>7sSl#xBbvf#{-ZrF}PJP?KSQxd{s&vOB4T*J< z)IYL!KGf{3@Q%wSKwu8~%vHB^j(3>$H5;{Nc%KZZb=wZHPMu~iksP)Y!>F0VP3fvV z(;BSpHa)M|9-O*N@$R~0;Wm|^prrOy@4h}Q9$M>zVIFQgYU&q=4jpU;B?qocsYVVR ziby09z3hy&ogdh49CrLNvChFOA8)Yt0l%L4S>I=XMwDD|UlZUsV$nzZpjP@{uQ!F&GVU-bK5T}`1oDI+3eP_J?2qVu|=^ZTI0Hpb3Dz*>9Q)U zAE9*kPENb#_YA*P&+A(-tu||(P&;m=Td80Z(`O`bxT$5U{=e6u^Yf3sLPjfs4Xsz$iKe-BDxuY6vk`V=o ztd$Msrt{5$`tE`Kexy#>GhmH-^@E zr+Dt4ZjAaN9KzGY0)4NBsPm)T%(0EG$LcgN0iDs8F4&bn-=UbUvLJj&>YY}3Pp^E}y>=gUSb!qY9O z=>AIA9#*P0tvl^*N0=;Aan)9waQ1{I(2D)?GFjKkE19f*n1J4zzbG1+qvJAm2#1Z3 zVMa4sSI%pyM#+Tj?LZpxU2|NOZ`Z2wu%_ZVn1_n7CrR zez@%__%oYswWQ(svPrzdmO)s_p@^_DX_AXSdZd=eD1u~7l(`^xbvvXxY9)t`e6{iL zS*Ax^nHo06jMV*Ns&;ss-xaz34=n)yljmM0wMqcJB@6ONN28hlh)QtD*Dv099*5F; z6}eJ_<|#&0)APDpu3_YR^FHKyjbY>lMX?V=D-wvfRL6xF)%-L(sa)hmboM`S%Y=^f zNw+VOr07Mf(@xzAEN1z1eOMXx3Yo~&03Twk>kjm#rT(D@#vGLnfei@!oj=0r;bZXO z5^!`#uMnBt6Q_e8lfJskLb!1QjG20-{TcNS8k{I8ZGv03i14WIyUcb$fYREH!zVk>XAgPe5)pqOO|FDTOB7f)|-6x>RDY`TzafG!4cbM&MMW4!YNbl zN!QK-hhwKWU9lFrCh5fS#*<^V#^dr4-SazrL!#kx>mHBz)i>Aa@(fX4npal+@($;h z@0~Bmx-F)zY&+U|z@3g`5Sq(T^5}9OgxiRK#7jz)l0!;jw&GwaV`B{m+SVgL*_8r=ld}D%+fHI@X zH`UvN%NaH&oI5?c`lAYiMfxTu9-phc941jD^QUt(0jGcV|HDn^`R^2aDN6cHKzf}= z#gOHN`96Q9Fp!M1X>DvP?vkB-l=alM)q9-0Ymd0v8>C55PgudLmyI>kHSyKGE7|t6 zRBTtKmK=_Lf5}uPu-NzY1WL{hMn6C|mIc}fF2xm`4VTa-3kkzu0XCH&_eI2lJhuU}W6T?Ru{)VLCw= zSE&JcLLo0egS;m(k_x158xyY z*@mB|IOyn}a{hAX7~E7;gUXwj$V_=%n;E3BhA=}DD-GW@GFod^=ChRqlEXjD*PA<+ z2l6b$tUczFUD|CbCw?{G++M_<8qo_;IpX3H@I>nGP)rYwD*aaRO-b$366vOh>yXF} zhQDLJic1PkXMp*Vysu9WY^VeF`y^RcS(GJC8J$$H z4Bg~gT3)h#r*iN79&KzCKP=OxU2#f>3$>EDI<$JW^_pe7m#IcmjiM)1M+p`!G30L$ zhsQicZAlSP6d2YKlvkOeU`ZQ`!xz;UQ=nW{FcGx*qa~2|q`hVLbb1dAk;X#XH`}LL z)m2(Dc}jJ()-eeKb8EZi!?-rw7GO2*Z2@9rtRr;{UX#=?+;c5|a*Rv7kM$tG87RldG>`=Ln`ty zEH-*+Ex9NSz#iWw5>}F@*|K0 zz%sGV-&2JhOZ+OKF^nY_k;g1~;r&wCdq=8Bp-^RK@jueg=jc-b9=W87`@fv@C|CR5 z6__}UEVt}LnJ05r9IF1zCFSx6VQ9*&_y;5vU0Hr38Yh=*=d#`Bz4KhK`U%}~*(;Ss z)}D#|?D~^_Vx^3p&bk2um*WbFgH(Ie%wFq&l~~f2e`n1MO|25t37cmHOQ)Anr2y}V z-(X=ZiTNCQciq=}E+&Y_>{CC<%FMu0p$UnI4F*C+Y|(?ejGW1I4DzwjLsTgoMan|M z$~KnzPDz$go&Jc#OCn)Xeskoq43Mcj=)ELu6j=PJHD2c)wE?)fJ}J>(Re{B~kl(3o z!P@reX*57oQ^QB`e7}?95)w*kE8{}3gadxK-v*Os8cS^_gs`Prj}y#pDGbENmim(1 z*Qy6kq{qCDe#`8`X9^5JoF5I-ty^BRAH9WvUkXrVOruOcz(BFj4CVlA_Nj=;!K~NW zs+q8;i=0*Bm&J}Y?zQODIv2p~3NcMGji_8D8@&qa2pA%ucal>=#%oCrvv|sZDZ&mH zA3<5g)or%3aodgyxK|$3V%tj`7TyQ6f0v=6aBR4}LFEfoE8zsEw#s|j#(vzo$qY8< z#+h4E^jp4LFV}=RwCwW>!2lQxE?ibqb>hQAhromFU_EV2GB+yTVs(v|KqGDx6gLd; z;-$xpHN=P00aTI`sYdl~sQ!E%b#89%*^DUV$lgqXLbV$WIm@WiP_^2a{ybMuIa%tQ zv!in7w;%chM$@vx;KWPc?Cw9!N_dUPGwq6N>1jR3wH0Zu#x^6V;4rcIxo5LY!A_7~ z%ju)es;+$HIsf67Nhjl2ETBBrkxRnTah6FBWEwhjSjqxKlGcToDUq7!KBwM?IU~h| z`Fg(#?f~N@!y(UIe^`G*t+fA>)$!viR&Qk12pK*O3ZW%)v}x!3la zlA2<0n--NfCxMfFp-0|<=TS>#!7u2)@zc~Z$k)OX+$>{Wgu8Hv#*Ty-zCgN41Jy^)O zyrPCkFhFaC%~%Gs;-=Am`=FGl>wO|)E9|ls4UM^DRC(?U-`6z!s7PF2*ErwbRZg=3 zwJmCG*JFP!ZqPFc7NPA)Rzh3oJ~ykPy$yn^qX4{ zk&z5bKT5}(ed_N8IHA*?TV4DGT-5iBq$QgtO3f5*@R+|wf0f?TgsY@Rs_fG-5s~~C zu5yATcrDzFtMSOe^;f(FxhLFOZM4>S;uYXQs-ptUUmS}wt#n&Ou{rdS8>ExoC!Pwy z&~;mc&QvMq5Ej;J4x?bhVjY)CkO+&O9BtwDczk^Xqe?^%yB-StCfrc-!yn+;`zoil z-dU%0k*=vZ3WnQ1q=eJE5YOOf^KJ$eeBfdWLsD1T)`m+Y44+S*xpDKMxbM&-9*gei zoz<-`A`A`8#u>#*1w?s;cEd+1)HnX1WAg_z8g(KQ37HwPE+_96lN+oH911Y>`xm_+ zRSEbF#oL3n1nzs?RRVTqN8>JWt+Q)%>6FfY;X8%$fT;eY{)*k6g}hy{sv|SO@_VlP z$7MyclWd1lt43(#>Yi6mSk&41WMc~Tw}`wQF0Xxxc)^Eh=_jq%tW0hNjlcPWa|yt5 z1uGu+y^D62hg6{Xn*{yw^JQU0|4TP$mqaBO-Wv>Td?hg|U+eJW8)(T^O}{{KR9L$f zd5-e=N82sfD+({q;*+;z2UyiOEo=V3gv0-|gRE_?=_1!_q1Yv(&~&~57+0SXdHyP6 zD-20at0+>-4DL)7o3cMyNOqZwfJvxCLUAKy799+6`jOYIb5}GmSMU7DLlVx#)9XmPvHzfkfD@395V9OF?y^R&&|L& zm}`)woV-%8JLz7OeR6LMZV1Z&N$Qy$UW;yWRW<3yiM&>^`fiJvJ3W@@tLU#iX;LAN z2GbkvsNt@|8!tU3V`-Xn=-}W61@v2tR68v=@rW}OcZDRi(L&?DQ#(tIM4ze_nwGyJGM{_W+LUF4+HJY=M@m$;Yx*o+(Wy7T z?`yBCqEX9f8*fatjO~+ee3tle=vqV0b(i}5gDH#6A83h-N#?g%D&}X6d-r;`3QS_V zPQ)pfuckgOY*wG~uK+xUEs=`EZ9B}qrwkH6z9Z$`hXI z|s9CX0Gmc-trv&Z`lE^pQ(oN*A)sbc>fGy3f5iJu=!3o z7+QBueOR&obj$99i&4XX{%5J-vL4Yf-A|%}5})M#HFmA~FI0h5LQlxk%&^tyq-&5C z{;6F^d@vPQEH^##@Eo97WyD`$(KaH}2(cG;itS$*E|IrBk&-d5NgH5DbN2zR83+SG z<&`>Iw1$szT}2ILz>NZfZ>BtVld%))MD+_UZ$jK7xxOBwWyrE#zng+G3d&!KX?Q=h z!@^Pt^Qgjp_s22i_P|s|?V|O=A1?{W*)nr5RG!-r7)#LsJ8;Q{uXPtC2MQZx)z~t# zV^o_eH>z{Ed|(!fmFMs1#Kg*bu~$3?td9Fh9#l?wx!0(eHLINVeGpu{#9O`8=uJQ# z?!9wD=YG7W?l5J*&PL4H#a{*$w~p!|a{bJ6NIv{eJoI>XfQ(-DOA59+O;MylD>b~K zlj=06GiVh&VCk@wz(QZ?H)=gx{HexY&AR_Wsr(C72f6IzQIbeAJ(T5~w$$KD^|v&= zl2~3MreLpoPKrdxh7ZM@4gzU0lZ_;p-UD8-lN z@xb}8@6WY_h7IU)HLjkm&bPC0ud`o0XdmTjsPOjM>!>b0O8&}y^2x$%`9NuBJ~uYc z-?{S3=;OpI23ckk$#zbI&M$2vL)?w5C`y0UXL4KZNtAl_6@_ss1IJoOHX$SGxq8}l zf0Aj%b19)3aVdTH3Ae@8N$Ki= z;)J{F-gH0#M!p;0W{I*Z-Xi4*CY$n-{$1*NEI90F4FCT3 zcuTfYk9~*gA1^MhnDPKL3Svqr)>=WVUKjJKdrijQZJX5~5HIXfh;hUU#<`x|OT6;< zjlj=94~tHSjim#TX_raKT8ISm-FywSkm2JX;GtpyF{6VVM41Gw280*OdJ|Qgp5_=q_~(?Mh6g zOWeh>!fyVbk=5&Mug98&Ma52P6f(Eo>J{`{xn_JKvQe}$`a6JOG*+-YpWIE-l|`rK zK}4A*TT2Y5o;2`x#k9Mhd?97PIs|3LoEpAwtGW!372#>3gQxR~x(|i#798Z_MLm^c z7`1vifL4%Y4XRU~*=4_ur$*D{A2nJz&oxr$t9*xZt%xGg8`Z2zz^`%VfEfz`qXlPw zGzt$>KNNzzPIeg_TBN`%pWmZF+hdd1D^3J{e%#V_{&Mt%uI=Dvb^I6{}2$2q6|0P|r zRrADx$u}=4U?U3BKn$&f57bD-deQtaf>k)~ijBWZJ%RTEhTf(l*Z2-GciUs{Y5X16Qi?>~Wc0Af8D#&eOlFY6SsUe`jy>d-U-?%ZjNV7m!bM^hT2k@10ce<0eG!rgd ztMoqc%F(YbHL!v~LvwI3f^&ZuB5`d>6Vtp4G@@GbN5-LcU}1(&Ev35DjPI<}5B30d zlJ~-_oE$S@^(}+5`R(U67u;Ipwm9QDuoOn6-I`zg;F8t*f?bnzNvK{xM#qpx!d0oI ztbQ*@mqMvvHukl4o>BV)Jl}0#ow{aH+O1Ac6l%bWE%Bh;W?EMPcE=HO=nhl8A?0ni zpU8BE=Q?G+XCe__L_jbi> z>wZhSZhSgdaJOG1I7&rygji|-a~VzOI7>XDJTZ0N=!r(b<-FsIHq&;EK`ROcqq&rl zmu(_;pV3NbYedz=5pj0-hk0&)moXbIHg-0ej~0%l$bf>*{QA@EJBEBNBsW5ge1bL#H7lZE+?;MF#2?@RoDv zlu9ZKEoR^@2NCJ^lkmSkfEPxo1uYeswk_n{&l%9}_d>DU(2@(A2BN3NTMtbJG6&5r zw~dix@}eb?KgD3G#Npbs`3E9zfzkHZSUGxo3e+d7*@kL$rjzngD%;?4X;kXLe8;O_TJxP;7C;P?&?_rI)^A5>3t`=~8)hun=! zsXV>bY5CW41i!~MP4qVf+1DB^{$i5kOUvnRL`yY832c@cUPT%Y5WA{mfR;8*^Cib) z;io@*AerqWcBIpadqZSAfVqx->QK(1hVYtxMFR7_`NBQ`*6(4`6@cY(kpAx+&hA8h zmHu41_euqP#GGZ)ztS6I1*iS4u4u^h%QN-d*?K*ph|WB?WMla%3V4DsvAm(};GU#Jw1}r-W!Y zd6eJt;F*0Zs^_5?Rp-Tj*k`Y!Et#b#ae{_ph(CdPZB^=2gb2Sr1GQDtIkeE zz;dCeMysJh?kBzUGJ2BQ$3%C5aKRQpp^itExCZGfquB9Y`oPAhRZ$GK>&@-{tmj$u zyJEM8+BQa>=$Ir&<;ljbv;t0SQ*8V#6aN9j`e~U7Iu?VPa`YBtJ`@DRD)HP>LNGV$ zvB=2C$R^-~5gX@~R(Bq`3`DJ22Qdt?Q|v$PZ`ioDrV*-T=$e_%L2JFT6-11!G$kSb zCWxiY_bxLtCzR#)eHT(}IZ*OFU1sc7c8nzOxQ36}mo(houWt9L#J##mGAbt1AkXFS zD!f%=B6=a$Z|K{HFS;lP*}a~U?uZSy1BlR;F}hef8>VLxql6Uhj=|t%si0~sxRy%B z1Y<=mj)ezrk_HMhh3w6yCOu0Jcat{_x>@(99PNI+ocITMwgo8x$lxN;Yy|S^mcolk zeX~CeDKZ_pmP$cs>sr^N-Lh0@W4lJJg)hP!mB*2Q&}&dTQ+-sioZ0<^sblGD2gm^4 z2wHa5WyE7;%*t0l8F?P8;<};cYV*!*U=f=Y8B|!uGqYs8D{xvKOdHL{NAd+3-K3%l zMl8=fjVHys4bWHbIRe=KsPr(QcH4|KrzFpvkyn)Ez=ySUb)m+Nb{kJ(AWn>k@!O0( zfCn>7{-i@xR22B7nVfU+aQ}vxr*j8KrNG)cyJ4MnKbh;jM7Oapx8mwzC5kf0P;1-# z@yXafY1&NFTEm5?bXuh{egi*9hR-nosZK5V;S^3Nw(LW8`R|l{ATXMnnYBSQFq(ZZ zK*FLB%g(X^6Ir#^QNSkj=ayNj_paval#!bR6d}~2)B5Ef%Rko8%RuC5Ce$zG=3Q7J zMpiI?dFiPL>-1c1P#`DlQu1MVG^fkcx#T9uGKIspnrTNi(7MZwSlQ%MRtrq^j#-&b zsohf5+1t+B>6tg2kgid@{_uoc$*&&7GuXnTbE3{?%2)ymDi2K7=u+~?{j-e=`k6N; z_rG7pB@~c_gSM_HJBRXJ;#DJ+n@7pR=60Bq2a-|RxAAOX@zHr?2slqjxC2h%MMAwx zPe26y&JOxPqVBT8Je*hs&gw5~Gm!ho!2~i87pbCWqf{!qGzl1Bs_NwAE)axr(7n2K zoXlqx%h1ADmJ_o8ES)uIQ=2FuW(@|T&R{h>_veRSU=fM!f5+)LuMB_Jy-|>jwdO%J zASTIr2d;9P@ha8Jg0qCnXrCos-}g%At4Iu=zc*Qcfn7u>qU9_27;_aopl@4Ox_ zV4sFYvNbVYlX!S}M;FRvC1KA!gCFF3ZG_08zJdf<_xd?V*vN+Rqt-)@X5?WO5`2z zMfAtv#(@33nKu+Bd57Ff2~6>zCEyzsiQ2G-%Qsf5J{U_4o2va7CWLU_eFjoyCHUL6 zy-K-ulEWDr_QfxOglEDkl!@U3m94#8ct_X@QR4_V4wg2Gb_5<4K+z1c0`>iALBsoKB!~S2JQI; zJ^duI5Nj02QQ7`*2YW{+H&`!0&?d?cbr^eqOk0oNuD4pRMjSm z-3mN@4>E0b{nXz#mz&Jvr^~vb;8)LFn_f=UJ77N?{7PXiqL6N-2Mfl5jHftvHvu-w z8K{MI(M7dey}Vykqn}6Jv}qxpi!E>@Fhd0rcNzfBMKLNb3|cEkud;ALcEYZ$yIXmI zUX1`5$E@09uef)*^L;=B%rsyd$eqKC-52Q}OP3*NvA3)H?XT}L;k07`vqfC)B$4M8 z!&Oi^ySDfA_JWHcPA`;9VHei+ex?dIIIOH>%n|4B)HnfN#gGd<6rZ&E7UM^HY$mTd z)7{xggmMTR6?(Qnz2xKtP#^{vSDjS!>X_DA&2pi~#MJYdP;R@)JIhd7bT0!qrDzC=v*4CSu@oa=!4dm=O&POE-|Zg; z!+H3Cpr3$cR>gW^_eWTF4E=Ne*NPSO0`1B<{PDO zSCiFHAd-%p)+QSRlQPU;Fyqd&{PjD)_O7N5upRd29n!*N`<8aN9&6>zK%)ZS41*s! zm5;t&fa{v$>rcA)r^*`_?)srexbpn_-(tOPD-1X|fhnC5s4EHX&qA5lUo)_)wUv;8Po`maZU=7o!ky*9 zx=ndkVCPmamg5d{?K)z$98Jei(K_R>IZ|}KkHyDljdrEmrcvRfFS&OKt1;RVHey}k zjk7!iMAOa#8eEI#P{HqA^@aK$Sp4#5w{~wg{W+|Vt_XprhE2=fJi(eds&`uVs}g7* znYRHmW**OG$P{3A90kGGyC0v!cQ2tM=eJFQ`wS;-vnQL<*YqUEyg<3Jj58hnV-zY9 zu24Nmub>ck`tHu}FINW}APX4v641dM0rxEn6@fHL7r^-Ka9a%UUB*AL)1_?4Tn*AD zf2L3Vx;i=zihQ?LAwGLuyda3T;l1XCgd13~^ls-2ITFdIeE)U`xNd9m@W;3~2qNrf zB8u$^@o=H20yHy^E22ss_I>^fVZh@zxan0qmcPA!dFN>3bH#nq3qM9IQe$9Z=CCXC z6U~BWSFSA4z6_3jAOWSr6R$*1FiW2v|x*0}2Sz{lr&} zUK!~tNwCh98iu$8SusQTwH>plJSL80D^L?6cS0(~B3MLZ1fMINc==*wMaR|$947Sh z=sUMW2C=?!G|7wTrz4oL+eh8W=G_=d9)1^e40o8k5iwWUO0;W;$koU@II4WL_zL{U zoX3BG`NBwDY%LO7q9Ubo9yaE7-?45XbLgL6;t*bH`MR$#yyJ4k|M;4<7`jN((^H!eYm$`n04g)z0KMwXalHpG+3z| zlYl%DvOFw`<%|}oJ!U3y{e6WY^TA@N!vgYJh8ilt;dy5I3qqu$pU-!QLa0!JzY`n+ zi)ld;RGH*5=~@cin8F6=3t|BC>)p_%Kzoz|ebT~8oBRG?rC(izW8?LGws^^_hTx;v zWGG!teOtgODEH@L{2Z~s*;J@q^?-6wsOeVB$`iV|?TbAzjmP73At~ zB`4*B^RnwLTeCWvexdV<@9_UPOM`8-8zRk6)SADpm;f%;1f<9yb`_jguC=-?`sG|D6Vsp#$8 zFT<@p{j4RH>btL_P4&K2xbNC7A1gf$so=IXHIVPs___M+y~NhU)!;XG3XYVa4Cg!| zT-18?^F=Rau}FjcV2up4Q+=jW)?``+v>=)WG?$uT68xTO6?vwWFY~H#Cm1Asv%d`` zr#h^PlwuiA=ANGt;q|VIfRQ^EasH<^N349@uA=i2?jnRs_nSDVN7`|m2ISn<#v|C; z)Q9`X{jUK_ML-j50(fqI?fL1&ncjMLmlhI4&ehTz*k zUyekJGr7Rs1eXP5{*SjDf9UM;Z0`S^*aKMb;xm|AHW5NoREPJFrbNH3>CLAMc1LdWg- z7Q$I03v+|5SG9)3j?5}VpXLNu0}`hv+$ZmA}^&a_!oL?{Q-7MzVxW26+OH9xjk|u-=yTrAx;X~{4|e=aJk-m z&&dmIZGpwPjqb;ZbTSm~9vir(&4k~e;+bE8L-RMl8Q8%49e8srylo4<#%78+K&Le% zsTP%6jh5&1=cu>z@rbwOFLh9&mp($RLJe{|8kma@mliY z6jwO3n&^2_44=HI+wH&OYQMeh;aRyhXoxL_DOT@u>Y$iajvBnI#hwf<{#ZUR-$|to zF>)Oe-ARbh>CLrrmofTBIm%n}&vI0!-+U7ewt)>!%!{706NUKC@?;{^hnPWU^Ku1b z2<_1`dm5eXms))pW0+dyvz!B(I^%^icdWR|gOK|IWSOoT}`VeA{ zjAx6zK`sejkXYv513jQMh@nKAzk_s4Z&w?xsSeI-X_ZNUeL^OHFWgrcH@P3cr7z z>*)YOu1rJt$x-UdUB?IBC&%vM$0LnKd?$Mab;9%CJus;1hNALDvuJ`V#uo@TNFa%{ z*^>Oby1PHL)X;K}A7G{{fvMl+8maLrvz~$xLP8&M$(_c(SFkTTalD3X;tP06vP2{Y zh;WY}UI?=N7l$3!01bB9cxoI?%0@LJQ{lBP$D9P)wrtCdZB8q-yQmnolBHXN7I1*C z)l0Wa8Q6yC9)}eqnzm-5DGrrI%c{5CXJfSc%)}rEz6G!U#hj;|hwV2fd;Oogpgq+t$5(QJ0+1BI${< z+>dr-7Mt7lU=QJ8jjD3~?`dN|{Q2QD4gp|QH$>ulYQme~y>TukE#9M+g4a;Ti?jt3 zW@Alw>kmQmhPX#A)2UX)vetJ=b&gZ$G ziDxtZe|Rl%fT&}PJ!K!tPSzXe)*E+P2Sdf$WwufxIF^S5KDc^FU%dhAc~}N?&T97a z{_kN8J8P5Iu4#-&T#m9Y@?;5!E(%wUevJ=b7C=GX>J#68PcSd3O<>GJgolfjw>RwpGl{dn~5sT>f8hqiT(gb^1^j8T*AOV6RX}nJS3mUI7X0_HebxqOq1}NFIem{ z%l*@Mov?4MGz}kI#EF7~kPuCbx+jk9r*@mW%S*U%6>K1u*``nZg94t`wRx`Ky}%Ny zfjK-Dhy@91fWl6dDy58Bl0(pJt&r1emN9ND(;6{g8F!V}J<3mhMJV2>%`!`oA^){7=-1!z1|B zi}c}^NauszRjCAJ=)zjs_^ToP*RrtwTaMn3_KY<>w`+ZQMB{v{!}^F_Vul20zp z4{QQCdKOfj|4!S<-#tZ)LB+v+CNAUjsrCFlzWLyi9a{R<6@nr&dxIiNAm%W`rbmfz zU=z9jZ+>vO~(&Kv_5qYrHRDyk5DFw~@~CI|bZzn8i|;UCwhAY=`3vCyO3dLaN++ zfcaiw0an(Tzf*|7_Mb5K`T7LF`-=E=-6up`2Tb>xUxJ5ZY$LCj(O#}1m0NoH(V{{n$rGV;p%jKhuf1l`3JgbjMi<)i z;)>N!*pw+u*DzgQGYI38_tUt?PKHPQ4l76#aR^lV4=o7L4j;;Fn<2aG#9H4*`9E{o zAs8zkKzup@=rX`(@}B`5|IH4ne{TnaFZr(u|?VFTD-qzXq+KNbWxa#J21n3lSVV~2A&k!Q^qQz;47%xD}y^0Fnhch_%SoB7S z%OEa(8aK!=5&M5g8N@%ffP?TP=Tv_wjy#`j6$3;3bf&gUEi76vo+_&T5`G*Pc*DR; z{lp?*E|#7#nkKif@s`t2tP;@waMt%W>>4G9zIIU|7B3lUc>Q~?~}bonM3VgXw!$-13=26vI>ezet9Xy`WmnoWoREHV6!zUvMR zp_ng_$L%1|T{-;|)4+6$+|;ForV-K>2vUD`g-6e?f_IZyo56vmmOs8j0v4SJp7IY6 zPdG0*Nv=8vGtbk%WxaxaeR9&z694jTaq(!2RDSAoLAb~Hc>5LfoMT}rAt$NC$2h!T z2b&kzeBD>>t~eQP2~6DE;L@w;M7fdi+pg$N<<2~x(*I?Ne0CY<^!e;-#sll#d&J9! zDQSvVp?cdd06;4+4m?j?#vQu=#Im3R6+z%%742R{YJI=@UkO(EL+11~u1@MrtM=m! zuAlLfBzxFL-KE4QuB#d?`At!?C<;J%f%QhSulNQ+@~ZJ?EhBlqz_B6x_qyYh0n1U6 zYFP!T2zob9c+srw_=|Ykw1m*f=>GO*O7KL3`HxZh6r#tcebcT6?yN^PttCTEF_~B9 z9!#b>|K*Bth+F*?cT?~3r-wD2tH%=!TC>iWV}X$EWU2D0ljoE-l})9A|M`~20oalm zRzxz$b&wJ{$kUH=9f9OKMZ+V7$OsaLNn5vLx{J>cZv13?MPgFr$s;qp^@x%&Is5k{ z^<6(+77fc+K00`ylJamh(eyu~vHW$a+VJ%!bkOd+N!<`rVDY5I=RjCHFqe z5WebQ7tb)vxe7~a_e2~%7njtg3h3yk4mZc0rd|0GbhSW(=5uAM*5fMGLU>PAe$RW< zl(@E&&(4RYF5T*1H=SKK+q?Q1W=7GgK!kY5jCTQwy_-&6hWf(KZ%M0Ox4(p^c9(i! z=<;_l6Vy&afPIrg^Tkmg`2(2&mE^AtzF)_hcQ2&FLq1dF7*UMx2oY; ze9|wdpn&lk7jW;=-X}ux$$C6X${QQHY-?yzZBVN3 zNmx$CdmVih*oj#?`wyYxk>Ks(UFuN07qm?ukhdgcXnuSRy~=NQz%smbP-YT zyOFx(*kj5-cpJ&5Dq8Lm=R@+xcTn~ZEM9w>sf7^#LGAk#sfk%}xe+^#rum4HJJNan ziS^j>^0A;VA`x+G6t;g5z%A^1yj1?UYITNxeV1O{{ca26p&cpsofGL>-iRt}Dcqnz zZX3WY-kr;#5PUnZM)~K+I!A=x)TMYKC(C{f-_Lfh*$rx5tkIZfYf;MKH{Y3vxoLsM(Cm7 zw3hV(0@U^E5)vQh@wAbH^Ef;rh%a*D;8M7r{)|w(X8*!L0vx+h*2 z>Md_ah-F=&e0v&rF&hNY+$dhb^U|z;XaSI#iF?d=xDBv`%N^SBM0k<3?+UfxE*5ii z#L~5gbTIZniIw#-;LAtYm)ZgS+`lso0dkRmVmSCXbc8jcPlk)%cczGpC3s3p`zztx z;Csk~2ZJTB%F{mwKfaTN#}L2Jm%u*}!4~#3wBfg{W%Q((_dEYO9AB`nlE%i z!+2mG957CL)3ZOwK|*5_|Df%yIpP}E7<0_Wh8;oEK)OIt#dnN>>62zb#!d_wJ8-7|p93$Q|H?bT^oPIFaBnEr zrxs>IgINHI1e~>|LH@Tv00BtgkV{Dk1L)AHGsj};kl1B+XoSWC{V$lW|2?q$-;eb8 zH&p`QN#~sN*|U!PSm2^rM8L==j8MS3B^)9G=LiPNl%%?BK&kNGoLL{R8@b>Ncn^7d zV>tJ#^k;ugjrp61{eca3UjRAQ@?XdKMWp_1JPRttUzSs%+c1_QoV(p$Tyg)tOdo~= z(4Ue4{|#b91Tf%*@He*qhA8P*pgrSH?*I_H2I&#nU(=KRMrcxjuZ#kZ?rDCiG z22$h;!0JW8_Ysx-4-n)3x8Y5oReWE%>|?w!4wK1@e>w-SWOC5!WBK>ctp5u>a`LfA zthM1A>v|S=6s^Uv=G_=YpwoW=_faH}nX>`)CeQgP1n|T?V8JSPVwgZMLq{{R^a=6tlmG_aQwFFg9tm!u$pu#RKlYUT!3Es`-<)>$ zKW)PY8|6aXc80+MlK7H_Z%RB!F4P#{yE#M3k5W=ob=Ensw1m}P6Hh=jN~lXuK)&V; z82$t#U1#zAISQCi_g_=^gh;``F90-9|0&S87gn+41lL|wD?QPkNeF#T$kGY+_it2J z1C^EJ9zRA55CYK{&174a05Jm~tYcKy?7L*`RMPKG?g5f!b^AZ!=fSsvZo;ur9d7v5 zpRt(9g{}kz?lm^?AYcN@p%@iB!G8biY|%fTlJq}ud;xF7G=s=S)%0UPY&u|>7XrT> z&E-m#7_khzawTHD3vfpe2)wID(1M`o@c){H{CAbB24H?)zee?wW$^`o{%?m$S8!=y zY;rQbGjCXbH7j2(0>C(6a)&0P@kbii8JLz{VxMh7j==)W4UMT(;y`)_3EdV-G&b^C)`tC!J z3jBR$4?l34)D3-s1jC5tf8n*Q%~0< zPbB!gj8Kfjyelr$_6cl^4_8f#|1d*%9|}wD-7H%|v?cGk zhblN8=jgEe`b{Z5_XmV?Qo8%5yl9RcnL1682vx3Hvn2keW@1!n?m3d z`@xN<%T$kf<%#@J3AwolMA~3rLnJ!IVlY^sudEzZdj4-CXe|Ko0Km;Z%?~=CwFj5) z0{=wF{qgtD3E{A(gr}q&fuOJi;5HZI3_frXU^fYq3X^G&YQPS|O&K2FLs*28KQ8Hi z>Yl>FE5gdQ379&lJZ;*tK_0{s4pye*sKkKu4m8DC>zX`9MKFN;ky@JUeZ*PtYLY2d zQTQL54yGYa?7MfU5wc1qQzMn)`~t6H0D^r2%^3?#qQOG!2F{6-ix(xtpBd=Y1-S$j z0)j21yM$=}RCnFQXa2GlacXXEZZhd5csscN0@_|I-i&S#UVMR49S;PClm4IvJM1g{ zp+Tta1$gz#RH6%m1vKiG)LH)THAaCsOaD{fM5sm}TB24&6`f60&jSYB{D%lT&~pCk z4h@MO$^AcPD(_>mA;2s^GqC!c`#;tq`gS@72rs5*hgK~0z;DadgVf+1(1kN*kYw4p zi-Y}X!E)uK!gY&YyuFz5FM?N$9;BNBWi<2yxR#2K0D@44PFLhT z-v9?rp^EDtwB!G8u$Y3posOgEy`~lpU}x~hkD&l((A}R)ngzg$rfqf>1`G)VI2%%9 z^D&Iy(A-P<n;H_bVCIFoV7k26KSr{`(U{{~bhpsS4Cq(|jZL63 zIurFo#<3qz69EO(wQd>c0I%y!7)L9-h7DW z?QM@*W}khkU%zBNxcS1VeM2q>>(aDCTj^Tdt}7z(y~I~$8T?gO13$7>J6-eB*Ms|L zx!R?kv|EDel9wkpf?nnb3u3c7`{T>ILsbk9BYa%~$Rogw+BlYu;VMlQj?>G#yN@Dp z6L!bC=I3n#P6W*Blg(l|q}Y>9sLor5oiodmJyx4q*}W{OTX_z{ZqA*fXTElfe>g}i zvb20MluGKb+h)f5`Sn`|Dy0L}iIPQ%r8U2s?=pUNdQ?%r@mVtkn!M!TJ*SwLTpR2e zXR{*Jg5Ye$iFr7_aN8s@Ks(2%#(tzfULax^ruPujzxAI2Z%%sN#}tGWCi*|cRF7)w z$6^?$Xv8HTDAs=&ii#xE77E5G={_dCgD8y!JUoToAQ~d+t1$4UOrQfXl913N@C{Du zw7j^Wo6~*X#>p$JV!Uy>phD#Vk|)}Ak79#FOZ)H_(b@52{;kNu?#Rsd`A_Rb8T<(9 z)P8!6FCBlOVrN~OkU9AMyFw-*0m&^Ndrl+VdGdr%R!#eI&eYDD%ERpz7p*waV9fQT zZ|yx>X@L2N`4>_W-p}?q4jdt`r8aq-BYq$DXX(3`bcigOxBSW{Zj{7aTH+}5Mew}& zQRmQP+QvbrULAx6HwvW$YiGo4_rSP=APQbO%K6`c(TD@xR5oE{hSdvz)s$r#Z(|^( ziIX;xGxTh%u2KfmOTL;cXQG!ZcE#l@d5E|x3qH?hPU7_dVHF5aErV744gyAK3M46_ zza^#REE*Ueg#MNiX`8HFpXc>ED%bE56JE!fTSwjhKs3g@CxnGBPx4)`_$JxYK9HG4BWjHT*jS37!J?DCm%^h-4kvrg#q1x zyBJrKX7K`{sU>0|At5qBQnMOI20e#Gqpw~$iO+r!LJRpVidLudYN|{2eSQhrr$v`?=$KR2PEkZ}{dK{wS;gvf#{k`k z;5T;FA^_#22fw>6w&OY2Vi}~B@Cv6@HJ_pc<9HRrsWE*=ptEhhNTltv(UfM02pB9> zcQA1^?!o;1*&zd=@+m<>EK`71=8XFH%0`SoTd42lFArVb+sk3_Ag9Mn!ZtjnciE(R zUg<;0h`jxjQpzj5;`XW`an}GHd%9mgE0Dr1Z~XkcgO9hyH=zDqLC6C&o6&S|onV~) zY~HU{?-Q8ZuCmT|l^?;*alFR3jjvbj3BFauxi@XCxW*M&c}om?pHyra~ht< zD+rWQ@k3^I%*69J^cc=phSaArfm3b)4?|zPSeF3qsK&0PzUyKh5!?s!jmCaoy1jBF zhtEBoL%bq=D7A9Y|`gEHiK+_wqF-(N_b1-f`qMvg=hg^+7d<(_>`ifO< z6mCNBOhT5RX1h>~0=KxkELB003uzo)fKGW>?XFwzbeB#ykIWG@fA%oswZ&;{pUYCO zyd!g??_j<|yoAX2KIMW$RPzRpU4QjuR(<&NwylIr#mkGM1+;zltuO4f&bx@QzG*of zTX?WHhdX}55o_+(PZubQBrb2_NTr{cI||)>0ppO%6bXGE`vV>mULV>KU!010!by(w>SH5&R$hcpEW!34f=WPi4ZX z6?%g@tyvNtl8`^x&oV^tK}yJ3$1?r8kAP7j2DXS&?z9crbhUK68(|?W@G^dt-WXh?#P-9m5CMUQd^?G2l>$fLE_Yha1b+2Pb(Y75TW-YCgyxU% zF0VG#k}ihk7`=ZLe}s3RVh&I4u)X6NdP0?~U=Yav+5Q>hlNDoE`Rg|c<9m6e_)A~4EPn1Yw*tSeP#aI9+v-`Z3d#M&NFqweeI z5PfdLjOnI*dV#h7?Yr60Pwwfxqg~e;2a5;rhV|DRB^Ql`7mBl=y;ElLzK(oT&9yR4 z{nbrs49Q3h$Ve>l4w&1BCF%$C%^LC_36C#;|9ApaH^Leb;3Zl_CoxSQ$4Px0T~{Z@ zJ?`hvlbV{F<7at)-9x~5FAU^_>iz@;Xm|`z%ZShs1n_@^!P9X9BZ`1?0>yYMs7bh3 zF7p1}YPG=Yvt7<(hT8~}p7H1xxigMGrK0e|D;NlN^?8Gc=NrjSQk{N0>XJ!HGCQ#o z@gun;>vGz{Yl`kZSA9QNz}y~XrIoU)kBl?C_M1s6UgtPh6`yT9XHMgu)X8Db>GFB_ z*Uw7LWieP~jN}XvpH~UamBVWUM`I!%QsS6;kd+bWG`6@pmGNLE&=;K)5ompkr>{Cm zZcSw~`zi<7@)n*vzM5C*Xp5kl{j|c&o;T*d^{aFjCm2B`8B*amAx+M~N5 z@Kb=mKaB(ezcSJ`W&14r4ZKBn07AbO;DejbK+@~OBP>SxLmE7&QJJd}4`YNFJZW|S zpZq=oBNS(6ZG;2WY^^w+B$bkHcMv98xR3X0e5)?xIUeDEhL|rlEtYH7N`eGm)@7IJC$8@%+=swF}YRObuI z)imDlU`{EntRV9#Xo@%7(nzuzbz}W9|8}fC!_)tsliRd6C1T91Uz*jp2XhEtecCjj zPP~nyKaIZi$=r}QHtmZmzUw$f^t^%Cx61jq$si8H)^^C+`Ah3t{?!XWppIGw!p`I# zE=`%eRrHj4(4<_I^*+Vyi_4xgZ?r1aouv(_0=Ke!@s8`xTB_X!c>qpX`XfHF40_*Z z;C-cbGtvhI%mn<855N}00bSR{1ag;#^X<&YANp-@wn2U`=V<4$~9Z&Df zwaqhIxLM*WuBkGIa(fd=ywE5z&777i_=zb|^&O*P8JFvm=9f=#^)Txk_ekl+vzkr5 ztr~ldRep`=GWO26)EXZQt1!5|6w#B;<>ZOf=qrXCa=%i=Pnk6WBVp*&i-Nh!v@)IezF#S|>|efpgBA0o6@>nN>dUM(G)e4adP!FQX48K09$$+Na2Bg>`A@nJNg3TK$&2XjWB&i)ofI zcrNumYQ%DSr#TW?+=pZMSzce#BRnBdw$<^M!)&!nxO<7oJQtJ&!^_B>$r%(_1hW_c zA~JDf!sQ;|I>{S5o>khe-|uX>^|=ufC2)tZ&DW?kl)`wOldMG<4Qh0B^vZHw%#-74 z$%*ntRZqEOGWM>>L?JBnHTl+Be6EmiRZ9~tvy45Ev6?4y^r)g5^UV-f#)X=5u?DyPE|JLRDyOhi#8FBhe$R! zhS7G~V)We?5!H#d6EOtm(VW^}<6t@OS+>NGhexzBGshm4NapL2&Mi9BD8M)BxY3AttXfJlcwVyW}pT2HnS{t+O_39$U z;TB!v!r3M2z1ngUN4<_muDob@{iU8sM6Zo5QM9pUC}*7y@BHF0JXLetKD9R}0v|F)H|gvb#x!BZL2Z1f`9u;E6wE!4uP^NUY!bZ-EpV zKKoSkZ|DB4nb6|P54Yv0h)Ek^a4jD`W+*uE#_fu+W56>`!J1xjwm)xOO}?Gx%eY?N zF-PfKp_mGP$pavLp#FzWyxp!<7 zFca_~55x3yUdsH`CJVs+l;-w&a;xYKLP{TjW22@7lHk{5K!AE|kv?zye*yu!zHlyqA(n{%O3D`Fv^{si*re-cN3 z(;+}ZZ~1-R=exnFhmKo~aEbdMmOb(CVwQG$OrnIZt@(L zrOC0iNM$0c7VGN(rU)a7WUfi9@Cn>H2+omP*YPVHij0pm>oeKbm`Jr$EP*s5LPIeE zXZ99bMoTL&8r_yCg3@OsxtW8X?6a$VjqCArcOSdtW?ug#2(DW_7rfVSa{hKMc3&5# zZf%gUy>jk0qkz@6UutMrVE-Ms9PM%R_31gbD`UL=m?U+(@6B0;X6&SW4%dx1ExSp= zwZV^~bhzcH!yKJ%1r0xNz)w2n5*3OR;pMa9V&0Uz37Q5mmk8f8&1$$(Pux1Qn4~ z2njfJ1mJmN!Sm>(G^W6{Hl|A*uaFUR!~yWQ$&X=_33E2>-xWuYK_?uIlnw-$^}XZb zxJeVPS=3Zf@nQ}(`gZ;2Rd3BsZJF=|FISuoj^33#=l#=(Qk=x-U`~Pat_^onHjo$m1+)N-76*+gqOeq&1<;Ilq62dPb+{~jnIvRy`iH530 z;;$?1#oV9UrVzdKN|48luFUPPqWjeIseE)qB8b8nUB@SU>>7(UGGnGCR+DPVo)02UG8^S7^f3+3d`@dRn36hX-Y0cSuOFtI8;??x&x94_r@m-*bXqyz zZdn#QcYS;AP`d<0!X6KWXVaMxY3&`NkMqudeBhh5c-hhGbMr?Mamh-Z;tlM5mAw;{ z^oJb;02k0c%g+^*V*I|2V}CfgKH3gj1#HyVht$)JrCPqDADgzGPFo0+@-cDBCbFWm zY`Gt|#SfI*SEL@x2%ml0JDOg=ZokXim{6{)6pqP$@DbaK@#bJjrSW{))UlDGC|0FT z#ABPYIe+b2tHU!r{_E(9kg8HErKs#yYfnL#fE5%}T|rQtkp^~Gy2A1Z@Kjg`UZE8i z@j5;}K7GT(bs;@D;gdZ*A?|2)(u4M4m_u(tVu4*z0Q+pHTj5|IsFN;WOQSa>EAgh zMF7xSMiXS#H~wzGcc|I!LWBn=0qUSS=2XS9dA3QBMf)yX0vt;-Y%qRRz3g)9Y2qqB z|4MIF(HZ!Gb7$tSzwIfshF@!uVKw+Mv!RhujQee|Rt*52mFR1v7HgEk_cJC7@ImH7i@zsmO@E#8 ziKXiuj!xnI&uoRZZ&ZY225hQhp;MXmKh>Iumv?wUm8mKd27wNhmyow z>qu7Fwgd>me>(If66V)W>`tci*A>a_V|9Pxt5et$Q2cZ&^)toAj*(J2W@6Vm{d+|; z?g;_&(13yVkv6``scX&irEj(&mip79BI`wLc{xj7Yf2^cjy3f-oi@sA+TLk6ym7opKrMkKH{3fogl6rvi|h}TpC0LM(0?2y`Yi`%6@ z5zh@^_ErH}z7M_G%k~LB&Ssv!Fr6n;4&%X@h5|{-cU}!5qNe3~clod6+iU21p!_4I zaE(^^yBN&DeJyWr|4(AhppPP?ejhs}?bBrqS{&R#%W<$UA#YKq>kDPEOhf@#U}8ce@UE5b+eh zgddlLBcwAx)O<3gC$RFD$TOmW&E9NrR9qX+$K$9=5N zzpxvxP1dfRjwsFtLO03znmwuXD5qClF2Q`BJ&_{E9obzLV2DJ)1P=rJ2OzOI(RKw3 z32{RHY$$_2_1EC}KB>Ed1B-{3*PLtg#bFLziQcFY8%9N#e=|6q5PajZGbm0LT%4W? z!!dN-+1Lr;sJ>SGh*DBpdbz9=SKuOS{aI%ooxEhS@bS*`>T8z9ioL)Ji4$`D32U|Y zD?47{Oi_JAkjD~rG-d^`3zXap9i{%x4NSkHVXET_peA5|6wWS#^Nky0GQ-d`xW2hx zN0-hZ1>5kv_JrMwJEk*-Srq)sVsUGnhL z(cSuZqAXU*a?)X(=VAQEvJcaW?~*DzRm@LqbML+NE@@o0uGv$VoRgv-(bmrFT7JCY zm!O(4iD&4sME$yKbZ|j#qi=r8>}T(iUM#Pirnuv>mWJbSMJ&85{5ObO7MefFm$iL< z?PQne$7G_tP2#RVOXsW5dhxV=>1p0j;h=SViF9CuD>x)&gxuvO)P?|67y$XlQD=t^ zd_5)E>~bWX3f%F=XeCiWH!Do;Qo@T`jgK>**Jn<`E3E+m!*C~DWy&PAn>jn3T_eo- zonUi#Y`zqkwLGdi3BaYB1IXlv2&EVRg2u~|$wQW;)gqLl|R88&1!uz9UNMym3z8beswQIoQ1xfA88}2lDAhquk8!X&j(`H0^w*KKuwlWY(6vvd$|6H6+Lo}TBNkzfe|a{tX;dOGjW#*^sBurwPYq<%n7JHkZv3jI8OsiMQBg` z!OoZ3!}Z>adsIQ137%Un1k(5Drzim&j!bBFK1T$Q{ln0y%FrDJiUdJa5%|F(Zb9<82xmZM%Ny z6}nuXJm#}|VO@djQ}^J(Xq|^~U?xun?SnlO1roykRu%Mxyn1A;=3>FN&iykl#Q2KJ<*%q-V6( zZyc*LC!B~t@kFqR=J3`xhnYlhv?0C6qU*m!1-OAoSaenlC*SydgkZSF3e$!h ze}aT>&;E}CMgsMQ93<9LT3tBmwfb{rO8MkCV%=s@!fqx~-yfQ-oUnBvoVn>w)}G*3 z66?1_3bb64b{o{#c}S;BwHTG_Ej@Rr`n|U_@mbN>^K`kk!U{m9!o69kNAK>)@Pq?t zNyc>Zf>@Rh(<88 zpE#oSjhVbxt*mt$3*lxOt$y$+aCaYzDu3|+56{=E;p}G8FEG=%^PL#YO^(GHX33yeiMn8UDQ|x|v0w=JO3S2$RT=c8*qQtSTIz2wb z^E}*+Z)4KWMtUshC}JHP=JWjIHS>bvw!33sRN_G$Toa+OO_FMIInqE4{7 z1+zY%ZkO4C!4|FAcCYkhR20e9w@k1Rm+W2dNPjQ^|IsLuU#WoM@B#spKQFS2m1B!< z2ntcQ&bIE+zwwXq*TqaA!T9wc$hCBJCPOvb5of=8$wPpZ-{#@|V*ka?VTeNKsg{co zMdpA(9@6<&Vw-7`WNbsNO`r4MwEO&|l8atH_MV@$bQF9dp|NXse=I0P9*#dyuPLR< zva;Y+`M_kM11>oX{&c23f2l?IJmln_{o};C(`GiWZDi+OYQW+Ohz^4E{uPw-*Hw4C z1VQT0+Qv#QF-Z847AO5_MCkA|TCr>iP1?PEuuTS!VOg zE7K(30i~nYbh_{o#h?VO%}*++k4DP0;9a8ntG8K=*SYBVzwu;#5}sgPx_5^PBj^+m zh81Tn_~qJuNE};yF#Pf$4WJZ{czSZxys+Zpy}F_S%dM5XWdb7eN921}l(x%}!kZwf z{Dpk)0bo%3p0X2$I7^Wg2ZQJ47Q{4Q0l;d&_KQ&mvEPM+amPI(b+hh z#xkY%a``GhjVC?V{^sEfTRNMUDfjI9i6wm?_f`drfZF$k^szHqzNG=a#7Ob{rFpu(L)M2LrCd92uz5HQB~uT}|7 z-$|dn{?;%)`x$4Bc0>zPK~*Es zn99FU8FGnuGxbQ=u@_~J<^jWE-J?}HrM8zQoRW<`zrth3pPevgxoELVMU_5zk^YEz zj*1w*P>Mf!U{C4IMcICkDE&#aT&%$+1Rp`Q!n4T2;0`tSc1}=pcf?Ok*_~Rpmsj5V zy;>==bZyH`x`uhDoBMB`z!x=1O>Yti9v02xMrXSt|BTQ(9Wr3{_QH-f5 z012Wl+9y&-n#Bghr%{~V zGw}Cs%{4uiz*CV-0$)tGQW=CB|vW#&zT46 z(H^{x>#cCwYwm3ZQ(F&-x$ZGi3`I0d*_de1O_nsq3=aIfu2RDZabkKSUslt5m#7Bn zi>W&ZvH(3-5M5LI&Pb3niwP`kG9JCz;;-~rwhT6)v%uxYU~9l4KjYJnnryjp;k>eB zd5%*<&>8ezFR(3-J;YLans?c*+phT~2rCXT^F`)_;4vQ_iXkFulY!Vkqy~qJkrTPP zv*rz@=~o@Mo?J;)^FE5kl1wpR8{5OV%}VP!RN>ltVP*iQK79yNAur+dAn@|mstMG~ z7*B%@sHtA!+F`BYy$oipMB=mAd$B+8-rAf=Sy6N=Na=>Ha1_J9QQOO#_GEv@Yx7i+ z9e&b=V-&odW~)l{>u2ie#5}z037$;rhoRD1D$4bozxAIEGW{Sa;g_U8$dR_eoh35G zA3uH!R~j>r?Axpkx4i>;vb&MNMxvPxW*^fjriR7f2;j?!i6NoEXN0{~B58H6g418* zHPf0`UP+wlA6?GT>hl#kE6EL-j;o)&xLNWplMVO+V9(vo?@slNN8 zhSQJkQS;T)7>70a8$IQVe(t6Qjeh6(?iNQeU2&V{8`s>{za&YHA!c&nJ1HB6!vnBC z^JD`v&tT7?4w$vdPZexpnv?kwZv(!G23{X*+`Tu^C#g%6ppz(Nz3$hQt2V|FdY|kw zOTY#{;Yb(uWe}0N{dBZdO`>{+^HM3Fi#15h!U-b+{ADmI)B-udlglGZRZh_AiLP~4AB9$m_ zs?+Kxt-cSO$+yqvdo1hrV3m9tay!qR-~ZO`n?yY-P=|zMR-YM6D^zGA(H?mwWIU zqY;iUiY;z0meq3Qb|ZPYzKK!9=%HfBNYyrkSV*&IAs-jdg!B+Gg**$4#;?gLh6@a| z#mCuk#w&t%ACUvaoM&-E*vY^?A1Sy=wLa?b^ZrSU#883ns}yn=NWpDaZ( z?lnDQ08Lc=xJLI7ra|#{>ewlq zD8N4jSg@K37eysRZ9$b@elg93lW#tRmK`-;5CcHZn#x<^9FCWF$v)pjz^#+4Wni@7 zw0k2q;kV=)PAK@1&t`tfD-gEju%d~9#GwHAi<2+$a~vhg4;*h9MC8%ujADEIGFwKV zphkE##o;UWDw;AUj6QIayTL#x3Cob(%d`N2ut2)SM3dd<(T)L4yrFrZ?Z@ZH%Uaki zBRy#_FN+OzZ61^0=Xe^jwwz_ujtq@;#@*5=x5V;-tNkJy)u#Dzf_XT`X*+l?*IpB0 zU*eXjC2StqLKG%u6s!GBc4Uvq;QqtBTSj(DsdU|K=2IEgvio!V_9m1X8TA8iFjv%C zLhFNr)rfF}7#ZmE<#VB5fHVt%O!Me}0$!en(P_oU!L93rmjNz_mPjv%A61(xZ(->R7>< zM(!)+slBg0C=bqYK7S9z;yUp6roZE#1g8ou}KJ zA(YX4Yk~N=fYbsdz#Dh^I<@+C6rsLl!A1X!}ozr+> zGKXUemi0YZ^`-5`F_4_xCLgkue#519+^O32qp0-m`tnlsvaYU2BK_~|hWh{+m&HhK zBO;|i?I-Mo#&Q@A=!nwbq_Kd&V=OH4JF1P!^d_38_41W6pceG3{b13_<-x0e^#a^_ zlL$=%*emWKjrY#!!UKFV8Ma$|&{VBebu{_}z$qV$zLbyCHWSP&oiONE(c<($e4qYCXB#kkL7<#{Sh& zgofL}JNym{BFM>$04Hx&Ln!j`QX#C8lki)-Gl)G}!(r#OrwsAh5u6SzWlnvwn@DCm!zZp#^(-jZ)8w&$Pw9me0Xw z^TK}Tz4xvDCBy35YzUeK;`Lm}x4!L2TIl0#q{8ZpOASARsJAk*o+A@_&-~gfsXX|t zPtlv`n%R=B$n3XPe=zn|7*Ak@8IUjvULK8zGG3HTr`ulLhu|E9haE$#59@G^~dx!OgO>Pp$hib5F%H7QHBnU^-2PlvX$sGF9fjS>AyB zUS{=uhTfX|;HxWyy3%!xs7bTLL9D7x;_O64oCP&b_1cCvfAzA4RuIS%h}=hrgs^#x z7?~`FA|@!DX?8F_>#r@6iwtpNYp7->5g46YEAM5OK$VVRja151*b^Mv)q+!&NBoq( zYW$3w;}v?1*6RPH&Wp*kcR#|U3Q|ZU_tZdn82DP>12Q%5(01QxeO;Z3g z(G~`@cJBLlRX;zK?(W!rQ6)BWmQ zrHiy0VOJB*-!%K`;RUz(`;ndzJuc{ml3q@zicpVdu^bxWU4%~%NyRw!PVSBWIJCP2 zo-$zCNUa!btrVpAQ=aPU$e<^HAMAEc&%1E37WF6B%$ ziLe1{hoITbsn)6(OOce(I9vVn zei(w<`uJymW%?TWhr#RLvqS{iADdT_hy4hdJlH5NO>_7*}?Lyh8`K+MX<$Il>S#w4nowk;+oC!HQ2fOoR;JfDQPCy zx`f5Lc6+_i+RC0pDPpW&A#d+W*CxE68Su+FT-?l`9SBh9aKkhJ^8|IhLDFvb#=je^G7o;2`5-)J?|f#N5DjBt3b=HZlZrFj$ubsdJZk) z{m@tS%l9=FM(oW$k$m*flaPu+`^FjK+n?M;F%tO`cMKrBF6WDQv9qitJdEopx$KgA zc}A;Ayz?8s3=%te7{V$VrOPi69FJ%97a}QG?-RrFYg%u_uB@8XM9mf5-2?mha|cqw z=q|Z(KMiVb$I50>^I_T0KR%hi+vw6t3m)PSQEBbaDu8cec1c zDsPX^p>K3IurvZPU}%rm?5GRECxupPRP7hKQbF{^>nTMI%tV3;A5vrtyb7@mnTdI1 zRCL6BX^oQ#)~}|kJkJ<-D=ey?=g!C7sMfoETbB)Jp#w(})VunGEl9JRfJUojkOn?L zB%lJU$<{huDZ_jfD%?8iy*+1|Ti_$asy}k5Jr&gNZ0WxH{Y+tfze_oOiAmz}eZDS_ zGHJSLMuY6LGnky=c3yDlhz z>980gmuq^%@@Wl*mF ztu{6z0X}d0odpHyd$K?1temgddaPOcA(ROe8#w@~LY7t$u>|?8awaD>jnw8!Y|HDL z28n0ZE(uQu^gAOw%cJ6?(C95|p3J|M<|?I&VpZ#!h=mQr%RaElkkE}6ovxGDm!8XK zO6<)3AXM6{4rP*NCApW0c4Hmz*P+H`)2->G!<*V-?nt&zTi?44>pvrwiKius6le@w zX!d^g6v}4iKf8z)(vACdJie$EVya{wX#Ajal&G4_INx+1fDyHIXPaXt?ODI7natJ^ z0zXg6u26iH7j=cFNW7K4*Z}PNu!K7yR(L zR{1ftuWny`(2cN&xQ^bTfk1*2?yx-ojP|<-IM|?0JJ4EglUXM+Ln|1n zk|X&rNAJ+u+TgpNY;@UT&ZUOkXpZ%3q*3xog^fVJj+ zDx^>a%_pubV%`Y2Yo|?*kH0<8pf&uXs(cefxUsq67dSFdS1f*&g^^;71^mz?o(hrL z$H~!yQfqz88623VB!mq9stwj-2#O7&V{d)peD(|DRwsAyERxn#dx`>d+U@^7wO_Qb zyrq@!D?ju}SVURFx#+u2&0GUL-shTsWMbFhMhsVbpGr(??PM62eY9WeWg)c~wp>pzpQ`dTgR%6p`=|^y@J?3baxg3; zT?ij0$}w`?M=S>6M;*tK1qbObN&rl@dNqc@2I408Oe6)0;T!*OsI}Dh=6BfHLj=8u zV!HJ|xXkrvjlj)MV~kcX<-Td=TTH(ON01BT2$Xi8tL#g}cm3?3zZ|5y@G#2h}DG{&A7*_EDe}KyMqAqV`Ls_Z!DFHqP68nR=WOr4(LSl9vX8ewf91EPi^%!Ua2>EpKcH_W5<+yC+?K z#w+gh7P9N+zOQj@t+Gq`UYMQA%e}@D`40bO8w#Kj(p=A*sr@>64e- z?2$!=oGCTbjcr<9+3%*w2S8bSwOA88F}u-c4ofg8TkK)Uf)v)>*3AU^FW){VdnTFg zM$WI`cZ@H{SlS8P=(#|>4d2t&{_1St_=JvRlkBeyni#f06T|%Y@k-x^N7q!nrbSX)pjD$V&WKfU#WQ)W8$tqlvgEwhZV-*tN3t<>Kk}i~w;wiAy zJwTKPVPUEdIn)#I6n}L8zlFiS;h7Xw zY!6PCn62(7+R|fkmwxkwDr#$EtXK$S#mq*7U(F+7*0dRp)CnpFM@5X_tTZ<@$j|QK zDYQA2x2zPW*7Q`d#(fq;SbFAn{>9+U6D~b9osNBP?+L)7JvU`D?Cs&+EBdT4Z?O}? zw-r(G>*SS{QD)zD;mdTVSQTB{VP+i@af6!!ruCJ6i=H5aP0%T*-rOK;LYn1=PqDsM}vqgF@Zg7Z|is1VEY>DCa z3axW10AAu)%gHhQ4C0>*K63tfh63k7VJj8$FB$}m{YXQQwc~>_jm=D@Ge=(69Y@J4 zi6_47?+n?G-3Y8%YBIU}Q6?Flxzz#&s_whrQuwBB7gr-A9l$@V-Y^Yr?puLw`(9R? z#7Nqw$tbdyElIom~}i&{Mdl z3B|SnLO3}1@e-t1T{!<;trB#qGQs}nR0Z5pI0I<(o2G=Av&S`x$~-mJ4y3D$)IQB5 zA3i(L#hIO7rsZZ}s*0s>4y@B)RWC>WNeMwezK69~NGk z26Vi5a~P8x385AX^oUA`iH*!O@(212)H?=wlyQ_>*P)Q>Ha8D%BCp`H2kVUr2iDTd zr&7;x_+sv)HAp`dyg8zi1_9{np zQ^=;z^(8*timT_XeVRp`=kK8z`E9>wX;{AR;yEA;=lXIgR3Gj=w0s_UF>sx_sCZ{} zF)yn88?2Rjw@(eg#gj18Z#* zPpOo`a3BEkkQcSLGz2FbU`tKM;4j+4DEuHH$p#?E=E(gc=#{?{0|5KIY2?UfU0vZj zTPkn;A5m5Mp8pPe#IPXy*{n25IdoNHI^I?#RhqLD*VGDN4$%lNg{)TejD({;y)hkk z@kM|;EhRpaE%RJk=|2@s{9fDXtV?Us7ED`X*-p;eWRpt46ct3Nxoa!~?%W_{TxV5( z+P{CSdGXWrbEDf@rge?|mtdOKmF41k*UOr`l6!|wg4zrMb!T43o($8l)+| zTGg*Dlk5AeE%jla2-Nu1Q7@E=f^?>X0^&ZERKB!*==V@OOXxcOS@Um1Gml7WD4KK9lrlA2bhx8EB6Ge0%|@yktSfY3glJ4O`LW`H{L8gNO?W1 zC+b%=4^iIS;gvR9v#Jfoq95#uS-eA^7l%Zau15O)U0D71B0O{ye`v|Bs~E$u)IX3( z9cuZbqyhJ$V1#mkKaENRBar?$foNFoXPKr5=`Z^Z)Qa zEM>llrHTZ6`TwEqx&x{H-hXblx+EDPk&vAgS&6QUviII8WQHi)Eu*XyMIoX>cG)|n zvMbpuG7?fmS@}J0Tz&HS^d0?vzrS4f-uF4rdCs$+*EzSfAgk|#al6Y&#zPu+Y=wOK z6PNElOBy>rIuYCkT~w~?I&22r;q@fTUh~JY_8m=*x_U& zFP@~Maz`Wf!WVY8dCkZ}c0@@!qcVatSq&a4QGygKN;X|%cSLtWt>r#A6OB^8FGy7( zlG(-S@yUj-`Iltw-l{0POu8 z&{8yIGVW|T(cuihm?|m0;^#Z#zZ!|vTy9&-AqGWsGI*OGJuvD3tXaj-TRIR5wmO4?FEhci1Y>_YG6Jw@d50ccxRbv#uDrB^Ng(W7-`n z=gt8vW|>c)IM?(1GJ(sTqaof|s&8$57aiIG#o%9&H&;%uEOwhgj!AxcOyza)wv$&Y z=4LhzLDm<=s(dTt&C-0tQ}3hw3nUKl739&SK6A2#QO_#&InvEnjL2-!`cy{k(Gxfz zLT_BA1PAA-2UM;xhIaH_n{6~v8Ix_GC=j&$W>_2ko_fKkJ6etsi87dWU{L68B-AZX z5*H#tksxOQ7^Cg<&`jyvZ~2}d0>LTd-RFR*xGKZpC04w#ny9 zv#8~c9EH<;0h(DpVp!*c;0TSl?zWR)M8cs=F^g3CZOiCS=H54Wn_z)6VtJjl=WqlG zPX`mdxt=RT6;)5^0z&t7Us(F;|Jhf*)Q^zeVUFqg#~H31?}aHXx>mc_Cz3weBITUs z7?R6gwYd*DG!t3`HQ-0mzdN2;csq2xb`r`j8hJcQ94ez1g-&1c@2qK92sRftf0n=~ zW~nepcGr6RI`=azXT3wO9qLn0Ogz%Or|ec6$(B~LyHI^8jOM`Td`GZs%YgH)r8vYV zZn23Ja(@?S(qHdD@=7pk%15=w+m6Mi)Mw`AbK!B8D@RSo&h1ybv9A^E(Du0Qb6EAf zuzj5ayRE-kk)5V2x6_DfZmm(M7ChWYx9hZvN6d9!NoBToC&*e~-_V;mt$RFc;_EY} zO{A*P0G62@8{tLL@9z%#bvD`YE6!(!RvA=&-NRDpaB@JH-|>B41@yY_zm8+Db%xy= zn1}AfSyn{t?_JH4tejb?=Z3+rLm>!kVy5)15dq0+{w!)V8V8 zi5Hpe;~p0@K6We3XDZENS?2-iQslHw8fWkq^n`#;WWB)2Wz^l;VsC+TtU;N9z{8zp6G>`FdMQS()R*DgX+&0~6;h5PHy zM4zj1SNVyl+`TM!KIkJ_YD#I!cy-`VvGzR?qf)ig)`E?FZ*HD7mO@8TnbG)1MMYts z(cI8;v6-F^OFxlzS7FoLi@r(-nzQsnfa8-~kJ&wDUO&pr-^{saYaJ-yFeP>(MDcz) zdiO`Q!DWv#0(XTeGkBMJnpuQ`lt`fogod_2BXS(^*4G@N)r&+PHXQs{`63)paywv< z%#~!Pag(q_9;q-KE^W7Vla^s84cbf?lGdk@epUy3g!PeKH_p;s?lV)6^mEy5%>{RF ztmNEKe37G6{ZYp3GW?ZXb}^{mTPEgsO{k1qmXkk}RZ)>i0O5~3A{Xg(13ZFU3il0p z$S5gdX7AUr@#sD-ePy{gsug)iiKu^Mz31P%T8j{o{&81-Q#-| zer@c0*2}=+@A}c_>@&2c??wn=MBwV8tol8B=9Az6&Yp)=hl38aItn9^eP4yA)MokI zU1?RW(GsdD*Tc4=)W}}>yks{IeXI&wYmlEE?2OT*_5BYM(>l5C?ObyAds2)Q?4mU^`14v`y;gHU({We9Z)snTkB^rU z#Rj;r+j9-GBG=6DKfDwy-2Bk&sIlGEw(gReYV-F(2vnwQyAXNxnPtlN?3Ov6LX81t z)(A9ig65bf!@Ex4OtGtXju`1#Bur>*#72&-oqgL4_cycdV7%lj3sv`)6N#j(n3T)D zNDsUbIdahR!L6#XH0|cSfJ^%ic&1vFU}UzS3lnR;5* zuUZ|;@QxlipSaJkNa-o33n^nxy{NdYLgcF-h6TsN_aF87eu|8%X{dt#GE&MMO^d{c z_19a=Uq4(_*lObOJE~N8_Y~@*;M7T@H=YsRy}w-igsU=7b{_3tQgI8NGO>t4p8RT(IN*%Wzqd*}i1n zT(fbM{G5hcF_%l}6(?S6YPCzA?u6>cx9T$KsK+gY zlE;%t&-!8NXs4&9$Y)0;CX}8$c|y{cX76R({#xtI-f}%I42pySdd0I{Z4QUZ&3N5s zzTYBsfvTl{pz$l{9amOAwLCNGTT|8FM>GVr*oDp4w?p}Vtg5EE6@FC(vDyrB7&7!B zz~}W=LQ{&Xs88viP9@6ibdM&~=O-0csEPdQ-al zcz2HAjaG@WDqt&_gD&&Y2-T(d+e8T(K(hQHvaGn~3joUHMT18u9){nqyIpfCe(fMb zXd{9@9OBXQb=p;Tyd(I6TI_d6rLd?2Rs8WJdYp2@^lOc?!RWh#A1ge$hPdvSys@+% z+jMeHx(yI7NlY%aaDs-}&o;<6Nm_U5VR|E}ni?fl+fht=6r5ntU6A$ffY`*)9}J03 z4f8AkpbLR1F>X?C84@U03<<}-G9=JzNj$}{i}dU-fl3CG?aQ#FLj}_(4!&8`k`M12 zf1-W7@%rcu4WH(rqsxh}me`cEgSb0#IrDb+NX0hqA#(-z!r@0JOGF;H7g%W4RowV4 zn(fY3?D3`ed(ludJyH)(Dl7sH2ejnL@1<2;BA6J3s^41ypG)lDZe^&_e=Kpmu19ur zThj!8(6Rh|2kQ>#6-PpTu$Qwrk+nLE`uMoV$_UFYf@ zjlQF|B-K_4481egCd^IfK}Or0Dc&=qp1L$4%JU8Y80Fe_~$E< zs8!3F@K^x22=E!v(-)0@RVXt!6 zIRNENlI!&|D;PpfC*t_GSF(j0mlVv$WQc0Dw@6C6Eacti^bTd|DZsZ{TgE%CTc2~V zpNRaB?1Ty0_P1Qk#9$T=0Vxt@N!Ex_6QaM1~Sfx#4MacL(VI}X(z_%#^6Jc2y| z$Vors6nDwpXVMTku)^C~6Q}VA(f^Kzk7r05CWT}}a#!&U70gq0Y*J?|N*KwDTp)aj zWOpNv5IeF~2XM0QzA_Yf#LK!r`YuN7!V9k2NU^kg%cYq~2+DZipaPTa<7-B#=cf0_ ze%Y;Udm3rlLo`szu)Pv`UkS|yJDb6T)?0|LRM&4s?$Buw&vQH)*IgL@-O@RuZ@wn7 zMXcaewW{CHYWtJe=fVWZ4#<<76xpgIO5Fnkv?K2*Oe@I3G0A|A-9f^cCp9Nr#v)JW z+ST$q8b496Qyr~23r7ruN3c9o1>rK9g~k?Njh7v>Ninf zPkx#^`uH0k#kf!Brn3|fUZ!WhNn>=niPhWMl6TYGL;cEgtq&C{CRQ!4Xm-)loAE1* zKC(!xE#Fb2DbrFU>>6Q7tWr~m+@rSqBd*+MNm-cJ@PUox560nyifui<9c`}a^|}XH z-dA`nvC?_CFEI9qP|I@dd#t%I_moYp;;>hzYD=S%pE)$$6OiO=1W97tGl;K$tvI)M zM9I3>1lfeCZX;)f(=AZFSAD2Bn&>r@NiPvh!qv$YW+Qz6C}pORQ0KI%?_oj_bw90O zzlUjxbu>8Qw8k-VwNwQBKQn?MyXbopxZC11ODcx3Lf)JhAr;$1Ac%CYJIB|+a{m}T zz;`KGHsAI24Mq;EtsbJnAwj`pfKhU#b=Z zYYEpim7=HV2f8@4W806J+~l3v(gZjRRRjj+rl@r=kGX2<9oYgk>Gt$u3whLx$-yuu zsya&-3#j}tiiN#78rX>ODBJ7q?-cd*0lmU;h2b9ym1k~$63}(BT}-{PYriX3r?|%s z@p8wjpNxtnp?s12gZ9CztSwGgy!2Y*x)YBY?IffCoP|NAcilw$N2A=_I(%VINr`QM@Vlfmzd%C}sW_&x^tIkGUVq`APR; zq9K5z(!qdLUOfO#?F^BvE}|0TjInUJN4OLSH=TP>m_H_s-B>t=4t{Tfg|2H0*j?Z| zwq2J9ZHRQQsO6aR2~a=bx(Q^6{0C@B>OF{sK>u~7US~HSPy2^LfTBcp94KKZk|>(- zRZj0R*R0NkI{%E5{o&K&eZ-fpM+&1BmyhCa->t&jufHQx<1|p=3C%)nz4_0>@W$iH6ZcxOKBduGMK{Zh zw5cTy${m_A*;$nv5Y+8k&9!$mH{75X=KQ7PVt17sDPRMAoPm$lL1N+xMP68gq+ zL67dmG49~J*JoOx(mXsJCm2nn$!(SzGgu0&Y+ktfw%{zq{x4B%ru|wKZywse0puj9 zj?qxQ<)_2?M`bzhPB~gUS2DTYvUi`$se^D(OHrh$MZd(OCA8JM@NqfD#(w7mR-WDz z{=7XSyrMIJb(pH7(t9loLCLOjPN#FxG|?l5{dvvZjFBHlzFxPF@AXO`RGNR)6Wpdm zVo8>3wG?#jYz@_yQWmm&!23dh}G_7av z8nR4j6v*u7kgbodN`;VEU)=NNsrccGW2^ZG_RY|G>!k3}0fokSO4k_~UHb3ehrMq1 z3;4lFAcvbuaJ*k5YRi6h<;BA5-u0%sLtz6st8jO(#A=$j79|ifu9e#vyJNZIKPnW_ zT@^U+_Td7jZD*iTlm zcuVS|wAwjwvrVjUOqkg&=8+Yf?ql;PUfBFtv9!h z_;@zhcZxdSIMw${JA#R~skk)sWc7_<|Di!}ed-^6Xvp5My=8WvWJii(-EG!sw+nMZ z`UkCwcW-P)91qSewONu+Z@j6}qIp+)uMQeI6Jto624~Kph4&MWf~}MQ-R%tAZ$SVT zh=HW1TW1mf_xS}>PY* zBj>brjF)C-XnVGoD0BH))JBA!d_J$nt)=WS^FdU+)I0EvMa>Dse+X#hJ`eC&P|k`k zPl(zX|9a%xK8`b;yCRPmSwkcEuGX}t=d`iu+{YT{APQc37L|DUXek}Z);G*tgA&Ew zQEyH+NXs=p$bBnbJB?W0z_v+p8EI8G4d^FQU(HuGVX9^|Y+bH#OjvjC;|9r-2~vb2 zaEX4D%B4$}YP!0*?s^CrDw@=5NgYx4$5l&K%0u>*f%dnD;8qAcy$yvBF7o!_U&jw&<$;poan)#S!d3pKZ$jHcBy3ybwuAxW7!CT>MAOe0hWCTk8 z!#j>IXWQRAYfFu$L(J1=Gjk5ov&hL8Bj#)ETQPsR%0L#mgs89Vg3^yIdxA>r*6OX- zdiEK%L*6Cly=_AVbk?C~(y{{r0uJweSWcX_RkUpsOA2ls$-tuiUwplj zjBf96D@UDGSLErcFpbml$CF5SS$C@J3?;=zH!|>LdaHl}vT_-%)FE>t1ga`;6H%PK z374VXaoVDt`RwEP10m72jw9I;m}odRLDh!b=k|Xdl0ZqZ0zCR=`Fy5KlDdqwlAkPw zLGd_6p-Vr1Xl9R+t`Gzthme|I^pPt`Pb=L?7!tOUX>(KRs!_HQP}AK!OM`3@>y35{ zCIx}?YdzQ^iSzA3z~4ft&t)YmIY7G)Zz&NBhWjQ0$mewt;F?S9tL=ZiBG*YM|JGLFW-Z}co+bR{q$JW~E-8{4WOzpelocPN z%GpM$$_pCwuwlo)sw_kNt;SDEgelyOc+#7yw%b5d5iHJTspsk-=6e|5DZ)o4)`LXRk*J{To!L)GBrJ|u|-nTUrS~W zsb4LUu(@3%rN>wyu$NFa(thNS7(8?VMJ9nsLEda(MQ*_S?;@-9z=DRrGAKKwv#w5o z;nQlp?&000)l07h<%%B?_+h&dQ&3QW-n0q53*o+6X*3-)?ohzTLTRK6hLo4&iXROJ zU=D2Gw^aeWMD-Q~-LVV4A%Cs9l;K@5vQ_r>IRf}{>dIx!h^_1CW^^J|6(g_=&Yo}4 zk&=?qx)jW!d(+HCNhv3FD>=&12IRBn@KXB`47)Oj-oWLYO`uGO)H=HT)14Y{r|9<8 zJ4N9lRiv*wOr0m(9)?!5j#P5VNfngHGcmc` z^pn;E_<`dU;opS%gp9T@OfR;L;5ed~m|CF>0_BZTpsnB!?yPsY&mxN#oL6JL({-3cMe#hgqh9Lar zg@>ng#dFel8zlX@?N*(M#c~6?AF@zf-i+>!0KR^!7js>&Y9y7Bz=eMMpZc=Hj#gLy zw<0EZIlKNP$T{}DESdIEi65e?5UG|s`p>qZf)HV3H&!G;*&{bqcbT9zZ9*x7khG?L z&k?~C9s#~QRQi{_tADgGFxILw6hK*1n9=xHS*3h@lmM4VAVU5cTN#H4=g7GEBtrr+ zMXkVdB9J6C5SrjLv z4DXOQu2>~AM+1LA!oKA;#4A;P%GyYIirK?4dH9k*Y>`UQX^Y13vZj&6>EWVpv5$Qgrp4Qa^e4~T+58}A zm%z~wu~#*cwxtoC;z_{7xK-vKHw{DO>@xExGq%dp-+V@GFFD3qEz*35H=9o&*wlO)9%J*>V+Bw0v^P~)et`8AypvDKP!Lt)JM(s_6n z(P$=e_>`SEiS#8`17d{lzz}~DH5Xw&_rNsqv z9V{HeyoD$7EpS~txR)^6{_W31(EJq0T&jOI7k5>D*Tk#QbBRj({hMu)ss(hZfD*lD zRut$&U`D9Wo=apY{=Q7M+$ZzpP|h$_9PQG{?FXuOSO>o;P^bMx6n^%f`eo+2+1x7KvuEy?B@A7M2M~V zVNSu;#^@#FOC=nG9V5!&mVpb#awyH}wlsTJ>~q$ij@bQi$al6yP~7^E?jJYYdD@QC%gu%Pc9B^~n?1EaHZjXmTJ@iJ9G;{PGES zB#|%V7=r*jQjf~pi=qS~wJ+Oopadx{7hz~UigMYXP@Qh?r}S_vu!zr}Xao^-eO_s- zk}wRE3odW6jjxQ|vde6Q&Ua`So=2j@Ue?JcDB9%#o_{rEmOr_mr$Cq4NQNI_GL=nd2tg?52$FA zZ}lJ?JOI1rknbvM+6(-dR<4{!zX7RJ5*CY~xNO8Q%VbMb{l>|$;H8-Vl>K<${kZ{< zxX`2fDacRg!PqGS_0*dIyEK`OIgOh9t_164onVeTk1jax+) z@x>F$FpDevs>*TSN<8guh(Xfm>qJi>qzIY+86!dM^fO+K&1^k$&#CHTcBw&q_k^p| zwo@f8U>-6wI%nk*++l4W8!}G#Rw@C1%C~}Aae!JW^jzu9z=)B>2fhF!;6;kY z)WcT0YCq~#U@(D4JRj!?pr)xnSI?tb*}^kHfQmbA#bU=%IaFpEN6hk$IICUcuFd8_ z6~StTZ>M{aU6ulVhS4cXCS<})E=s-Sgqr)o=7dfYX{-4e5!^oB$_PfZ8H}hleBtr+ z8DrEO@^DN>99eV$Xn!e1wgm1RYZroegtt2HyGNDc@ayt0) zdO`vR%t zW;xcqr`t~@@_#_Af(~ZnAPRjGOhySat1vjc{V*ua6L)R`I5-FrdKCRUYFBte!T?fT;0|Q?aPh!qA=4c1h?&#M^40fCpR_=s@zz*>RrZ(T8(>>$&O`S-z(BGx6VZF`2c zO}KnX4@sOdTdH4BN3i(4!#?Wm0U4U$XVvY4$x`BA4A8zQ9l{!B=X z{kR9L4(&QqdGvS zWNB0X$h2{Qcw`yxRX#j=Pm+H%s<@}l*%UvJnlEQYo6iqCs!idRPC#O#ypWy;gwmk* zv`V|SGKSxWrRU~w!qiF^FL6|{?!JAEkeLY->4fTYX3}f$skGT{f2;*Od25Tc7@8C0 zAcM3^9c|4Tt`ADVbY@PoOY@uR zGpE^pa@M;dzLTn6!4x0l{eR5MQFSx;Uh2&gbslFqZ7=$8$L$-vbkGZ{(@ zK0tw$fc77$$qgo3vbt>PPFo?Vf-WWg)G07QTh+yTgycM>RJ1@nAI2KX`a*d?27d9L z05uKd#wC~M5f1VqTJrplwB#PdiBTssc*1Xf>(^|*z4YXCE<_n2Rk@TXGH^PwyMwM_ z-a|C!g_U} z_-1jh5(5(9mrm!20mUcsFfs19a~F#3u6a2Y+d0-|1Y_ml36A@xwNazM%?DApt`H7_ zd5zJyJ$sBn0v`R&KUXLH$BRp(p*o?#TXj>#pIbg#hNe}9fd$5le8YbL_Z4hab6x#2 zv=bP&1`Z_;M$&lDG|yX$rq^MAaJoS`-LLzXv1wAAj+W%uXt$ne&0GBr1j65+!eg%LEsiQW%nUNLKg_say5}S!qeYW%;dAbmtqz$`1cTir>^-3A+E#Ne zlpsi9hTN-sIa6hL%lV>-r=CoEb!c~vCambbe9_fSxFHZ_9-aCnG{S?ez=IpWgY;8n zQP>~w$eg8ZAbOZ3{3MxB!GM+8NAB9Prw0O^CQHjL$Fva_5tnxZ^HvR%AS|^@0Cxez z=6y{SGujEU7eZQpCMpo%2PaLqik&+gH>gpU(H-N-Ae5Z~WF<=5L+A%e-cI=Wh`ev* zGoX-y!kvHsHHIW{v(ta(gR8-~s3pDL&2poT@F4^@GBpK-P8{D`$D2~MyR`f=w7P{9 z5}aVx+8XW?zF0Zf$Pw)SIw@ zwP*g4U6=V2l42M^B8dO@TA}a9Z3tQ7W#Qyzjk*Q(fY2r0l=5kgs#akSF*a2st*5MQ zg?%-Gg_ZC4UuPE_)MjR7bP%vnq{&oH3D&Ova2qRzD>D7!<}#vGjp6NWNv zl_E_+!h5@<0!P#hP&DnT((5u15+x4b$^$Df&;M7wD@XLs!p0Ik%nj~&5}mHXU<5>- zdn_P|Yu};{;c!0Gny`kkTMc4~VwwfJ4RVrWl}j5qk`7PGkmWayc8TFSVARImb|OfF z!BVxh{qNJ@84%aOw@}Uy`}Sz~z$EvJ#K}i|-E>G`dhXip%UC2K)2lusfWH1q=HFTP zM}>@+shF;n45Nngi4%>`i>1?)+6_WUYw<}n%td+0AZ!!vAUOPe;P4+K4*&iq=}B$B zxXVpX&EX3YaGvI8{U;0PEbr)~!MP!v5hEC*IH3a~&@-;6%;1W98=UR$02Y96#NGPi zS)XcpXR#Tf*@~Uh8NYJx|1@B9Vh7i&)R4mtr9AXCK2*y+oSjik^LzZ9qVqm%P%V5{-rBRj&2%ZM_=MtdQ5WlzgYCq-_ z1ZOc`&xB5xBI!Q-QcB;zAH_0*>iC@dOa z>2x6(hCK)$6Lm!{|EW<4Zr=mtwX)dliPfnEGBmCihTP}v8h#K*bc4CRo3Z?jX8BtqnSh>+v~CdSlCmNon`XaJKWO$1gN9wGBL+l9IX z!HES`$^>Z&>#KpdP%IvzyXW7H^9$@8wV%< zIQVSP5qA&h~)J=+w{}WE2q(dc85l`cdg?p{I@1JSNDw>*|$@+0j%Mnz$ziN zP7ID6WG5-){%-j{s+PnCWy_o!icojv4Qw4n7O2OX0}Et6$R~rS$>awpmK7AOXARggWCK`Ax{f&ZFg;Vpk|OVg2SwdE~HCJomC!kCs^!i7k_xPEMier&jYpaM-k3CD}a$7R|tJmMq7(Sa;FE4JLh+=HS^jIuT> zOL!t8i>Vx|E4mS!-SuhQC!ga9CRC9@AGf_}a)(8i`Bm{M#}|94Ct|nm$1>34oXA|e zJSKNHnX)os8QgH&D;AQT(9d>Q#-IKA`Z!y~K3WEB!eRJUtIyHY#vXa}M+UydNw#ft zXR#ON>P+r5KvDk{luH`Qsk&ZfZ!+xGN%rC$9{AQw%}#B$bUtEq!F@ljcsud(LXFz- zy_BSvQCKQAtmh{CUr7FGW)*9$Ugup%%SRCSg zdL}vU=4X-+WnU-@On$3_L^Ow(}G+lzuCl3^gU+Ps$LUR+(Ay>97c8 zEBweS?mp4t_ZLS=EMpy?t2>OHgc)8C@IY)9gKyuW`AJQ~}TKOVRVx^(o@(eT5*u3g-ZQbVMCv7b=Y%a5vF zNX+JR7o70{QPwl<^ZK^qTX9?2@DcwZ1z4KIHWV5|%7FC+KK%NEK?3K*nEQRXKC@0M z(loeiXV&3~G9H>Pwhdq8(*hw*b#kZK@c#Oce3ZDBG@ZZ*<-=z_XBNkW=#P}C488wC zzG1!uBzJX@?=mL*M{<0jN&@jPpFQ+btM3GQWR3mMm{)F)-#~ahQYFex4HEaJfdwn64UMsR|ylLKcwkwJ*z@k`Q){Hz=Hgl`}0)m)*$jSXvEU!rahwg}U? z(jGFbHP`FW5Q1W(_UlgKjRO@yo<11RyMehsms&5M&^G3;ohEp}D^4L{D8g|}A9JTg zc)09WA@jw)fnAq-VR>PLG~idNx*6w0m0Q-{0lT>2v8x| zF#Os&Nei$=X#J69)jARviM_&~vRck<_#$>a+4#*Ex(|F&ACyd$UldrHep(AHVQw|e$r5=FWoRYem&6n8=6YiP<{GN zvwh%PJN$S$(zts&)12+FfrSZ3o;=gg+qhu{{5E0rQOc5go6^SB7G*_6GA286H%wWn z38W-fePG$}KAsd`EDK^-vco7;9R{^YG6x?&#^49Xiw}}kmUcvktUt5ig)TeZsI(KN z2!AB5ds4le!7%O+u;F_tNPm5$+wjlCgy;HFsJ4Y<*{UyUONxxDbbsFPy~-(AOw`B_ zL?=J@WZewF3utaXrBIFYcGn;$s+#`6nW2K0!L==|hq{)l^5>f@zuRJM{92Aod~J>) z4cyW{z_!}Tp(OJ78wbKeU`70DG?5!tidRdx>sDmx+{C_P9rqEqf-dz2)?!z((E82! z;=fr@cpP@M)#3>4IslJ6>Uu9Pe2=+QU9@SV5x4@;AnTskoZm@G#uiAktd^I&D_W_x z3pYK7>fWzZub25Z61n!hm$C_-Q)ddgb~IQB#FQx&lOHG>!qd`726MPHM>PlWyD zrh6CfY;=3%NF|JcdH-*00s+c~j6L3c)!+|mlAq;FZ^He`6VenM?C?s#;-Be&&Rycj zK+YP*KzJ46L*?^w@5Zm=&VlSU*VNwI_yK<1mB)|;eOFuA?{pi(gjIk`%j7q*ja&V* zVX<)!*ObA9f;}F7!lG#QYh0{BK?Tvm26@jza1ThucY_GvbqRn@Q-=BpPa~XzOIE8Y z3LdYFHd2l&gn;sW~>8!gw_tDKU8|=3z zNbhj;L-D^Ybyd-EhY2GU?hxG>Q|=uta+y!d*7;TJes;+J*)T9@5Nv6>zfJ6jc)^84 z5%B^MnKlSN@vL8caRMMjLj9j1irw%5UYi$XOm#oouGs3cdV$!YLxvC;@}~!H{Capc z$V^o~P29#0@VoUr7@O8YRA%h(Im^4E>9jaX`PGaYp7;0#&>)42T@NxgzQ48$xK1MW z^E|xs{e;EU8bMF%9y>$V^OqeWg(iosrsM97W*EO5Qr(=&?Cy@03Z&i_kB^@d2F~tP z&p-a_D5^;9SpS)A#4lsxR|4tIR+O@+k@3sg?7C^EzmaesNQlhY9rQa+=;r&L9C=i8 zaq9C_!x701j9)9g1Tg-b?bmN;a{;jDSX#@TBYkVDDET;*Fm=}GdcMl9s|ZKx4*9P; zcW>Yke&m$9z7)z)``p#&dE}o}-^Z4E9#z@McxTA>;|^3*|Hi;>!le>Ls)(%CCzNNF zM9b4XuiNKTK)Ux{XyVES4y@zwHoTADoVOZ~qPY~0zNsLvS154Wn0EZzy^YpQcx@HZ z+kdY4zVpqV$jecmhLwM133ja@Ss?Noo-pva;HE|&02X9~yM$6D$DlY4@P~K;S6<+X zKu8-yd#ipU9KMx`=vseewiuqP(LGb8>SYU4b-X)~#O>kxn3zp~({$}Tlbl^mWx&jz zad3TbasX5*i5y9y_+_%ckU2&Qz>Z6O0NaE(Oq5&7)JiAy;3En=7T`$YiBjT1KYQ_S z-Of)HjHj;qu?m4d;vk}Pd8-CDam^|?o)cIf{2o;JVziAPY_O3(mjtLFi4KUud56fp zEy^U=CZC8MvAm0kvCMRpVGsiKXeU3obB#I%1Nh%IDGxwGct!r@g{nEu=~i>#yxT=>xUK zS-<+@40b?O?zDh7o_mt;KZ4->%U=J&zHDj4Nfv65xn41~DYCQDU97DsK5#x{R8FQn z`VHT9+#-0{>AJUJc;;V2>hSd8U-19`G8zzzBSh=3_Lzm)9W#mFq-Gy*4xZzH(V7p^ z;=Y1|Yi@c)f`9r7h&wA)wBx#@;y+j$7IzxM?#Q#*M!di&Hq4yML2U!*c1_tybHExs z>)##v8|5dBR!tDukq1n221S-t ztB8Q5VvOzyN&jjjf07tN2r}}zf>K21hocCk>yT@Xcq)e0fN7X9JPHUhcI(`M-vdv&gKAEfWR4a?>P9V#D&^8Ls z?sXvF&qe-XpejSq5cGa%sEvtWjH=5Hs*ccW96JMe^BRLFtT>3u*tN$-_~>vQE51Q? z*F{EvoIOey6w68|MnFH6K8u?=_yzz-49SGb>J=5&aV2;jt~XuR2l(}cX&FlSVBDu| z>a$$Eh1^NsU%fcK=D^+wHr0n9l6!xf*tL|=k}1Klfb_^sQ2-jRAq(+feXsH}7w zeuANan#JL{+`nLw76tO9)=U!i6ln3aEhp)IW1_I8vZ$ef2Na6QmyU2zQ#%}}t(K{2 zr$oTzi!`^wv9<6xN3tcp3V^*1|F1NMeqG)=siqw$Wj-UJj>MgjN^!X*>^g^pK_@Bv zB{Azwq_yu}Mxe&S=jvtGpWX06QOESc#nd;$Ukxckk}dUGVs@FOUC$@MxkL2WDsa98 zA5_)@Si?2!`%V1+zX%1wLQX37C?Tk{SEr4XbZ46yj)&VUiXNzX9)FFH@E#PQW1QSA z@Y%$A%Vb?pH@whhe)shaWe)c|X`9TI#9~P@p@$;GQa_>Kt9*gdXi#eja?|TC(?WoHCkXNZ{DqGoy0H;fEMn zOP&Wz;Rq6nN3L@lUW;fz7*F|3=XU&fgfJe`ocir}T0iTx%E>pCZ@7NuFK-r(8>Xc+OHLhyB!~+-*E34% zBBcr9vT|T#{heQ5;4tXgR6((N7578rvU04ZpcFWK|UPS18un!xSLd%9sz1cmdkDF$`unCpp+)w$g|T z$9bW;8(VA;X=XHyMB`Z%Erf%}aV3wcS$ui;G9L84!FH_Es>!O#bLMcyIoI*imdTTi zjc%z9+I~h4BNzSxW5hqp2kHMf`U3rLV?*e-ze?x9CQoWxmbJ?VWd{?^K`;{m4K3xw z5k35xaFp;y=B9@I8`TuQSgb7S=|v03lmVHQ*@0o-+w|Op#X-x@=*n$NAnPGep?<#` zzeEN;1jEQ7>5li;cyiZeCOidOl+ZExjiN_pkNJV8&#y%=+0uzdU&JCHgd$cOlQlwc zR-mm(q0dc!OBf5P_SOx)t5h_1Dh(E~UkddX`mNyriDLmLt{4{8%5r9bd2QNtIP*Fg z8(s{v@^M+N7Eecnj_;+?K61yW76xmoM5CR+~qPM<%_!pd% z>`?-7>W4k%wbLlw%X&@kuGo~uaq7IPwJPq~sTc&%vi6h>K+R_K@I&C=UH=#ztFAvG ztcs-d$HPA0SUbOm5tpe0+%py9;Da z?U^Y+mHsxw$~z21xV0^$N{-N`uF3W_Oi%GU;&EQ4y@S>OhmAuBi!Cq16Ech-x~W}* z@qydlMpYvci908e6WJ5fVDZ7VWSZt4YZ_-&E3;J>GannUU-XFqzTc&aoZ^|ca2WXw zZN5;J8#vTH0EK!d{xf-ff#gmuy}&R%gDcp*2wl;|;_GyA0$n=Z!TA6GM%U|=09+A{ zcofe$(Wls&#DX;=8M|E$>it!z?Aj(=3=PC$lhO9_9x~QywE* z*j#WPFYksx*GQGU+HVQ5M)I=TS$&ho#cjf#eu=kx2%+rEj4R3ofnF_8n=Y>{3ReqU zJHS@JfH!>`BU;ij?40JCRmkX9fssQA8fKO;517Vny~^USJqXKxY(ZM$E93n{tZp0z zZ>HIn&99 z)mt8T#7DR#kBNH5z@iPyZfwHQ!m7Op|8uDzBVef)xyY&P&#fHDow*un(OqIW;=fv! z#G*hU3H?q$+cK=b5>C}Y`_$-8RLNft7_aRmAMEA$Ge6@E&iv={Pw}cs_(Ofn zvZAkD_(NoG4Y|5sTzykBNQDWqpU3dz{QLdfrCJ)oduV9zhGinA#7RtroFTgqW*?CK zw4RkjBf*fSXxZPIad`S(s;@QQ+YMGvo7H(e$mM`BlkflenA(+u_rA!a5tlqtgc{rM zFb5!2kV936*v{i?U>HTj&RRzr|F!^gid&V(Xce^mxYvO*Mnl!uTRkYdPIwy7y+4n5 z<1+!o%~8to{A4~h(Y|^L$W4rZgi-BJ_>hb=Itby`?lQmM*hkV7 z|H{u_U$3}0T^%~q+9__n)sqP#TZ7LL$+VxibdU>Vktg=DgEePJmA-8a@{TILhhH|bn$nTGGuEa$f}wo1+BDU zi4LwFy|ty9&Vl_}W#X!0RU+t5xOO1=cSEJbVFA56UesY5U|(QRvh<>~-%O}FB&c>w z87h;*S?#vyH%NMXR7wmlcg)DKR$bHQzs&@Oxl|?GKnt4O_S8jfsqNTxG^(kyVyBF* z57^c9!%%ot$fiP0ac%jX6U3T|qJ|#b*Ek-vU#FpLO0OH%>bUfrD}t?=JpCE+t~OgeDNoXS6yrDrN%RytDz893AWaTJl75%Pz@jl zP1v?a{LO0Pbv_ldR$O>LyRfABJhLHoNW(0c+!nAYJ#f-#%m!NV2z2c6TLekC`V=k? z1ZJKKKg7BTTz^qV$xh7;HDbdu>rYXyi{l`gncJFi48Rg=$E4TDS-lzoIlO`2n_pkx z?jhdF)Ej_*=5!XCiXR4hW1Mc^!z_$#=OLnAdUx@4olyBi#?!`}GmU8GEPi?{tK_0cKh_4 z%Iz2IHCb*mlUc+gv1i@eEk^cMUAmKfd}<*FR|DRIP53LEPG95cX(&kZ1lKjJGjZb! zTm^<*@4*+2muDJccbQ2+&b&DJZQdqpyUE-;%PPGWclP|aFeBJsb9~>fA3sK~#w}L& zhL?ixo+9hSOBjP7h}>Ip0p8z97-`_IAccY5^}B2^NUgakwwLxIQ#w?NewYs!@gC;! zoY=q2?|uW`(44*iD0+I)PP`8MTi^icE#Hf$PvO$d>5{9$p0ZbQRw=*d{q}5xj&gp) z%ls<{^O=<4FC;SUN1SruqM{WJVUL0E5c;3i#B*%p8W{L-PEQ`K6hLS?{GQVg?MnVe zmHeGbr$^;;oDSLSa^{>y;m4q(ehmekPI3E&Cwa@bY;#9h&5{fpN)xYsYw}stD4d#t z&$aEygxXCnxs<;*;`P?}-l;bd=Dqwq>d1ldxka{&=^wQAg^Y%dH4fVz+c|Y9=^Qfn z@@l5dXdh3-cVV+>Ed#$D4&IOR%13;b&uSIUHu;bgl)bm8(;rdMF`>vT9`M=f(f8Sc zmS?PEs^KY(L&4B+KUG2T(BZx$Ov%ZC@qL_InV zo;)i2Xq*`$p68@poeo*a>@`cFKCa+whwkN9XJ;sNNGvKQsABx7T*C!Q#2zT~;K;1M%J%a;GxV1(+U7J0H~lzP_#u#* zxVH$=MDg=|+($lDHxWb6qASmPXPezH)N?K~IpdFpB$X;7JWzqsYqhQbCOF~8$SV52I%_8oNp)^^n0`1|88r(3};8Tn=-?M}d?&y68x^l-t<6r7oo=lpIwAJG+PK7vLZ0_k;?4-;- z5gyMUJ^Oa{&2{UvxvQvIi+}aX+F8*4LEF4yS!Vfof8?dBJ1UljSjIfhe;IdH7UAkP z8xsrd(P%uAeYK5_uGeRdy2%mjHiw*Jr=*a{5rd4LfhNP zUTgW5X&h9@sv4iS7_7U1R;U(@z5iE?Q{~ZFjl9_0;w&w>RC##X@`w$z$XxZ+=si1a zs>hPTb0~a#aw&P27-xrdP3Ux11 z6<{Xazc|{6KgktwJXavTkB(D;?(dWMT9k8s7hAK$m=Z7jlC76ad!LEB-i4Z__(QXcW&7q5vfCt8h-iDq zE&2u0nutMF-M%gioa)l?o*m5YZxEtew6&9IAGq_P{PSpk*!j7%qmx?`hRB}cxN z-)$e`7Vc#)U8r{0WLSj+x>dy0p({WYQDo5EoC(@l?5C>_P@0-tk;TP|>4ommRJbS4=oYYEEp z;uC&S-P;f%zlrVs8z5+1oOBB+KS$zYo4dZ*tk3u~X^UiL=#bgwO`X)GX-_;6{X=ZsiYMfuo!^Rd`h z%U11!t}!Iu`Q=RGFM2QNfB!1hNGD!`n3DaC5{{bZ%6@c?RWXKoy$E^$K~|f#-`+b9 z^h?j#$#ik&>rsZy*UKe|doCr7_&1Yyy|)TD>EIx?JpQC2xY=}PQCqpWhuDX>Ok%cTw>Kb8iQcJA4A#|WerIUBMTcKzENRto2o^C*lh{7$} z-ql-G9BB{SE~;f3Z#G%3%3dH?=DR%|HL`rD)FO*k<@{db%NA9Diz0*|<5<(&V~FZ@5Gy?H!T?H@mU-$j%xZB&R- zsU&30GPG(#r6g-8vTqr?Ns-E0in5oGEJI?*GDVTBCA*o)zKk)18D`APbDeUg=f|tx z_xXOG`;YE(rsm9beXh^?{wyapM2JAXm7gp+U+Wq4;=7Rv5sc2-?}ZpF7+YqcI;n2)amo^>?eR0E8dVeJzl^hZbJgtt7=_Z_0WIl?Fx~8@!@(m>ol*}mcTtwvZ8 z69eX^dh2Zbd}a#zsGt0+&a6X*)oDU=4*oP8{Kq_5)N?JI-ew)SZ*JBeYPA?Zspn+W z(c&7ygevV=0HeS>SV9cHm45AuWT7B-?%S5+HDtAgpsyN`^z}x0ipkGWyB_3)Br}Tg z{H$=corX9ARI-gHszyEsaZ|0H>KlLbF&Qohm|lz4)L3_7^!noDp2ZoNlR3GB-RX{h;D_ucw#kLO)NH6xC~@L%wRq zI0iAF=JMsRvd>y{-vBPS{kUe?)p6~bfO(DQD&9rKg1P2%f{lYj3Fnc!O}We;BifKn z>CLPuyE@~1yhHIbzMl;G`tHd-{fL5G&Y^vwc`~ey)xEi;c&nLb z3qwLeArjkY_4!TKvw8?or-0D-$NQFCjycuD_hJpi#;sXQC7>KoV1DGXwA65y6Odhj(sb;8 z9?zKdk%^@YwzG5SW7*mK~Z$V12_>u5K#MziDjp%VCYjGM?{hy4+KIVjX216HF3T zizv4$Io|Q}eres->IP`?kF`)HFYJt20stUfWBW9G534)2-W%m!t0(k(=GIGjuLj8+ z<1I><9S-L?{Bf7@!cK*?CsJkR%UTdy5neN=tjsA>1jfnYS(3B-Tqo^89uLa12}j;w z=0zjc;5~j$n+pS(4y`nu@NUm*HFp+5`dziUxHHjX9R(v~Cc=mMEMGHp#cSV0Qw78P zIlWRjNE*|v>QLT~w8+BZl@zExpFiK&dc4C^WNvISvB}>0EkN;TKkASO7inl#@OIs@;+CWu$5N7iq)Dw)Sd#G|i+ zoIIN@6vCxp5&5CX+B8VIO{Mplb`TjgWt_T2$=IXD<;Q(Aw`X~iMrE2Ni|q1q+D!NW zt(VgZt4XON^D1LMHE?OhpIC?NMR?wr5TPIjGJ78-n`}mEUTw;^bPHhJx6md6Tyxf+ z4G{dtWPKRt81l^x-ScgGOGSVK=O0?*H-)pPBNc7M0SoQm+A^i5sh+?Z6h1==H8pl9 z*jF|*jdLl8Cm0^g0r|LuIU0?@^v?f!0_$8pc)j=ENYva2p=D2Hz}!>)%94zv!nQ3D z2y&uDD_{41Z-nv?RC{m>5^@r0)XLj~50ibgA>iW#R%T6Bi00fDE3wW@tcvG2!CZ~l zb8h3Mkjwq+__?{tr$;_$qOlV+mmj!o8c3F$x!2{nq0VIfWEJL2ezH)32E9RMH5B^U z`+X<-&rkJAiOtQ73?D!Lc28IgS~wTG-H$TBe-|Av(&IAPBxBSs%I`bWUzy6^qv=pI z7GZ_;9o~)>tCufJni(4o3DK}h&iCcRIkH>^uVcj+c3$NkS(SB^Y%4ucUnc-Hh;&JcZbIVyF^X-ocYHwf(K$X1>~pBF3v~&}A#ZdlnaWA0 zFi)9Ze0)I$02#gQLM9uP@HeCL<_(PRb~~1M14M*(xBu*mCVqziH;X;GUvXm--F$5x zt)Qu|2tVHqSAJgQ19snC3-i~yoTz+|7Ce8vL*b4SnCbSxyK-+4$nGAqeKPb6NogYdv^+pXw`@4TRzphBvJimPXD>LvwD9yeY zo!Ei$7vpLE_Y`(4yhI2Cw}94($BY2fi7YSDe$Fpozr=w@R-o<}LZtkxXSu3)+f6ke zFfE#TBpv+XjhlYzPXg3729yq$uHBD;e~9}m7+i_m`T+lTS{MStp8p+XZ{M}&T%%m9 zzJ>mQ%H4CT!9-aS1TqVi@WO;(4}STZsvFoFWfgd^>7^jy()>+=$ms@9U1Ku=@^A)V z%fFLDi>Hwd>OGLh9k#JUI(Fm+U+h18)i7nh?j7khk8>3`zoZR`ya6rK(`pKeflz~- z{;4ksu&QzZ_XYRAomfIf_B<-rRGjhpFrNbTsY~b^&Mufq=Q$c-_bN8!h2g2lz|KXRCj~o`i)3 zY!$FPr-}i#R|Rl8(V`CIrDBq(a(u0iSsa*)=a5v=wP}%h;+B9Q_p0Q!vZ=Y`A_~+M zt3Ug>_r;cDa!;fO&fQHsv94&(>7<#m_8{{BRtC|-ae@(*M?RAbe#g4?;(}1cfTFLa zv%}rt#Dy<;#8f8oxV@<-sSpakR2OJA*fn}Sv(uMnBCe><+(8FOmd)>#5iWn1rw%-b zXJZ#)O>f%Heec#3sQ4MEhn_9x_gbi`3JB4VEPXU}ab39sl+3V#nMmNzoa<$3;P<~# z-0=Ci%9Vo8ZKb&1J#HgO8|xaP1ud|wPtL*7$*iu)nk@8{ZyEVtbCmhCliPU#nA!hm z!n{$ZWnU%p2Z1S3#`?UWDoqo+r)O9U$)DJ&eZVH{8D5PD9LwAOt*K1gQ{LOAgFS<# zF$AA<`s~mDx@P=ue_^_ z?R=ALN>~ZGFaUq4Dm7b3|9a44U!@s}|=;=(XwTu6Y@7`v$Ls@y)v9`6YX*_|T8nG)8ZLSCu}eUQ4}J7*T>)i{+Aq9=l)jl`5r_(ZyNXh-r3JLG-%VS_58#kov2((Bc0 zG99YVynu3myVu--DKG}I(Z;baNzrgxXsJ$niiUEkYooeGpHE*3^_{)Q18rY(@(U)U z2|GM~UiMcICI@*MUM)Ip+(gGF#K8roaxCwf7sAdyL>IKSy14p4t#9~*)amSY=4ewY z3j4zp%?km5)%fXA&x9=%^Z6)I#yB~7s^eUOmRebxK~VYNZsB0Pq{)iO7W{lF{lSK_ zRMnAMi`Wpn=S)E;%FjY&ejd#mYrYVFxVOO15rnN=EWa_ikf0aiYGnZw5^K$HInfHj zvvcwZm3<(7+%fJxB3~)3T+zA}%fg`pL?1Bxs6FUlK!3(oB91l1&(H6-AW>W?5E0hcG`O&yaS(AxQWoR;YHLdYQuufT#ed=2H zcv!PfSeqFRWFhtIdD=pO9w7LnDX~q?KB)eGbC28KWrg$Z@eWJ+-o$N2EyXcqB)8nm z#|e^qD+3r3to6FSUtO&b!Z@G)$&p6QyV!X;J-D=|-|3FNZmgSzXQ@WDlNzne^X?@z z2YE_Cuw?Ur{T2uIR|;~rL5T|0SQG^7(?ux>AMR&pvL^Ve@*{me_-Jbs)8kywAq135 z6Q*(AQf2Uml@QLz_4U|cTxyl05*9atC!xPA8+qj6Q5%#X~M9bkJF& zjv*m*#L)9I0T^K-G>H*gPlA*BvNk}$9OAmVv;mpTmnpkfWF~DhiuN)nYjDf`bmqdI zLwXkgY^&$rc_-uF&r<(^79(j#0H$wr4oiMEXYSe zmUk@I)2yF)Ws?P_!}`)(O{(hiITY$=4q#k0z_|9#Alm5#sR}g{23@KuR&#Q6vftTH zW?@8_@m1rKJAVgt-5r$XRREO&!=vgqz>AygY!ws1>2C~3{uy_t3g8wPm)1=vZZWS` zwMrS9=2k|9O8D%PWDVO%GHY%e8{A4t)y8%|FLB@Vr5J;45rx#2_ekP&n#<7FtQu>- zuUnkgs%ebi?7G$iZt5_FUjQG$8WGxf_oqZXwAPv~~HS z3)@AKo((Iux3+pVs5cF}6VUV)Dw;amc{^|KwuovHUWTacgJ)|n0f+vO4d_0(Sz??oob4RU@6`4A5Lvh}3AvEiRZOXjhcdh_W zJ@s=XlG_+9=KZx?V&Uj<^Ly7d%a1S;zqDc>6d-8^4D!xBzTXlxUo#kWVhj5Zs1MB0 zoKKxZ68n*n$A#5%5sb>G3$y_*LWodkaEzhCkT1d4GUkd+X1TlqcYHR38M%KQwdba` zzFyjgqkaAM{&woxsZ1xgy7ATV<6HDJe;#Y`;WGEP-v6e)Pr9>e2SZAnyBk~33Qx)r zPg?;fCmQx<3BlMNVIX4Tjj}@uB#TN~5IKSe7Y|H$GsV zcWQ1We^VZgA)~qLk?`@1fUb2xjBzFN2(;)sD}8DG-Fo*|US|2|zj6;vMc&tQuFxfw zeC4pvwsZROc<)qws$QGZZ3eE%=|L&FG>vJvo~c2?OWC0$w|Dg`I9%WIP!o>bz$9}3ZfCS#|YV42-Hm)!Uu57xo_KaZYr#(+jUr8IOtMI*Ek+Y^!iBGQN z&`cACv3-I?BB%N=awPB@OS`P?Yhtv?1}H~`!yVK7>WWSkKVH++h~sa(v#~X8v)D)z zn;NfyBrYP#*`wYme=OaeU(LKWJg>GPPhZo#qcj=#~5AEGmO&O{uFZ50ui@9clgb|JXDY;EYMvI0hr5!(3iE)$tdq}fi938*_2RFMXDLmbZY3t= zRd$74!#Hbnx!F^)*PXm*CK)^Rtw5$4%bIm7^Q=7{!t4F>WqW^7GG0JP6wF{bcQc7| zIJDonJde51EIw}0p0J5*I<``W=tgaZp?Bzak!(>lM=4yFxhFL$jkdZz^zKr zsymvJsiE8@KCUxtzC}pnX4fk|4=VvB@7~&wZK3_xciLq$<2>?ysLUstf~85GH_s+G zT9P^e^F$9n5_edCFR`=f?UB5BFjS{IvCO$%bt>G)kGuY)mH_g^kpQ@TOyEX7*mmgQ zzw2^=r@31FInQtBF+2@@dJQ_B@q(n3y<%g>L{z#9@sy|OR`+sZq6FVhN-};k0Y~ac zCq$GEE_k&cA?NxlD{%Wxw%SzhsN0X9S?E*~6ZJIl)@PJ%;j~a@wKe9`1c`iE!`aT; zHa*F5%0;n0eQUR@^2g*bG~&d^Wl)m^#gkDj@ohSlxngG0VrOz^9(oOz4u^jbjm%~t zfS@l-j-5dtvvi|xQm(%9#5cotPPxOT+t9ZL-I}z;Q2@z2E*K`^w5NULTdR5V_5zmI zzKHG{R14(~0d~=amUXE}j}7C7vKqp-^Z3{UinKyFIV{xHI;p{F9=6OcUWoUvC>MBK zKXiC_Nka5$i*;OH3Jw4{ztyM%Fr(Y_NW|%PfHT zX^v=$dVuYrgcQ@?8B3Ie7fwa@tSKE1u4%8>x|h538_oXv?J|09+3^B zS(`a5%Ue1EcY_LvW;~v5Vo^0Wc3ACW(CQ7qQj3DdPNV62D?q5;7Zgr)Sd+Y) zKVg12YpnNcV(b;{D6&&J5-)Ag?LeZbo^ny_L{G`5iaY!qU)PRO7BumDNVBIhhH_PG zLm6>xo&C4_Ek`fKWchpcdtd%|JS6Eni;-%dy-_WhK19lu(V!n0&tIt4A@i4)Q-*lZ z5!3uCY0R@$0+g{9nXsA(-#yz{bybRy=y8l`_9Z{}^1aa+AN6!qlykQfzhnlul@h;V zRnfmr#ALcrRI(?)qF&Y2(!^to)AiFSXZ2XWNB+^&M>Z$u^S%E{)}Rk8c(?S|>g1g= zG~`CUeUN0oQ6R`;8=GFL`_J)q*}}B|K>J#_bpJJ>!|`IOy7_JJ&hQMy6}Ai+n&&%Wph7QGK2b(0*x;6V^&Y-?-bkn)0ng78ibu!VP_&x-E3G5E_is`w& zuZOWNp(E^JESx`iZpDK4X<^6>hIND*j0gPzpqocb9)Pro|=!H zyW1$+_I`ZFu;n_o{+8)X>X~YF@-rD^gCgM#Em*W-tMGWa&e?mbr!olmO&Jb2ziG!e z6NgaPq9X9ZCow@!bI&s^AG)SrY_ObK0Pj(L)O}S(W#nx!_3{&%yi|6nk6-`Wm3Am# zxs>i+GpAEgmaI#d*R86v)1wXT_oXAcu3zMuf9NX~CV_q1pJ%h7>JVOT_?~@sek(2C zz&84P4*9vm8tzJK_lb9Kx$e#LS^`C(b4Bp|Qd`Dxa9`9=)U^}Z-(re*f{$F>=Nu#) z`pNR~>9nG8`L^RV8-fzmM`yc8uTd2_s_Bl=R|lDz8j6BOeT-S^BQ3{%=~t;&T!3lY zlB2Ia{lhSTbcq;w35}K3B}_!EGw&XIinDChX^&WG(CuTmM|pJb{KD{)%i4a_fl`^! zL&Hf@OrPQ8z7F)bQlEV7R*7jtJ3l*Sn#$7GBd(lnuTc(o3ZzdRplw0n5M z)vt#ZO|&`IHsk9S@9375!yjl1iK8%E>i)AhDh8SyxJ)N2aPACGFI;hoTWNKbF5~4O zg)jxq-^|Qwth0;vRXCazxa#GKFi^`_pb zO*P@ersrAyz;rb^O%E3L`f4UxAIfqpLOQs9&;4bG<j2~+hg7- zHCo-@6dPVHGmq3yKB}C2(EIfG&xbO4@}JdmEIBQ-6|2u`?&OXiow83_jQ*Q>fotRR zs{5~KfOxZ)@eQLDkJ7*S1S;T}YIt#n7mtHv$P4r6z6a3(0(uNm{<_|wIM0c>kFo9d zWXOd&W!ED*wf%(5Gf`#g6BQF}#XkqclyE=dzP|yEP3!GK3I9ha#H&;HES@*>Qt-Rm@U|^o zQmEoyiV7=5zi(bL$=0Y3QeE*V`{6mr1FVZ+I59Ar+DtVnT)Y0A#fmZeSGwXnWkS2r z#jYQ>KAIf6>+38Wiq3OrA)`td{H~R|eOs}{KUVV8U;@0ITI8t&H%~v`0{^%Q`?5>O zm%Pc08vT62KIc_V+P*ZbAG=+1J zw?CaBoSGUb=dXUeSKspC_1Js&3v@)NhL7Hl_46Y{$X6VCpHjRrlfqs<~Sn9NA8 ziTfE=Ose$eX_+@y&psV&h_{O7d}Nd4zmPmtFQNCb8-M8X{R zzqJZM625D4YaTeR)sRf{q_6s6z-8g}&ujz$ zzG#*XJn~%N3D7+Ln5u`kgP*@t*uMMMP(Vp4LpPb@odmE8qMoI&SAKB6ICp$+ul--g zH;uf}imQBRn*RACMc?@!-8j;a^!wX+&s3b^b~Y;lxyb$64TBU>m%x3tuG!Lghini{ zi^>Ufb+oZXw4U;vhq&0!Cpg1D4ErI(2Hi@e!g(r@!MJpegJ6a6&(7P!W9Fg+!BgL! zJ+f@b7)%f?CC;tRt8)ypI*)xm*KS+P3HExFOE-(-ojBMl=S^dHuWzKmlS%VLSgx1h z+9O3;Ri z8HO)1*T1^B@BE&Pc8Z&R1v#0o_2hDdMkqOXxASt6GdHak@Y2^3XtQM_?jw2NCqh0r z{uKui*u1;h>nNyI*!B3|K&QJFoEGvf@86zG2?Z=-`VpfDJCyO@zB8dT_pezph1Cjo zchn8K4NT7~JjsYrT76UMA8GFH^gtspIXCW>c=jDOZJI3r`T6d2N3c(E>4ogslz7aY zt=d$McZ!~eJ0==>8adto(#HqyC9=n%>;q4^7=BcaZC;jKXjyZnk+MAx85q zvT&P2zG2vf%qj1eugCsygc}tq_N-Eb#Olq0xzXfmfRIR^IGYQ=GEt%@R5Y*Nti&l=qXuB_= zm68Z}i}bxv36{S1+~9)>9-pK z_d{Nm>zEJhRo%P<{w8Zdi5*R{MFYNAyKc|MHRm%g+7S%0E+MvTxKXyL$P_$J$tvV@ zDTgXhX8(uN`+vk2OSc*bq+r!{+?x3G+MBsC$Yel~#)faY9B=jj-u;wGe6~o8W=O5& z{_KX!)&A@a-mUCytpl-H{!-lWxPH0WwL4b~x*hsC?^h?wVPSm2cKZsB69T|d|I-b$ zY@L6OaB-v7d(Q3I_yTZ#QkFrMydQogL$cXR%3=L&oyxo%8h2&U>2Ipwja)m$xryMN z(Tgj(X$Z(y^`q#o9Xs6h=ThW2Mh!Z)xXqGQl2o~F=lT0yQ#c{M37lc(<~uuJ?!5x6 zR>QZqvwvl^nY&tU!VYdypmipQ6#lF*>kDuN&EH+ZtmLUIvNvkmr6s-&Y?(bvYWdNy zR}EjB*wq~I&%i4s%DLAZU9M8u;tsD*WN%*KFA2`nQ=%1a`4T6Awb~{}(crZ%J64A^ z3ai9_D>sKDBdSKCcQ*xjyy6zPkpbjK%_E21Fe(d-b+*;tGyEr0wbCx!Kl1#XRF2{P z6PXuJTRpQIyQS5Crv$Uj~|JAXN9NSv1)$e^0AJ=Jo8~Db~6QFv(`hYmM8dBaF zJ~-~MGXMnoMG06S*lSz7czZ=WwyqU<(&>(lQ}AqUk(N~DS74$LN8d>{JAwTod5n`` z)pPY8I2DQi%c&G-SxvfHJO88Oje2`70-m8ZjI74IJx56sdGLGVuw4v?(x10-mSJtASm(5Au>DIuYY+l?xfFKo1+3n4J-521H%B+YQ6B!4VYz`y&=^d-wit( zOSuocD-jv^tYqGOywodIh_l*9iNnIz^^4u^W;GzVQb;B%!N0~%CsRVH|6)v|;5 z^@%vJ($38Nf}!X4JvrE%FQ_dg8zY-7K7Qkp26&loJvVn{V_2Lg`@7YEPt^F-(eU%x zf5E;`-6yNa9q#~o=+)j~8otTMc1>^Rj%QwZ9i=4$A<92zzO(KBDX`5Xk@y#E-eEuG z_eJ2@HDQBoO=nkJFB|=u#1Fyo8%Y~(6oB)L50r*oCUMz|!0UVl8;xa$$vyJ`R(qIV zYH#exx7N<>fdhU!hp+kA91|!yT9wBe7zQ2V&B-pd?bZXN^Q9o+`{mCKGli6Z`D=qL zEA#il{?g5xV|2FyNc)j7Km=%GUf`t?_IzA8KV5U*8{kDyjbQsod`|?-H%m`_*(s06 z1@8Q;qM~A$_0HsgHX@mv&-b|7DX?1po0t1!bCkn{5OIgn3=97iog094!`FMThAv2V@my;u1C}R~9AWR&j723j$V&y}SPp z{x{fTRI2!R@gX`g@HvJHomaUAgdK4-4+#fCgt-;RyWPNoDEbJ)t9i2zXs^~klOXKBU|(#;zW;v9YQ5L~*t&l6<~15JQ9YEd%|SXVzIpbzYHKXSB|OQ* z>Ny$+6b;wp{<|V`gNua2*ohb7aMXo`gn}M4`_~w0CIh4w@>=Z*HRHdxKIPcj^zpk; z^rZdI}}0h7HENPeo;&p$?63ZV4HnYPk@ll)s+85~=WM%HS2C-*Q% zkBXN}DIO9&>Ku?56DFQ558l9$1wBC8jy>Ewa0}1|syqIoNWd^ovrQmZTKJ9A!q?+A zDa-e5pyA;TO2hjv981q$kGD?@d6}g<6JG-AdKJ%-6jFvWdfLf))2AC@XmBA?|vRl&OAc!qUKxyy9 zeAtbt+zpJgS<&K)+FUZ=CSAJUga3?7fijluh5U5hY|@$&#J#5$?**epQ%+rj$$ zHOv~=-pSI_c(x8I8ZgUhwX>$RjdTDC*EkIU6MO<>kkx#oz+99?Mt=*L=Urife;pFS zee=rIt5?NWadFvSdR#a7d3f41_cO&7xJNp~Yu}HjXoBOUTSdHsA2<^QR?W|QCi4$N zEaAIATM$iH{DSg#8F8F|5-zg;LntvK1Yza&yGnSKN;b8HygvzO(g=d)|Jog7?119H zLL*Xa(acv%z!QRZjDWG+4KP72wl|tm1s*TD3M2Y==5mB^j9(sGrlaqbVoyMHUr*#d z$z~q#)pFTgiZIb>rvc5;)tOsh5m@?6@o9)pt)rTTQ%w%o;8(tP0c**){ZAkgXqh7Y z_AWMUB_M?;$i7{A5g4!@3g?Y-DEjt3whzI{go;Zaz4iVcSP|bU_-Frfbfsm?x%E!o zCri20X%~#C1zu#p=@fkZ#j`1~;P`!QjqKpDCNsg{N6f>{gIO|5d2;cSz*WC;9A^aD z@;9FXZc_=E0sr43ASncnHBd_EiIFidON6eLCKZUqSC4qC;7A8`BE(zlAOFu0{999) z!Rf`Diby>gm_Tudrtq^LIDDXk%BW5=gq5ErAUCCI<1E`sEW6meFA8K-%~yl8&rwLP zfw`t#dx5W@YibYs3fy4;$s-!FSGeqgc7D-YFEq$8pywagXkf=4f#(6`BOcTqWs^4t zcwUZ)X;1z}?tfQ|u7h>eVW(i7CX;XH?u$EYO$USb|Lc;Pe^-FlaH^4h0rdSinEcnu=FJXBVSy?!>+5|I)V;s6Ia;NV7)m?% z!>ky%9ikzaGPabv7e5h~S`mC?`@Scqb8ui|Pl{#_0l%W{;s2o8!IM_5Tqy@)PW$$p zyerRWlL$_W0yW>n4I@E-y(J%$*f>@*q(@cQbX|7wW+${Y64;vUSVKAZ9|Vy!uLc6F zyQljOELADcak>6qj_cG_J5VrKVw7W@=~2!E>Z9%a|FW+$?0t2DeZ?)rLo0&nA)5C> zD>~I30{s%t4Woj+z6k8!ZJ{7WGN&0s(L~9t>0yP^)Nm#0uGNx7M}~bei z`Tt!>wp8u|v)N93TK0u&ubqKa3^y*|Pz6YKx2u~l?CZV(_wlnEQs5l#G8&0@L%!}R z@UIfxp+Bw=A8RQCx|BY5g5SZTl@9n(UL&9Zwv@~4*fXKuZmu<6g6E&xc~_pk*|z%T z67=jE!041x@3gbiY|ECCvb%Y6kcX6m5?`>mgVIp;nH4yw)F7tJAC3bdRJdV$L=nah zVS@fqJ#)#>5i!gGtEj98e>^QXu=*wd^NWmsQeYqXngLizL2CP7U!0=_G{A3lNJGZ;`!I|??$+Cq%d zf}-xyOlSCofm*vE8XrRH{FTNWTR9dh4I)8UD#m-bgPEMqxB4bz{^DsAcm>xkfXzi) zwJZEpeOcA#c>CO%*iR2~;;!$W-BClh(UxGSerQ=oS*1#rM z7+Mv?Z}9V%iHW=*+uf57sNJ9e0*z`1m73I+CeC74!7WbD@15Vf1@79;UW;wXuUQChWUIb8X`iFYJuufnAR0lge zItC|iqE3TFU5q$>{_l%Aum(ceCVrL1x(P|;d#Hia>wa#h10qb|@~iT&zxto6`Z5>Q zQ+kCNXM_%jKOD@G0AT6>h-@|elPxLli@^?SbZ)R&eA$IoGRUw0hECEDMA>c<;A&sy z|B&$$moGKo7VHaKLCAIg3DRupY5DED7qhFf zK45uYjsHwPP1yyWEN$_Y4O%Y~#T0(dMX13BL~#6S_10&Am_e05Hz9OD{~HA+m%tnl zg6z~~R{PIJW#7OX47e%fz)DyTbm+4*UGA3baNL#Rr0IN*wv`0gQhF zM|ohG-gQj_Dz~Ys(pL@K2BKe5;P$<62DaH8teBywhl3Ax`9`}WAeGD`xu}w%!Ygv4 z?jVN`*iw;D`pXlrD4Ii}*s7w)juZZDZqf4Gh+a(j=#&~3ZCH`Bw$cn~awy4XB;TkL zswCfEJh!9D$ZIOO=@Zo75UDxoqK;${`dU%tBIKtm)BMRtq18vkj1-{Ofv2){K}Chk zNIFYUZ(;T;jm85yEy|onKXtt!Xei^>vAhRcXvtk9y$;zIeWKdq$ za|Y53n1)clM)}1GqniJ|1_4CduuZuBoK!KMf9Y@s7C)b%LXj$M4a2Jri@1I>jje`y zHJm72Sk?y6$72T-a%T{WKfejB*&Xt@@2FJ3OKJQmLuy>$nK65G3F&Tr?eTC7jWF@aeK)k+kpZw--&!{>%>NC(^`SVga~A`OWLei4i;CCTBS z9gDR6X*gQQ%#9r2Eh1In<+A17U?5wmdY0HthX z?y2l^f8~EMg0&AYV0?I09AOsz~|;}FX%BB=X;k?C)*2(wpxII4w%5_v)vAtZQOPnK9a>rrIz~>zsXuP2^mQ?#uh+FzG1P zv{BjI%(R?E%xbwB6Zh(F{1oLpw)-_#^tL2a->4U{o1pZXfx0zsfa7Q#o zReXZcDr_S@pWs42E=OH4-Nvez9>zw1jxvWa;(ICG(BtlB_=vNw^=0l2%eT9Rda#XNR3i7w*oXY|KnpYM3EOE6}RuXe7BhIw@}|X_jpL%%e?Z(kA26M@+o| zz^%o{ZHkO!4rq*x7qemr+Uq~-AWn(94~8Rz?43$2q)k-ilRxfWeOH&^;5o*esz@WO z_0dh5@wphl%$obu8QbRk-Lc0eZMOX$RP&FZbw07I4C+->pB{;xnwFP|C`Fg2?bS-i zhBQFr6Qwx^Kes!IKomjAy!45goCe@&>F)vh406{ z>#g62n|REK!~6GU+E|gFC^^z5!sxl#gh^G<6v&|zZETpa>S~DK8%Q{r-x*eoCBs`U zg@^N#pzg1KTj3tOE{SNS+FspG+AVNGr8dP*>#K2dKSbjy^Qb+3+6ixF1LggcOK?bZ zsNO41D`CxU8QOe0HnNymn*XCORKef#!-tGdI+|r~m^jr^hiyc1z6XQ+GNdk2Q~6QJ zS#rTV1{nc*dK>d(oqq<{-Y+DgcNeHI+jwj`qPcvaxE(W;W(3nw3`+le5GeekpqHxb zkylV`O9E8_=P5CcN=f45&9XyZ%;!xUrb)`qb7vnU9K3#7gfj2^v7cXEy&+B$C_bys z*6IA2ePWTGGVhudlI^txkP1V5^>ziu252;SiGn|-~ zYEMGV4x(xhQjV$(lM~IyqXj_YY7%baF~{Qq6kw<9l*c26S`1PB)9lu=*00Ja%^?)) zZPWW3l%Hqcfq99=P2e_BfXBviSns2Vo6T=%ZbN_SpA;O=3mqxMJ%` z?b#h6_|2-bA(AZG4UvV;%2#n$EI(cEKcnWntuFiBLf&xqmLOc;D^tt+n-J@v25%FW z$;_#R4NVFp)SSog+R{rNCwPJK@@=*$3eR9t4E^srCm^**cYF5A?E;PBc! zc;_+cee>5X31X-W3e^DlLYlMF)`EIf`vq)ZXsX*RdW^oyqUR|3kQ7i5G3oCbJD7P;Pv#iG z(Y?;&Ce3~KDEmupWb)POo9U8gxOfAWo8(1IGCt)BCj zW0~D>w=S`8f7y4-{8`u1ninOzhy+uATCf~2Wm6UWGJ-tP`8VpR6;y@bb2Y+2(;QxpNURT=etNgBHv+0MOX#@^ zJKovg-kQNN0@oHU=POx*zyYO>0fUYK32rrm@(E2%AUIRP& z(mOA%uV2751#H2V&j`7)q&=JR=5_c9d)qpE7&Lbf4DVKcvKv#*$4J1CPff+2$<0J1 znh&?+SB-MDF)PRzY$O)v&r%?=KHjyjE)`7BOcx4bP^{@;H==s(l|mxOj~eTr3ha3# zSNHKs2r2rhB;}~4dxM&%-+;VS^|!43m+4Jv1V*lHn+jfhe6(A1eJjy93yp=eNGrw= zV~Y*t(~xjdCqJNh5+~jr&&JRVbeb0W#?SQEXk$lNnL2F`aw3KC-D~+nxh{Z2b$r*? zJuqn7jD|dX)hqt%mh0!mPr82POzS|-6zO!GB*+sc$?f~z&bX!}QvE!7q}hIzL`mXJ zh2+a-UVE8-#)?codqw749YHZiewN`am>?5@ovoy~f#!Pz>!W;rlVrTJyGDP7RKK)) zy_xawoBl7Z4exi&Dqc21g?9k$efLQMhf1{g@OQm$m%o|+od5dpj;rasWQ&7)Q>gB} zG{m!bCjq!g_xihw&WY|3hEU(3kmrI?efj&cSsNC)wY@JzvFQHG_EG;w|K}8dbZzYo zK{`&8I!9tul(6E3(A?79-jp-x%^ExW!rlbpaeRr1( z&Qu=NjfA>(M=||Qhcxkp&87APUf#+f>|3L8m+HehO`43!e%JRISo{nQbd9#W3%q$l zOTNT$_N5x?qA_OE;f^j z0^Vpo`HL}7(~L}d%>$x;ItAA(?(jzUjamQj6xX(hqna517S9&o|87jzG1GNAqT_Td zk#@@Ivc}|t7CThYJ}@+77SfOy(}1Hr$oW~F=Y7w$YQwW?2uKS^@j25#6KoH&MYx|L zTMLf|LwpF zNLMH^{0Pn{F-gY?XGZA&rAxF)^{oe2&uLT8kVK+!Z-klYGid8_Xxp6&ztwRWb_6OI zIY3u}eG0SM@vUJ!NHs9l`&pC zz229tLpJf7d*)h24KQLkLB1(T*V_uGmiz9|M9FA`t?p2$T7WdZprbM$M6b%hx2WYa zFAe>IuZNT?k7A=OV^(kI%LkDj)!gAR0-AMP$|B#aTT2-^z_SFzF1V2|#Z zaeQpTW-+7D)IxHA{}d}!Y@C`%BcHlA8`Lcmc12P*8$~TI*A*$MhMK-ZyAz5pXP12) zCfb)BeAyfX&jkR35iF^LN_MZU7MrNu&i-gwQ;Arp;p$V?e78Hv>&HW{xkyxIj(1Ov z5a@rMq5z=;Udni3WxR6nZcV@bnUz+-U=GOTi0=XCJ48$HMziYf~IDqqCD{N1xD74bm1EqbsvnOV?iMi8+o(9$w~nEYC7aH!)u&b zWfkw&*fnZAHDE3lO7-{3Hyk+{TKy=wo|oq|Ye*Y=Z<;cXWR!b&;&!xk`)A1=@>PL~ zHb#>o$6#N^AAm01AxB%+FAKimQ%aHeQ~D-B@4Du)i*Vas-?>uv~)xOpH z%>DZ*ZJEi7*h`i3oQXq8T0b-z2hYs){K%uHA&f2Ui>-2!OSJhg-}ls+D;d3SR>Q#1b9S!Umg_qU;g@5n9|_sKj-~S&9JYZ< zK;UYiL>PmnaXxckMu!o9EpOp3x21P`hB5=8;F7Q~*KgH$-#o#?mQfv3z+ZiY_#o^C(4a&n%lfs%qIH9*{wMYTiDaN=91DIqQHIGgzhr`pCWl31da(Fw zZc?^xYNO3fP2hb5#XlcU*M#N6I+%&an)@9>HaMybd7J3B$==)*VzZ$07_kf;kk%+saS1&S1WgCb~>@oPi7QI=jDM?B^_5 zMlHAJrq%cjAN#eHyPJ~BTL0Ct=pM~^*mKf%Abc>r+c$a`Ti%I)Jh*mqjft4)ep8-u zUxh)hbf`Gk_u9EHaMjohy9O5K#$`sSk>_{zNX`6~Xw81efW!KemQ?HbBE(EyCHpSa zn(j_j-|mF%t3KZv8+6DMjA}8Y>5GX`793*<0)x27_#suP9PA1889F`ANj}j`fTACt zy$M?~5NDek+hbDdK7Y@PUWz~Tx=UCr4*V^F<#PzcrSFY$$1SzOA)u4e%q=u3!Ypca+%HAe}Z9&&UmZ z6GW$HPNJt6bjF<8)C7MSX0!ytY!1`~lx*wKNm3ha6bpYX$v+Eqt&;0cr?J&0KwC)B zhwa!kEq_=8R-2pH6$sy-s5+?nK$a-lGy+Bvl@}VxC=uu6VNnUes33$4h&6STZ}Iud zveH^DQyG{@Zy~d*aXLFcv?eFJp4abCEOEX0BpfSXPcF6<$w%(pR?2!#qubz7*Xt>5 zo%Sf!OgAAiORqGK*_}(!-1q38p?R*vt&ml)ZEA9)KIw2F90Aji`bG{5C-n?R$1CC{ z-Vv@#)^NqLTp0J;nT+J(!J77JmTee}Up6IOFuhO_5M24)sX z*w=>h2u-)iIGeP-J;CGD0!6#MS|8=>(1Eb`W7Orh@d3N&m4TRn5bKbf!lR7D!qf2| zFH@xJ9~Z`cgWPs}=PS0`o&ofwzp9}lEU(KsRdqK6?fI^*M27j92!|_k(@T>X2OB#F zpZf5sCAu5>jk`?Hf1vJ|0Pw}>pIP_mTWU_^4uTG^KoV#HW7_HBOWgU`^A z@BMy%|K5M@Yp!_aIp;a&JZF7B6>icb=aO%20EZz?aFEY3c{^%ib1LX^9)0x*`t`nq zFnfupQ(;?3dj@)f2-2hiqJ>izeUkzw3iojAOyRboz5Ajz9WcLJ0r$}qXTn5Ro5mz5 z$IiXZGU7v%@ zhSHwl99V1>zSb6o`Ai;6x$HtVQ*7Wv6jv*@@O?MZcpDz~fKr1*JK5*Hh3b3XPiJ_2 zp0i^;93QlOJ`*OPfs~~#o-U$@_yUl!g+vFvMpKHM z)_lY}*_oq9Vs8K#8)Wlp>;Y}$L$paZ(d9#$KKL@kD<(sVFLIG$kW39(GNtoia*Mn^ z_UzC}qvs!uk+xR84RMr?F+u7=$^vsIt1E#NPKIK+O)~?I!`FWAIb%bM`R^NXA_{2E z-c0=P2C9Yyv6}JcL6+n0C9hpZvmNsL5^R*q=s))WxbO)I3Npc*SIShw>Ja?CH8a82 zf=&zSp7TX+dmlPzwA`@TQ^OMo3|6969!&FxMQ329sPKlUSDm+e9ezp1)2h2PAAxC*2P=b@m62NZH=TV-c=@DG>; zlZ+nx(<7l{nN8ZJicr6dj=DmVjXxxDw0gdCA?$#|7KlCThjvCNETC zU%WWE)dJZGyoCKh9m$K}N2!OUxI2lLli1>KJ zSE4SW8H@l(nhlNo_JA^CN@YthOn*E_Qq7eQrkeqy-@Jdtx?^SpQPu#MBH3!nhWd-{ znNXX~U$6kmAtvr}$JugTmuO3tDe z^MI*C9@y*p_+=RSOD~Xo^Pv3(G~YaTV(4LZSfa2lM`3{Jhxu5fGxU@MMefBs1KNMu48#J{af&KBsOMUNB%y2%tp4?;25uvAV0Q*4VV=d} z&0t{N&LH|4O*Uff)|@@F9VIUTu>{7CgDF+)1I;9a#5eYESQCBvs{`El)WXv|FlwlPtoItcf z{XSv_Zh~mZoKj{)*P-rdL{(-^E?@$OGO_24{oaVo4gYTgd)H+@BJd+|c{A8UZIwd? z4q#`*RkfJ^@P&P8?0f?6j!dhy>=ne$2pRkR+RmH-@H-et93$6ekkXI8?AQLflD3xR zkl=Ek>rAI_?Wo&ny!YaT@>?fp^KE=%;-0MDdgR>>ZhhHYKi_SJEV_}mHlMq|B#Kdf z8>3!l=&&Pw+b*^SOZSLB+gxAn{4yj-DlNuZt5)}S;{b0{g5Z!-_K55ne!n{6Y0m@D zsq9BxL9!3P(4IH0r`UOJ4-m(V3oQoaC+BzsIX(PZ#eP42rm@|Y7t8|(Ei_7k6iX`1 zn|v#V`+KR$H_SOcrlZOkPq#&!q%pYdH^qPR_6t~^;sV}@Xbw4NJ8-};G3fzydAj1)uf$^|(t z*)?>Z4=|`dd=Q$!k~+3*O`eK{xjc;)(l9#@%_j!&QTgXauh8poPCsP}X`k-mm1OmZ zbM3p4SP9S?vdHWtb{+(ZF+f{r`Yr{50uUqcaEw>JUad_SfuqYc6^2_%t6&Pf4(W^= zTGbUW`^BdvL96QgjvQLZ8&p5mVxAdoezS*95<1>fma}=pRn?o%;2Nad?h2e;N6-q^ zc@!YKY>9PNZ%BK~nwG~3}ZdW8!(^xGaGUWGI5>N)MB)<^4e+&)j0 z2@YX)=bntE(&@#Drfr`=Ix(tqM19x_Xh}_F)j$2~Nu-FIG)BQ*dx}1muDp4uW6+V~ zTucs($#epOLc~cB9IfC~Z8T&{fgZcKf{hy}kp=ahm^vbIuZq!6y5fjZ{;hX^KfaRX zrG+{Zyq_dF3v?D0r-~{zKOCpYz7x&FVo49P9mLs}U&> zv7AKh!4)`4NfPs}InkjOrgA;^0}65vFF()K6~ts~C**IgOx}yp^h?sh%)ou!uNIw^ zW8Eu2vQT6i+C;s`qOuzq^WIx-x%c~%{m68|?&X?cvPi47r0Z|VsP_qvM$HiY>*Kq# z=7{pSDfNaGnt(PZqy;N|AJYhxt%jAbt~3Lbl`gq^;iRGKp?nBk?La|7W2Ua2suw{*xe{unHKdxUmA>iYZ3$<+v$w&5OwO z#s=FtZczRS{lL!j$Cm?_)Kv}&Z=^q*P^~X=2vlm-MGYU2SD=k!^?yU#3R+acgA^$8 zrVDdGYTk2A=>J(~6MqAdKBm;>kaJyB>J1>f)_9(906d51aDID_mPk^_DFQXtS`ntY z#PLV{F6auddBjV9(&_PZ841(Swu3E^V5Y3`52TO|b7!SqtJvD(_x60{57+~(3btEM zgl(18eF3KVp&Hh9rVRlD8Q zfx6PejbXEY;jRd$YU?-={Oe(v3N_scl^UrslM6;kC)RtIRb`z!8DRu_QV0ohM0w(e zU~o=ggcn?l6kB5W0yVV9PfEt6@`DqQ-F+;=1i5NM(p}MogkYi2(Ye|=5FjLj{f_Hz*gmP)4giKH$rL;?4PS4rno z`GalhtQG+)^#XChEO3T`IN0xfinDbZ!6P|Zp8D%R54lt}9F0sPN$I>lp`AU}8JmBU zO^X(>24`8sM!w#ZOQ;U%2vD4e$(Q1OJ$uD1sP6uQ z+jL9e*5mg+nCnv8paq73+A=Mp(PF*zG{zK{&XAf8a6EGYF)bn;$#B-ug$ciWZ0ovg zKJ9x8&k|CWUL_wIeNeU!D9%KWrCvbc7CcJX{_dVDMpUO4(GKQFnCou;LV=+Cq>2gl zKM_^k<@r`OcftOVVH&$7P>iq33enB%|B*sWFctMdfLuWQeO?6vC2Y?4orb#CjK6x^ zCZrw(dP09M21!3c_gn>6!@*q`te}9>&v`lbhhX|rk-1oOy}9q5{YkP$ zL{qPK^HsH>Ac8}zQ{@&a#ZH7gl$tli=uv!V%7+Mt*{~}QQlnX;FHivU+4WB$kJ=o* z`)~L-RN7hO+dQ_XPsfQJH&R-I;F8#}Hh|$?{X~Eh057Dm5+JG4;ipUQ!9c z9>fiPbX|MSRxMZ}=m}y58c9;&dOK3ld1`c@SHO3bTt0`qPLbQW52yZ^At0k}+7MVA z=F^-r-7~B+ybgl2-0Rs9Dg~jOBM6cWX4bsvOnWVf)KW8zcfaJ=ayfF7AM{%_t3u<_IwHQ#S!_TFDofzjntwb>hj;t?vsp!@H z3qRsj5VT{$HcE;2)N7X5uf9*Q?~X- z`V}R+G#<=b9QoedYv3XTMiR8B7&Gsy+{ZA}QLBT?>a(k;?13OMo^SL)gW6KhGb|Ar zlmjqclgLiBz-c7$Ryl{Eu?R1M!jRy*1p*zRR0#j(6A**A4t`!wHvII{d0!431h=-prA|s)uCTf00~2QvBEbMyaKi1Iy3<`%coEz|5%YO z{Ob$72qXWjgWuzhD80yZo2qb|r2V_2vU$aM8$1~%lnVJf2^w3VI&e>^$W^B zp!@^nt3hq}F!0Jr7uq8D;PRkbk%+!cVu$Xb23GE9{3;hWP z8_M(V>9?rflZQ@dz;YEK-L_bHuGxf&I$%N9`q?~8i;9{7io7u^UdhKG zc`6)PL1lnYrXwuLm7w#f*Y(xZ^9_J^N+{+2S>_`>7s1NLOF3WJa8>%bmXRK_QVC_b zGi<)S?DWcod#3zk4fE{gC8m=|ntz)>c4Xc?1WR^8&z)w|T+bcVKO-F1y+@PZEC?q5 z+6OUzxqH9gzkt`Ee8tQMxY1`SFR0M`{3X+?s34T2mXe%RF4s_BZb5h8+P$!F%yA{vdfqkkp>Mxg;xFXV;) z{BNaVM->mEM^={XrtsziBlU@>!HK;0S8V=y-JiB`a@}+Wp0p#b8FDZ`eNzN0e1FQk zcP)PY=jmm5DYo~8sRcW=@3S63*54If@YAwCeLd~wtw4afK|=MKn{OkSh54w%Sv45U zta>tEf>GwquG0}VYsMJi7X9gqA~+EKAq^70wgs%)(PHMCplWIAE4D%o^oi5z^snL2 zuFF;;uA}B^$mv^9<@5#j0sC(&Yb;^PB9v!CnK$iHW$fp%7pX%TKv=3ev=sGGvsb?Z zT>T{aj-Mlu&5%#1A7M8BndLvRL-zx+|K8Pkdmoki{>(q!hrqw2iZryqw8$#CrgUSI zt{c~SUuJd)D@)yo{W*|YM&k<-p!F(4drI-*JiEk75;n+FpbZ>>+eh z%CRLDs9Lwn{3gG-CqLknuB9@4hL6FVw;qeqrIPJ=w7B0!TmTJ$GScmMvwc**r~4AV z|7=^zukpLC%XZIJjcwMhxC!Z6W1S=#s@=N($74*Ltvw#Og%s958{ZAF$PvF_RU#bM zqihA~dTre@>0x?FVq!jVjq)74UOnXK>JREoQY+rPgEU2A?N4$JVHP8vD`YH3+XB4< zaT9F}J+j|vF$WImwtaliTrS6GcKC%c#f310YLaOZA=;6O8}qq#e72?>xS4*%lj<&o zi?tf@TQQnNuXp)Ck03YzqGuS;ZFK@-u?us zgqB7~5Xjck?UiaDxH_qd+l0_-M4{Ih$rgkt?^^1@ib4c@oNYR`OC5xOz3M}!udFm| zv1{MT^QdW%04%HeCwe9isLabe-P4t(X-&(6E&~m|Lp4?YD*Ck4Rs8ZDpz(VZG!y3W zAN>MUth-oI&&g;hoa&wvL|;2q&#r2V%q086#e3&WKUGD6D_Z3i#6ERdffrb&uh!Nwks#wB^<^DdCkaJ#lvdSFL-ln?=GAP#Mf~lhzl)>AvjgDe|H*Lu3)K$znBbfQBt;YygF`=fRK%mJ24f2?wFbysk{+2VsSvCS?H^r zrAQP&Nc#OX4qfZo4l`b!pi&jFi^GrkJ_!$DA)IKqW5eHysHYUJZ$a}`Tzhhtr3I4ZdJ7drRo@L zPI$A|8FX`tSU2Nch#Pl)#*Oo0*&F}+p_&2*hv47b3t%66ON&qt?PWAYQ0LPUc|y0u zY8w1di!}`+`1PKLr?|IQ{N}C!NsKB-DIc_XG#Tl_^*dXM!l_Swngn-r%W%n*1~!5u8BXAkwi)ZK>zT?A(J;7Y2Fx=w~7jqJX{7^xhqV z0$9$V`XZ31zg}a`&sGi$1Va>fJm%&M0y@$;II9Q7NP9tR^DY#-a2_?Nj7RK5B8Dg= z>!5jG?m0aTKX_PX-*r<^Nr_1SX?8)T1b(?2cL}6^@NhduGx`O9H@auS5mlLO>qJAb zWTXXXaC;T^VkUlO)!ZYrGmc-IhC`Urr4sc$Eyrrq;gMfiJ20+TA(K!vfo2R1CYtvT z?OLj!&_0`c;|*~}Ual))4DQDqKpKIX0RRp?CkHsEl8{F}RKC^xJzm**db%k|)0tM! zVBa#Qb^p`F5%dzt%@0CuzEk^)MuT7CmwPFDm(f7m-zzK1iTo3zkvQv{-;BZ|NusaI zW2|WykP2b$-M`Q`ehdTQxUlUn*q1miqt&xndVZ+L?luF!{9tgX>MK6HKzoJD|3RcD z#C+?iVX{RSLFk2^ct|F*t+*q8hpes~u$6sXza+}XTo3RmdFYMBm)Ow5*^)AL%d~M7 zeyM+`=9u5f*R-3F>`nTgyU~(Rxiwg&D5cYrzm&ygG?lB-ripW!x%;6LRBLBM^(rPc zkmFa&QIClGpYYoq&h0W=fmvcfT%rOz^C*HmR#7emTWBviRWQ2+cP}s{B1>Y6G6lKM z#FS+bH=bMU#$c@_E!J@-BG30Quscv1RCJ#L>(;j=qt<^B8zjp^u|b=^q;Z?-AA5e9 z#t&&s{Z_L9eM!;q4T!+~=xEy|I9`irtT}$B8ANNVZ-Y9M?Vq}wJhHmVunFImenHCl z^~3oES{TwTvD#iFZ5cvv(8ti~u?z8ft}}cFTi9M0WjOmsviD(;5Gm;t>UeJ6ITkCT zb&E^31$!F?;3;F{hv(*mj*y z)jUGx7MWQVUZ9@tCS0dCT}x2aWP(`6|Ck=F1}s+Zv(FgS-SXdoNjs5gED9|PJ`8Sv zyvCEJ-oSzaq-FMn!IQ1D|DWYp!2F)O5JpJhKR4qvx~m{{>Wg=RQHl;AfXK(coZ(x7 zSuV552D;f~gF2iaf8oyVYg)H~7Oyc}Kxsqvye@o0&a z7yv8~8iGk0Dtl&g2_{G`LFj}Z>YW9HKT-eJ@fY_ON)q~$h$%`|HHA-uDb;qnGSh;H z@c@F9x@vB!h+3B5P-Lx;K7QZsK}V3MiukMlkHENOrqP*H#{%8k*gtpOT2LzpMuJd} z-7kI($tD{iK1UZXbOWlio~C!F_VX*WfUchPXSkLSBGNQntR7}~;<;1TbNhkCfds(dRQqcx$R4KMWF2V+}8tgB;P<65aQyw?&QL#k%zFcZHk=|@G z9fLX$ju+4dBl(zTMlaBOO!;aM9+jmYn_5PbvL3|n)p0oRNM(2|cCLqeR>9g4T+XGk z!I)EWv%N8ULkXh?Qq?{On|o-j3kig%I|5>0nkQ1nP&pCY4I4UsT!XrO2vuf8UUSoD z>`{NLL?d{BU60ps3o5{$8O@*a;)HgaIp}3{`MC=Tdj4oBB^%-akGSL4WjWwp>k?yu z48qLsX&~js&L76Ver8@)7s-S94V96cqf%bs!K?b1;Y)WJ$>cd3D!}-~S-pNGI zZ;_J^p6#nMy)?@gRhGQQO9ZrrNDqJHm42yk2Yr}@;Bu$IBqI-aRJKyOoFe=Y z0w4PXFR~ZZ#85KQhBE|yX$Jy7T3FG>9}D*;tD5n*aWNm+!-Bu&U%?~S(_bqa3Y+OVz>u=kW>g_N{YD&~?krU^pv54w zXB#NLe(pba+rtCE)0qOHvsbqhi#yCtMXU#6LN_Muq!0t`6LDV-RG|0beEpndceyqY zL)>HG$>L$wyXng&cL}(dtMpbIqrW=?|K3`)=hrqh_Y_hT9%l0h{l3vu@p|qCIO6DA zCY0_(_fcR*=n{Rp%V^H8n(bog?w3k1t`{FqXSUr*6xmU}2{fVfjz%P^37N?DF0boE zw?|>v+y_J4Z0z|-9kD$1Q&v%OG)c%%%kw!X{ZJ$?`>jS84MY}MPf!TSS7y`c8Hmup z3%G(uVJo{^G$>^z-GgAg2&h{%&uCF38Qi;Acuz3QAxt&HCIj3%J_L$=8Z&tw6nh17 z8?Rjoiv2od7G|dN7|NjZeW<4Av2f_RAY^WRPenJ{oAsZYPk(d&Nar;(Sdnuh@c{{* z9fnqW=ojBq(@!yWCSYqM)>Hm>kq@t#3g2O0FQrqyz6r7Obz)X6O4sKrZT%L!Uk-w5rffwqhhEa**=_mdS&jUDTme( zg=X`PUuFWzl;VXWc1Q|CSN z@(=2QvUW&^EQ?2Xk9fJbdn%HOG5EoD{;}hslGb) zJcjBEk}s`%olIGVYRP&JDx^Os#E;jdG!lmR`*hbgSG>k z(YB{zc*4O}W$tC3ML5U}LE7z^fr}X0OJJ0g47e~{j+2tdYjVZ(xzIfn?DN)&|`^7^7a)%Yl)|tHfyCpBIC<{ zlWpb}oGr?qxJ0Wb2f_1{GL|$XnMY_MzqtCth~U}O;HYUo^g-TJ#tHxBAuf@Hy3kvG z0Q59U;Z*h_l8asm-OOkq>XD<>4S4nHzSyN+=h~1wHkpBd$mfs*dQ#^v43(07@>(df zHX~kHgx+q{D_cb2BRr9dN^4b~is%K=yuD3hM7B8kQ5N!kjX#)dN2AQt4>6_k->-l- z`lk05u@AJyZ_|UrsMaj9V~yRjuBKBF$+=UywDVXEIj1|V`C*9Lrqp_)LYtuxU#`hL zra6NT^+<84YWMPcT}kzQUE$>Y4JHL^^&(Qf6V1&EIg1l<*NRnp6MY z*U@gGxl$N!RoI;u71HPu+6DQJN9ltCtMgJ_-)%;Vs1@+|ORD*LW$LDkc`TDno3&^P zs+N4j12+>bq=+?FyGn3-oiYm74zCt)YGTi46UdQro_ea}*-M|W7uR%U+${g*RJB~b z9uKFomo&LcHO(U+>qqETag|imI^ib- zRyR$YIjCU}EH={}5_tXb77s4)oR`6WL;4rQ-Kb zG|HFKA593){(4VbxaUnTXY&sMso$mg?-mABO?~!Gz=SI{ahXpH4O$uV7OQ$c4j^}E z=3hBUFJ#x{<|c`SyZ*l{Db04=4R8}bjc<`!?a2D}Z+Yp~+`dO^_CB)nk?eLLUmal& zU?6afM~QDO{ox(Hfs1vmOQr8I9TxXb+d2E5eQG)ylZr9YcEuNTN$r*owL5bwtnY7e zoW(AT(|E5rBbP(#XqQ%+TAz6I7LK*{rDxk44my=8M|(9FCT!^*ez$#U{{`F9mDTD^ zMN`cR%|g9nh9e&@>jVuy+E(F6bkgP$X>?8!$LR#R2|aciFly#$!Wo5{u=VD26svl6 z+vSu!Hh51sH}s<}GT%u_$|ST)>WSMQV#Igp6O(VOWH#vRVF1wzFN~Cb`mF^ zww>IqSr7B{Ci1apV}z_0Tq#`=n{3d~12re}^wwH_Hi{B?Oc*`K9^USkXtH7t&!@jX z5pW_>Bd^`g>`l4CWc#EG_v2(QpT#w;$=cIQsp&tG(`#+EhHo#M98YX=57;c73BR<} z#I)m)@bb3>{Fw>P->i;3X&uaVYs$7`iW|DXn`#Qkb8sVNiXRMcdaL7MkkkHmKh}m$uQ#dQCI4;VK>^c@ZV^5E?}R)Vx36!+ zO*V*r$@x0adaNl<05gu*sjLa5Fk!-^w^Xxob3|kqi%3p=V_x)U-@Lx{s(!8l=BX|v z(PgXHo6K7Kv7C1l2n{Nl4LRel?rX2JZR&Mz&P;IO%Nnk<2wi_q;q&@@IkGeP(>RGv&$nKm zYxqXAcXzK-cwfdE0S29{k$&+N1#zzfMq{JBdR<3l@+-s1*G34#ZoCc7UzFs_h?6ni z9@6BIfRDIvfXU9^Y1dRi9`HwQTI;?#im##wH?8fEE&BiHG_TWQ#_UcVjdg zjyX>3umF^C+u`#rt}QRGmY!66Bs7FSyA~t=*d?z6yGHwmTRW|KpRo2 zysNy}PKis3<<>9`39OB^ILpr(Dqx7s-^-WxQR#9dXU;$~U*2e8j^#PIPxb1mI%lS| z8df+J-(-CBrCzyLO#DcI+q({IS`rP5d->6lx>CJ%mk^3)fCzjIIe zmPP`w1q6HGGgx zubSTeAmOjbeEqQT3UQOkd=otDL94efb&Y@Qrg?hwbn?inO&*i?Z*s9~TeruyXOUa9 z7$1{{^?2StNoTPeB-ABbSg$=ipx-WKg|~?{H{>^WmL4>c_j6@u{188NEhn+NK0f0> zgNtv;NBJsvnK3&%!mx{ne!|30$h68^OlpXamrjW6FrQ1w*xe7NZp-fBMNZ^cB(nO>zkzu!^Ov3 znhK?)wvgh_7U2kM@;Kc-sOzN}CZxR5{_(egH{7z{JH|)4dAz)(9mxH?z^eeaf#zv* zJD|;cAbWUHrC6ODEt76k>X%6FO62j%ZyI^i|48^|qS9nNZne^QJua9u6?~{-TZbmC ztE!v%MAcxk@X8$fQb~xF$WuS~@-yV)`qd_Po8z@eR{5gwp`=P9RV^l;5Ch+`;BmGb zPtpiM&SIm7Qgknui%qUo*t#&M&Zaw4tR7mv4Qy01C;uRh#uW3x{Dd41$NT%{={(Fd zx5z!)4ZIqMoxa8daOv3Ti#6{n3;&`z)~)2FAkuhgpphUjcFuk{7B4mCBnK$CIATL0 zNgEq~*{1WYymwc>g?|ngZqQom5a?R!noW5(hujS9zk4PKaA^r)#_!;qEbUvE-NX$Z z>2uktjSQB#?i=e#_r-4*a(vfoK^rC@m<0p*9%p^;c3zZ87%SJzTh2KY(VqfrOc=@C zq*o?`w=2EJd1{yIeim<_9reCo}=3zh7n zF(0{!tGFa3iDO@7whZpmVH#@a)nXN?GmQKs1Ri8g|4joAY0iV9@6`9CwwJ`O$!4}K zxoc>-ooFlBosL&0CRfKd-%3*26|OFXajLm(X@8h}SG*Uj0|(;^R>pV%47S}bqkx2X z3k+co2w{%JyNH=r0t(#*;`GwIiP8psC!4P6jnruF-QvQ7!5+HEI1vExLnOs*{0D_l zZWyng4lCPwXTr+cCMo=w@!|)^?HksFA1wTymWF5buOoaFZ^|C~oaQ-B{uI_`*Okef zI$~B5a!JSjJzw5-Mu&+T1i@}@Vs*mA@YVL_*u!ZB1|i*P3&yE-fBKR7#y^D7A&wXinF&PppoCXKU9({x0JjHl9 z#iiScwGdUk8@a}E`yzWMaaT+KjP?N6yZdpI61KBZ@4kZQZd}L_CfkyO!yVo0!>$!u z70*+#m^@a|oSdn0 zoYrZV0=Y?1+>L^cGw*pZH{^t z)Xf!(?Qkn4ae3ReDV5ul`N-slVGE7uPbBT(G(EC`v_okx`|3RWP?u&tI0Cz!2f1+R z+T&)aO(I#HEu!8WcJ&Se7XVcCVp7877o1np7>{|MsnqV#rsi_hA%c`BY&UJHo9rdZ zG4#e^SY{~O0>e1*Wl}^fWaadW?WGx=KC{`e)A|smZ)TJ?n&&)IdrKTpH;f@>P0uO| zDtork>iM$E`i97=y7eFq=k9Q6w}Ii{Wku^{eheiR9dx?mDnJukC^Ob4KKabz=^*ey7*0 z&DSGMNVD5Q8haB~8Ez0daIdc>pD~P7{F+g6&%~GkZljCcgnZLxTc^MAkvKQ!M!#l{ zLMgXw#e#t8+xRHY^Jc70j>JTJReihEw41+@W0F`d+40GwGN*kiA!op>dCD!B@R)FL zs%qHlWNEtYfYv9uPeq+&T6T@+@fasakCZrAKo$UFnYy2tM208es@WQm3C{Nb|2*q# z{ezjpWi-!cdO(!UU4~51ZPqs)1?_9{!fe~Hd25H8b~iYTb2Pv2y})HYI+~E@GVIr< zRE_om;Q39n8>Isi^C!S`n&B8kUAr=`b|sjvn$9ucNG^Hcxtm_>60m!+?XNKsyW195 z`)&q3-?%jT`tD@?bb^1qrypE787D3JCO@~=S9`axHiiymxn(3Mr9t?Gm3C`HGkE`C zRv21o(9Gb6ckk*pdxUPh>xuM{LhAI*NY*LT15LCCX)FkFSfW$wdhP&{<`!l4n9Lp= zgMPuOBSa{39fmuW?di8osEK!91tEbV)0Z<#$TjWGowHe;YRKwbgxkmV;`CFbKi9+l zkym_Zwx7X(#{p^CxG^GqU z{3YcF0$irv>iL?b1`q9erF1=)29ALZ(}qUHzqucW9H-)m&(z~+lr%W?92ob(h@9`|6SxmC+{q{g~ge)yw!PCHg%obsK>_^ zw1QLH7Ff3@y=Xt!b-&&}7-t;Cy7FR*}cnTA#b>f>FvC=~n?pLWlQ z+1g^xOl?uQH(|8z*^R{MoL?=2485oeVP&(Ig{wfrKzCPuHx4UJyozJdBV^k$V2N0H zwbzAvi(cBu3AnZT>M*1=OvHZDhkVqkdc46nLv~e6lf~$$K)z_sz<9`Y(I!XV#NK6$ z;cAT46C^heO*e-k!)zADbe$T9-o~nosT7@zof8x9!|%8@O+I0y2UDPTaZoL06u4ay z{$wrX3WM64*^*BubC(}NxUEGP?UnoUpXN!?;qcJvmewGIVq z@*&HncH#xQGa*Gu{F)+zd>AP>5t19$H+xHlb2-wQIO83gc=8!><2U;tUGV0aaMX{_ z!a5@-#9GA@w(x7Ih1pipt*`HV#!~=Xx*(}xf%tz3*iLvASw*uLTPQ9cy4i~hcWpqE zT#`T^U_aKnQzgJN7@0bn*wl&Im2yF_D_#tzG8PeIlx&U-GlPM)2WJKY-{f7pnW#f< zz0}~fHgQ1fqontIxba@7!BMr4BtKCrejjk2>+E5FMJp>l^YR$!x*9^}npQg)vYf zulKgB2_MGC%-~^0iVs0kqjX-}g;Pwa5A-WHpn3XI5LBBm|2p)<7CX#Wth2*}`FnUU ztw!OBx5{kO;5KSZq#g~P04(( zT$HA1()pTxPR#^Tqe)KRgRAeYv>}N1)>85K5}&F2*2%2n?ftL~-iQO76_7paIDdT% z092yCciR2eS(xLLKAe*p2H+jI*_YJk8SG8Jl-w&)Z^5pZ-LHk*<`-fgCTZJW94QEJ zV-=~tMoKKG()3&@176s51Fi#(@tD{6srn|rsi_dVuyv`pUYGV)qz`F28>R-glMjsb zl&y8s*kvA}HTekMNgU=U%Q)F_npazs87J=FdU~SdNXhf43jTEmyn+pG@LpVf@sFol zFS5#=I&>p={Q=`UFMKu!2m2VG-q%pYWcBCH3Mns(?W5A;1A(n!P8QcJypo1LXeE%O z!iK_yFScl?OBr$P<;${T05}OJhL%j_>LmG-EKG0o(E|y{c>fu0^U#Z zpm&Q0WVBO5dbetqg=(5_?sR;;BiDQeSiVg0$u zHxjMNB$XZy*4X^GNk^L~nH|B|Ki_LxeDmjg8Io z%b}8X{>}N`Y3+aRG`X@9Gd^NATzFo7?TZ?rH7Cs*orXug@9cW_Id&@Dy2!}n;7Wc~ zhc6vQj1wXf@#&`uBK<5P8mAJopK6jh+l2jYCaQ0=X^6E@NSRRap5S$LXmI}&uZ6WI zW$fE$7@Z&GP?3|b6d)|3DY{cBY|D1VoM$Z}4KJjF=xnfz#IwwYU8eqwZpq_Zu5szZ zjRN_`1HVVnS|+WGV(}<(ls1XZ=e?0tE9IZvE&95GmoHgOKvNK2=ouRKYE1KUDeF|l zpJYls|LVBFIuC_;xNjQ}Oj(c2;Kei(y*IBMiD-^X92^`JdULe+6q5*6dax$mNh!sg z_1mj@(?&J3{1p8MgVlFNS;v~sRja_X+~&J-d!_eDSfqWjrqc`0>}pPb+8a@2s-y9R z;N$+f{h`)LvRfXTPQkt|TZ?O5#0rt3!->m#u}?Qtl^i?^fownC8#t2lmI3 z>YA29lZ2|yq|^-_B_hV@(ao&Iy zeimU`&%4Sl)*p0wx2?$f&X0s_4%H!!bIjHy=3$M4BR5YtNeT78-dNX_+`mbMnB{m$ zuYrzLN9>>rIdY>*^>)iIMsj?m_TO&3{j>7#gC%r&N||&?(Et;}^Za>u=&Fk1HP5h>2 z(!~%pY~uqAA`Vqohdb;umnQl6WZTMQsG7Rw?gcLKKgKTypzx9l=s39h4RF@ia9*El z?^r~hqE1E_l!fhc9O(EgG&tDQc&V;Vp$FTWBR19%d9p~!`P;{;gH@LrJp^)iJ*C34 zCf{xx?$qS(v6|>+d%xNwUPD{ekDGX9ymy1sa&L_a_h9r%frOexQky zX}lDwW-}>#l6-)^SL8y27^c3#!B)Z;SJPANlqzJU+eak zhe84YpX_AL;9n9f{QU-P@uptJPBCT>Vx*SLOFiU+=m<+~kw-hVQ42BuelI_w+fn{x zpJ@%*Trl)cO+5n`$7+82=I-|*rHSqDC%S86+`j&}V&T(hSZ1alerWPzK*~;x)8uvE zmOt5h_R1tp>Ny=vo09(M7M|l0?(9+op+1K#!l;xi7$j`6FBmF0+;J%9hae_6NYVV!H>nU zy9aMea^7KA%1GYEgw-uPx(%yr{W02-$@c5tMXmC&dvsnm_4-SQ9-ExX>9<3U-&WG7 zRkpj#u2yQT7&+*3PTVS3xc6OpPkPh4nED=z$~zJkWM8pw_m;J#^ycgnSwVj3<~-HR z!+#KWqB^{wxtiW*t(EPrI3Qd^GZ3tvEOEd!iq)fJ$cR|gfQO)5`$sAc+Jn2x)VyaU z-F-9C()Ze344$)DMEGaU+?|yUIysX(2M5pM9wle^T^Tr5voD~!@cGKH3(V%y;zQRzXI8;@M$@hk=|DOs)utG$R~66CItvbE zr+3}x`e;+#oockjzS)w65Y#ss_~#=j2ZG#>x(>D|@z&4)aqiUaBzYqRlbeaYd%x^c zbh>IUE?eJna)8WsiVrj1#uHv>+11-fWWb#rV)HPIvzF+BC$Jx-D57_YFfy06{pMBr zUo&JGO&se+32Q@oc9B=fu8tO-p`W;kJiV?=@(F1M1)H$pj)tank(x;aSFleld6PL< z7GX{6F*((Ob$olyYcz>roQQFRLLV)O^F*7fQtv|cvYc~M@z1$U^Fy@R&88+K2=jCL2b_@%GQxaDda4<0ZjQ04beE2bPitBmH_-w^qc3h3qxM6j?*7tE7 z-rnMZ(*#|ca94Mh`lV%`-iH%Y8*IDl3)#5bHYdoCE@t(=lWEBq=L#5Z;?#M40LwD; zH}lPtO~Py*B{x#l?+(MIuPT)`{vrD!X}PV@pwnOck5!mN#wTyrbKPl{e=Jlf4U+0B zdAs07;_*A`_fav@K$iw%4h52O&!?X^up#g5O?l6d!2s zx2x@rzsZ>$HDK4uO>|x(z$6#8h1trw1D-*(mTPdBB3E4w&me?G4_ru%N(p}m%~L!| z{-jK<^#U@VCQ9<5pZ0;GK`6JW1OWD;5!5lnSG~mKPiY04XJyblk!~g}+}0nvy1GIb zcZ;Mo-@#)-x*vE7TSyw%-#0^Uh&`%l*LHs!!-tUlmD8p&wtWkhE+2VV@qStHo_7xK z1`P_&#J*xm@OM3EzVWK^{g5-7>%lUJf1S|3_(+lOy(ZayhpSyh28+Rm!%@-FVp0;t zwNEMvw{P#c-KCVC*l>1e;t*RnQ@Gp6@jYqpKmKUOHTn87ioJMBz~?Lar=;S!b4ITn zmU3*(wk+Bds5&u~#Gm>8iRzSql+*V^%Ud(X<CMJ0cJ)iUV(=q_D5FjLYb+8F!S=d(j}P*5ZY@wHs^aW@Ja7?9_j07$gqm# z41dH;gbVngxoT)#cf?sqS=PPMQ~tgeKRkHIFYtjTg~8&GSxGan&CDoVJ1}&Ei3L{A zL49($(z_ii!y1VC<6)Hou9+VsEC(E&L`GfS`un~sY?Uz_%Gguv^zmy|MU_8!Klqg>eT zp&K&%SUV+Ia1)2se@UoAuK8Xe#sJ&yx(C&l(lVzU2hB?kCduJkE*83uzvCciaSUIO zy7+SZN?P(2QLM-Wy{q$)OM10L5?ls_EG{ckKJvu|R)+Q)LCg5}`s0>kwl2KwuRlwK zwseG)#dcU37ihA3OyX8Gy~~YTYyKGt4&Dd@cz!jU2U{T2`#O_hzbE&x~ zy4U4Ej}*DizPIFcNmZytGu_4PP!YdS|2ml*(XtCA@uL%wO0AudrG9Qw1D6VVV=XGg z5zlghpwr50ZqoSUmW#f3{_x%eX%j+ztDk`mFWKdjj70#U$1Tg+$|zsg*!H1TfK1|s zS7tq=Je_cVy=+C3Ym!~1^1WR3oORM~LauUUjrpgy-6;x@$~ju5bwXO(_U{)z@**RP zaJ@n?I+xzo?+#C!8Z~Yp*VngvkmNERqF1-K35;aC#e%hdIWqZxhs-QzEAw)qv870W z#bBcCrH`=wF=;kJx5KFdI|hpfp;f+?V?!>yd4KaXI9Tq{N$Wosy0?Zf;?uM?XT+le zbTH0wc+UwK@fYzE??Ta*P9sym!UyQ((H7msyg@-N8U}#v6~ff_-K|x4!sR!v%evM-F7wX=^(HNDJntn1vRDzbcv&KD14lbiP-|@o|CIg2%6&a4 zX%}y^JoNE&OSbnNzpPh1JT=%mVmLAWy5lp)c5GXU7Iee;mxzq?RyVyHx4^M;;f4Ri*pssN%rRsyM>toJCx=|zajuqPT zRrd-i&>>4tGS+3yizfMpr1;D|%wFGuCkTFr@yh=;G%a53o|&2XSp-cuq{EHxdxRBG zv60~fA$Ni@>T|enf~mbwX5qQ#7ZzcB42!#0T3%Ikb(;O2Lp7|o_W%MK@`9NGO&K~8 zu#83s+!@L3#tY!o=DYK!`Dhq!CT2Sh`rxyjlt_j!!n$9az+El^@lk#I1qys?3^M7w znbSWK<>359P>W;uKvqjj%Lwkc*z`TyWFvflwDa#$gi+5x7HNmaFFEj#gq1A|lhz;T z`1D+)v#Tq*)}LL^VAx1f#=Q<&=i|CZ#W+xN&wzw4Z=V=bK~v7>7WY2)Cvx8J6ZP^> zXr);rbFCKzylq!~gCUX-p>%ljVD^_`SLhzmTlXio-|SFCe0+R`7q_WqP!uPoI0xuR z>Los=4A_RiUP_~GTA8A!xY%+mG}2B(cRoeR2j+kc3V#WtWsA{b&LQ)#!I?vp-Z(>m z?5i?a>K2RO$Ix>~9PnabS24bqfXRQ& z$JrlIPw@zBW<5t%D(Y@i*gyB7E+V)5iJX2-qTcgTFGem-P#5#hpepQJvL0e80Qc6E zfgxWRJvQWF$*6q>TIkFa`t!Ntc zBC2NRBJ`M2RTH#7VeNW-6zSm9zos0Gp!P5SfX7!vmIQB>Hw-A_gY1ytCb@(X&VqJp} zq-2%GT&;OGVrP~>I{nu{KOD&7=aP; z@@n3N9>W1dxPy}RqBJkExijm7G&b4}H`)aNx?je-=3_M#_|=o^Vsb#+2K~-cBq9dd zRR#~w7mS}SC|KDrmZroLu%GHXlLwDRh`?8y=&w~oZ;?M>Zz4-$*>N9of3H`wRnF_( zCwVTddw{fcL%`$Vs&fd=#mC%KilQ6B?b#Jd+1Vl=%B$$2JoF^(dbZU1!4Q#|OUu~y z(+KLKU9q-;1$QqdYo%h>6GtT`TqWbLV|AUjW8*alWXv7g+qK~J%AOVf4cGRLepXahC;8b@b_=x>5NnO%( z;V63RwZK2z#{E`D5xn_-HnWvKpaopywL8cLQi>M8$%o1Y(ip`?bhksEFS4nps7Q;H7c0#!BM zhuSutFDsmohL$b?p1+2*q5Di9PuCuMBCgix3(|ctb*)kB`Syg*g9fjS0?d=X{0Oyy zx4QMB*B4(y&7k`uNce=RA1Xr=KtJUVW-|mqp|n@dGYz5D@`|wrQt5hvjFm~Tc=hnd z4NcC2HVs!>j{X?o;tbkZwm($mPNPx}hm1NQ`n+CoUgmah`~|!dbg$foAD@4qF!hS?c9e`OrXEQ%>W(VFE7UAdny2S+lf}V`g>|_b8EvN@ zNNJ!xPh@I(rs@sN{AjS(9PBrm`L!sK$w7Qs)6hnM$vNp|qvJr`bN@n7|H6NkgYfM? zp0%uVGA7PkunqsT&9xXo(Emxj-vu>Ij71W{%jY6>cHwBHAIQM+>wb`=?^(YKL+tEz zd+|x{bEdU+oChG=ounF**djAZRfb`{X%%iB1Ig+r-QY`MG@3i)WG<63Eh0qDhp=|1 zwoOWU7Hk&Y0V#s6;rNw!5tT>o`3}yp7Y#1ZF&Iy$&KHd&VtfU1ie>Q*Km6Irk27VE z*o^+=l9giVD?!YdCgbsDfV6GjcFl!r9B;sOv8j8f_OjWoFi2rlj8V4`LvN>jP(Z-F zOHBzH4SX>wwru8TC};bNg$MRVdhgp}Tq-J)P*X%i`^GzYge&R(2xJ;ygTxhejUt-I z)5=-T=Q><9k~sN&i{zr_qV;Df36V#m0BLI;$MA32{5?C!FJNx)K^7J8En&sfIKT0G zmUVYe5e|zrwdz~TV#f1r>PFU6P79{AHOXUMUU_&%f4zM`m|`_hTM&+l;A7io^|vEy zhB~KXD>shk_W5)`)P$l2xADU`xG7=kxem3VI%Qxh&<;HL$?(BM(Z^%W4V5~+Vg|(E z8LL8pj~+(d>k~pEv?p;xok65~ zDX+qYwrAljeh2IH3KQ=x`_tZ(&uz%DM?e}IN#e8cBC(qBXR)yhpI*f#X z-zdfQ{2X4z@Khz+620C9P2#3!v!0D5o)FurH&sj8m&SsKI-f4%EW-jvO#+jAgM6f} zweq2QlycAT6~_Kpyl^;`lpYfoA1r>iz}(%tJcrYZGv_}WUrk@MDl*A_^T$n2s)RU0 zb0_3x^$5(*lQ4Pd)0?j^D-Oco$C}iH8qDUatT~r9j4|ws{VbWaY=W=S`0YF zothn$r@m}>OUV;;#w{@bbJ|AE|fxZCK;uQV_%Bt=2TH(b1j=xgQq`Aln+0@ zGjKq=lt3u^v^`aQQVJ0LEELQ7|2Sg@c-G!t>BX;MFHjyfGsnx^&KY%ws#L4OWTtVQ zFi}LlgoGEYr?ez|NtA&048qbLSqs45BP-=v?~Lnk5Ohnehl>Umrg)P-zJ< z@aLZz#oJ29DC^Jz)746IA1i?Y^xlu^;0|C5*pxAKY&yrq=ehs?<-ksg-38<_D8?P$ zgv+YxVnj`d4lK-G_>(y~POUqNqI)MT92*aao>-U^ed=2dPoTfsP&hB$lx~ii8FyObzk9Vc0Rda`J18dhp ztgR%ZL($BbABBoMv%tJ}C%*o*eYW``yeUf_a(yWfeoo!08NX9LTJliTSoc!hdn+Jh zI7V!Zmi6}6pt?njOoR{U^%7K#7d<|isxG7%H@i1c%(evwm!LBc^%cr`7aVm1IXTmT zOmyhkfPu@+om1!bqrirIo7~k)$Cm=IVIOn?wGQU(*9~Ns!!NjqNL~x*Xrc23gj*0t zUAdTQ&2oM*Nn~YDN?IUUqvE86HR9&|Y^}TwHLPXnl|n=mdWV;H8Ejq?UBxt#svb-e zZR`VSw`iuFiQUlaSPvL=K&rEi(GL_D&KY{x*x(Tqw&X>*hnrl2LwYq9;ZMIxr{2pEzJ{M zCl%Yk5zOj=m)3IkR33wEl1J=ypH|ybQHr?tL+z%)JY?0vEVupJSd?4Q%)V(J4;u z@-H(jH8l5{NbwtJSg~1dCHsCBGQ7O|Kx>+x1;3Mu*3?Hp_`f;L4oz*>P#+A9iVlHj zx4HqXsl!ybW*D^_5abj(7w4mGNV8XKW{8fnc^QpbrlDRm*P?VcD6GPN-LxE5pF&~v za`D2dVf6tNB2IMY7*}x^bJJznx1_{+CNlHp!{%R-fkpu61b|k6fM2Y3M(-u4boR-pIf9YOa z5{qFo-+qy66Sxp;bUGJ9k#eM zR-K}CXl3NbM<(0mezqgYdXp#Aqk;IV$eG3Tjg1irFLJ04>=3Lqn^^azVNENg?A;8y zCaZ*+D^Tmk?7!47?8ou-^$I?+xqbFYhfAz!mr?;Z7okXX&XPp})td)x-n^2nxC!re zGT<)eHxC~`-H-JxnOMp-hu|3MPJ_Y9`pQ`G+_Den{OT^dTZXY-C2(%+#L1Ymm2sPA z@7lDXC#&^N;(+Cp>PX6SwJRm3plqXU<+Hr)xAE*nZ>joToN0bd?Z>5}xHWA!!;f>*1vhC&>oXFUmKmp%A05R3$&okO8-E>5dDN+x z(>t{vNb>xhdMQ0nn8}y9cq$l0fxlZ5Xm7g^3b+fHYJ22Deb5kNJqeKNa|A>+*1dw+ zN-x&kxa(dMjGtjGHU~||ML{IbXrQ?Kxm6>K?6T>G50i50T!*Fr3K2MrpY)mzBc;zI z0%C9>&9`U%5r1+v{Pu23d*w|P=MC!#Dy)efO=N*fGtm{>1qi)cIb~%#p{e-wcpi5K z+`uVg-90X5X57OsRwx+_9Xkbp8jz8j`eVFC8sc%-ZIQ{Ue+a{d9Ob>=?ixgfhRx$V z)=JBVv0bGY9jOXwRMokrZbxk1G4SYYH*AjC{CSPk9a_#8>lgk>(Px%NQz1PgEJSbi zrDO&oN^4(Q#pv7Sq36VbQ*0Pm88k$Qvwev;<#J_x38p>fdr0-wO>TK%!c?m{MenvD z!~@Q&=X8G^$LtnkqPM$sQ10XWG+A-};4Q>xtiz!)Gn+}9ZsmneSlDX>zxg=# z;`UQ#D2fu5DvvixeEy`Cg(o6dshS>`G|^CW2qy_D*OT$hBe$*97XcSt9c~YVr9(Hy z+w!fD^AFwYa0T(PS(Y|C{{clz6UOiJXUX7(34X1rh zCIu|jjS=dgbxPm5s3*I*%RX+%`w~?-BMQ}FsbmKcNUjk~WnGurHqIGCVzHp=Wq*$G zW2m48rz!vj(r`k430DTsM8QC@Td1B!#C21f8Wb=3{$D2Sc7E7NuPJ2?mSItIucW$rd?+;k;CH;hP zl$K7Sl&>ajDCk3b`|x%W@RLq|VW;%#Hf{M(iAG<{E!H(O}L} z@a=6C1H!qQ`Tn~SB^^$pq2BHODW`wz*;tCi(Fv+UA5Hd!`$}oHg)g$KEgR4(_|mF; zg6Q`ytx7nv6ij_7=p|s&H{++xt?MMAbDysM_U`L$)qgYi$AVA*iNxzmi&Ol=f~gdX zHx&4r2DAZtq_=#$c04;&aJ^kXST2SnH%z`Sbg|5>1Bkz}O9`NoIRuZHSdy?+T*R-T zExtIR*7J)H2g=}bL=_--*-$<+^k(*qkhnX(RgbDmqo;`6*CkRbG0yajUCKx#V7w1a zkR4F@$=laCVb-g3&t8izoG4LmhGpup9)1wT&bo~g`eyD*>>sO`&LH2a)E7H+se?t7?hx zI;}GU?ojlD#9)3n3aDQtDK(dM;WJH-CsPJ&`vT`YXjg&G?g>W2_E&Jmnk5>sS)3tp zQrpV}JuhZ2YgWlDZaPqWM*qYB{i1Dq;7ODCJp z4Jp3BfOzQ$x8clXzk}MY9A5W$<7$4Fw*~l}w}s;rj755fH*mx=Y36W?3`oSQ~mKEBVQ$Ok>?*IQQY0<D0( zPf)w60TD9w+EattiWh!#qe?)h>K65 z3I^r*EGX-;cD=H(jIaWH^w)R6{hCw7(KVR+UOpHnir+D6IrC4+p&A9GLpY;JGOBK$ zLBZ2*FMx)rPEVsqzSw?oYPD9`(NxpfzP)Ao=dsbk)Wk#95`phKqY}|Qm9vwyW|z>m z+oe&SmLRyn-AU({CmCw%c6j|$rPU_6RSD{65W*T+f+fDq=DIw?c;7w}20PXw)~XLGI}9QBLE=(4e+`kE`0 z96M;apBkI6S_OHR4n3#UEMRFBVG@M`%YmUqcUY_zd0{ZuAXmEY*0U%VVhl;WJG-C2 zChrgh$C4Gde$Ku_1>>mRxQu>$&7Au(dvUGdz*;x3{EUg$k0ojJW5X0E&w(-zB!TkR zHCqdOBDj&&B+Aw^NpvU^rxMGM9?6;bO=n;OVE45NqoS6v2{!=h!W$-ZSFHgVA`qyY zJ06u^vkN9Qhv$@3b6QxG*&QE)hkR=mwByE#L$x#qIM@B1-_g5TW&}KkK{7_|D*Nl0 zuX}(?H2ZHDfDi&h!;82y!HgRksWlEmItwE+&h4(+j}AWWvJlkeOr7zYpyBuCv0rL? z)kTQ!=z1lugn~NWxHmr5+P{?V9m}BZ3VDs>spUCc;o{L5M^K7qV{(Uqk_SXER8x8> zcMfu-p`ZL-WCFKRUY@NlE|$5ux1lC|uR6N8eG=25W>yEfMY(|2N?&pk(l8iJz1ngF zFyYnj71E2wn)`5@EMOGIasmpDP&mvY85koe(7fduzU%V2b2^x!}XqZFm9& zNEzJi!+))I_Y<&fVuNrI7085?Qq!O5%crnF`VC?DA?`p{`jV}HzHB30Rm{HyOp)pN z*S%bL^lcolyNQzF9o@4VI+mM&BR|?=za^+=gUdHAZ;AQMD%elY#3a=FB zmE!yYyi)vMIS8*5=dH27i{P!XR{|&hfm8f*jZL{X)cD;2b#S^W35pRr`3`jN{Bc5q ziy@E&1e^%LLJq5;Pma~U3&X36*77B+y;hjuK*V!lm@;rPwor04$gS|Gp#biOLEeMc z@ckd3>St7827wq)zbYmmEU|lvPMzU!^moKJv%+QQ)bM0(s&R|~#vp`S6?#MQhes8n z^q+(F%-{20EBt;(j|vw!Y7-W&O`~n9S+zs4@q2oRT^hmNy+v>hpUYYpwr@D3I*x@j z;38qNCq;y7J99R!wmI;P@BbID-lL}2e+5dcgdy^K0(aS7x^p$8@Nb1F1a4&hF0B4| zbTu}@H;*zjH59PPP+z5y&$rO*?XAMKa%uysk+r_LDdz_PC$G2H|D!4R1582YBb%>> z`OO%(1_H%bwKY4vV|#^H{mfTiXn5LC0QEK?=F6}7>LcI$DP2zXTHYzi|7Z%12nv7$ zDPaGR<-7$(tmgUKzk8j(A>c>B6rAo@b>r~s?~AzqJ2;=m=f*eV{M#4*{Vv(Pve!&K zD-W&mk#GLQa~@fq^BD4+$B^edzwUq;p6A6c?5WC v9swR;Aj|^{gn59${~Fq+%}1SOGS~kQwI=lb1H`WHz`vu1%ns%saJ=&0C#_vh literal 0 HcmV?d00001 diff --git a/infra/abbreviations.json b/infra/abbreviations.json new file mode 100644 index 00000000000..9ee376cd5e0 --- /dev/null +++ b/infra/abbreviations.json @@ -0,0 +1,11 @@ +{ + "appManagedEnvironments": "cae-", + "containerRegistryRegistries": "cr", + "insightsComponents": "appi-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "resourcesResourceGroups": "rg-", + "storageStorageAccounts": "st", + "webServerFarms": "plan-", + "webSitesFunctions": "func-" +} \ No newline at end of file diff --git a/infra/app/sk-func.bicep b/infra/app/sk-func.bicep new file mode 100644 index 00000000000..54a374f607e --- /dev/null +++ b/infra/app/sk-func.bicep @@ -0,0 +1,45 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param allowedOrigins array = [] +param applicationInsightsName string = '' +param appServicePlanId string +@secure() +param appSettings object = {} +param serviceName string = 'sk-func' +param storageAccountName string + +module api '../core/host/functions.bicep' = { + name: '${serviceName}-functions-dotnet-isolated-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + allowedOrigins: allowedOrigins + alwaysOn: false + appSettings: appSettings + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + runtimeName: 'dotnet-isolated' + runtimeVersion: '7.0' + storageAccountName: storageAccountName + scmDoBuildDuringDeployment: false + managedIdentity: true + } +} + +var contributorRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + +resource rgContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, contributorRole) + properties: { + roleDefinitionId: contributorRole + principalType: 'ServicePrincipal' + principalId: api.outputs.identityPrincipalId + } +} + +output SERVICE_API_IDENTITY_PRINCIPAL_ID string = api.outputs.identityPrincipalId +output SERVICE_API_NAME string = api.outputs.name +output SERVICE_API_URI string = api.outputs.uri diff --git a/infra/core/database/postgresql/flexibleserver.bicep b/infra/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 00000000000..effda63de48 --- /dev/null +++ b/infra/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,64 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/infra/core/database/qdrant/qdrant-aca.bicep b/infra/core/database/qdrant/qdrant-aca.bicep new file mode 100644 index 00000000000..a5f1573ebff --- /dev/null +++ b/infra/core/database/qdrant/qdrant-aca.bicep @@ -0,0 +1,72 @@ +param containerAppsEnvironmentName string +param storageName string +param shareName string +param location string +var storageAccountKey = listKeys(resourceId('Microsoft.Storage/storageAccounts', storageName), '2021-09-01').keys[0].value + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-11-01-preview' existing = { + name: containerAppsEnvironmentName +} + +var mountName = 'qdrantstoragemount' +var volumeName = 'qdrantstoragevol' +resource qdrantstorage 'Microsoft.App/managedEnvironments/storages@2022-11-01-preview' = { + name: '${containerAppsEnvironmentName}/${mountName}' + properties: { + azureFile: { + accountName: storageName + shareName: shareName + accountKey: storageAccountKey + accessMode: 'ReadWrite' + } + } +} + +resource qdrant 'Microsoft.App/containerApps@2022-11-01-preview' = { + name: 'qdrant' + location: location + dependsOn:[ + qdrantstorage + ] + properties: { + environmentId: containerAppsEnvironment.id + configuration: { + ingress: { + external: true + targetPort: 6333 + } + } + template: { + containers: [ + { + name: 'qdrant' + image: 'qdrant/qdrant' + resources: { + cpu: 1 + memory: '2Gi' + } + volumeMounts: [ + { + volumeName: volumeName + mountPath: '/qdrant/storage' + } + ] + } + ] + scale: { + minReplicas: 1 + maxReplicas: 1 + } + volumes: [ + { + name: volumeName + storageName: mountName + storageType: 'AzureFile' + } + ] + } + } +} + +output fqdn string = qdrant.properties.latestRevisionFqdn + diff --git a/infra/core/host/appservice-appsettings.bicep b/infra/core/host/appservice-appsettings.bicep new file mode 100644 index 00000000000..574342a8147 --- /dev/null +++ b/infra/core/host/appservice-appsettings.bicep @@ -0,0 +1,16 @@ +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/infra/core/host/appservice.bicep b/infra/core/host/appservice.bicep new file mode 100644 index 00000000000..4dc3e71412c --- /dev/null +++ b/infra/core/host/appservice.bicep @@ -0,0 +1,119 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + location: location + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + location: location + properties: { + allow: false + } + } +} + +module config 'appservice-appsettings.bicep' = if (!empty(appSettings)) { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/infra/core/host/appserviceplan.bicep b/infra/core/host/appserviceplan.bicep new file mode 100644 index 00000000000..c444f40651d --- /dev/null +++ b/infra/core/host/appserviceplan.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/infra/core/host/container-app-upsert.bicep b/infra/core/host/container-app-upsert.bicep new file mode 100644 index 00000000000..d9be752b095 --- /dev/null +++ b/infra/core/host/container-app-upsert.bicep @@ -0,0 +1,104 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep new file mode 100644 index 00000000000..7781caa8013 --- /dev/null +++ b/infra/core/host/container-app.bicep @@ -0,0 +1,161 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep new file mode 100644 index 00000000000..f29079a021d --- /dev/null +++ b/infra/core/host/container-apps-environment.bicep @@ -0,0 +1,40 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep new file mode 100644 index 00000000000..38f47e068c2 --- /dev/null +++ b/infra/core/host/container-apps.bicep @@ -0,0 +1,37 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/infra/core/host/container-registry.bicep b/infra/core/host/container-registry.bicep new file mode 100644 index 00000000000..02af29925e9 --- /dev/null +++ b/infra/core/host/container-registry.bicep @@ -0,0 +1,82 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/infra/core/host/functions.bicep b/infra/core/host/functions.bicep new file mode 100644 index 00000000000..e5805c70d36 --- /dev/null +++ b/infra/core/host/functions.bicep @@ -0,0 +1,87 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + 'AzureOptions__FilesAccountKey': storage.listKeys().keys[0].value + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/infra/core/monitor/applicationinsights-dashboard.bicep b/infra/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 00000000000..b7af2c1a106 --- /dev/null +++ b/infra/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1235 @@ +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/infra/core/monitor/applicationinsights.bicep b/infra/core/monitor/applicationinsights.bicep new file mode 100644 index 00000000000..9cb814321c1 --- /dev/null +++ b/infra/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} +param includeDashboard bool = true +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (includeDashboard) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/infra/core/monitor/loganalytics.bicep b/infra/core/monitor/loganalytics.bicep new file mode 100644 index 00000000000..770544ccab4 --- /dev/null +++ b/infra/core/monitor/loganalytics.bicep @@ -0,0 +1,21 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep new file mode 100644 index 00000000000..a56f9ecb1b4 --- /dev/null +++ b/infra/core/monitor/monitoring.bicep @@ -0,0 +1,33 @@ +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} +param includeDashboard bool = true + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + includeDashboard: includeDashboard + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep new file mode 100644 index 00000000000..75461a6542c --- /dev/null +++ b/infra/core/storage/storage-account.bicep @@ -0,0 +1,76 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } +param fileShares array = [] +param tables array = [] + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } + resource fileServices 'fileServices' = if (!empty(fileShares)) { + name: 'default' + resource share 'shares' = [for fileShare in fileShares: { + name: fileShare + }] + } + + resource tableServices 'tableServices' = if (!empty(tables)) { + name: 'default' + resource table 'tables' = [for table in tables: { + name: table + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/infra/main.bicep b/infra/main.bicep new file mode 100644 index 00000000000..fdc51682042 --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,165 @@ +targetScope = 'subscription' + +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +param location string + +@secure() +param githubAppKey string +param githubAppId string +param githubAppInstallationId string +param openAIServiceType string +param openAIServiceId string +param openAIDeploymentId string +param openAIEmbeddingId string +param openAIEndpoint string +@secure() +param openAIKey string + +param apiServiceName string = '' +param applicationInsightsDashboardName string = '' +param applicationInsightsName string = '' +param appServicePlanName string = '' +param logAnalyticsName string = '' +param resourceGroupName string = '' +param storageAccountName string = '' +param containerAppsEnvironmentName string = '' +param containerRegistryName string = '' + + +var aciShare = 'acishare' +var qdrantShare = 'qdrantshare' + +var metadataTable = 'Metadata' +var containerMetadataTable = 'ContainersMetadata' + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +var tags = { 'azd-env-name': environmentName } + +// Organize resources in a resource group +resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}' + location: location + tags: tags +} + +module storage './core/storage/storage-account.bicep' = { + name: 'storage' + scope: rg + params: { + name: !empty(storageAccountName) ? storageAccountName : '${abbrs.storageStorageAccounts}${resourceToken}' + location: location + tags: tags + fileShares: [ + aciShare + qdrantShare + ] + tables: [ + metadataTable + containerMetadataTable + ] + } +} + +// Monitor application with Azure Monitor +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + scope: rg + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' + } +} + +// Container apps host (including container registry) +module containerApps './core/host/container-apps.bicep' = { + name: 'container-apps' + scope: rg + params: { + name: 'app' + location: location + tags: tags + containerAppsEnvironmentName: !empty(containerAppsEnvironmentName) ? containerAppsEnvironmentName : '${abbrs.appManagedEnvironments}${resourceToken}' + containerRegistryName: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}' + logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName + applicationInsightsName: monitoring.outputs.applicationInsightsName + } +} + +module qdrant './core/database/qdrant/qdrant-aca.bicep' = { + name: 'qdrant-deploy' + scope: rg + params: { + location: location + containerAppsEnvironmentName: containerApps.outputs.environmentName + shareName: qdrantShare + storageName: storage.outputs.name + } +} + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan './core/host/appserviceplan.bicep' = { + name: 'appserviceplan' + scope: rg + params: { + name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' + location: location + tags: tags + sku: { + name: 'Y1' + tier: 'Dynamic' + } + } +} + +var appName = !empty(apiServiceName) ? apiServiceName : '${abbrs.webSitesFunctions}api-${resourceToken}' + +// The application backend +module skfunc './app/sk-func.bicep' = { + name: 'skfunc' + scope: rg + params: { + name: appName + location: location + tags: tags + applicationInsightsName: monitoring.outputs.applicationInsightsName + appServicePlanId: appServicePlan.outputs.id + storageAccountName: storage.outputs.name + appSettings: { + SANDBOX_IMAGE: 'mcr.microsoft.com/dotnet/sdk:7.0' + AzureWebJobsFeatureFlags: 'EnableHttpProxying' + FUNCTIONS_FQDN: 'https://${appName}.azurewebsites.net' + 'GithubOptions__AppKey': githubAppKey + 'GithubOptions__AppId': githubAppId + 'GithubOptions__InstallationId': githubAppInstallationId + 'AzureOptions__SubscriptionId': subscription().subscriptionId + 'AzureOptions__Location': location + 'AzureOptions__ContainerInstancesResourceGroup': rg.name + 'AzureOptions__FilesShareName': aciShare + 'AzureOptions__FilesAccountName': storage.outputs.name + 'OpenAIOptions__ServiceType': openAIServiceType + 'OpenAIOptions__ServiceId': openAIServiceId + 'OpenAIOptions__DeploymentOrModelId': openAIDeploymentId + 'OpenAIOptions__EmbeddingDeploymentOrModelId': openAIEmbeddingId + 'OpenAIOptions__Endpoint': openAIEndpoint + 'OpenAIOptions__ApiKey': openAIKey + 'QdrantOptions__Endpoint':'https://${qdrant.outputs.fqdn}' + 'QdrantOptions__VectorSize':'1536' + } + } +} + +// App outputs +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId + diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 00000000000..05edbfaaee1 --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "githubAppKey": { + "value": "${GH_APP_KEY}" + }, + "githubAppId": { + "value": "${GH_APP_ID}" + }, + "githubAppInstallationId": { + "value": "${GH_APP_INST_ID}" + }, + "openAIServiceType": { + "value": "${OAI_SERVICE_TYPE}" + }, + "openAIServiceId": { + "value": "${OAI_SERVICE_ID}" + }, + "openAIDeploymentId": { + "value": "${OAI_DEPLOYMENT_ID}" + }, + "openAIEmbeddingId": { + "value": "${OAI_EMBEDDING_ID}" + }, + "openAIEndpoint": { + "value": "${OAI_ENDPOINT}" + }, + "openAIKey": { + "value": "${OAI_KEY}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Activities/IssueActivities.cs b/sk-azfunc-server/Activities/IssueActivities.cs new file mode 100644 index 00000000000..300db30bbfe --- /dev/null +++ b/sk-azfunc-server/Activities/IssueActivities.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.DurableTask.Client; +using Octokit; + +namespace SK.DevTeam +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")] + public class IssuesActivities + { + private readonly GithubService _ghService; + + public IssuesActivities(GithubService githubService) + { + _ghService = githubService; + } + + [Function(nameof(CreateIssue))] + public async Task CreateIssue([ActivityTrigger] NewIssueRequest request, FunctionContext executionContext) + { + var ghClient = await _ghService.GetGitHubClient(); + var newIssue = new NewIssue($"{request.Function} chain for #{request.IssueRequest.Number}") + { + Body = request.IssueRequest.Input, + + }; + newIssue.Labels.Add($"{request.Skill}.{request.Function}"); + var issue = await ghClient.Issue.Create(request.IssueRequest.Org, request.IssueRequest.Repo, newIssue); + var commentBody = $" - [ ] #{issue.Number} - tracks {request.Skill}.{request.Function}"; + var comment = await ghClient.Issue.Comment.Create(request.IssueRequest.Org, request.IssueRequest.Repo, (int)request.IssueRequest.Number, commentBody); + + return new NewIssueResponse + { + Number = issue.Number, + CommentId = comment.Id + }; + } + + [Function("CloseSubOrchestration")] + public async Task Close( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "close")] HttpRequest req, + [DurableClient] DurableTaskClient client) + { + var request = await req.ReadFromJsonAsync(); + + var ghClient = await _ghService.GetGitHubClient(); + var comment = await ghClient.Issue.Comment.Get(request.Org, request.Repo, request.CommentId); + var updatedComment = comment.Body.Replace("[ ]", "[x]"); + await ghClient.Issue.Comment.Update(request.Org, request.Repo, request.CommentId, updatedComment); + + await client.RaiseEventAsync(request.InstanceId, SubIssueOrchestration.IssueClosed, true); + } + + + + [Function(nameof(GetLastComment))] + public async Task GetLastComment([ActivityTrigger] IssueOrchestrationRequest request, FunctionContext executionContext) + { + var ghClient = await _ghService.GetGitHubClient(); + var icOptions = new IssueCommentRequest + { + Direction = SortDirection.Descending + }; + var apiOptions = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + var comments = await ghClient.Issue.Comment.GetAllForIssue(request.Org, request.Repo, (int)request.Number, icOptions, apiOptions); + return comments.First().Body; + } + } +} diff --git a/sk-azfunc-server/Activities/MetadataActivities.cs b/sk-azfunc-server/Activities/MetadataActivities.cs new file mode 100644 index 00000000000..a48d6e91ca9 --- /dev/null +++ b/sk-azfunc-server/Activities/MetadataActivities.cs @@ -0,0 +1,34 @@ +using Azure.Data.Tables; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.Functions.Worker; + +namespace SK.DevTeam +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")] + public static class MetadataActivities + { + [Function(nameof(GetMetadata))] + public static async Task GetMetadata( + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "metadata/{key}")] HttpRequest req, + [TableInput("Metadata", Connection = "AzureWebJobsStorage")] TableClient client, + FunctionContext executionContext) + { + var key = req.RouteValues["key"].ToString(); + var metadataResponse = await client.GetEntityAsync(key, key); + var metadata = metadataResponse.Value; + return new OkObjectResult(metadata); + } + + [Function(nameof(SaveMetadata))] + + public static async Task SaveMetadata( + [ActivityTrigger] IssueMetadata metadata, + [TableInput("Metadata", Connection = "AzureWebJobsStorage")] TableClient client, + FunctionContext executionContext) + { + await client.UpsertEntityAsync(metadata); + return metadata; + } + } +} diff --git a/sk-azfunc-server/Activities/PullRequestActivities.cs b/sk-azfunc-server/Activities/PullRequestActivities.cs new file mode 100644 index 00000000000..aa911da576d --- /dev/null +++ b/sk-azfunc-server/Activities/PullRequestActivities.cs @@ -0,0 +1,240 @@ +using System.Text; +using Azure; +using Azure.Core; +using Azure.Data.Tables; +using Azure.Identity; +using Azure.ResourceManager; +using Azure.ResourceManager.ContainerInstance; +using Azure.ResourceManager.ContainerInstance.Models; +using Azure.ResourceManager.Resources; +using Azure.Storage.Files.Shares; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.Functions.Worker; +using Microsoft.DurableTask.Client; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Octokit; +using Octokit.Helpers; + +namespace SK.DevTeam +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")] + public class PullRequestActivities + { + private readonly AzureOptions _azSettings; + private readonly GithubService _ghService; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger logger; + + public PullRequestActivities(IOptions azOptions, GithubService ghService, IHttpClientFactory httpClientFactory, ILogger logger) + { + _azSettings = azOptions.Value; + _ghService = ghService; + _httpClientFactory = httpClientFactory; + this.logger = logger; + } + + [Function(nameof(SaveOutput))] + public async Task SaveOutput([ActivityTrigger] SaveOutputRequest request, FunctionContext executionContext) + { + var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net"; + var parentDirName = $"{request.Directory}/{request.IssueOrchestrationId}"; + var fileName = $"{request.FileName}.{request.Extension}"; + + var share = new ShareClient(connectionString, _azSettings.FilesShareName); + await share.CreateIfNotExistsAsync(); + await share.GetDirectoryClient($"{request.Directory}").CreateIfNotExistsAsync(); + + var parentDir = share.GetDirectoryClient(parentDirName); + await parentDir.CreateIfNotExistsAsync(); + + var directory = parentDir.GetSubdirectoryClient(request.SubOrchestrationId); + await directory.CreateIfNotExistsAsync(); + + var file = directory.GetFileClient(fileName); + // hack to enable script to save files in the same directory + var cwdHack = "#!/bin/bash\n cd $(dirname $0)"; + var output = request.Extension == "sh" ? request.Output.Replace("#!/bin/bash", cwdHack) : request.Output; + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(output))) + { + await file.CreateAsync(stream.Length); + await file.UploadRangeAsync( + new HttpRange(0, stream.Length), + stream); + } + + return true; + } + + [Function(nameof(CreateBranch))] + public async Task CreateBranch([ActivityTrigger] GHNewBranch request, FunctionContext executionContext) + { + var ghClient = await _ghService.GetGitHubClient(); + var repo = await ghClient.Repository.Get(request.Org, request.Repo); + await ghClient.Git.Reference.CreateBranch(request.Org, request.Repo, request.Branch, repo.DefaultBranch); + return true; + } + + [Function(nameof(CreatePR))] + public async Task CreatePR([ActivityTrigger] GHNewBranch request, FunctionContext executionContext) + { + var ghClient = await _ghService.GetGitHubClient(); + var repo = await ghClient.Repository.Get(request.Org, request.Repo); + await ghClient.PullRequest.Create(request.Org, request.Repo, new NewPullRequest($"New app #{request.Number}", request.Branch, repo.DefaultBranch)); + return true; + } + + [Function(nameof(RunInSandbox))] + + public async Task RunInSandbox( + [ActivityTrigger] RunInSandboxRequest request, + [TableInput("ContainersMetadata", Connection = "AzureWebJobsStorage")] TableClient tableClient, + FunctionContext executionContext) + { + var client = new ArmClient(new DefaultAzureCredential()); + + var containerGroupName = $"sk-sandbox-{request.PrRequest.SubOrchestrationId}"; + var containerName = $"sk-sandbox-{request.PrRequest.SubOrchestrationId}"; + var image = Environment.GetEnvironmentVariable("SANDBOX_IMAGE", EnvironmentVariableTarget.Process); + + var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup); + var resourceGroupResource = client.GetResourceGroupResource(resourceGroupResourceId); + + var scriptPath = $"/azfiles/output/{request.PrRequest.IssueOrchestrationId}/{request.PrRequest.SubOrchestrationId}/run.sh"; + + var collection = resourceGroupResource.GetContainerGroups(); + + var data = new ContainerGroupData(new AzureLocation(_azSettings.Location), new ContainerInstanceContainer[] + { + new ContainerInstanceContainer(containerName,image,new ContainerResourceRequirements(new ContainerResourceRequestsContent(1.5,1))) + { + Command = { "/bin/bash", $"{scriptPath}" }, + VolumeMounts = + { + new ContainerVolumeMount("azfiles","/azfiles/") + { + IsReadOnly = false, + } + }, + }}, ContainerInstanceOperatingSystemType.Linux) + { + Volumes = + { + new ContainerVolume("azfiles") + { + AzureFile = new ContainerInstanceAzureFileVolume(_azSettings.FilesShareName,_azSettings.FilesAccountName) + { + StorageAccountKey = _azSettings.FilesAccountKey + }, + }, + }, + RestartPolicy = ContainerGroupRestartPolicy.Never, + Sku = ContainerGroupSku.Standard, + Priority = ContainerGroupPriority.Regular + }; + await collection.CreateOrUpdateAsync(WaitUntil.Completed, containerGroupName, data); + + var metadata = new ContainerInstanceMetadata + { + PartitionKey = containerGroupName, + RowKey = containerGroupName, + SubOrchestrationId = request.SanboxOrchestrationId, + Processed = false + }; + await tableClient.UpsertEntityAsync(metadata); + return true; + } + + [Function(nameof(CommitToGithub))] + public async Task CommitToGithub([ActivityTrigger] GHCommitRequest request, FunctionContext executionContext) + { + var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net"; + var ghClient = await _ghService.GetGitHubClient(); + + var dirName = $"{request.Directory}/{request.IssueOrchestrationId}/{request.SubOrchestrationId}"; + var share = new ShareClient(connectionString, _azSettings.FilesShareName); + var directory = share.GetDirectoryClient(dirName); + + var remaining = new Queue(); + remaining.Enqueue(directory); + while (remaining.Count > 0) + { + var dir = remaining.Dequeue(); + await foreach (var item in dir.GetFilesAndDirectoriesAsync()) + { + if (!item.IsDirectory && item.Name != "run.sh") // we don't want the generated script in the PR + { + try + { + var file = dir.GetFileClient(item.Name); + var filePath = file.Path.Replace($"{_azSettings.FilesShareName}/", "") + .Replace($"{dirName}/", ""); + var fileStream = await file.OpenReadAsync(); + using (var reader = new StreamReader(fileStream, Encoding.UTF8)) + { + var value = reader.ReadToEnd(); + + await ghClient.Repository.Content.CreateFile( + request.Org, request.Repo, filePath, + new CreateFileRequest($"Commit message", value, request.Branch)); // TODO: add more meaningfull commit message + } + } + catch (Exception ex) + { + logger.LogError(ex, $"Error while uploading file {item.Name}"); + } + } + else if (item.IsDirectory) + { + remaining.Enqueue(dir.GetSubdirectoryClient(item.Name)); + } + } + } + + return true; + } + + [Function(nameof(Terminated))] + public async Task Terminated( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "container/{name}/terminate")] HttpRequest req, + [TableInput("ContainersMetadata", Connection = "AzureWebJobsStorage")] TableClient tableClient, + [DurableClient] DurableTaskClient client) + { + var containerGroupName = req.RouteValues["name"].ToString(); + var metadataResponse = await tableClient.GetEntityAsync(containerGroupName, containerGroupName); + var metadata = metadataResponse.Value; + if (!metadata.Processed) + { + await client.RaiseEventAsync(metadata.SubOrchestrationId, SubIssueOrchestration.ContainerTerminated, true); + metadata.Processed = true; + await tableClient.UpdateEntityAsync(metadata, metadata.ETag, TableUpdateMode.Replace); + } + + return metadata; + } + + [Function(nameof(CleanContainers))] + public async Task CleanContainers( + [TimerTrigger("*/30 * * * * *")] TimerInfo myTimer, + FunctionContext executionContext) + { + var httpClient = _httpClientFactory.CreateClient("FunctionsClient"); + var client = new ArmClient(new DefaultAzureCredential()); + var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup); + var resourceGroupResource = client.GetResourceGroupResource(resourceGroupResourceId); + + var collection = resourceGroupResource.GetContainerGroups(); + + foreach (var cg in collection.GetAll()) + { + var c = await cg.GetAsync(); + if (c.Value.Data.ProvisioningState == "Succeeded" + && c.Value.Data.Containers.First().InstanceView.CurrentState.State == "Terminated") + { + await cg.DeleteAsync(WaitUntil.Started); + await httpClient.PostAsync($"container/{cg.Data.Name}/terminate", default); + } + } + } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/ExecuteFunctionEndpoint.cs b/sk-azfunc-server/ExecuteFunctionEndpoint.cs deleted file mode 100644 index b4850eff299..00000000000 --- a/sk-azfunc-server/ExecuteFunctionEndpoint.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Net; -using System.Text.Json; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Http; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Orchestration; -using Models; -using Microsoft.SKDevTeam; - - -public class ExecuteFunctionEndpoint -{ - private static readonly JsonSerializerOptions s_jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - - private readonly IKernel _kernel; - - public ExecuteFunctionEndpoint(IKernel kernel) - { - this._kernel = kernel; - } - - [Function("ExecuteFunction")] - [OpenApiOperation(operationId: "ExecuteFunction", tags: new[] { "ExecuteFunction" }, Description = "Execute the specified semantic function. Provide skill and function names, plus any variables the function requires.")] - [OpenApiParameter(name: "skillName", Description = "Name of the skill e.g., 'FunSkill'", Required = true)] - [OpenApiParameter(name: "functionName", Description = "Name of the function e.g., 'Excuses'", Required = true)] - [OpenApiRequestBody("application/json", typeof(ExecuteFunctionRequest), Description = "Variables to use when executing the specified function.", Required = true)] - [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(ExecuteFunctionResponse), Description = "Returns the response from the AI.")] - [OpenApiResponseWithBody(statusCode: HttpStatusCode.BadRequest, contentType: "application/json", bodyType: typeof(ErrorResponse), Description = "Returned if the request body is invalid.")] - [OpenApiResponseWithBody(statusCode: HttpStatusCode.NotFound, contentType: "application/json", bodyType: typeof(ErrorResponse), Description = "Returned if the semantic function could not be found.")] - public async Task ExecuteFunctionAsync( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "skills/{skillName}/functions/{functionName}")] - HttpRequestData requestData, - FunctionContext executionContext, string skillName, string functionName) - { - try - { - var functionRequest = await JsonSerializer.DeserializeAsync(requestData.Body, s_jsonOptions).ConfigureAwait(false); - if (functionRequest == null) - { - return await CreateResponseAsync(requestData, HttpStatusCode.BadRequest, new ErrorResponse() { Message = $"Invalid request body." }).ConfigureAwait(false); - } - - var skillConfig = SemanticFunctionConfig.ForSkillAndFunction(skillName, functionName); - var function = _kernel.CreateSemanticFunction(skillConfig.PromptTemplate, skillConfig.Name, skillConfig.SkillName, - skillConfig.Description, skillConfig.MaxTokens, skillConfig.Temperature, - skillConfig.TopP, skillConfig.PPenalty, skillConfig.FPenalty); - - var context = new ContextVariables(); - foreach (var v in functionRequest.Variables) - { - context.Set(v.Key, v.Value); - } - - var result = await this._kernel.RunAsync(context, function).ConfigureAwait(false); - - return await CreateResponseAsync(requestData, HttpStatusCode.OK, new ExecuteFunctionResponse() { Response = result.ToString() }).ConfigureAwait(false); - } - catch (Exception ex) - { - // Log the contents of the request - var requestBody = await new StreamReader(requestData.Body).ReadToEndAsync(); - Console.WriteLine($"Failed to deserialize request body: {requestBody}. Exception: {ex}"); - - return await CreateResponseAsync(requestData, HttpStatusCode.BadRequest, new ErrorResponse() { Message = $"Invalid request body." }).ConfigureAwait(false); - } - } - - private static async Task CreateResponseAsync(HttpRequestData requestData, HttpStatusCode statusCode, object responseBody) - { - var responseData = requestData.CreateResponse(statusCode); - await responseData.WriteAsJsonAsync(responseBody).ConfigureAwait(false); - return responseData; - } -} diff --git a/sk-azfunc-server/Models/AddToPRRequest.cs b/sk-azfunc-server/Models/AddToPRRequest.cs new file mode 100644 index 00000000000..198907945b8 --- /dev/null +++ b/sk-azfunc-server/Models/AddToPRRequest.cs @@ -0,0 +1,10 @@ +public class AddToPRRequest +{ + public string Output { get; set; } + public string IssueOrchestrationId { get; set; } + public string SubOrchestrationId { get; set; } + public string PrSubOrchestrationId { get; set; } + public string Extension { get; set; } + public bool RunInSandbox { get; set; } + public IssueOrchestrationRequest Request { get; set; } +} diff --git a/sk-azfunc-server/Models/CloseIssueRequest.cs b/sk-azfunc-server/Models/CloseIssueRequest.cs new file mode 100644 index 00000000000..be0bc28207b --- /dev/null +++ b/sk-azfunc-server/Models/CloseIssueRequest.cs @@ -0,0 +1,7 @@ +public class CloseIssueRequest +{ + public string InstanceId { get; set; } + public int CommentId { get; set; } + public string Org { get; set; } + public string Repo { get; set; } +} diff --git a/sk-azfunc-server/Models/ContainerInstanceMetadata.cs b/sk-azfunc-server/Models/ContainerInstanceMetadata.cs new file mode 100644 index 00000000000..eac5d4b8dc2 --- /dev/null +++ b/sk-azfunc-server/Models/ContainerInstanceMetadata.cs @@ -0,0 +1,12 @@ +using Azure; +using Azure.Data.Tables; + +public class ContainerInstanceMetadata : ITableEntity +{ + public string PartitionKey { get; set; } + public string RowKey { get; set; } + public string SubOrchestrationId { get; set; } + public bool Processed { get; set; } + public DateTimeOffset? Timestamp { get; set; } + public ETag ETag { get; set; } +} diff --git a/sk-azfunc-server/Models/DevLeadPlanResponse.cs b/sk-azfunc-server/Models/DevLeadPlanResponse.cs new file mode 100644 index 00000000000..3c8c6a01c25 --- /dev/null +++ b/sk-azfunc-server/Models/DevLeadPlanResponse.cs @@ -0,0 +1,17 @@ +public class Subtask +{ + public string subtask { get; set; } + public string prompt { get; set; } +} + +public class Step +{ + public string description { get; set; } + public string step { get; set; } + public List subtasks { get; set; } +} + +public class DevLeadPlanResponse +{ + public List steps { get; set; } +} \ No newline at end of file diff --git a/sk-azfunc-server/Models/ErrorResponse.cs b/sk-azfunc-server/Models/ErrorResponse.cs deleted file mode 100644 index 5673ce32896..00000000000 --- a/sk-azfunc-server/Models/ErrorResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; - -namespace Models; - -internal class ErrorResponse -{ - [JsonPropertyName("message")] - [OpenApiProperty(Description = "The error message.")] - public string Message { get; set; } = string.Empty; -} diff --git a/sk-azfunc-server/Models/ExecuteFunctionRequest.cs b/sk-azfunc-server/Models/ExecuteFunctionRequest.cs deleted file mode 100644 index 126708d04dd..00000000000 --- a/sk-azfunc-server/Models/ExecuteFunctionRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; - -namespace Models; - -#pragma warning disable CA1812 -internal class ExecuteFunctionRequest -{ - [JsonPropertyName("variables")] - [OpenApiProperty(Description = "The variables to pass to the semantic function.")] - public IEnumerable Variables { get; set; } = Enumerable.Empty(); -} - -internal class ExecuteFunctionVariable -{ - [JsonPropertyName("key")] - [OpenApiProperty(Description = "The variable key.", Default = "input")] - public string Key { get; set; } = string.Empty; - - [JsonPropertyName("value")] - [OpenApiProperty(Description = "The variable value.", Default = "Late for school")] - public string Value { get; set; } = string.Empty; -} diff --git a/sk-azfunc-server/Models/ExecuteFunctionResponse.cs b/sk-azfunc-server/Models/ExecuteFunctionResponse.cs deleted file mode 100644 index 0c3c076d745..00000000000 --- a/sk-azfunc-server/Models/ExecuteFunctionResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; - -namespace Models; - -internal class ExecuteFunctionResponse -{ - [JsonPropertyName("response")] - [OpenApiProperty(Description = "The response from the AI.")] - public string? Response { get; set; } -} diff --git a/sk-azfunc-server/Models/GHCommitRequest.cs b/sk-azfunc-server/Models/GHCommitRequest.cs new file mode 100644 index 00000000000..8d402c7ac9b --- /dev/null +++ b/sk-azfunc-server/Models/GHCommitRequest.cs @@ -0,0 +1,12 @@ +namespace SK.DevTeam +{ + public class GHCommitRequest + { + public object IssueOrchestrationId { get; set; } + public object SubOrchestrationId { get; set; } + public string Org { get; set; } + public string Repo { get; set; } + public object Directory { get; set; } + public string Branch { get; set; } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Models/GHNewBranch.cs b/sk-azfunc-server/Models/GHNewBranch.cs new file mode 100644 index 00000000000..54f13091f23 --- /dev/null +++ b/sk-azfunc-server/Models/GHNewBranch.cs @@ -0,0 +1,10 @@ +namespace SK.DevTeam +{ + public class GHNewBranch + { + public string Org { get; set; } + public string Repo { get; set; } + public string Branch { get; set; } + public object Number { get; set; } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Models/IssueMetadata.cs b/sk-azfunc-server/Models/IssueMetadata.cs new file mode 100644 index 00000000000..6260ba0741b --- /dev/null +++ b/sk-azfunc-server/Models/IssueMetadata.cs @@ -0,0 +1,19 @@ +using Azure; +using Azure.Data.Tables; + +public class IssueMetadata : ITableEntity +{ + public long? Number { get; set; } + public int? CommentId { get; set; } + + public string? InstanceId { get; set; } + + public string? Id { get; set; } + + public string? Org { get; set; } + public string? Repo { get; set; } + public string PartitionKey { get; set; } + public string RowKey { get; set; } + public DateTimeOffset? Timestamp { get; set; } + public ETag ETag { get; set; } +} diff --git a/sk-azfunc-server/Models/IssueOrchestrationRequest.cs b/sk-azfunc-server/Models/IssueOrchestrationRequest.cs new file mode 100644 index 00000000000..987fd13a200 --- /dev/null +++ b/sk-azfunc-server/Models/IssueOrchestrationRequest.cs @@ -0,0 +1,8 @@ +public class IssueOrchestrationRequest +{ + public string Org { get; set; } + public string Repo { get; set; } + public long Number { get; set; } + public string Input { get; set; } + public string Branch => $"sk-{Number}"; +} \ No newline at end of file diff --git a/sk-azfunc-server/Models/NewIssueRequest.cs b/sk-azfunc-server/Models/NewIssueRequest.cs new file mode 100644 index 00000000000..0e63c143c36 --- /dev/null +++ b/sk-azfunc-server/Models/NewIssueRequest.cs @@ -0,0 +1,6 @@ +public class NewIssueRequest +{ + public IssueOrchestrationRequest IssueRequest { get; set; } + public string Skill { get; set; } + public string Function { get; set; } +} diff --git a/sk-azfunc-server/Models/NewIssueResponse.cs b/sk-azfunc-server/Models/NewIssueResponse.cs new file mode 100644 index 00000000000..f871d6f5896 --- /dev/null +++ b/sk-azfunc-server/Models/NewIssueResponse.cs @@ -0,0 +1,5 @@ +public class NewIssueResponse +{ + public long Number { get; set; } + public int CommentId { get; set; } +} diff --git a/sk-azfunc-server/Models/RunAndSaveRequest.cs b/sk-azfunc-server/Models/RunAndSaveRequest.cs new file mode 100644 index 00000000000..20202ac8c8c --- /dev/null +++ b/sk-azfunc-server/Models/RunAndSaveRequest.cs @@ -0,0 +1,6 @@ +public class RunAndSaveRequest +{ + public IssueOrchestrationRequest Request { get; set; } + public string InstanceId { get; set; } + +} diff --git a/sk-azfunc-server/Models/RunInSandboxRequest.cs b/sk-azfunc-server/Models/RunInSandboxRequest.cs new file mode 100644 index 00000000000..62c2079f2d1 --- /dev/null +++ b/sk-azfunc-server/Models/RunInSandboxRequest.cs @@ -0,0 +1,8 @@ +namespace SK.DevTeam +{ + public class RunInSandboxRequest + { + public AddToPRRequest PrRequest { get; set; } + public string SanboxOrchestrationId { get; set; } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Models/SaveOutputRequest.cs b/sk-azfunc-server/Models/SaveOutputRequest.cs new file mode 100644 index 00000000000..9e7bb8507f3 --- /dev/null +++ b/sk-azfunc-server/Models/SaveOutputRequest.cs @@ -0,0 +1,12 @@ +namespace SK.DevTeam +{ + public class SaveOutputRequest + { + public string IssueOrchestrationId { get; set; } + public string SubOrchestrationId { get; set; } + public string Output { get; set; } + public string Extension { get; set; } + public string Directory { get; set; } + public string FileName { get; set; } + } +} diff --git a/sk-azfunc-server/Models/SkillRequest.cs b/sk-azfunc-server/Models/SkillRequest.cs new file mode 100644 index 00000000000..a8f372054f3 --- /dev/null +++ b/sk-azfunc-server/Models/SkillRequest.cs @@ -0,0 +1,9 @@ +namespace SK.DevTeam +{ + public class SkillRequest + { + public IssueOrchestrationRequest IssueRequest { get; set; } + public string Skill { get; set; } + public string Function { get; set; } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Models/SkillResponse.cs b/sk-azfunc-server/Models/SkillResponse.cs new file mode 100644 index 00000000000..bb9858ff976 --- /dev/null +++ b/sk-azfunc-server/Models/SkillResponse.cs @@ -0,0 +1,5 @@ +public class SkillResponse +{ + public T Output { get; set; } + public string SuborchestrationId { get; set; } +} \ No newline at end of file diff --git a/sk-azfunc-server/Orchestrators/IssueOrchestration.cs b/sk-azfunc-server/Orchestrators/IssueOrchestration.cs new file mode 100644 index 00000000000..03f24bdbb07 --- /dev/null +++ b/sk-azfunc-server/Orchestrators/IssueOrchestration.cs @@ -0,0 +1,75 @@ +using System.Text.Json; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.DurableTask; +using Microsoft.DurableTask.Client; +using Microsoft.Extensions.Logging; +using static SK.DevTeam.SubIssueOrchestration; + +namespace SK.DevTeam +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")] + public static class IssueOrchestration + { + [Function("IssueOrchestrationStart")] + public static async Task HttpStart( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "doit")] HttpRequest req, + [DurableClient] DurableTaskClient client, + FunctionContext executionContext) + { + ILogger logger = executionContext.GetLogger("IssueOrchestration_HttpStart"); + var request = await req.ReadFromJsonAsync(); + string instanceId = await client.ScheduleNewOrchestrationInstanceAsync( + nameof(IssueOrchestration), request); + + logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId); + return ""; + } + + [Function(nameof(IssueOrchestration))] + public static async Task> RunOrchestrator( + [OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request) + { + var logger = context.CreateReplaySafeLogger(nameof(IssueOrchestration)); + var outputs = new List(); + + var newGHBranchRequest = new GHNewBranch + { + Org = request.Org, + Repo = request.Repo, + Branch = request.Branch, + Number = request.Number + }; + + var newBranch = await context.CallActivityAsync(nameof(PullRequestActivities.CreateBranch), newGHBranchRequest); + + var readmeTask = await context.CallSubOrchestratorAsync(nameof(ReadmeAndSave), new RunAndSaveRequest + { + Request = request, + InstanceId = context.InstanceId + }); + + var newPR = await context.CallActivityAsync(nameof(PullRequestActivities.CreatePR), newGHBranchRequest); + + var planTask = await context.CallSubOrchestratorAsync>(nameof(CreatePlan), request); + var plan = JsonSerializer.Deserialize(planTask.Output); + + var implementationTasks = plan.steps.SelectMany(s => s.subtasks.Select(st => + context.CallSubOrchestratorAsync(nameof(ImplementAndSave), new RunAndSaveRequest + { + Request = new IssueOrchestrationRequest + { + Number = request.Number, + Org = request.Org, + Repo = request.Repo, + Input = st.prompt, + }, + InstanceId = context.InstanceId + }))); + + await Task.WhenAll(implementationTasks); + return outputs; + } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Orchestrators/SubIssueOrchestration.cs b/sk-azfunc-server/Orchestrators/SubIssueOrchestration.cs new file mode 100644 index 00000000000..ebacb221091 --- /dev/null +++ b/sk-azfunc-server/Orchestrators/SubIssueOrchestration.cs @@ -0,0 +1,154 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.DurableTask; +using Microsoft.SKDevTeam; + +namespace SK.DevTeam +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")] + public static class SubIssueOrchestration + { + public static string IssueClosed = "IssueClosed"; + public static string ContainerTerminated = "ContainerTerminated"; + + private static async Task> CallSkill(TaskOrchestrationContext context, SkillRequest request) + { + var newIssueResponse = await context.CallActivityAsync(nameof(IssuesActivities.CreateIssue), new NewIssueRequest + { + IssueRequest = request.IssueRequest, + Skill = request.Skill, + Function = request.Function + }); + + var metadata = await context.CallActivityAsync(nameof(MetadataActivities.SaveMetadata), new IssueMetadata + { + Number = newIssueResponse.Number, + InstanceId = context.InstanceId, + Id = Guid.NewGuid().ToString(), + CommentId = newIssueResponse.CommentId, + Org = request.IssueRequest.Org, + Repo = request.IssueRequest.Repo, + PartitionKey = $"{request.IssueRequest.Org}{request.IssueRequest.Repo}{newIssueResponse.Number}", + RowKey = $"{request.IssueRequest.Org}{request.IssueRequest.Repo}{newIssueResponse.Number}", + Timestamp = DateTimeOffset.UtcNow + }); + bool issueClosed = await context.WaitForExternalEvent(IssueClosed); + var lastComment = await context.CallActivityAsync(nameof(IssuesActivities.GetLastComment), new IssueOrchestrationRequest + { + Org = request.IssueRequest.Org, + Repo = request.IssueRequest.Repo, + Number = newIssueResponse.Number + }); + + return new SkillResponse { Output = lastComment, SuborchestrationId = context.InstanceId }; + } + + [Function(nameof(CreateReadme))] + public static async Task> CreateReadme( + [OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request) + { + return await CallSkill(context, new SkillRequest + { + IssueRequest = request, + Skill = nameof(PM), + Function = nameof(PM.Readme) + }); + } + + [Function(nameof(CreatePlan))] + public static async Task> CreatePlan( + [OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request) + { + return await CallSkill(context, new SkillRequest + { + IssueRequest = request, + Skill = nameof(DevLead), + Function = nameof(DevLead.Plan) + }); + } + + [Function(nameof(Implement))] + public static async Task> Implement( + [OrchestrationTrigger] TaskOrchestrationContext context, IssueOrchestrationRequest request) + { + return await CallSkill(context, new SkillRequest + { + IssueRequest = request, + Skill = nameof(Developer), + Function = nameof(Developer.Implement) + }); + } + + [Function(nameof(ImplementAndSave))] + public static async Task ImplementAndSave( + [OrchestrationTrigger] TaskOrchestrationContext context, RunAndSaveRequest request) + { + var implementResult = await context.CallSubOrchestratorAsync>(nameof(Implement), request.Request); + await context.CallSubOrchestratorAsync(nameof(AddToPR), new AddToPRRequest + { + Output = implementResult.Output, + IssueOrchestrationId = request.InstanceId, + SubOrchestrationId = implementResult.SuborchestrationId, + Extension = "sh", + RunInSandbox = true, + Request = request.Request + }); + return true; + } + + [Function(nameof(ReadmeAndSave))] + public static async Task ReadmeAndSave( + [OrchestrationTrigger] TaskOrchestrationContext context, RunAndSaveRequest request) + { + var readmeResult = await context.CallSubOrchestratorAsync>(nameof(CreateReadme), request.Request); + context.CallSubOrchestratorAsync(nameof(AddToPR), new AddToPRRequest + { + Output = readmeResult.Output, + IssueOrchestrationId = request.InstanceId, + SubOrchestrationId = readmeResult.SuborchestrationId, + Extension = "md", + RunInSandbox = false, + Request = request.Request + }); + return true; + } + + [Function(nameof(AddToPR))] + public static async Task AddToPR( + [OrchestrationTrigger] TaskOrchestrationContext context, AddToPRRequest request) + { + var saveScriptResponse = await context.CallActivityAsync(nameof(PullRequestActivities.SaveOutput), new SaveOutputRequest + { + Output = request.Output, + IssueOrchestrationId = request.IssueOrchestrationId, + SubOrchestrationId = request.SubOrchestrationId, + Extension = request.Extension, + Directory = "output", + FileName = request.RunInSandbox ? "run" : "readme" + }); + + if (request.RunInSandbox) + { + var newRequest = new RunInSandboxRequest + { + PrRequest = request, + SanboxOrchestrationId = context.InstanceId + }; + var runScriptResponse = await context.CallActivityAsync(nameof(PullRequestActivities.RunInSandbox), newRequest); + bool containerTerminated = await context.WaitForExternalEvent(ContainerTerminated); + } + + // this is not ideal, as the script might be still running and there might be files that are not yet generated + var commitResponse = await context.CallActivityAsync(nameof(PullRequestActivities.CommitToGithub), new GHCommitRequest + { + IssueOrchestrationId = request.IssueOrchestrationId, + SubOrchestrationId = request.SubOrchestrationId, + Directory = "output", + Org = request.Request.Org, + Repo = request.Request.Repo, + Branch = request.Request.Branch + }); + + return default; + } + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Program.cs b/sk-azfunc-server/Program.cs index 4b154eb80b4..a04ed3fe570 100644 --- a/sk-azfunc-server/Program.cs +++ b/sk-azfunc-server/Program.cs @@ -1,13 +1,16 @@ using System.Text.Json; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; +using Microsoft.Extensions.Options; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding; +using Microsoft.SemanticKernel.Connectors.Memory.Qdrant; +using Microsoft.SemanticKernel.Memory; +using Octokit.Webhooks; +using Octokit.Webhooks.AzureFunctions; namespace KernelHttpServer; @@ -16,25 +19,67 @@ public static class Program public static void Main() { var host = new HostBuilder() - .ConfigureFunctionsWorkerDefaults() + .ConfigureFunctionsWebApplication() + .ConfigureGitHubWebhooks() .ConfigureAppConfiguration(configuration => { var config = configuration.SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true); + .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(); var builtConfig = config.Build(); }) .ConfigureServices(services => { - services.AddSingleton(_ => s_apiConfigOptions); services.AddTransient((provider) => CreateKernel(provider)); - - + services.AddScoped(); + services.AddScoped(); + services.AddOptions() + .Configure((settings, configuration) => + { + configuration.GetSection("GithubOptions").Bind(settings); + }); + services.AddOptions() + .Configure((settings, configuration) => + { + configuration.GetSection("AzureOptions").Bind(settings); + }); + services.AddOptions() + .Configure((settings, configuration) => + { + configuration.GetSection("OpenAIOptions").Bind(settings); + }); + services.AddOptions() + .Configure((settings, configuration) => + { + configuration.GetSection("QdrantOptions").Bind(settings); + }); + services.AddApplicationInsightsTelemetryWorkerService(); + services.ConfigureFunctionsApplicationInsights(); // return JSON with expected lowercase naming services.Configure(options => { options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; }); + + + services.AddHttpClient("FunctionsClient", client => + { + var fqdn = Environment.GetEnvironmentVariable("FUNCTIONS_FQDN", EnvironmentVariableTarget.Process); + client.BaseAddress = new Uri($"{fqdn}/api/"); + }); + }) + .ConfigureLogging(logging => + { + logging.Services.Configure(options => + { + LoggerFilterRule defaultRule = options.Rules.FirstOrDefault(rule => rule.ProviderName + == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider"); + if (defaultRule is not null) + { + options.Rules.Remove(defaultRule); + } + }); }) .Build(); @@ -43,43 +88,27 @@ public static void Main() private static IKernel CreateKernel(IServiceProvider provider) { - var kernelSettings = KernelSettings.LoadSettings(); + var openAiConfig = provider.GetService>().Value; + var qdrantConfig = provider.GetService>().Value; var kernelConfig = new KernelConfig(); - kernelConfig.AddCompletionBackend(kernelSettings); using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { builder - .SetMinimumLevel(kernelSettings.LogLevel ?? LogLevel.Warning) + .SetMinimumLevel(LogLevel.Debug) .AddConsole() .AddDebug(); }); - return new KernelBuilder().WithLogger(loggerFactory.CreateLogger()).WithConfiguration(kernelConfig).Build(); - } + var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient(qdrantConfig.Endpoint, qdrantConfig.VectorSize)); + var embedingGeneration = new AzureTextEmbeddingGeneration(openAiConfig.EmbeddingDeploymentOrModelId, openAiConfig.Endpoint, openAiConfig.ApiKey); + var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration); - private static readonly OpenApiConfigurationOptions s_apiConfigOptions = new() - { - Info = new OpenApiInfo() - { - Version = "1.0.0", - Title = "Semantic Kernel Azure Functions Starter", - Description = "Azure Functions starter application for the [Semantic Kernel](https://github.com/microsoft/semantic-kernel).", - Contact = new OpenApiContact() - { - Name = "Issues", - Url = new Uri("https://github.com/microsoft/semantic-kernel-starters/issues"), - }, - License = new OpenApiLicense() - { - Name = "MIT", - Url = new Uri("https://github.com/microsoft/semantic-kernel-starters/blob/main/LICENSE"), - } - }, - Servers = DefaultOpenApiConfigurationOptions.GetHostNames(), - OpenApiVersion = OpenApiVersionType.V2, - ForceHttps = false, - ForceHttp = false, - }; + return new KernelBuilder() + .WithLogger(loggerFactory.CreateLogger()) + .WithAzureChatCompletionService(openAiConfig.DeploymentOrModelId, openAiConfig.Endpoint, openAiConfig.ApiKey, true, openAiConfig.ServiceId, true) + .WithMemory(semanticTextMemory) + .WithConfiguration(kernelConfig).Build(); + } } diff --git a/sk-azfunc-server/README.md b/sk-azfunc-server/README.md index e96323b2f20..a44f1d992b1 100644 --- a/sk-azfunc-server/README.md +++ b/sk-azfunc-server/README.md @@ -1,81 +1,5 @@ -# Semantic Kernel Azure Functions Starter +# Demo -The `sk-csharp-azure-functions` Azure Functions application demonstrates how to execute a semantic function. +## How do I get started? -## Prerequisites - -- [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0) is required to run this starter. -- Install the recommended extensions - - [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) - - [Semantic Kernel Tools](https://marketplace.visualstudio.com/items?itemName=ms-semantic-kernel.semantic-kernel) - -## Configuring the starter - -The starter can be configured by using either: - -- Enter secrets at the command line with [.NET Secret Manager](#using-net-secret-manager) -- Enter secrets in [appsettings.json](#using-appsettingsjson) - -For Debugging the console application alone, we suggest using .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) to avoid the risk of leaking secrets into the repository, branches and pull requests. - -### Using .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) - -Configure an OpenAI endpoint - -```powershell -cd sk-csharp-azure-functions -dotnet user-secrets set "serviceType" "OpenAI" -dotnet user-secrets set "serviceId" "text-davinci-003" -dotnet user-secrets set "deploymentOrModelId" "text-davinci-003" -dotnet user-secrets set "apiKey" "... your OpenAI key ..." -``` - -Configure an Azure OpenAI endpoint - -```powershell -cd sk-csharp-azure-functions -dotnet user-secrets set "serviceType" "AzureOpenAI" -dotnet user-secrets set "serviceId" "text-davinci-003" -dotnet user-secrets set "deploymentOrModelId" "text-davinci-003" -dotnet user-secrets set "endpoint" "https:// ... your endpoint ... .openai.azure.com/" -dotnet user-secrets set "apiKey" "... your Azure OpenAI key ..." -``` - -Configure the Semantic Kernel logging level - -```powershell -dotnet user-secrets set "LogLevel" 0 -``` - -Log levels: - -- 0 = Trace -- 1 = Debug -- 2 = Information -- 3 = Warning -- 4 = Error -- 5 = Critical -- 6 = None - -### Using appsettings.json - -Configure an OpenAI endpoint - -1. Copy [settings.json.openai-example](./config/appsettings.json.openai-example) to `./config/appsettings.json` -1. Edit the file to add your OpenAI endpoint configuration - -Configure an Azure OpenAI endpoint - -1. Copy [settings.json.azure-example](./config/appsettings.json.azure-example) to `./config/appsettings.json` -1. Edit the file to add your Azure OpenAI endpoint configuration - -## Running the starter - -To run the Azure Functions application just hit `F5`. - -To build and run the Azure Functions application from a terminal use the following commands: - -```powershell -dotnet build -func start --csharp -``` +Check - [Getting started](../docs/github-flow-getting-started.md) diff --git a/sk-azfunc-server/Services/GithubService.cs b/sk-azfunc-server/Services/GithubService.cs new file mode 100644 index 00000000000..ce5119c5713 --- /dev/null +++ b/sk-azfunc-server/Services/GithubService.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Options; +using Octokit; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")] +public class GithubService +{ + private readonly GithubOptions _githubSettings; + + public GithubService(IOptions ghOptions) + { + _githubSettings = ghOptions.Value; + } + public async Task GetGitHubClient() + { + // Use GitHubJwt library to create the GitHubApp Jwt Token using our private certificate PEM file + var generator = new GitHubJwt.GitHubJwtFactory( + new GitHubJwt.StringPrivateKeySource(_githubSettings.AppKey), + new GitHubJwt.GitHubJwtFactoryOptions + { + AppIntegrationId = _githubSettings.AppId, // The GitHub App Id + ExpirationSeconds = 600 // 10 minutes is the maximum time allowed + } + ); + + var jwtToken = generator.CreateEncodedJwtToken(); + var appClient = new GitHubClient(new ProductHeaderValue("SK-DEV-APP")) + { + Credentials = new Credentials(jwtToken, AuthenticationType.Bearer) + }; + var response = await appClient.GitHubApps.CreateInstallationToken(_githubSettings.InstallationId); + return new GitHubClient(new ProductHeaderValue($"SK-DEV-APP-Installation{_githubSettings.InstallationId}")) + { + Credentials = new Credentials(response.Token) + }; + } +} \ No newline at end of file diff --git a/sk-azfunc-server/Services/WebHookEventProcessor.cs b/sk-azfunc-server/Services/WebHookEventProcessor.cs new file mode 100644 index 00000000000..ba35597c181 --- /dev/null +++ b/sk-azfunc-server/Services/WebHookEventProcessor.cs @@ -0,0 +1,118 @@ +using System.Net.Http.Json; +using System.Text; +using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Orchestration; +using Newtonsoft.Json; +using Octokit.Webhooks; +using Octokit.Webhooks.Events; +using Octokit.Webhooks.Events.IssueComment; +using Octokit.Webhooks.Events.Issues; +using Octokit.Webhooks.Models; +using Microsoft.SKDevTeam; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2007: Do not directly await a Task", Justification = "Durable functions")] +public class SKWebHookEventProcessor : WebhookEventProcessor +{ + private readonly IKernel _kernel; + private readonly ILogger _logger; + private readonly GithubService _ghService; + private readonly IHttpClientFactory _httpClientFactory; + + public SKWebHookEventProcessor(IKernel kernel, ILogger logger, GithubService ghService, IHttpClientFactory httpContextFactory) + { + _kernel = kernel; + _logger = logger; + _ghService = ghService; + _httpClientFactory = httpContextFactory; + } + protected override async Task ProcessIssuesWebhookAsync(WebhookHeaders headers, IssuesEvent issuesEvent, IssuesAction action) + { + var ghClient = await _ghService.GetGitHubClient(); + var org = issuesEvent.Organization.Login; + var repo = issuesEvent.Repository.Name; + var issueNumber = issuesEvent.Issue.Number; + var input = issuesEvent.Issue.Body; + if (issuesEvent.Action == IssuesAction.Opened) + { + // Assumes the label follows the following convention: Skill.Function example: PM.Readme + var labels = issuesEvent.Issue.Labels.First().Name.Split("."); + var skillName = labels[0]; + var functionName = labels[1]; + if (skillName == "Do" && functionName == "It") + { + var issueOrchestrationRequest = new IssueOrchestrationRequest + { + Number = issueNumber, + Org = org, + Repo = repo, + Input = input + }; + var content = new StringContent(JsonConvert.SerializeObject(issueOrchestrationRequest), Encoding.UTF8, "application/json"); + var httpClient = _httpClientFactory.CreateClient("FunctionsClient"); + await httpClient.PostAsync("doit", content); + + } + else + { + var result = await RunSkill(skillName, functionName, input); + await ghClient.Issue.Comment.Create(org, repo, (int)issueNumber, result); + } + } + else if (issuesEvent.Action == IssuesAction.Closed && issuesEvent.Issue.User.Type.Value == UserType.Bot) + { + var httpClient = _httpClientFactory.CreateClient("FunctionsClient"); + var metadata = await httpClient.GetFromJsonAsync($"metadata/{org}{repo}{issueNumber}"); + var closeIssueRequest = new CloseIssueRequest { InstanceId = metadata.InstanceId, CommentId = metadata.CommentId.Value, Org = org, Repo = repo }; + var content = new StringContent(JsonConvert.SerializeObject(closeIssueRequest), Encoding.UTF8, "application/json"); + _ = await httpClient.PostAsync("close", content); + } + } + + protected override async Task ProcessIssueCommentWebhookAsync( + WebhookHeaders headers, + IssueCommentEvent issueCommentEvent, + IssueCommentAction action) + { + // we only resond to non-bot comments + if (issueCommentEvent.Sender.Type.Value != UserType.Bot) + { + var ghClient = await _ghService.GetGitHubClient(); + var org = issueCommentEvent.Organization.Login; + var repo = issueCommentEvent.Repository.Name; + var issueId = issueCommentEvent.Issue.Number; + + + // Assumes the label follows the following convention: Skill.Function example: PM.Readme + var labels = issueCommentEvent.Issue.Labels.First().Name.Split("."); + var skillName = labels[0]; + var functionName = labels[1]; + var input = issueCommentEvent.Comment.Body; + var result = await RunSkill(skillName, functionName, input); + + await ghClient.Issue.Comment.Create(org, repo, (int)issueId, result); + } + } + + private async Task RunSkill(string skillName, string functionName, string input) + { + var skillConfig = SemanticFunctionConfig.ForSkillAndFunction(skillName, functionName); + var function = _kernel.CreateSemanticFunction(skillConfig.PromptTemplate, skillConfig.Name, skillConfig.SkillName, + skillConfig.Description, skillConfig.MaxTokens, skillConfig.Temperature, + skillConfig.TopP, skillConfig.PPenalty, skillConfig.FPenalty); + + var interestingMemories = _kernel.Memory.SearchAsync("waf-pages", input, 2); + var wafContext = "Consider the following architectural guidelines:"; + await foreach (var memory in interestingMemories) + { + wafContext += $"\n {memory.Metadata.Text}"; + } + + var context = new ContextVariables(); + context.Set("input", input); + context.Set("wafContext", wafContext); + + var result = await _kernel.RunAsync(context, function); + return result.ToString(); + } +} \ No newline at end of file diff --git a/sk-azfunc-server/config/AzureOptions.cs b/sk-azfunc-server/config/AzureOptions.cs new file mode 100644 index 00000000000..393e56d3e76 --- /dev/null +++ b/sk-azfunc-server/config/AzureOptions.cs @@ -0,0 +1,9 @@ +public class AzureOptions +{ + public string SubscriptionId { get; set; } + public string Location { get; set; } + public string ContainerInstancesResourceGroup { get; set; } + public string FilesShareName { get; set; } + public string FilesAccountName { get; set; } + public string FilesAccountKey { get; set; } +} \ No newline at end of file diff --git a/sk-azfunc-server/config/GithubOptions.cs b/sk-azfunc-server/config/GithubOptions.cs new file mode 100644 index 00000000000..90ed5cdc04c --- /dev/null +++ b/sk-azfunc-server/config/GithubOptions.cs @@ -0,0 +1,6 @@ +public class GithubOptions +{ + public string AppKey { get; set; } + public int AppId { get; set; } + public long InstallationId { get; set; } +} diff --git a/sk-azfunc-server/config/KernelConfigExtensions.cs b/sk-azfunc-server/config/KernelConfigExtensions.cs deleted file mode 100644 index 3c18d79670f..00000000000 --- a/sk-azfunc-server/config/KernelConfigExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.AI.ChatCompletion; - -internal static class KernelConfigExtensions -{ - /// - /// Adds a text completion service to the list. It can be either an OpenAI or Azure OpenAI backend service. - /// - /// - /// - /// - internal static void AddCompletionBackend(this KernelConfig kernelConfig, KernelSettings kernelSettings) - { - switch (kernelSettings.ServiceType.ToUpperInvariant()) - { - case KernelSettings.AzureOpenAI: - //kernelConfig.AddAzureTextCompletionService(deploymentName: kernelSettings.DeploymentOrModelId, endpoint: kernelSettings.Endpoint, apiKey: kernelSettings.ApiKey, serviceId: kernelSettings.ServiceId); - kernelConfig.AddAzureChatCompletionService(kernelSettings.DeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey); - break; - - case KernelSettings.OpenAI: - kernelConfig.AddOpenAITextCompletionService(modelId: kernelSettings.DeploymentOrModelId, apiKey: kernelSettings.ApiKey, orgId: kernelSettings.OrgId, serviceId: kernelSettings.ServiceId); - break; - - default: - throw new ArgumentException($"Invalid service type value: {kernelSettings.ServiceType}"); - } - } -} diff --git a/sk-azfunc-server/config/KernelSettings.cs b/sk-azfunc-server/config/KernelSettings.cs deleted file mode 100644 index c3a0de141fc..00000000000 --- a/sk-azfunc-server/config/KernelSettings.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Text.Json.Serialization; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -#pragma warning disable CA1812 -internal class KernelSettings -{ - public const string DefaultConfigFile = "config/appsettings.json"; - public const string OpenAI = "OPENAI"; - public const string AzureOpenAI = "AZUREOPENAI"; - - [JsonPropertyName("serviceType")] - public string ServiceType { get; set; } = string.Empty; - - [JsonPropertyName("serviceId")] - public string ServiceId { get; set; } = string.Empty; - - [JsonPropertyName("deploymentOrModelId")] - public string DeploymentOrModelId { get; set; } = string.Empty; - - [JsonPropertyName("endpoint")] - public string Endpoint { get; set; } = string.Empty; - - [JsonPropertyName("apiKey")] - public string ApiKey { get; set; } = string.Empty; - - [JsonPropertyName("orgId")] - public string OrgId { get; set; } = string.Empty; - - [JsonPropertyName("logLevel")] - public LogLevel? LogLevel { get; set; } - - /// - /// Load the kernel settings from settings.json if the file exists and if not attempt to use user secrets. - /// - internal static KernelSettings LoadSettings() - { - try - { - if (File.Exists(DefaultConfigFile)) - { - return FromFile(DefaultConfigFile); - } - - Console.WriteLine($"Semantic kernel settings '{DefaultConfigFile}' not found, attempting to load configuration from user secrets."); - - return FromUserSecrets(); - } - catch (InvalidDataException ide) - { - Console.Error.WriteLine( - "Unable to load semantic kernel settings, please provide configuration settings using instructions in the README.\n" + - "Please refer to: https://github.com/microsoft/semantic-kernel-starters/blob/main/sk-csharp-azure-functions/README.md#configuring-the-starter" - ); - throw new InvalidOperationException(ide.Message); - } - } - - /// - /// Load the kernel settings from the specified configuration file if it exists. - /// - internal static KernelSettings FromFile(string configFile = DefaultConfigFile) - { - if (!File.Exists(configFile)) - { - throw new FileNotFoundException($"Configuration not found: {configFile}"); - } - - var configuration = new ConfigurationBuilder() - .SetBasePath(System.IO.Directory.GetCurrentDirectory()) - .AddJsonFile(configFile, optional: true, reloadOnChange: true) - .Build(); - - return configuration.Get() - ?? throw new InvalidDataException($"Invalid semantic kernel settings in '{configFile}', please provide configuration settings using instructions in the README."); - } - - /// - /// Load the kernel settings from user secrets. - /// - internal static KernelSettings FromUserSecrets() - { - var configuration = new ConfigurationBuilder() - .AddUserSecrets() - .AddEnvironmentVariables() - .Build(); - - return configuration.Get() - ?? throw new InvalidDataException("Invalid semantic kernel settings in user secrets, please provide configuration settings using instructions in the README."); - } -} diff --git a/sk-azfunc-server/config/OpenAIOptions.cs b/sk-azfunc-server/config/OpenAIOptions.cs new file mode 100644 index 00000000000..188f33c4e4c --- /dev/null +++ b/sk-azfunc-server/config/OpenAIOptions.cs @@ -0,0 +1,9 @@ +public class OpenAIOptions +{ + public string ServiceType { get; set; } + public string ServiceId { get; set; } + public string DeploymentOrModelId { get; set; } + public string EmbeddingDeploymentOrModelId { get; set; } + public string Endpoint { get; set; } + public string ApiKey { get; set; } +} \ No newline at end of file diff --git a/sk-azfunc-server/config/QdrantOptions.cs b/sk-azfunc-server/config/QdrantOptions.cs new file mode 100644 index 00000000000..cebef7eb4f3 --- /dev/null +++ b/sk-azfunc-server/config/QdrantOptions.cs @@ -0,0 +1,5 @@ +public class QdrantOptions +{ + public string Endpoint { get; set; } + public int VectorSize { get; set; } +} \ No newline at end of file diff --git a/sk-azfunc-server/config/appsettings-notyet.json b/sk-azfunc-server/config/appsettings-notyet.json deleted file mode 100644 index 87c5d1e6e98..00000000000 --- a/sk-azfunc-server/config/appsettings-notyet.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "serviceType": "AzureOpenAI", - "serviceId": "gpt-4", - "deploymentOrModelId": "gpt-4", - "endpoint": "https://lightspeed-team-shared-openai-eastus.openai.azure.com/", -} \ No newline at end of file diff --git a/sk-azfunc-server/config/appsettings.json.azure-example b/sk-azfunc-server/config/appsettings.json.azure-example deleted file mode 100644 index fb63bb6dd5a..00000000000 --- a/sk-azfunc-server/config/appsettings.json.azure-example +++ /dev/null @@ -1,7 +0,0 @@ -{ - "serviceType": "AzureOpenAI", - "serviceId": "text-davinci-003", - "deploymentOrModelId": "text-davinci-003", - "endpoint": "https:// ... your endpoint ... .openai.azure.com/", - "apiKey": "... your Azure OpenAI key ..." -} \ No newline at end of file diff --git a/sk-azfunc-server/config/appsettings.json.openai-example b/sk-azfunc-server/config/appsettings.json.openai-example deleted file mode 100644 index f4472a25c9f..00000000000 --- a/sk-azfunc-server/config/appsettings.json.openai-example +++ /dev/null @@ -1,7 +0,0 @@ -{ - "serviceType": "OpenAI", - "serviceId": "text-davinci-003", - "deploymentOrModelId": "text-davinci-003", - "apiKey": "... your OpenAI key ...", - "orgId": "" -} \ No newline at end of file diff --git a/sk-azfunc-server/host.json b/sk-azfunc-server/host.json index 278b52cdee3..6b68371f9dc 100644 --- a/sk-azfunc-server/host.json +++ b/sk-azfunc-server/host.json @@ -7,5 +7,12 @@ "excludedTypes": "Request" } } + }, + "extensions": { + "durableTask": { + "storageProvider": { + "type": "AzureStorage" + } + } } } \ No newline at end of file diff --git a/sk-azfunc-server/local.settings.json b/sk-azfunc-server/local.settings.json index f7a08ea5367..95443230e1e 100644 --- a/sk-azfunc-server/local.settings.json +++ b/sk-azfunc-server/local.settings.json @@ -4,6 +4,28 @@ "CORS": "*" }, "Values": { - "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated" + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "FUNCTIONS_FQDN": "http://localhost:7071", + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "AzureWebJobsFeatureFlags": "EnableHttpProxying", + "SANDBOX_IMAGE" : "mcr.microsoft.com/dotnet/sdk:7.0", + "GithubOptions:AppKey": "", + "GithubOptions:AppId": "", + "GithubOptions:InstallationId": "", + "AzureOptions:SubscriptionId":"", + "AzureOptions:Location":"", + "AzureOptions:ContainerInstancesResourceGroup":"", + "AzureOptions:FilesShareName":"", + "AzureOptions:FilesAccountName":"", + "AzureOptions:FilesAccountKey":"", + "OpenAIOptions:ServiceType":"", + "OpenAIOptions:ServiceId":"", + "OpenAIOptions:DeploymentOrModelId":"", + "OpenAIOptions:EmbeddingDeploymentOrModelId":"", + "OpenAIOptions:Endpoint":"", + "OpenAIOptions:ApiKey":"", + "QdrantOptions:Endpoint":"http://qdrant:6333", + "QdrantOptions:VectorSize":"1536" } -} \ No newline at end of file +} + diff --git a/sk-azfunc-server/local.settings.template.json b/sk-azfunc-server/local.settings.template.json new file mode 100644 index 00000000000..905ff383f53 --- /dev/null +++ b/sk-azfunc-server/local.settings.template.json @@ -0,0 +1,52 @@ +{ + "IsEncrypted": false, + "Host": { + "CORS": "*" + }, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + // For local development, keep the default value + // for Azure deployment, it will be injected as a variable in the bicep template + "FUNCTIONS_FQDN": "localhost:7071", + // For local development, keep the default value + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "AzureWebJobsFeatureFlags": "EnableHttpProxying", + // This is the container image used as a base for the sandbox + "SANDBOX_IMAGE" : "mcr.microsoft.com/devcontainers/universal:2-linux", + // The private key generated for the Github App + "GithubOptions__AppKey": "", + // The App Id for the Github App + "GithubOptions__AppId": "", + // The instalation ID for the Github App (once installed to a repo or an org) + "GithubOptions__InstallationId": "", + // Azure subscription id + "AzureOptions__SubscriptionId":"", + // Location for the deployed resources in Azure + "AzureOptions__Location":"", + // Resource group in Azure, where ACI sandbox instances are going to be created + "AzureOptions__ContainerInstancesResourceGroup":"", + // Azure storage file share name (doesn't work with Azurite) + "AzureOptions__FilesShareName":"", + // Azure storage file share account name + "AzureOptions__FilesAccountName":"", + // Azure storage file share account key + "AzureOptions__FilesAccountKey":"", + // If using Azure - AzureOpenAI + "OpenAIOptions__ServiceType":"", + // the service id of the OpenAI model you want to use + "OpenAIOptions__ServiceId":"", + // the deployment id of the OpenAI model you want to use + "OpenAIOptions__DeploymentOrModelId":"", + // the embedding deployment id for the semantic memory + "OpenAIOptions__EmbeddingDeploymentOrModelId":"", + // the endpoint for the provisioned OpenAI service + "OpenAIOptions__Endpoint":"", + // the key for the provisioned OpenAI service + "OpenAIOptions__ApiKey":"", + // if using Codespaces, keep the default value + "QdrantOptions__Endpoint":"http://qdrant:6333", + // keep default + "QdrantOptions__VectorSize":"1536" + } +} + diff --git a/sk-azfunc-server/sk-csharp-azure-functions.csproj b/sk-azfunc-server/sk-csharp-azure-functions.csproj index 602415f8641..8bee3639321 100644 --- a/sk-azfunc-server/sk-csharp-azure-functions.csproj +++ b/sk-azfunc-server/sk-csharp-azure-functions.csproj @@ -17,13 +17,26 @@ + + + + - - - - - - + + + + + + + + + + + + + + + diff --git a/skills/DevLead.cs b/skills/DevLead.cs index fd7790399f0..86467c6aa02 100644 --- a/skills/DevLead.cs +++ b/skills/DevLead.cs @@ -9,42 +9,28 @@ public static class DevLead { For each step or module then break down the steps or subtasks required to complete that step or module. For each subtask write an LLM prompt that would be used to tell a model to write the coee that will accomplish that subtask. If the subtask involves taking action/running commands tell the model to write the script that will run those commands. In each LLM prompt restrict the model from outputting other text that is not in the form of code or code comments. - Please output a JSON array data structure with a list of steps and a description of each step, and the steps or subtasks that each requires, and the LLM prompts for each subtask. + Please output a JSON array data structure, in the precise schema shown below, with a list of steps and a description of each step, and the steps or subtasks that each requires, and the LLM prompts for each subtask. Example: - [ { - "step": "Step 1", - "description": "This is the first step", - "subtasks": [ + "steps": [ { - "subtask": "Subtask 1", - "description": "This is the first subtask", - "prompt": "Write the code to do the first subtask" - }, - { - "subtask": "Subtask 2", - "description": "This is the second subtask", - "prompt": "Write the code to do the second subtask" - } - ] - }, - { - "step": "Step 2", - "description": "This is the second step", - "subtasks": [ - { - "subtask": "Subtask 1", - "description": "This is the first subtask", - "prompt": "Write the code to do the first subtask" - }, - { - "subtask": "Subtask 2", - "description": "This is the second subtask", - "prompt": "Write the code to do the second subtask" + "step": "1", + "description": "This is the first step", + "subtasks": [ + { + "subtask": "Subtask 1", + "description": "This is the first subtask", + "prompt": "Write the code to do the first subtask" + }, + { + "subtask": "Subtask 2", + "description": "This is the second subtask", + "prompt": "Write the code to do the second subtask" + } + ] } ] } - ] Do not output any other text. Input: {{$input}} {{$wafContext}} diff --git a/skills/skills.csproj b/skills/skills.csproj index eb4501ad295..2cb017c9f4d 100644 --- a/skills/skills.csproj +++ b/skills/skills.csproj @@ -7,6 +7,6 @@ - + diff --git a/util/seed-memory/Program.cs b/util/seed-memory/Program.cs index 73cc3de3fc3..f5d59f4ad87 100644 --- a/util/seed-memory/Program.cs +++ b/util/seed-memory/Program.cs @@ -25,7 +25,7 @@ static async Task Main(string[] args) .AddDebug(); }); - var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient("http://qdrant", 1536, port: 6333)); + var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient(kernelSettings.QdrantEndpoint, 1536)); var embedingGeneration = new AzureTextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey); var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration); @@ -45,13 +45,20 @@ public static async Task ImportDocumentAsync(IKernel kernel, string filename) var pages = pdfDocument.GetPages(); foreach (var page in pages) { - var text = ContentOrderTextExtractor.GetText(page); - var descr = text.Take(100); - await kernel.Memory.SaveInformationAsync( - collection: "waf-pages", - text: text, - id: $"{Guid.NewGuid()}", - description: $"Document: {descr}"); + try + { + var text = ContentOrderTextExtractor.GetText(page); + var descr = text.Take(100); + await kernel.Memory.SaveInformationAsync( + collection: "waf-pages", + text: text, + id: $"{Guid.NewGuid()}", + description: $"Document: {descr}"); + } + catch(Exception ex) + { + Console.WriteLine(ex.Message); + } } } } \ No newline at end of file diff --git a/util/seed-memory/config/KernelSettings.cs b/util/seed-memory/config/KernelSettings.cs index 042945c868e..a21061443ed 100644 --- a/util/seed-memory/config/KernelSettings.cs +++ b/util/seed-memory/config/KernelSettings.cs @@ -29,6 +29,9 @@ internal class KernelSettings [JsonPropertyName("orgId")] public string OrgId { get; set; } = string.Empty; + [JsonPropertyName("qdrantEndoint")] + public string QdrantEndpoint { get; set; } = string.Empty; + [JsonPropertyName("logLevel")] public LogLevel? LogLevel { get; set; } @@ -71,6 +74,7 @@ internal static KernelSettings FromFile(string configFile = DefaultConfigFile) var configuration = new ConfigurationBuilder() .SetBasePath(System.IO.Directory.GetCurrentDirectory()) .AddJsonFile(configFile, optional: true, reloadOnChange: true) + .AddEnvironmentVariables() .Build(); return configuration.Get() diff --git a/util/seed-memory/config/appsettings.template.json b/util/seed-memory/config/appsettings.template.json new file mode 100644 index 00000000000..3fe9615a4b4 --- /dev/null +++ b/util/seed-memory/config/appsettings.template.json @@ -0,0 +1,9 @@ +{ + "serviceType": "AzureOpenAI", + "serviceId": "", + "deploymentOrModelId": "", + "embeddingDeploymentOrModelId": "", + "endpoint": "", + "apiKey": "", + "qdrantEndoint": "" +} \ No newline at end of file diff --git a/util/seed-memory/seed-memory.csproj b/util/seed-memory/seed-memory.csproj index e36401b3741..500e84ac712 100644 --- a/util/seed-memory/seed-memory.csproj +++ b/util/seed-memory/seed-memory.csproj @@ -12,8 +12,8 @@ - - + +