From b6dfc176edf3fb2edffc99b110c0f962527cf62e Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Fri, 28 Jun 2024 13:51:49 +0100 Subject: [PATCH] Initial commit on gh-pages --- .github/FUNDING.yml | 2 + .github/workflows/autoupdate.yaml | 73 +++ .github/workflows/pr.yaml | 107 +++++ .github/workflows/test.yaml | 70 +++ .gitignore | 3 + Dockerfile | 8 + LICENSE | 21 + Makefile | 34 ++ README.md | 320 +++++++++++++ api.go | 259 +++++++++++ cache/cache.go | 134 ++++++ cache/cache_bench_test.go | 116 +++++ cache/cache_suite_test.go | 34 ++ cache/cache_test.go | 215 +++++++++ cache/memory/memory.go | 157 +++++++ cache/memory/memory_bench_test.go | 82 ++++ cache/memory/memory_test.go | 168 +++++++ cache/redis/redis.go | 96 ++++ cache/redis/redis_test.go | 130 ++++++ config/config.go | 6 + details.go | 65 +++ details_test.go | 81 ++++ events.go | 88 ++++ go.mod | 54 +++ go.sum | 120 +++++ graphql.go | 212 +++++++++ graphql_test.go | 432 ++++++++++++++++++ logging/logger.go | 204 +++++++++ logging/logger_bench_test.go | 140 ++++++ logging/logger_suite_test.go | 31 ++ logging/logger_test.go | 182 ++++++++ main.go | 122 +++++ main_test.go | 140 ++++++ monitoring.go | 11 + monitoring/defaults.go | 15 + monitoring/helpers.go | 173 +++++++ monitoring/helpers_bench_test.go | 44 ++ monitoring/helpers_test.go | 228 +++++++++ monitoring/monitoring.go | 174 +++++++ monitoring/structs.go | 14 + proxy.go | 130 ++++++ proxy_test.go | 127 +++++ ratelimit.go | 124 +++++ semver.yaml | 15 + server.go | 265 +++++++++++ static/app/.keep | 0 static/app/banned_users.json | 1 + static/app/banned_users.json.lock | 0 static/app/ratelimit.json | 16 + static/kubernetes-deployment.yaml | 92 ++++ .../kubernetes-single-deployment-with-ro.yaml | 165 +++++++ static/kubernetes-single-deployment.yaml | 121 +++++ static/monitoring-at-glance.png | Bin 0 -> 343820 bytes struct_config.go | 57 +++ 54 files changed, 5678 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/autoupdate.yaml create mode 100644 .github/workflows/pr.yaml create mode 100644 .github/workflows/test.yaml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 api.go create mode 100644 cache/cache.go create mode 100644 cache/cache_bench_test.go create mode 100644 cache/cache_suite_test.go create mode 100644 cache/cache_test.go create mode 100644 cache/memory/memory.go create mode 100644 cache/memory/memory_bench_test.go create mode 100644 cache/memory/memory_test.go create mode 100644 cache/redis/redis.go create mode 100644 cache/redis/redis_test.go create mode 100644 config/config.go create mode 100644 details.go create mode 100644 details_test.go create mode 100644 events.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 graphql.go create mode 100644 graphql_test.go create mode 100644 logging/logger.go create mode 100644 logging/logger_bench_test.go create mode 100644 logging/logger_suite_test.go create mode 100644 logging/logger_test.go create mode 100644 main.go create mode 100644 main_test.go create mode 100644 monitoring.go create mode 100644 monitoring/defaults.go create mode 100644 monitoring/helpers.go create mode 100644 monitoring/helpers_bench_test.go create mode 100644 monitoring/helpers_test.go create mode 100644 monitoring/monitoring.go create mode 100644 monitoring/structs.go create mode 100644 proxy.go create mode 100644 proxy_test.go create mode 100644 ratelimit.go create mode 100644 semver.yaml create mode 100644 server.go create mode 100644 static/app/.keep create mode 100644 static/app/banned_users.json create mode 100644 static/app/banned_users.json.lock create mode 100644 static/app/ratelimit.json create mode 100644 static/kubernetes-deployment.yaml create mode 100644 static/kubernetes-single-deployment-with-ro.yaml create mode 100644 static/kubernetes-single-deployment.yaml create mode 100644 static/monitoring-at-glance.png create mode 100644 struct_config.go diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..4047684 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [ lukaszraczylo ] +custom: [ monzo.me/lukaszraczylo ] \ No newline at end of file diff --git a/.github/workflows/autoupdate.yaml b/.github/workflows/autoupdate.yaml new file mode 100644 index 0000000..100300d --- /dev/null +++ b/.github/workflows/autoupdate.yaml @@ -0,0 +1,73 @@ +name: Autoupdate go.mod and go.sum + +on: + workflow_dispatch: + schedule: + - cron: "0 3 * * *" + +env: + GO_VERSION: ">=1.21" + +jobs: + # This job is responsible for preparation of the build + # environment variables. + prepare: + name: Preparing build context + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + id: cache + with: + go-version: ${{env.GO_VERSION}} + cache-dependency-path: "**/*.sum" + + - name: Go get dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: | + go get ./... + + # This job is responsible for running tests and linting the codebase + test: + name: "Unit testing" + runs-on: ubuntu-latest + container: golang:1 + needs: [prepare] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Ensure full history is checked out + token: ${{ secrets.GHCR_TOKEN }} + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{env.GO_VERSION}} + cache-dependency-path: "**/*.sum" + + - name: Install dependencies + run: | + apt-get update + apt-get install ca-certificates make -y + update-ca-certificates + go mod tidy + go get -u -v ./... + go mod tidy -v + + - name: Run unit tests + run: | + CI_RUN=${CI} make test + git config --global --add safe.directory /__w/graphql-monitoring-proxy/graphql-monitoring-proxy + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Update go.mod and go.sum" + commit_options: "--no-verify --signoff" + file_pattern: "go.mod go.sum" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml new file mode 100644 index 0000000..c75935f --- /dev/null +++ b/.github/workflows/pr.yaml @@ -0,0 +1,107 @@ +name: Run tests on PR + +on: + pull_request: + branches: + - "main" + push: + paths-ignore: + - "**/**.md" + - "**/**.yaml" + - "static/**" + branches: + - "!main" + +env: + GO_VERSION: ">=1.21" + +permissions: + # deployments permission to deploy GitHub pages website + deployments: write + # contents permission to update benchmark contents in gh-pages branch + contents: write + +jobs: + # This job is responsible for preparation of the build + # environment variables. + prepare: + name: Preparing build context + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + id: cache + with: + go-version: ${{env.GO_VERSION}} + cache-dependency-path: "**/*.sum" + + - name: Go get dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: | + go get ./... + + # This job is responsible for running tests and linting the codebase + test: + name: "Unit testing" + # needs: [prepare] + runs-on: ubuntu-latest + container: golang:1 + # container: github/super-linter:v4 + needs: [prepare] + + # services: + # # Label used to access the service container + # redis: + # # Docker Hub image + # image: redis + # # Set health checks to wait until redis has started + # options: >- + # --health-cmd "redis-cli ping" + # --health-interval 10s + # --health-timeout 5s + # --health-retries 5 + # ports: + # # Maps the container port to the host machine + # - 6379:6379 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{env.GO_VERSION}} + cache-dependency-path: "**/*.sum" + + - name: Install dependencies + run: | + apt-get update + apt-get install ca-certificates make -y + update-ca-certificates + go mod tidy + git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Run unit tests + run: | + CI_RUN=${CI} make test + + - name: Run benchmark + run: | + go test -bench=. -benchmem ./... -run=^# | tee output.txt + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "go" + output-file-path: output.txt + fail-on-alert: true + github-token: ${{ secrets.GITHUB_TOKEN }} + comment-on-alert: true + summary-always: true + # auto-push only if it's on main branch + auto-push: false diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..94c05cb --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,70 @@ +name: Test and release + +on: + workflow_dispatch: + push: + paths-ignore: + - "**/**.md" + - "**/**.yaml" + - "static/**" + branches: + - "main" + +env: + GO_VERSION: ">=1.21" + +permissions: + # deployments permission to deploy GitHub pages website + deployments: write + # contents permission to update benchmark contents in gh-pages branch + contents: write + +jobs: + shared: + uses: telegram-bot-app/ci-scripts/.github/workflows/build-test-publish-inject.yaml@main + with: + enable-code-scans: false + should-deploy: false + secrets: + ghcr-token: ${{ secrets.GHCR_TOKEN }} + + test: + name: "Benchmarking the results" + needs: [shared] + runs-on: ubuntu-latest + container: golang:1 + # container: github/super-linter:v4 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{env.GO_VERSION}} + cache-dependency-path: "**/*.sum" + + - name: Install dependencies + run: | + apt-get update + apt-get install ca-certificates make -y + update-ca-certificates + go mod tidy + git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Run benchmark + run: | + go test -bench=. -benchmem ./... -run=^# | tee output.txt + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "go" + output-file-path: output.txt + fail-on-alert: true + github-token: ${{ secrets.GITHUB_TOKEN }} + comment-on-alert: true + summary-always: true + # auto-push only if it's on main branch + auto-push: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08b9003 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +graphql-proxy +test.sh +banned.json* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..af12b50 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM gcr.io/distroless/base-debian12:nonroot +WORKDIR /go/src/app +ARG TARGETARCH +ARG TARGETOS +# silly workaround for distroless image as no chmod is available +COPY --chmod=777 --chown=nonroot:nonroot static/app /go/src/app +ADD dist/bot-$TARGETOS-$TARGETARCH /go/src/app/graphql-proxy +ENTRYPOINT ["/go/src/app/graphql-proxy"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..edfcb87 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Lukasz Raczylo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f1e4a66 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +CI_RUN?=false +# ADDITIONAL_BUILD_FLAGS="" + +# ifeq ($(CI_RUN), true) +# ADDITIONAL_BUILD_FLAGS="-test.short" +# endif + +.PHONY: help +help: ## display this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST) + +.PHONY: run +run: build ## run application + @LOG_LEVEL=debug PURGE_METRICS_ON_CRAWL=true BLOCK_SCHEMA_INTROSPECTION=false CACHE_TTL=10 JWT_ROLE_RATE_LIMIT=false JWT_ROLE_CLAIM_PATH="Hasura.x-hasura-default-role" JWT_USER_CLAIM_PATH="Hasura.x-hasura-user-id" HOST_GRAPHQL=https://hasura8.lan/ HEALTHCHECK_GRAPHQL_URL=https://hasura8.lan/v1/graphql ./graphql-proxy + +.PHONY: build +build: ## build the binary + go build -o graphql-proxy *.go + +.PHONY: test +test: ## run tests on library + @LOG_LEVEL=info go test -v -cover -race ./... + +.PHONY: test-packages +test-packages: ## run tests on packages + @go test -v -cover ./pkg/... + +.PHONY: all +all: test-packages test + +.PHONY: update +update: ## update dependencies + @go get -u -v ./... + @go mod tidy -v diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a6e4fb --- /dev/null +++ b/README.md @@ -0,0 +1,320 @@ +## graphql monitoring proxy + +Creates a passthrough proxy to a graphql endpoint(s), allowing you to analyse the queries and responses, producing the Prometheus metrics at a fraction of the cost - because, as we know - $0 is a fair price. + +This project is in active use by [telegram-bot.app](https://telegram-bot.app), and was tested with 30k queries per second on a single instance, consuming 10 MB of RAM and 0.1% CPU. [Benchmarks](https://lukaszraczylo.github.io/graphql-monitoring-proxy/dev/bench/) are available. + +![Example of monitoring dashboard](static/monitoring-at-glance.png?raw=true) + +- [graphql monitoring proxy](#graphql-monitoring-proxy) + - [Why this project exists](#why-this-project-exists) + - [How to deploy](#how-to-deploy) + - [Note on websocket support](#note-on-websocket-support) + - [Endpoints](#endpoints) + - [Features](#features) + - [Configuration](#configuration) + - [Speed](#speed) + - [Caching](#caching) + - [Read-only endpoint](#read-only-endpoint) + - [Maintenance](#maintenance) + - [Hasura event cleaner](#hasura-event-cleaner) + - [Security](#security) + - [Role-based rate limiting](#role-based-rate-limiting) + - [Read-only mode](#read-only-mode) + - [Allowing access to listed URLs](#allowing-access-to-listed-urls) + - [Blocking introspection](#blocking-introspection) + - [API endpoints](#api-endpoints) + - [Ban or unban the user](#ban-or-unban-the-user) + - [Cache operations](#cache-operations) + - [General](#general) + - [Metrics which matter](#metrics-which-matter) + - [Healthcheck](#healthcheck) + - [Monitoring endpoint](#monitoring-endpoint) + +### Why this project exists + +I wanted to monitor the queries and responses of our graphql endpoint. Still, we didn't want to pay the price of the graphql server itself ( and I will not point fingers at a particular well-known project), as monitoring and basic security features should be a standard, free functionality. + +### How to deploy + +You can find the example of the Kubernetes manifest in the [example standalone deployment](static/kubernetes-deployment.yaml) or [example combined deployment](static/kubernetes-single-deployment.yaml) files. Observed advantage of multideployment is that it allows the network requests to travel via localhost, without leaving the deployment which brings quite significant network performance boost. + +#### Note on websocket support + +Proxy in its current version 0.5.30 does not support websockets. If you need to proxy the websocket requests - you can use following trick whilst setting up the proxy. As I'm a big fan of Traefik - there's an example which works with the mentioned above combined deployment. + +
+ Click to show working Traefik Ingress Route example. + +```yaml +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: hasura-internal +spec: + entryPoints: + - websecure + routes: + # NON WEBSOCKET CONNECTION + - kind: Rule + match: Host(`example.com`) && PathPrefix(`/v1/graphql`) && !HeadersRegexp(`Upgrade`, `websocket`) + services: + - name: hasura-w-proxy-internal + port: proxy + middlewares: + - name: compression + namespace: default + + # WEBSOCKET CONNECTION + - kind: Rule + match: Host(`example.com`) && PathPrefix(`/v1/graphql`) && HeadersRegexp(`Upgrade`, `websocket`) + services: + - name: hasura-w-proxy-internal + port: hasura + middlewares: + - name: compression + namespace: default +``` + +In this case, both proxy and websockets will be available under the `/v1/graphql` path, and the websocket connection will be proxied directly to the hasura service, bypassing the proxy. + +
+ +### Endpoints + +* `:8080/*` - the graphql passthrough endpoint +* `:9393/metrics` - the prometheus metrics endpoint +* `:8080/healthz` - the healthcheck endpoint +* `:8080/livez` - the liveness probe endpoint +* `:9090/api/*` - the monitoring proxy API endpoint + +### Features + +| Category | Detail | +|------------|-----------------------------------------------------------------------| +| monitor | Prometheus / VictoriaMetrics metrics | +| monitor | Extracting user id from JWT token and adding it as a label to metrics | +| monitor | Extracting the query name and type and adding it as a label to metrics| +| monitor | Calculating the query duration and adding it to the metrics | +| speed | Caching the queries, together with per-query cache and TTL | +| speed | Support for READ ONLY graphql endpoint | +| security | Blocking schema introspection | +| security | Rate limiting queries based on user role | +| security | Blocking mutations in read-only mode | +| security | Allow access only to listed URLs | +| security | Ban / unban specific user from accessing the application | +| maintenance | Hasura event cleaner | + + +### Configuration + +All the environment variables **should** be prefixed with `GMP_` to avoid conflicts with other applications. +If `GMP_` prefixed environment variable is present - it will take precedence over the non-prefixed one. +You can still use the non-prefixed environment variables in the spirit of the backward compatibility, but it's not recommended. + +| Parameter | Description | Default Value | +|---------------------------|------------------------------------------|----------------------------| +| `MONITORING_PORT` | The port to expose the metrics endpoint | `9393` | +| `PORT_GRAPHQL` | The port to expose the graphql endpoint | `8080` | +| `HOST_GRAPHQL` | The host to proxy the graphql endpoint | `http://localhost/` | +| `HOST_GRAPHQL_READONLY` | The host to proxy the read-only graphql endpoint | `` | +| `HEALTHCHECK_GRAPHQL_URL` | The URL to check the health of the graphql endpoint | `` | +| `JWT_USER_CLAIM_PATH` | Path to the user claim in the JWT token | `` | +| `JWT_ROLE_CLAIM_PATH` | Path to the role claim in the JWT token | `` | +| `ROLE_FROM_HEADER` | Header name to extract the role from | `` | +| `ROLE_RATE_LIMIT` | Enable request rate limiting based on role| `false` | +| `ENABLE_GLOBAL_CACHE` | Enable the cache | `false` | +| `CACHE_TTL` | The cache TTL | `60` | +| `ENABLE_REDIS_CACHE` | Enable distributed Redis cache | `false` | +| `CACHE_REDIS_URL` | URL to redis server / cluster endpoint | `localhost:6379` | +| `CACHE_REDIS_PASSWORD` | Redis connection password | `` | +| `CACHE_REDIS_DB` | Redis DB id | `0` | +| `LOG_LEVEL` | The log level | `info` | +| `BLOCK_SCHEMA_INTROSPECTION`| Blocks the schema introspection | `false` | +| `ALLOWED_INTROSPECTION` | Allow only certain queries in introspection | `` | +| `ENABLE_ACCESS_LOG` | Enable the access log | `false` | +| `READ_ONLY_MODE` | Enable the read only mode | `false` | +| `ALLOWED_URLS` | Allow access only to certain URLs | `/v1/graphql,/v1/version` | +| `ENABLE_API` | Enable the monitoring API | `false` | +| `API_PORT` | The port to expose the monitoring API | `9090` | +| `BANNED_USERS_FILE` | The path to the file with banned users | `/go/src/app/banned_users.json` | +| `PROXIED_CLIENT_TIMEOUT` | The timeout for the proxied client in seconds | `120` | +| `PURGE_METRICS_ON_CRAWL` | Purge metrics on each /metrics crawl | `false` | +| `PURGE_METRICS_ON_TIMER` | Purge metrics every x seconds. `0` - disabled | `0` | +| `HASURA_EVENT_CLEANER` | Enable the hasura event cleaner | `false` | +| `HASURA_EVENT_CLEANER_OLDER_THAN` | The interval for the hasura event cleaner (in days) | `1` | +| `HASURA_EVENT_METADATA_DB` | URL to the hasura metadata database | `postgresql://localhost:5432/hasura` | + +### Speed + +#### Caching + +The cache engine is enabled in the background by default, using no additional resources. +You can then start using the cache by setting the `ENABLE_GLOBAL_CACHE` or `ENABLE_REDIS_CACHE` environment variable to `true` - which will enable the cache for all queries without introspection. You can leave the global cache disabled and enable the cache for specific queries by adding the `@cached` directive to the query. + +In the case of the `@cached` you can add additional parameters to the directive which will set the cache for specific queries to the provided time. +For example, `query MyCachedQuery @cached(ttl: 90) ....` will set the cache for the query to 90 seconds. + +You can also set cache for specific query by using `X-Cache-Graphql-Query` header, which will set the cache for the query to the provided time, for example `X-Cache-Graphql-Query: 90` will set the cache for the query to 90 seconds. + +You can also force refresh of the cache by using `@cached(refresh: true)` directive in the query, for example: + +``` +query MyProducts @cached(refresh: true) { + products { + id + name + } +} +``` + +Since version `0.5.30` the cache is gzipped in the memory, which should optimise the memory usage quite significantly. +Since version `0.15.48` the you can also use the distributed Redis cache. + +#### Read-only endpoint + +You can now specify the read-only GraphQL endpoint by setting the `HOST_GRAPHQL_READONLY` environment variable. The default value is empty, preventing the proxy from using the read-only endpoint for the queries and directing all the requests to the main endpoint specified as `HOST_GRAPHQL`. If the `HOST_GRAPHQL_READONLY` is set, the proxy will use the read-only endpoint for the queries with the `query` type and the main endpoint for the `mutation` type queries. Format of the read-only endpoint is the same as `HOST_GRAPHQL` endpoint, for example `http://localhost:8080/`. + +You can check out the [example of combined deployment with RW and read-only hasura](static/kubernetes-single-deployment-with-ro.yaml). + +### Maintenance + +#### Hasura event cleaner + +When enabled via `HASURA_EVENT_CLEANER=true` - proxy needs to have a direct access to the database to execute simple delete queries on schedule. You can specify number of days the logs should be kept for using `HASURA_EVENT_CLEANER_OLDER_THAN`, for example `HASURA_EVENT_CLEANER_OLDER_THAN=14` will keep 14 days of event execution logs. Ticker managing the cleaner routine will be executed every hour. + +Following tables are being cleaned: +- `hdb_catalog.event_invocation_logs` +- `hdb_catalog.event_log` +- `hdb_catalog.hdb_action_log` +- `hdb_catalog.hdb_cron_event_invocation_logs` +- `hdb_catalog.hdb_scheduled_event_invocation_logs` + + +### Security + +#### Role-based rate limiting + +You can rate limit requests using the `ROLE_RATE_LIMIT` environment variable. If enabled, the proxy will rate limit the requests based on the role claim in the JWT token. You can then provide the JSON file in the following format to specify the limits. +The default interval is `second`, but you can use other values as well. If you want to disable the rate limiting for a specific role, you can set the `req` to `0`. + +Available values: +`nano`, `micro`, `milli`, `second`, `minute`, `hour`, `day` + +To define path in JWT token where the current user role is present, use the `JWT_ROLE_CLAIM_PATH` environment variable. + +You can also set up the `ROLE_FROM_HEADER` environment variable to extract the role from the header instead of the JWT token. This is useful if you want to rate limit the requests for unauthenticated users. It's worth mentioning that `ROLE_FROM_HEADER` takes a priority over the `JWT_ROLE_CLAIM_PATH` environment variable and if its set, the proxy will not try to extract the role from the JWT token. + +*Default/sample configuration:* + +```json +{ + "ratelimit": { + "admin": { + "req": 100, + "interval": "second" + }, + "guest": { + "req": 50, + "interval": "minute" + }, + "-": { + "req": 100, + "interval": "day" + } + } +} +``` + +If you'd like to change it - mount your configmap as `/app/ratelimit.json` file. +Remember to include the `-` role, which is used for unauthenticated users or when claim can't be found for any reason. +If rate limit has been reached - the proxy will return `429 Too Many Requests` error. + + +#### Read-only mode + +You can enable the read-only mode by setting the `READ_ONLY_MODE` environment variable to `true` - which will block all the `mutation` queries. + +#### Allowing access to listed URLs + +You can allow access only to certain URLs by setting the `ALLOWED_URLS` environment variable to a comma-separated list of URLs. If enabled - other URLs will return `403 Forbidden` error and request will **not** reach the proxied service. + +#### Blocking introspection + +You can block the schema introspection by setting the `BLOCK_SCHEMA_INTROSPECTION` environment variable to `true` - which will block all the queries with introspection parts, like: + +`__schema`, `__type`, `__typename`, `__directive`, `__directivelocation`, `__field`, `__inputvalue`, `__enumvalue`, `__typekind`, `__fieldtype`, `__inputobjecttype`, `__enumtype`, `__uniontype`, `__scalars`, `__objects`, `__interfaces`, `__unions`, `__enums`, `__inputobjects`, `__directives` + +If you'd like to keep blocking of the schema introspection on but allow one or more of from the list of above for any reason, you can use the `ALLOWED_INTROSPECTION` environment variable to specify the list of allowed queries. + +`ALLOWED_INTROSPECTION="__typename,__type"` + +### API endpoints + +#### Ban or unban the user + +Your monitoring system can detect user misbehaving, for example trying to extract / scrap the data. To prevent user from doing so you can use the simple API to ban the user from accessing the application. + +To do so - you need to enable the api by setting env variable `ENABLE_API=true` which will expose the API on the port `API_PORT=9090`. Nedless to say - keep it secure and don't expose it outside of your cluster. + + Then you can use the following endpoints: + +* `POST /api/user-ban` - ban the user from accessing the application +* `POST /api/user-unban` - unban the user from accessing the application + +#### Cache operations + +* `POST /api/cache-clear` - clear the cache +* `GET /api/cache-stats` - get the cache statistics ( hits, misses, size ) + +Both endpoints require the `user_id` parameter to be present in the request body and allow you to provide the reason for the ban. + +Example request: + +```bash +curl -X POST \ + http://localhost:9090/api/user-ban \ + -H 'Content-Type: application/json' \ + -d '{ + "user_id": "1337", + "reason": "Scraping data" + }' +``` + +Ban details will be stored in the `banned_users.json` file, which you can mount as a file or configmap to the `/go/src/app/banned_users.json` path ( or use `BANNED_USERS_FILE` environment variable to specify the path to the file). The file operation is important if you have multiple instances of the proxy running, as it will allow you to ban the user from accessing the application on all instances. + +### General + +#### Metrics which matter + +You can always enable `PURGE_METRICS_ON_CRAWL` environment variable to purge the metrics on each `/metrics` crawl. This will allow you to see only the current metrics, without potential leftovers from the previous crawls. This is useful if you want to monitor the metrics in real-time and / or limit the amount of data ingested into the monitoring system. When enabled you will most likely need to update your monitoring queries. + +With the `PURGE_METRICS_ON_CRAWL` enabled, the `graphql_proxy_requests_failed`, `graphql_proxy_requests_skipped` and `graphql_proxy_requests_succesful` metrics will remain between resets. + +If you prefer more control over the metrics purging - you can enable `PURGE_METRICS_ON_TIMER` environment variable and set the interval in seconds. This will allow you to purge the metrics on a regular basis, for example every 90 seconds. It could be better solution if you have multiple crawlers checking the metrics endpoints and you want to avoid the situation when metrics are purged by for example healthcheck. + +#### Healthcheck + +If you'd like the `/healthz` endpoint to perform actual check for the connectivity to the graphql endpoint - set the `HEALTHCHECK_GRAPHQL_URL` environment variable to the exact URL of the graphql endpoint. The query executed will be `query { __typename }` and if the response is not `200 OK` - the healthcheck will fail. Remember that the endpoint is a full URL which you'd like to check, so it should include the protocol, host and path - for example `http://localhost:8080/v1/graphql` and it's NOT the same as value of `HOST_GRAPHQL` environment variable which should provide only the host, without path, ending with slash. + +#### Monitoring endpoint + +Example metrics produced by the proxy: + +``` +graphql_proxy_timed_query_bucket{cached="false",user_id="-",op_type="mutation",op_name="updateUserDetails",vmrange="1.000e-02...1.136e-02"} 6 +graphql_proxy_timed_query_count{op_name="",cached="false",user_id="-",op_type=""} 78 +graphql_proxy_timed_query_bucket{op_name="MyQuery",cached="false",user_id="-",op_type="query",vmrange="5.995e+00...6.813e+00"} 1 +graphql_proxy_timed_query_sum{op_name="MyQuery",cached="false",user_id="-",op_type="query"} 6 +graphql_proxy_timed_query_count{op_name="MyQuery",cached="false",user_id="-",op_type="query"} 1 +graphql_proxy_executed_query{user_id="-",op_type="mutation",op_name="updateKnownSpammer",cached="false"} 1486 +graphql_proxy_executed_query{user_id="-",op_type="query",op_name="checkIfAdminsNeedRefreshing",cached="false"} 13167 +graphql_proxy_executed_query{user_id="1337",op_type="query",op_name="checkIfKnownMedia",cached="false"} 429 +graphql_proxy_executed_query{user_id="-",op_type="query",op_name="checkIfSpamAIRequiresUpdate",cached="false"} 8891 +graphql_proxy_requests_failed 324 +graphql_proxy_requests_skipped 0 +graphql_proxy_requests_succesful 454823 +graphql_proxy_cache_hit{microservice="graphql_proxy",pod="hasura-w-proxy-internal-6b5f4b4bbb-9xwfc"} 7 +graphql_proxy_cache_hit{pod="hasura-w-proxy-internal-6b5f4b4bbb-9xwfc",microservice="graphql_proxy"} 1 +graphql_proxy_cache_miss{microservice="graphql_proxy",pod="hasura-w-proxy-internal-6b5f4b4bbb-9xwfc"} 23 +``` diff --git a/api.go b/api.go new file mode 100644 index 0000000..bb76d1d --- /dev/null +++ b/api.go @@ -0,0 +1,259 @@ +package main + +import ( + "fmt" + "os" + "sync" + "time" + + "github.com/goccy/go-json" + fiber "github.com/gofiber/fiber/v2" + "github.com/gofrs/flock" + libpack_cache "github.com/lukaszraczylo/graphql-monitoring-proxy/cache" + libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +var ( + bannedUsersIDs = make(map[string]string) + bannedUsersIDsMutex sync.RWMutex +) + +func enableApi() { + if !cfg.Server.EnableApi { + return + } + + apiserver := fiber.New(fiber.Config{ + DisableStartupMessage: true, + AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION), + }) + + api := apiserver.Group("/api") + api.Post("/user-ban", apiBanUser) + api.Post("/user-unban", apiUnbanUser) + api.Post("/cache-clear", apiClearCache) + api.Get("/cache-stats", apiCacheStats) + + go periodicallyReloadBannedUsers() + + if err := apiserver.Listen(fmt.Sprintf(":%d", cfg.Server.ApiPort)); err != nil { + cfg.Logger.Critical(&libpack_logger.LogMessage{ + Message: "Can't start the service", + Pairs: map[string]interface{}{"port": cfg.Server.ApiPort}, + }) + } +} + +func periodicallyReloadBannedUsers() { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for range ticker.C { + loadBannedUsers() + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Banned users reloaded", + Pairs: map[string]interface{}{"users": bannedUsersIDs}, + }) + } +} + +func checkIfUserIsBanned(c *fiber.Ctx, userID string) bool { + bannedUsersIDsMutex.RLock() + _, found := bannedUsersIDs[userID] + bannedUsersIDsMutex.RUnlock() + + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Checking if user is banned", + Pairs: map[string]interface{}{"user_id": userID, "banned": found}, + }) + + if found { + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "User is banned", + Pairs: map[string]interface{}{"user_id": userID}, + }) + c.Status(fiber.StatusForbidden).SendString("User is banned") + } + return found +} + +func apiClearCache(c *fiber.Ctx) error { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Clearing cache via API", + }) + libpack_cache.CacheClear() + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Cache cleared via API", + }) + return c.SendString("OK: cache cleared") +} + +func apiCacheStats(c *fiber.Ctx) error { + return c.JSON(libpack_cache.GetCacheStats()) +} + +type apiBanUserRequest struct { + UserID string `json:"user_id"` + Reason string `json:"reason"` +} + +func apiBanUser(c *fiber.Ctx) error { + var req apiBanUserRequest + if err := c.BodyParser(&req); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't parse the ban user request", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return c.Status(fiber.StatusBadRequest).SendString("Invalid request payload") + } + + if req.UserID == "" || req.Reason == "" { + return c.Status(fiber.StatusBadRequest).SendString("user_id and reason are required") + } + + bannedUsersIDsMutex.Lock() + bannedUsersIDs[req.UserID] = req.Reason + bannedUsersIDsMutex.Unlock() + + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Banned user", + Pairs: map[string]interface{}{"user_id": req.UserID, "reason": req.Reason}, + }) + + if err := storeBannedUsers(); err != nil { + return c.Status(fiber.StatusInternalServerError).SendString("Failed to store banned users") + } + + return c.SendString("OK: user banned") +} + +func apiUnbanUser(c *fiber.Ctx) error { + var req apiBanUserRequest + if err := c.BodyParser(&req); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't parse the unban user request", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return c.Status(fiber.StatusBadRequest).SendString("Invalid request payload") + } + + if req.UserID == "" { + return c.Status(fiber.StatusBadRequest).SendString("user_id is required") + } + + bannedUsersIDsMutex.Lock() + delete(bannedUsersIDs, req.UserID) + bannedUsersIDsMutex.Unlock() + + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Unbanned user", + Pairs: map[string]interface{}{"user_id": req.UserID}, + }) + + if err := storeBannedUsers(); err != nil { + return c.Status(fiber.StatusInternalServerError).SendString("Failed to store banned users") + } + + return c.SendString("OK: user unbanned") +} + +func storeBannedUsers() error { + fileLock := flock.New(fmt.Sprintf("%s.lock", cfg.Api.BannedUsersFile)) + if err := lockFile(fileLock); err != nil { + return err + } + defer fileLock.Unlock() + + bannedUsersIDsMutex.RLock() + data, err := json.Marshal(bannedUsersIDs) + bannedUsersIDsMutex.RUnlock() + + if err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't marshal banned users", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return err + } + + if err := os.WriteFile(cfg.Api.BannedUsersFile, data, 0644); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't write banned users to file", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return err + } + + return nil +} + +func loadBannedUsers() { + if _, err := os.Stat(cfg.Api.BannedUsersFile); os.IsNotExist(err) { + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Banned users file doesn't exist - creating it", + Pairs: map[string]interface{}{"file": cfg.Api.BannedUsersFile}, + }) + if err := os.WriteFile(cfg.Api.BannedUsersFile, []byte("{}"), 0644); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't create and write to the file", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return + } + } + + fileLock := flock.New(fmt.Sprintf("%s.lock", cfg.Api.BannedUsersFile)) + if err := lockFileRead(fileLock); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't lock the file [load]", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return + } + defer fileLock.Unlock() + + data, err := os.ReadFile(cfg.Api.BannedUsersFile) + if err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't read banned users from file", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return + } + + var newBannedUsers map[string]string + if err := json.Unmarshal(data, &newBannedUsers); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't unmarshal banned users", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return + } + + bannedUsersIDsMutex.Lock() + bannedUsersIDs = newBannedUsers + bannedUsersIDsMutex.Unlock() +} + +func lockFile(fileLock *flock.Flock) error { + if err := fileLock.Lock(); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't lock the file", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return err + } + return nil +} + +func lockFileRead(fileLock *flock.Flock) error { + if err := fileLock.RLock(); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't lock the file for reading", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return err + } + return nil +} diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..443bbf6 --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,134 @@ +package libpack_cache + +import ( + "sync/atomic" + "time" + + fiber "github.com/gofiber/fiber/v2" + "github.com/gookit/goutil/strutil" + libpack_cache_memory "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/memory" + libpack_cache_redis "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/redis" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +type CacheConfig struct { + Logger *libpack_logger.Logger + Client CacheClient + Redis struct { + URL string `json:"url"` + Password string `json:"password"` + DB int `json:"db"` + Enable bool `json:"enable"` + } + TTL int `json:"ttl"` +} + +type CacheStats struct { + CachedQueries int64 `json:"cached_queries"` + CacheHits int64 `json:"cache_hits"` + CacheMisses int64 `json:"cache_misses"` +} + +type CacheClient interface { + Set(key string, value []byte, ttl time.Duration) + Get(key string) ([]byte, bool) + Delete(key string) + Clear() + CountQueries() int64 +} + +var ( + cacheStats *CacheStats + config *CacheConfig +) + +func CalculateHash(c *fiber.Ctx) string { + return strutil.Md5(c.Body()) +} + +func EnableCache(cfg *CacheConfig) { + if cfg.Logger == nil { + cfg.Logger = libpack_logger.New() + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Initializing in-module logger", + }) + } + cacheStats = &CacheStats{} + if ShouldUseRedisCache(cfg) { + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Using Redis cache", + }) + cfg.Client = libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{ + RedisDB: cfg.Redis.DB, + RedisServer: cfg.Redis.URL, + RedisPassword: cfg.Redis.Password, + }) + } else { + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Using in-memory cache", + }) + cfg.Client = libpack_cache_memory.New(time.Duration(cfg.TTL) * time.Second) + } + config = cfg +} + +func CacheLookup(hash string) []byte { + obj, found := config.Client.Get(hash) + if found { + atomic.AddInt64(&cacheStats.CacheHits, 1) + return obj + } + atomic.AddInt64(&cacheStats.CacheMisses, 1) + return nil +} + +func CacheDelete(hash string) { + config.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Deleting data from cache", + Pairs: map[string]interface{}{"hash": hash}, + }) + atomic.AddInt64(&cacheStats.CachedQueries, -1) + config.Client.Delete(hash) +} + +func CacheStore(hash string, data []byte) { + config.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Storing data in cache", + Pairs: map[string]interface{}{"hash": hash}, + }) + atomic.AddInt64(&cacheStats.CachedQueries, 1) + config.Client.Set(hash, data, time.Duration(config.TTL)*time.Second) +} + +func CacheStoreWithTTL(hash string, data []byte, ttl time.Duration) { + config.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Storing data in cache with TTL", + Pairs: map[string]interface{}{"hash": hash, "ttl": ttl}, + }) + atomic.AddInt64(&cacheStats.CachedQueries, 1) + config.Client.Set(hash, data, ttl) +} + +func CacheGetQueries() int64 { + config.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Counting cache queries", + }) + return config.Client.CountQueries() +} + +func CacheClear() { + config.Client.Clear() + cacheStats = &CacheStats{} +} + +func GetCacheStats() *CacheStats { + config.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Getting cache stats", + }) + cacheStats.CachedQueries = CacheGetQueries() + return cacheStats +} + +func ShouldUseRedisCache(cfg *CacheConfig) bool { + return cfg.Redis.Enable +} diff --git a/cache/cache_bench_test.go b/cache/cache_bench_test.go new file mode 100644 index 0000000..271fdd5 --- /dev/null +++ b/cache/cache_bench_test.go @@ -0,0 +1,116 @@ +package libpack_cache + +import ( + "testing" + "time" + + "github.com/alicebob/miniredis/v2" + libpack_cache_memory "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/memory" + libpack_cache_redis "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/redis" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +const ( + Parallelism = 4 + RequestPerSec = 10000 +) + +func BenchmarkCacheLookupInMemory(b *testing.B) { + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: libpack_cache_memory.New(5 * time.Minute), + TTL: 5, + } + EnableCache(config) + + hash := "00000000000000000000000000000000001337" + data := []byte("it's fine.") + CacheStore(hash, data) + + b.SetParallelism(Parallelism) + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + CacheLookup(hash) + } + }) +} + +func BenchmarkCacheLookupRedis(b *testing.B) { + redis_server, _ := miniredis.Run() + mockedCache := libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{ + RedisServer: redis_server.Addr(), + RedisDB: 0, + }) + + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: mockedCache, + TTL: 5, + } + config.Redis.Enable = true + EnableCache(config) + + hash := "00000000000000000000000000000000001337" + data := []byte("it's fine.") + CacheStore(hash, data) + + b.SetParallelism(Parallelism) + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + CacheLookup(hash) + } + }) +} + +func BenchmarkCacheStoreInMemory(b *testing.B) { + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: libpack_cache_memory.New(5 * time.Minute), + TTL: 5, + } + EnableCache(config) + + hash := "00000000000000000000000000000000001337" + data := []byte("it's fine.") + + b.SetParallelism(Parallelism) + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + CacheStore(hash, data) + } + }) +} + +func BenchmarkCacheStoreRedis(b *testing.B) { + redis_server, _ := miniredis.Run() + mockedCache := libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{ + RedisServer: redis_server.Addr(), + RedisDB: 0, + }) + + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: mockedCache, + TTL: 5, + } + config.Redis.Enable = true + EnableCache(config) + + hash := "00000000000000000000000000000000001337" + data := []byte("it's fine.") + + b.SetParallelism(Parallelism) + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + CacheStore(hash, data) + } + }) +} diff --git a/cache/cache_suite_test.go b/cache/cache_suite_test.go new file mode 100644 index 0000000..dda51d6 --- /dev/null +++ b/cache/cache_suite_test.go @@ -0,0 +1,34 @@ +package libpack_cache + +import ( + "testing" + + "github.com/alicebob/miniredis/v2" + assertions "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type Tests struct { + suite.Suite +} + +var ( + assert *assertions.Assertions + redisMockServer, _ = miniredis.Run() +) + +func (suite *Tests) BeforeTest(suiteName, testName string) { +} + +func (suite *Tests) SetupTest() { + cacheStats = &CacheStats{} + assert = assertions.New(suite.T()) +} + +// TearDownTest is run after each test to clean up +func (suite *Tests) TearDownTest() { +} + +func TestSuite(t *testing.T) { + suite.Run(t, new(Tests)) +} diff --git a/cache/cache_test.go b/cache/cache_test.go new file mode 100644 index 0000000..b6851d2 --- /dev/null +++ b/cache/cache_test.go @@ -0,0 +1,215 @@ +package libpack_cache + +import ( + "fmt" + "sync" + "time" + + "github.com/alicebob/miniredis/v2" + libpack_cache_memory "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/memory" + libpack_cache_redis "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/redis" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +func (suite *Tests) Test_cacheLookupInmemory() { + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: libpack_cache_memory.New(5 * time.Minute), + TTL: 5, + } + + type args struct { + hash string + } + tests := []struct { + name string + args args + want []byte + addCache struct { + data []byte + } + }{ + { + name: "test_non_existent", + args: args{ + hash: "00000000000000000000000000000000000000", + }, + want: nil, + }, + { + name: "test_existent", + args: args{ + hash: "00000000000000000000000000000000001337", + }, + want: []byte("it's fine."), + addCache: struct { + data []byte + }{ + data: []byte("it's fine."), + }, + }, + } + for _, tt := range tests { + suite.Run(tt.name, func() { + if tt.addCache.data != nil { + CacheStore(tt.args.hash, tt.addCache.data) + } + got := CacheLookup(tt.args.hash) + assert.Equal(tt.want, got, "Unexpected cache lookup result") + }) + } +} + +func (suite *Tests) Test_cacheLookupRedis() { + // redis_server := envutil.Getenv("REDIS_SERVER", "localhost:6379") + // config.Client = libpack_cache_redis.NewClient(&libpack_cache_redis.RedisClientConfig{ + // RedisServer: redis_server, + // RedisPassword: "", + // RedisDB: 0, + // }) + + mockedCache := libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{ + RedisServer: redisMockServer.Addr(), + RedisDB: 0, + }) + + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: mockedCache, + TTL: 5, + } + + type args struct { + hash string + } + tests := []struct { + name string + args args + want []byte + addCache struct { + data []byte + } + }{ + { + name: "test_non_existent", + args: args{ + hash: "00000000000000000000000000000000000000", + }, + want: nil, + }, + { + name: "test_existent", + args: args{ + hash: "00000000000000000000000000000000001337", + }, + want: []byte("it's fine."), + addCache: struct { + data []byte + }{ + data: []byte("it's fine."), + }, + }, + } + for _, tt := range tests { + suite.Run(tt.name, func() { + if tt.addCache.data != nil { + CacheStore(tt.args.hash, tt.addCache.data) + } + got := CacheLookup(tt.args.hash) + assert.Equal(tt.want, got, "Unexpected cache lookup result") + }) + } +} + +func (suite *Tests) Test_cacheConcurrency() { + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: libpack_cache_memory.New(5 * time.Second), + TTL: 5, + } + + const numGoroutines = 10 + const numOperations = 1000 + + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(id int) { + defer wg.Done() + for j := 0; j < numOperations; j++ { + key := fmt.Sprintf("key-%d-%d", id, j) + value := []byte(fmt.Sprintf("value-%d-%d", id, j)) + CacheStore(key, value) + retrieved := CacheLookup(key) + assert.Equal(string(value), string(retrieved), "Concurrent cache operation failed") + } + }(i) + } + + wg.Wait() +} + +// func (suite *Tests) Test_cacheEviction() { +// config = &CacheConfig{ +// Logger: libpack_logger.New(), +// Client: libpack_cache_memory.New(3 * time.Second), // 3 seconds TTL +// TTL: 3, +// } + +// // Fill the cache +// for i := 0; i < 20; i++ { +// key := fmt.Sprintf("key-%d", i) +// value := []byte(fmt.Sprintf("value-%d", i)) +// CacheStore(key, value) +// time.Sleep(100 * time.Millisecond) // Ensure different creation times +// } + +// // Wait for the TTL to expire for the first half of the items +// time.Sleep(3100 * time.Millisecond) + +// // Check that the oldest items have been evicted +// for i := 0; i < 10; i++ { +// key := fmt.Sprintf("key-%d", i) +// retrieved := CacheLookup(key) +// assert.Nil(retrieved, fmt.Sprintf("Old item %s should have been evicted", key)) +// } + +// // Check that the newer items are still in the cache +// for i := 10; i < 20; i++ { +// key := fmt.Sprintf("key-%d", i) +// expected := []byte(fmt.Sprintf("value-%d", i)) +// retrieved := CacheLookup(key) +// assert.Equal(expected, retrieved, fmt.Sprintf("Recent item %s should be in cache", key)) +// } +// } + +func (suite *Tests) Test_cacheRedisFailure() { + mr, err := miniredis.Run() + if err != nil { + suite.T().Fatal(err) + } + defer mr.Close() + + config = &CacheConfig{ + Logger: libpack_logger.New(), + Client: libpack_cache_redis.New(&libpack_cache_redis.RedisClientConfig{ + RedisServer: mr.Addr(), + RedisDB: 0, + }), + TTL: 5, + } + + // Test normal operation + CacheStore("test-key", []byte("test-value")) + retrieved := CacheLookup("test-key") + assert.Equal([]byte("test-value"), retrieved) + + // Simulate Redis failure + mr.Close() + + // Operations should not panic, but should return errors or nil values + CacheStore("another-key", []byte("another-value")) + retrieved = CacheLookup("another-key") + assert.Nil(retrieved, "Lookup should return nil when Redis is down") +} diff --git a/cache/memory/memory.go b/cache/memory/memory.go new file mode 100644 index 0000000..fafaa68 --- /dev/null +++ b/cache/memory/memory.go @@ -0,0 +1,157 @@ +package libpack_cache_memory + +import ( + "bytes" + "compress/gzip" + "io" + "log" + "sync" + "time" +) + +type CacheEntry struct { + ExpiresAt time.Time + Value []byte +} + +type Cache struct { + compressPool sync.Pool + decompressPool sync.Pool + entries sync.Map + globalTTL time.Duration + sync.RWMutex +} + +func New(globalTTL time.Duration) *Cache { + cache := &Cache{ + globalTTL: globalTTL, + compressPool: sync.Pool{ + New: func() interface{} { + return gzip.NewWriter(nil) + }, + }, + decompressPool: sync.Pool{ + New: func() interface{} { + r, _ := gzip.NewReader(bytes.NewReader([]byte{})) + return r + }, + }, + } + + go cache.cleanupRoutine(globalTTL) + return cache +} + +func (c *Cache) cleanupRoutine(globalTTL time.Duration) { + ticker := time.NewTicker(globalTTL / 2) + defer ticker.Stop() + + for range ticker.C { + c.CleanExpiredEntries() + } +} + +func (c *Cache) Set(key string, value []byte, ttl time.Duration) { + expiresAt := time.Now().Add(ttl) + + compressedValue, err := c.compress(value) + if err != nil { + log.Printf("Error compressing value for key %s: %v", key, err) + return + } + + entry := CacheEntry{ + Value: compressedValue, + ExpiresAt: expiresAt, + } + c.entries.Store(key, entry) +} + +func (c *Cache) Get(key string) ([]byte, bool) { + entry, ok := c.entries.Load(key) + if !ok { + return nil, false + } + + cacheEntry := entry.(CacheEntry) + if cacheEntry.ExpiresAt.Before(time.Now()) { + c.entries.Delete(key) + return nil, false + } + + value, err := c.decompress(cacheEntry.Value) + if err != nil { + log.Printf("Error decompressing value for key %s: %v", key, err) + return nil, false + } + return value, true +} + +func (c *Cache) Delete(key string) { + c.entries.Delete(key) +} + +func (c *Cache) Clear() { + c.entries.Range(func(key, value interface{}) bool { + c.entries.Delete(key) + return true + }) +} + +func (c *Cache) CountQueries() int64 { + var count int + c.entries.Range(func(_, _ interface{}) bool { + count++ + return true + }) + return int64(count) +} + +func (c *Cache) compress(data []byte) ([]byte, error) { + var buf bytes.Buffer + w := c.compressPool.Get().(*gzip.Writer) + defer func() { + w.Close() + c.compressPool.Put(w) + }() + w.Reset(&buf) + if _, err := w.Write(data); err != nil { + return nil, err + } + if err := w.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (c *Cache) decompress(data []byte) ([]byte, error) { + r, ok := c.decompressPool.Get().(*gzip.Reader) + if !ok || r == nil { + var err error + r, err = gzip.NewReader(bytes.NewReader(data)) + if err != nil { + return nil, err + } + } else { + if err := r.Reset(bytes.NewReader(data)); err != nil { + return nil, err + } + } + defer func() { + r.Close() + c.decompressPool.Put(r) + }() + + return io.ReadAll(r) +} + +func (c *Cache) CleanExpiredEntries() { + now := time.Now() + c.entries.Range(func(key, value interface{}) bool { + entry := value.(CacheEntry) + if entry.ExpiresAt.Before(now) { + c.entries.Delete(key) + } + return true + }) +} diff --git a/cache/memory/memory_bench_test.go b/cache/memory/memory_bench_test.go new file mode 100644 index 0000000..054cbfb --- /dev/null +++ b/cache/memory/memory_bench_test.go @@ -0,0 +1,82 @@ +package libpack_cache_memory + +import ( + "fmt" + "testing" + "time" +) + +// Assume that New function initializes the cache and it is defined somewhere in the libpack_cache package. + +func BenchmarkMemCacheSet(b *testing.B) { + cache := New(30 * time.Second) // Initializing the cache with a TTL of 30 seconds + key := "benchmark-key" + value := []byte("benchmark-value") + + b.ResetTimer() // Reset the timer to exclude the setup time from the benchmark + + for i := 0; i < b.N; i++ { + cache.Set(key, value, 5*time.Second) + } +} + +func BenchmarkMemCacheGet(b *testing.B) { + cache := New(30 * time.Second) // Initializing the cache + key := "benchmark-key" + value := []byte("benchmark-value") + cache.Set(key, value, 5*time.Second) // Pre-set a value to retrieve + + b.ResetTimer() // Start timing + + for i := 0; i < b.N; i++ { + _, _ = cache.Get(key) + } +} + +func BenchmarkMemCacheExpire(b *testing.B) { + key := "benchmark-expire-key" + value := []byte("benchmark-value") + ttl := 5 * time.Millisecond // Setting a short TTL for quick expiration + + for i := 0; i < b.N; i++ { + cache := New(30 * time.Second) + cache.Set(key, value, ttl) + time.Sleep(ttl) // Wait for the key to expire + _, _ = cache.Get(key) + } +} + +func BenchmarkMemCacheStats(b *testing.B) { + cache := New(30 * time.Second) // Initializing the cache + key := "benchmark-key" + value := []byte("benchmark-value") + cache.Set(key, value, 5*time.Second) // Pre-set a value to retrieve + cache.Get(key) +} + +func BenchmarkCacheSet(b *testing.B) { + cache := New(5 * time.Second) + b.ResetTimer() + for i := 0; i < b.N; i++ { + cache.Set(fmt.Sprintf("key-%d", i), []byte("value"), 5*time.Second) + } +} + +func BenchmarkCacheGet(b *testing.B) { + cache := New(5 * time.Second) + cache.Set("test-key", []byte("test-value"), 5*time.Second) + b.ResetTimer() + for i := 0; i < b.N; i++ { + cache.Get("test-key") + } +} + +func BenchmarkCacheDelete(b *testing.B) { + cache := New(5 * time.Second) + b.ResetTimer() + for i := 0; i < b.N; i++ { + key := fmt.Sprintf("key-%d", i) + cache.Set(key, []byte("value"), 5*time.Second) + cache.Delete(key) + } +} diff --git a/cache/memory/memory_test.go b/cache/memory/memory_test.go new file mode 100644 index 0000000..948091c --- /dev/null +++ b/cache/memory/memory_test.go @@ -0,0 +1,168 @@ +package libpack_cache_memory + +import ( + "fmt" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/suite" +) + +type MemoryTestSuite struct { + suite.Suite +} + +func (suite *MemoryTestSuite) SetupTest() { +} + +func TestCachingTestSuite(t *testing.T) { + suite.Run(t, new(MemoryTestSuite)) +} + +func (suite *MemoryTestSuite) Test_New() { + suite.T().Run("should return a new cache", func(t *testing.T) { + cache := New(2 * time.Second) + suite.NotNil(cache) + }) +} + +func (suite *MemoryTestSuite) Test_CacheUse() { + cache := New(30 * time.Second) + tests := []struct { + name string + cache_value string + }{ + { + name: "test1", + cache_value: "test1-123", + }, + { + name: "test2", + cache_value: "test2-123", + }, + } + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cache.Set(tt.name, []byte(tt.name), 5*time.Second) + c, ok := cache.Get(tt.name) + suite.Equal(true, ok) + suite.Equal(tt.name, string(c)) + }) + } +} + +func (suite *MemoryTestSuite) Test_CacheDelete() { + cache := New(30 * time.Second) + tests := []struct { + name string + cache_value string + }{ + { + name: "test1", + cache_value: "test1-123", + }, + { + name: "test2", + cache_value: "test2-123", + }, + } + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cache.Set(tt.name, []byte(tt.name), 5*time.Second) + c, ok := cache.Get(tt.name) + suite.Equal(true, ok) + suite.Equal(tt.name, string(c)) + cache.Delete(tt.name) + c, ok = cache.Get(tt.name) + suite.Equal(false, ok) + suite.Equal("", string(c)) + }) + } +} + +func (suite *MemoryTestSuite) Test_CacheExpire() { + cache := New(30 * time.Second) + tests := []struct { + name string + cache_value string + ttl time.Duration + }{ + { + name: "test1", + cache_value: "test1-123", + ttl: 2 * time.Second, + }, + { + name: "test2", + cache_value: "test2-123", + ttl: 5 * time.Second, + }, + } + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cache.Set(tt.name, []byte(tt.name), tt.ttl) + c, ok := cache.Get(tt.name) + suite.Equal(true, ok) + suite.Equal(tt.name, string(c)) + time.Sleep(tt.ttl) + c, ok = cache.Get(tt.name) + suite.Equal(false, ok) + suite.Equal("", string(c)) + }) + } +} + +func (suite *MemoryTestSuite) Test_ConcurrentReadWrite() { + cache := New(5 * time.Second) + const numGoroutines = 100 + const numOperations = 1000 + + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(id int) { + defer wg.Done() + for j := 0; j < numOperations; j++ { + key := fmt.Sprintf("key-%d-%d", id, j) + value := []byte(fmt.Sprintf("value-%d-%d", id, j)) + + if j%2 == 0 { + cache.Set(key, value, 5*time.Second) + } else { + _, _ = cache.Get(key) + } + } + }(i) + } + + wg.Wait() +} + +func (suite *MemoryTestSuite) Test_LargeItems() { + cache := New(5 * time.Second) + largeValue := make([]byte, 10*1024*1024) // 10MB + cache.Set("large-key", largeValue, 5*time.Second) + + retrieved, found := cache.Get("large-key") + suite.Assert().True(found) + suite.Assert().Equal(largeValue, retrieved) +} + +func (suite *MemoryTestSuite) Test_ZeroTTL() { + cache := New(5 * time.Second) + cache.Set("zero-ttl", []byte("value"), 0) + + _, found := cache.Get("zero-ttl") + suite.Assert().False(found, "Item with zero TTL should not be stored") +} + +func (suite *MemoryTestSuite) Test_LongTTL() { + cache := New(5 * time.Second) + cache.Set("long-ttl", []byte("value"), 24*365*time.Hour) // 1 year + + retrieved, found := cache.Get("long-ttl") + suite.Assert().True(found) + suite.Assert().Equal([]byte("value"), retrieved) +} diff --git a/cache/redis/redis.go b/cache/redis/redis.go new file mode 100644 index 0000000..01edcbb --- /dev/null +++ b/cache/redis/redis.go @@ -0,0 +1,96 @@ +package libpack_cache_redis + +import ( + "context" + "strings" + "time" + + "sync" + + redis "github.com/redis/go-redis/v9" +) + +type RedisConfig struct { + ctx context.Context + client *redis.Client + builderPool *sync.Pool + prefix string +} + +func (c *RedisConfig) prependKeyName(key string) string { + builder := c.builderPool.Get().(*strings.Builder) + defer c.builderPool.Put(builder) + builder.Reset() + builder.WriteString(c.prefix) + builder.WriteString(key) + return builder.String() +} + +type RedisClientConfig struct { + RedisServer string + RedisPassword string + Prefix string + RedisDB int +} + +func New(redisClientConfig *RedisClientConfig) *RedisConfig { + c := &RedisConfig{ + client: redis.NewClient(&redis.Options{ + Addr: redisClientConfig.RedisServer, + Password: redisClientConfig.RedisPassword, + DB: redisClientConfig.RedisDB, + }), + ctx: context.Background(), + prefix: redisClientConfig.Prefix, + builderPool: &sync.Pool{ + New: func() interface{} { + return &strings.Builder{} + }, + }, + } + + _, err := c.client.Ping(c.ctx).Result() + if err != nil { + panic(err) + } + return c +} + +func (c *RedisConfig) Set(key string, value []byte, ttl time.Duration) { + c.client.Set(c.ctx, c.prependKeyName(key), value, ttl) +} + +func (c *RedisConfig) Get(key string) ([]byte, bool) { + val, err := c.client.Get(c.ctx, c.prependKeyName(key)).Result() + if err == redis.Nil { + return nil, false + } + if err != nil { + return nil, false + } + return []byte(val), true +} + +func (c *RedisConfig) Delete(key string) { + c.client.Del(c.ctx, c.prependKeyName(key)) +} + +func (c *RedisConfig) Clear() { + c.client.FlushDB(c.ctx) +} + +func (c *RedisConfig) CountQueries() int64 { + keys, err := c.client.Keys(c.ctx, c.prependKeyName("*")).Result() + if err != nil { + return 0 + } + return int64(len(keys)) +} + +func (c *RedisConfig) CountQueriesWithPattern(pattern string) int { + keys, err := c.client.Keys(c.ctx, c.prependKeyName(pattern)).Result() + if err != nil { + return 0 + } + return len(keys) +} diff --git a/cache/redis/redis_test.go b/cache/redis/redis_test.go new file mode 100644 index 0000000..c9ed41b --- /dev/null +++ b/cache/redis/redis_test.go @@ -0,0 +1,130 @@ +package libpack_cache_redis + +import ( + "testing" + "time" + + "github.com/alicebob/miniredis/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type RedisConfigSuite struct { + suite.Suite + redisConfig *RedisConfig + redis_server *miniredis.Miniredis +} + +func (suite *RedisConfigSuite) SetupTest() { + suite.redis_server, _ = miniredis.Run() + suite.redisConfig = New(&RedisClientConfig{ + RedisServer: suite.redis_server.Addr(), + RedisPassword: "", + RedisDB: 0, + }) + suite.redisConfig.Delete("testkey") +} + +func TestRedisConfigSuite(t *testing.T) { + suite.Run(t, new(RedisConfigSuite)) +} + +func (suite *RedisConfigSuite) TestSet() { + key := "testkeyset" + value := []byte("testvalue") + suite.redisConfig.Delete(key) // Ensure the key is deleted before the test + + // Test writing a new key-value pair + suite.redisConfig.Set(key, value, 0) + storedValue, found := suite.redisConfig.Get(key) + assert.True(suite.T(), found) + assert.Equal(suite.T(), value, storedValue) + + // Test overwriting an existing key-value pair + newValue := []byte("newvalue") + suite.redisConfig.Set(key, newValue, 0) + storedValue, found = suite.redisConfig.Get(key) + assert.True(suite.T(), found) + assert.Equal(suite.T(), newValue, storedValue) + + suite.redisConfig.Delete(key) // Clean up after the test +} + +func (suite *RedisConfigSuite) TestSetWithExpiry() { + key := "testkey_with_expiry" + value := []byte("testvaluewithexpiry") + expiry := 2 * time.Second + suite.redisConfig.Delete(key) // Ensure the key is deleted before the test + + // Test writing a new key-value pair + suite.redisConfig.Set(key, value, expiry) + storedValue, found := suite.redisConfig.Get(key) + assert.True(suite.T(), found) + assert.Equal(suite.T(), value, storedValue) + _, found = suite.redisConfig.Get(key) + assert.True(suite.T(), found, "Key should exist") + + // Test that key expires after the specified time + suite.redis_server.FastForward(3 * time.Second) + _, found = suite.redisConfig.Get(key) + assert.False(suite.T(), found, "Key should have expired after 2 seconds") + + suite.redisConfig.Delete(key) // Clean up after the test +} + +func (suite *RedisConfigSuite) TestGet() { + key := "testkeyget" + value := []byte("testvalue") + suite.redisConfig.Set(key, value, 0) // Set the key-value pair + storedValue, found := suite.redisConfig.Get(key) + assert.True(suite.T(), found) + assert.Equal(suite.T(), value, storedValue) +} + +func (suite *RedisConfigSuite) TestDeleteKey() { + key := "testkeydelete" + value := []byte("testvalue") + suite.redisConfig.Set(key, value, 0) // Set the key-value pair + suite.redisConfig.Delete(key) + _, found := suite.redisConfig.Get(key) + assert.False(suite.T(), found) +} + +func (suite *RedisConfigSuite) TestCheckIfKeyExists() { + ttl := time.Duration(10) * time.Second + key := "testkeyifexists" + value := []byte("testvalue") + suite.redisConfig.Set(key, value, ttl) // Set the key-value pair + _, found := suite.redisConfig.Get(key) + assert.True(suite.T(), found) + + suite.redisConfig.Delete(key) + _, found = suite.redisConfig.Get(key) + assert.False(suite.T(), found) +} + +func (suite *RedisConfigSuite) TestGetKeys() { + ttl := time.Duration(10) * time.Second + suite.redisConfig.Set("testkey1", []byte("testvalue1"), ttl) + suite.redisConfig.Set("testkey2", []byte("testvalue2"), ttl) + suite.redisConfig.Set("otherkey", []byte("othervalue"), ttl) + + keys, _ := suite.redisConfig.client.Keys(suite.redisConfig.ctx, "testkey*").Result() + expectedKeys := []string{"testkey1", "testkey2"} + assert.ElementsMatch(suite.T(), expectedKeys, keys) + + suite.redisConfig.client.Del(suite.redisConfig.ctx, "testkey1", "testkey2", "otherkey") +} + +func (suite *RedisConfigSuite) TestGetKeysCount() { + ttl := time.Duration(10) * time.Second + suite.redisConfig.Set("testkey1", []byte("testvalue1"), ttl) + suite.redisConfig.Set("testkey2", []byte("testvalue2"), ttl) + suite.redisConfig.Set("otherkey", []byte("othervalue"), ttl) + + assert.Equal(suite.T(), 2, suite.redisConfig.CountQueriesWithPattern("testkey*")) + assert.Equal(suite.T(), 1, suite.redisConfig.CountQueriesWithPattern("otherkey*")) + assert.Equal(suite.T(), int64(3), suite.redisConfig.CountQueries()) + + suite.redisConfig.client.Del(suite.redisConfig.ctx, "testkey1", "testkey2", "otherkey") +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..82998d0 --- /dev/null +++ b/config/config.go @@ -0,0 +1,6 @@ +package libpack_config + +var ( + PKG_NAME string = "not-specified" + PKG_VERSION string = "0.0.0-dev" +) diff --git a/details.go b/details.go new file mode 100644 index 0000000..dc35a1a --- /dev/null +++ b/details.go @@ -0,0 +1,65 @@ +package main + +import ( + "encoding/base64" + "fmt" + "strings" + + "github.com/goccy/go-json" + "github.com/lukaszraczylo/ask" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" + libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring" +) + +const defaultValue = "-" + +var emptyMetrics = map[string]string{} + +func extractClaimsFromJWTHeader(authorization string) (usr, role string) { + usr, role = defaultValue, defaultValue + + tokenParts := strings.SplitN(authorization, ".", 3) + if len(tokenParts) != 3 { + handleError("Can't split the token", map[string]interface{}{"token": authorization}) + return + } + + claim, err := base64.RawURLEncoding.DecodeString(tokenParts[1]) + if err != nil { + handleError("Can't decode the token", map[string]interface{}{"token": authorization}) + return + } + + var claimMap map[string]interface{} + if err = json.Unmarshal(claim, &claimMap); err != nil { + handleError("Can't unmarshal the claim", map[string]interface{}{"token": authorization}) + return + } + + usr = extractClaim(claimMap, cfg.Client.JWTUserClaimPath, "user id") + role = extractClaim(claimMap, cfg.Client.JWTRoleClaimPath, "role") + + return +} + +func extractClaim(claimMap map[string]interface{}, claimPath, name string) string { + if claimPath == "" { + return defaultValue + } + + value, ok := ask.For(claimMap, claimPath).String(defaultValue) + if !ok { + handleError(fmt.Sprintf("Can't find the %s", name), map[string]interface{}{"claim_map": claimMap, "path": claimPath}) + return defaultValue + } + + return value +} + +func handleError(msg string, details map[string]interface{}) { + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, emptyMetrics) + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: msg, + Pairs: details, + }) +} diff --git a/details_test.go b/details_test.go new file mode 100644 index 0000000..f5e5a70 --- /dev/null +++ b/details_test.go @@ -0,0 +1,81 @@ +package main + +func (suite *Tests) Test_extractClaimsFromJWTHeader() { + jwt_token_for_tests := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiSGFzdXJhIjp7IngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsiZ3Vlc3QiLCJ1c2VyIiwiZ3JvdXBhZG1pbiIsInBheWFkbWluIl0sIngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6Imd1ZXN0IiwieC1oYXN1cmEtdXNlci1pZCI6IjE2NyIsIngtaGFzdXJhLXVzZXItdXVpZCI6ImRkM2U2ZTM1LTA0MDktNDNiMC1iZmYxLWNlZjNjNmVkNWYxMCJ9LCJpc3MiOiJBdXRoU2VydmljZSIsImV4cCI6MTY5NjgwMTcyNiwibmJmIjoxNjk2NTg1NzI2LCJpYXQiOjE2OTY1ODU3MjZ9.dsJ5JKzG5tXOlqeZ_Gfe2XC-vyrcwtYwOGfhvt8q9UY" + + type args struct { + authorization string + } + + tests := []struct { + name string + args args + wantUsr string + wantRole string + jwt_token_path string + jwt_role_path string + }{ + { + name: "test_empty", + wantUsr: "-", + wantRole: "-", + }, + { + name: "test_invalid_path", + args: args{ + authorization: jwt_token_for_tests, + }, + wantUsr: "-", + wantRole: "-", + jwt_token_path: "invalid", + }, + { + name: "test_invalid_role_path", + args: args{ + authorization: jwt_token_for_tests, + }, + wantUsr: "-", + wantRole: "-", + jwt_role_path: "invalid", + }, + { + name: "test_valid", + args: args{ + authorization: jwt_token_for_tests, + }, + wantUsr: "167", + wantRole: "guest", + jwt_token_path: "Hasura.x-hasura-user-id", + jwt_role_path: "Hasura.x-hasura-default-role", + }, + { + name: "test_invalid_token", + args: args{ + authorization: "invalid", + }, + wantUsr: "-", + wantRole: "-", + }, + { + name: "test_invalid_three_part_token", + args: args{ + authorization: "invalid.threepart.token", + }, + wantUsr: "-", + wantRole: "-", + }, + } + for _, tt := range tests { + suite.Run(tt.name, func() { + if len(tt.jwt_token_path) > 0 { + cfg.Client.JWTUserClaimPath = tt.jwt_token_path + } + if len(tt.jwt_role_path) > 0 { + cfg.Client.JWTRoleClaimPath = tt.jwt_role_path + } + gotUsr, gotRole := extractClaimsFromJWTHeader(tt.args.authorization) + assert.Equal(tt.wantUsr, gotUsr, "Unexpected user ID") + assert.Equal(tt.wantRole, gotRole, "Unexpected role") + }) + } +} diff --git a/events.go b/events.go new file mode 100644 index 0000000..155dd41 --- /dev/null +++ b/events.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "fmt" + "time" + + "github.com/jackc/pgx/v5/pgxpool" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +const ( + initialDelay = 60 * time.Second + cleanupInterval = 1 * time.Hour +) + +var delQueries = [...]string{ + "DELETE FROM hdb_catalog.event_invocation_logs WHERE created_at < NOW() - interval '%d days';", + "DELETE FROM hdb_catalog.event_log WHERE created_at < NOW() - interval '%d days';", + "DELETE FROM hdb_catalog.hdb_action_log WHERE created_at < NOW() - INTERVAL '%d days';", + "DELETE FROM hdb_catalog.hdb_cron_event_invocation_logs WHERE created_at < NOW() - INTERVAL '%d days';", + "DELETE FROM hdb_catalog.hdb_scheduled_event_invocation_logs WHERE created_at < NOW() - INTERVAL '%d days';", +} + +func enableHasuraEventCleaner() { + if !cfg.HasuraEventCleaner.Enable { + return + } + + if cfg.HasuraEventCleaner.EventMetadataDb == "" { + cfg.Logger.Warning(&libpack_logger.LogMessage{ + Message: "Event metadata db URL not specified, event cleaner not active", + }) + return + } + + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Event cleaner enabled", + Pairs: map[string]interface{}{"interval_in_days": cfg.HasuraEventCleaner.ClearOlderThan}, + }) + + go func() { + pool, err := pgxpool.New(context.Background(), cfg.HasuraEventCleaner.EventMetadataDb) + if err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Failed to create connection pool", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + return + } + defer pool.Close() + + time.Sleep(initialDelay) + + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Initial cleanup of old events", + }) + cleanEvents(pool) + + ticker := time.NewTicker(cleanupInterval) + defer ticker.Stop() + + for range ticker.C { + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Cleaning up old events", + }) + cleanEvents(pool) + } + }() +} + +func cleanEvents(pool *pgxpool.Pool) { + ctx := context.Background() + for _, query := range delQueries { + _, err := pool.Exec(ctx, fmt.Sprintf(query, cfg.HasuraEventCleaner.ClearOlderThan)) + if err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Failed to execute query", + Pairs: map[string]interface{}{"query": query, "error": err.Error()}, + }) + } else { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Successfully executed query", + Pairs: map[string]interface{}{"query": query}, + }) + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..11c9d67 --- /dev/null +++ b/go.mod @@ -0,0 +1,54 @@ +module github.com/lukaszraczylo/graphql-monitoring-proxy + +go 1.22.4 + +require ( + github.com/VictoriaMetrics/metrics v1.34.0 + github.com/alicebob/miniredis/v2 v2.33.0 + github.com/avast/retry-go/v4 v4.6.0 + github.com/goccy/go-json v0.10.3 + github.com/gofiber/fiber/v2 v2.52.4 + github.com/gofrs/flock v0.9.0 + github.com/google/uuid v1.6.0 + github.com/gookit/goutil v0.6.15 + github.com/graphql-go/graphql v0.8.1 + github.com/jackc/pgx/v5 v5.6.0 + github.com/lukaszraczylo/ask v0.0.0-20230927103145-2ff1123b4415 + github.com/lukaszraczylo/go-ratecounter v0.1.12 + github.com/lukaszraczylo/go-simple-graphql v1.2.17 + github.com/redis/go-redis/v9 v9.5.3 + github.com/stretchr/testify v1.9.0 + github.com/valyala/fasthttp v1.55.0 +) + +require ( + github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gookit/color v1.5.4 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fastrand v1.1.0 // indirect + github.com/valyala/histogram v1.2.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0e7902c --- /dev/null +++ b/go.sum @@ -0,0 +1,120 @@ +github.com/VictoriaMetrics/metrics v1.34.0 h1:0i8k/gdOJdSoZB4Z9pikVnVQXfhcIvnG7M7h2WaQW2w= +github.com/VictoriaMetrics/metrics v1.34.0/go.mod h1:r7hveu6xMdUACXvB8TYdAj8WEsKzWB0EkpJN+RDtOf8= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= +github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= +github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= +github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/gofrs/flock v0.9.0 h1:QqEH0zKHPdEyY4YbJLleD9Il4ft7h6hn3gECO6Ss4rQ= +github.com/gofrs/flock v0.9.0/go.mod h1:O+L78Axre/Bc0Ya3RlNiGP+Rt0tFHWjtHTQ+B2uPZw8= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo= +github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY= +github.com/graphql-go/graphql v0.8.1 h1:p7/Ou/WpmulocJeEx7wjQy611rtXGQaAcXGqanuMMgc= +github.com/graphql-go/graphql v0.8.1/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lukaszraczylo/ask v0.0.0-20230927103145-2ff1123b4415 h1:lvI8Wlbg4PxkRcg2f10wgoaRpfN19v+YdRek3+dLtlM= +github.com/lukaszraczylo/ask v0.0.0-20230927103145-2ff1123b4415/go.mod h1:M+UVdyqZs++xtEPrascaVmZdOMhCnxjZ2SgH+xHpR0c= +github.com/lukaszraczylo/go-ratecounter v0.1.12 h1:VO6hHYGw/Jy9JUizXf/bS0AI2QX1ueWWAWckMFVJ/w4= +github.com/lukaszraczylo/go-ratecounter v0.1.12/go.mod h1:TqXEOCtFJStk1i0tkipprv1kiDHGon1MVUisjSTBSKM= +github.com/lukaszraczylo/go-simple-graphql v1.2.17 h1:XxUUgxcCIZSVLzI4UfhBDXoFoMlygcXHfAJwXxawr1s= +github.com/lukaszraczylo/go-simple-graphql v1.2.17/go.mod h1:pSKmm9OLGoS9pjmIvhBB/fo0+LganRrL29CN3fdkRPw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= +github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= +github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= +github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= +github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= +github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= +github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/graphql.go b/graphql.go new file mode 100644 index 0000000..2072427 --- /dev/null +++ b/graphql.go @@ -0,0 +1,212 @@ +package main + +import ( + "strconv" + "strings" + "sync" + "unsafe" + + "github.com/goccy/go-json" + fiber "github.com/gofiber/fiber/v2" + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/parser" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" + libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring" +) + +var ( + introspectionQueries = map[string]struct{}{ + "__schema": {}, "__type": {}, "__typename": {}, "__directive": {}, + "__directivelocation": {}, "__field": {}, "__inputvalue": {}, + "__enumvalue": {}, "__typekind": {}, "__fieldtype": {}, + "__inputobjecttype": {}, "__enumtype": {}, "__uniontype": {}, + "__scalars": {}, "__objects": {}, "__interfaces": {}, + "__unions": {}, "__enums": {}, "__inputobjects": {}, "__directives": {}, + } + introspectionAllowedQueries = make(map[string]struct{}) + allowedUrls = make(map[string]struct{}) + mu sync.RWMutex +) + +func prepareQueriesAndExemptions() { + mu.Lock() + defer mu.Unlock() + for _, q := range cfg.Security.IntrospectionAllowed { + introspectionAllowedQueries[strings.ToLower(q)] = struct{}{} + } + for _, u := range cfg.Server.AllowURLs { + allowedUrls[u] = struct{}{} + } +} + +type parseGraphQLQueryResult struct { + operationType string + operationName string + activeEndpoint string + cacheTime int + cacheRequest bool + cacheRefresh bool + shouldBlock bool + shouldIgnore bool +} + +var ( + queryPool = sync.Pool{ + New: func() interface{} { + return make(map[string]interface{}, 4) + }, + } + resultPool = sync.Pool{ + New: func() interface{} { + return &parseGraphQLQueryResult{} + }, + } +) + +func parseGraphQLQuery(c *fiber.Ctx) *parseGraphQLQueryResult { + res := resultPool.Get().(*parseGraphQLQueryResult) + defer resultPool.Put(res) + *res = parseGraphQLQueryResult{shouldIgnore: true} + + m := queryPool.Get().(map[string]interface{}) + defer queryPool.Put(m) + for k := range m { + delete(m, k) + } + + if err := json.Unmarshal(c.Body(), &m); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't unmarshal the request", + Pairs: map[string]interface{}{"error": err.Error(), "body": unsafeString(c.Body())}, + }) + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsSkipped, nil) + } + return res + } + + query, ok := m["query"].(string) + if !ok { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't find the query", + Pairs: map[string]interface{}{"m_val": m}, + }) + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsSkipped, nil) + } + return res + } + + p, err := parser.Parse(parser.ParseParams{Source: query}) + if err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't parse the query", + Pairs: map[string]interface{}{"query": query, "m_val": m}, + }) + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + } + return res + } + + res.shouldIgnore = false + res.operationName = "undefined" + res.activeEndpoint = cfg.Server.HostGraphQL + + for _, d := range p.Definitions { + if oper, ok := d.(*ast.OperationDefinition); ok { + // If we haven't set an operation type yet, use this one + if res.operationType == "" { + res.operationType = strings.ToLower(oper.Operation) + if oper.Name != nil { + res.operationName = oper.Name.Value + } + } + + if cfg.Server.HostGraphQLReadOnly != "" && res.operationType != "mutation" { + res.activeEndpoint = cfg.Server.HostGraphQLReadOnly + } + + if res.operationType == "mutation" && cfg.Server.ReadOnlyMode { + cfg.Logger.Warning(&libpack_logger.LogMessage{ + Message: "Mutation blocked", + Pairs: map[string]interface{}{"query": query}, + }) + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsSkipped, nil) + } + _ = c.Status(403).SendString("The server is in read-only mode") + res.shouldBlock = true + return res + } + + for _, dir := range oper.Directives { + if dir.Name.Value == "cached" { + res.cacheRequest = true + for _, arg := range dir.Arguments { + switch arg.Name.Value { + case "ttl": + if v, ok := arg.Value.GetValue().(string); ok { + res.cacheTime, _ = strconv.Atoi(v) + } + case "refresh": + if v, ok := arg.Value.GetValue().(bool); ok { + res.cacheRefresh = v + } + } + } + } + } + + if cfg.Security.BlockIntrospection { + res.shouldBlock = checkSelections(c, oper.GetSelectionSet().Selections) + if res.shouldBlock { + return res + } + } + } + } + return res +} + +func unsafeString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +func checkSelections(c *fiber.Ctx, selections []ast.Selection) bool { + for _, s := range selections { + if field, ok := s.(*ast.Field); ok { + if checkIfContainsIntrospection(c, field.Name.Value) { + return true + } + if field.SelectionSet != nil && checkSelections(c, field.GetSelectionSet().Selections) { + return true + } + } + } + return false +} + +func checkIfContainsIntrospection(c *fiber.Ctx, whatever string) bool { + whateverLower := strings.ToLower(whatever) + mu.RLock() + defer mu.RUnlock() + + if _, exists := introspectionQueries[whateverLower]; exists { + if len(cfg.Security.IntrospectionAllowed) > 0 { + if _, allowed := introspectionAllowedQueries[whateverLower]; allowed { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Introspection query allowed, passing through", + Pairs: map[string]interface{}{"query": whatever}, + }) + return false + } + } + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsSkipped, nil) + } + _ = c.Status(403).SendString("Introspection queries are not allowed") + return true + } + return false +} diff --git a/graphql_test.go b/graphql_test.go new file mode 100644 index 0000000..f49924a --- /dev/null +++ b/graphql_test.go @@ -0,0 +1,432 @@ +package main + +import ( + "fmt" + "strings" + + fiber "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" +) + +func (suite *Tests) Test_parseGraphQLQuery() { + + type results struct { + op_name string + op_type string + cached_ttl int + returnCode int + is_cached bool + shouldBlock bool + shouldIgnore bool + } + + type queries struct { + headers map[string]string + body string + } + + tests := []struct { + name string + suppliedSettings *config + suppliedQuery queries + wantResults results + }{ + { + name: "test empty body", + suppliedQuery: queries{ + body: "", + headers: map[string]string{}, + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: true, + op_name: "", + op_type: "", + }, + }, + + { + name: "test empty json", + suppliedQuery: queries{ + body: "{}", + headers: map[string]string{}, + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: true, + op_name: "", + op_type: "", + }, + }, + + { + name: "test empty with some random garbage", + suppliedQuery: queries{ + body: "{\"variables\": {\"id\": \"1\"}}", + headers: map[string]string{}, + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: true, + op_name: "", + op_type: "", + }, + }, + + { + name: "test valid query with op name", + suppliedQuery: queries{ + body: "{\"query\":\"query MyQuery { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } }\"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: false, + op_name: "MyQuery", + op_type: "query", + }, + }, + + { + name: "test valid query with op name, variables and cache", + suppliedQuery: queries{ + body: "{\"query\":\"query MyQuery @cached { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } }\", \"variables\": {\"id\": \"1\"}}", + }, + wantResults: results{ + is_cached: true, + shouldBlock: false, + shouldIgnore: false, + op_name: "MyQuery", + op_type: "query", + }, + }, + + { + name: "test valid query with op name, cache and ttl", + suppliedQuery: queries{ + body: "{\"query\":\"query MyQuery @cached(ttl: 60) { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } }\", \"variables\": {\"id\": \"1\"}}", + }, + wantResults: results{ + is_cached: true, + cached_ttl: 60, + shouldBlock: false, + shouldIgnore: false, + op_name: "MyQuery", + op_type: "query", + }, + }, + + { + name: "test valid query with op name, force refreshed cache", + suppliedQuery: queries{ + body: "{\"query\":\"query MyQuery @cached(refresh: true) { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } }\", \"variables\": {\"id\": \"1\"}}", + }, + wantResults: results{ + is_cached: true, + cached_ttl: 0, + shouldBlock: false, + shouldIgnore: false, + op_name: "MyQuery", + op_type: "query", + }, + }, + + { + name: "test valid query with op name, cache and INVALID ttl", + suppliedQuery: queries{ + body: "{\"query\":\"query MyQuery @cached(ttl: nope) { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } }\", \"variables\": {\"id\": \"1\"}}", + }, + wantResults: results{ + is_cached: true, + cached_ttl: 0, + shouldBlock: false, + shouldIgnore: false, + op_name: "MyQuery", + op_type: "query", + }, + }, + + { + name: "test mutation query with op name", + suppliedQuery: queries{ + body: "{\"query\":\"mutation MyMutation { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } }\"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: false, + op_name: "MyMutation", + op_type: "mutation", + }, + }, + + { + name: "test mutation query with config: read only", + suppliedSettings: func() *config { + parseConfig() + cfg.Server.ReadOnlyMode = true + return cfg + }(), + suppliedQuery: queries{ + body: "{\"query\":\"mutation MyMutation { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } }\"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: true, + shouldIgnore: false, + op_name: "MyMutation", + op_type: "mutation", + returnCode: 403, + }, + }, + + { + name: "test simple query with introspection __schema", + suppliedQuery: queries{ + body: "{\"query\":\"mutation MyMutation { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __schema } }\"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: false, + op_name: "MyMutation", + op_type: "mutation", + }, + }, + + { + name: "test simple query with introspection __schema config: block introspection", + suppliedSettings: func() *config { + parseConfig() + cfg.Security.BlockIntrospection = true + return cfg + }(), + suppliedQuery: queries{ + body: "{\"query\":\"query MyIntroQuery { tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __schema } }\"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: true, + shouldIgnore: false, + op_name: "MyIntroQuery", + op_type: "query", + returnCode: 403, + }, + }, + + { + name: "test user supplied query with introspection #1 - config: block", + suppliedSettings: func() *config { + parseConfig() + cfg.Security.BlockIntrospection = true + cfg.Security.IntrospectionAllowed = []string{} + return cfg + }(), + suppliedQuery: queries{ + body: "{\"query\":\"{__schema {queryType {fields {name description}}}}\"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: true, + shouldIgnore: false, + op_name: "undefined", + op_type: "query", + returnCode: 403, + }, + }, + + { + name: "test user supplied query with introspection #1 - config: block & allow __schema", + suppliedSettings: func() *config { + parseConfig() + cfg.Security.BlockIntrospection = true + cfg.Security.IntrospectionAllowed = []string{"__schema"} + return cfg + }(), + suppliedQuery: queries{ + body: "{\"query\":\"{__schema {queryType {fields {name description}}}}\"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: false, + op_name: "undefined", + op_type: "query", + returnCode: 200, + }, + }, + + { + name: "test invalid query", + suppliedQuery: queries{ + body: "{\"query\":\"query MyQuery tg_users(where: {handle: {_eq: \\\"tozuo\\\"}}) { id __typename } \"}", + }, + wantResults: results{ + is_cached: false, + shouldBlock: false, + shouldIgnore: true, + op_name: "", + op_type: "", + }, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + cfg = &config{} + parseConfig() + ctx_headers := func() *fasthttp.RequestHeader { + h := fasthttp.RequestHeader{} + for k, v := range tt.suppliedQuery.headers { + h.Add(k, v) + } + return &h + }() + + ctx_request := fasthttp.Request{ + Header: *ctx_headers, + } + + ctx_request.AppendBody([]byte(tt.suppliedQuery.body)) + + ctx := suite.app.AcquireCtx(&fasthttp.RequestCtx{ + Request: ctx_request, + }) + + // defer func() { + // cfg = &config{} + // parseConfig() + // suite.app.ReleaseCtx(ctx) + // }() + + assert.NotNil(ctx, "Fiber context is nil") + + if tt.suppliedSettings != nil { + cfg = tt.suppliedSettings + } + prepareQueriesAndExemptions() + parseResult := parseGraphQLQuery(ctx) + assert.Equal(tt.wantResults.op_type, parseResult.operationType, "Unexpected operation type "+tt.name) + assert.Equal(tt.wantResults.op_name, parseResult.operationName, "Unexpected operation name "+tt.name) + assert.Equal(tt.wantResults.is_cached, parseResult.cacheRequest, "Unexpected cache value "+tt.name) + assert.Equal(tt.wantResults.cached_ttl, parseResult.cacheTime, "Unexpected cache TTL value "+tt.name) + assert.Equal(tt.wantResults.shouldBlock, parseResult.shouldBlock, "Unexpected block value "+tt.name) + assert.Equal(tt.wantResults.shouldIgnore, parseResult.shouldIgnore, "Unexpected ignore value "+tt.name) + + if tt.wantResults.returnCode > 0 { + assert.Equal(tt.wantResults.returnCode, ctx.Response().StatusCode(), "Unexpected return code", tt.name) + } + }) + } +} + +func (suite *Tests) Test_parseGraphQLQuery_complex() { + // ... existing tests ... + + // Add these new test cases + suite.Run("test complex query with multiple operations", func() { + query := ` + query GetUser($id: ID!) { + user(id: $id) { + name + email + } + } + mutation UpdateUser($id: ID!, $name: String!) { + updateUser(id: $id, name: $name) { + id + name + } + } + ` + body := fmt.Sprintf(`{"query": %q}`, query) + ctx := createTestContext(body) + result := parseGraphQLQuery(ctx) + assert.Equal("query", result.operationType) + assert.Equal("GetUser", result.operationName) + assert.False(result.shouldBlock) + }) + + suite.Run("test query with custom directives", func() { + query := ` + query GetUser($id: ID!) @custom(directive: "value") { + user(id: $id) { + name + email + } + } + ` + body := fmt.Sprintf(`{"query": %q}`, query) + ctx := createTestContext(body) + result := parseGraphQLQuery(ctx) + assert.Equal("query", result.operationType) + assert.Equal("GetUser", result.operationName) + assert.False(result.shouldBlock) + assert.False(result.shouldBlock) + }) +} + +func (suite *Tests) Test_checkAllowedURLs() { + tests := []struct { + name string + path string + allowed []string + expected bool + }{ + {"allowed path", "/v1/graphql", []string{"/v1/graphql"}, true}, + {"disallowed path", "/v2/graphql", []string{"/v1/graphql"}, false}, + {"empty allowed list", "/v1/graphql", []string{}, true}, + {"multiple allowed paths", "/v2/graphql", []string{"/v1/graphql", "/v2/graphql"}, true}, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + allowedUrls = make(map[string]struct{}) + for _, url := range tt.allowed { + allowedUrls[url] = struct{}{} + } + app := fiber.New() + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + ctx.Request().SetRequestURI(tt.path) + ctx.Request().URI().SetPath(tt.path) + result := checkAllowedURLs(ctx) + assert.Equal(tt.expected, result) + }) + } +} + +func (suite *Tests) Test_checkIfContainsIntrospection() { + tests := []struct { + name string + query string + allowed []string + expected bool + }{ + {"allowed introspection", "__schema", []string{"__schema"}, false}, + {"disallowed introspection", "__type", []string{"__schema"}, true}, + {"non-introspection query", "normalQuery", []string{}, false}, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + cfg.Security.IntrospectionAllowed = tt.allowed + introspectionAllowedQueries = make(map[string]struct{}) + for _, q := range tt.allowed { + introspectionAllowedQueries[strings.ToLower(q)] = struct{}{} + } + ctx := createTestContext("") + result := checkIfContainsIntrospection(ctx, tt.query) + assert.Equal(tt.expected, result) + }) + } +} + +func createTestContext(body string) *fiber.Ctx { + app := fiber.New() + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + ctx.Request().SetBody([]byte(body)) + return ctx +} diff --git a/logging/logger.go b/logging/logger.go new file mode 100644 index 0000000..7b1a964 --- /dev/null +++ b/logging/logger.go @@ -0,0 +1,204 @@ +package libpack_logger + +import ( + "bytes" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + "time" + + "github.com/goccy/go-json" +) + +const ( + _ = iota + LEVEL_DEBUG + LEVEL_INFO + LEVEL_WARN + LEVEL_ERROR + LEVEL_FATAL +) + +var LevelNames = [...]string{ + "none", + "debug", + "info", + "warn", + "error", + "fatal", +} + +const ( + defaultFormat = time.RFC3339 + defaultMinLevel = LEVEL_INFO + defaultShowCaller = false +) + +var defaultOutput = os.Stdout + +type Logger struct { + output io.Writer + format string + minLogLevel int + showCaller bool +} + +type LogMessage struct { + output io.Writer + Pairs map[string]any + Message string +} + +func (m *LogMessage) String() string { + return m.Message +} + +var fieldNames = map[string]string{ + "timestamp": "timestamp", + "level": "level", + "message": "message", +} + +func New() *Logger { + return &Logger{ + format: defaultFormat, + minLogLevel: defaultMinLevel, + output: defaultOutput, + showCaller: defaultShowCaller, + } +} + +func (l *Logger) SetOutput(output io.Writer) *Logger { + l.output = output + return l +} + +var bufferPool = sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, +} + +var defaultPairs = make(map[string]any) + +func GetLogLevel(level string) int { + for i, name := range LevelNames { + if name == strings.ToLower(level) { + return i + } + } + return defaultMinLevel +} + +func (l *Logger) log(level int, m *LogMessage) { + if m.Pairs == nil { + m.Pairs = defaultPairs + } + + m.Pairs[fieldNames["timestamp"]] = time.Now().Format(l.format) + m.Pairs[fieldNames["level"]] = LevelNames[level] + m.Pairs[fieldNames["message"]] = m.Message + + if l.showCaller { + m.Pairs["caller"] = getCaller() + } + + buffer := bufferPool.Get().(*bytes.Buffer) + defer bufferPool.Put(buffer) + buffer.Reset() + + var encoder = json.NewEncoder(buffer) + err := encoder.Encode(m.Pairs) + if err != nil { + fmt.Println("Error marshalling log message:", err) + return + } + + // if not running in test - use stderr and stdout, otherwise - use logger's output setting + if flag.Lookup("test.v") != nil { + m.output = os.Stdout + if level >= LEVEL_ERROR { + m.output = os.Stderr + } + } + + // Use logger's output setting instead of os.Stdout or os.Stderr + l.output.Write(buffer.Bytes()) +} + +func (l *Logger) Debug(m *LogMessage) { + if l.shouldLog(LEVEL_DEBUG) { + l.log(LEVEL_DEBUG, m) + } +} + +func (l *Logger) Info(m *LogMessage) { + if l.shouldLog(LEVEL_INFO) { + l.log(LEVEL_INFO, m) + } +} + +func (l *Logger) Warn(m *LogMessage) { + if l.shouldLog(LEVEL_WARN) { + l.log(LEVEL_WARN, m) + } +} + +func (l *Logger) Warning(m *LogMessage) { + l.Warn(m) +} + +func (l *Logger) Error(m *LogMessage) { + if l.shouldLog(LEVEL_ERROR) { + l.log(LEVEL_ERROR, m) + } +} + +func (l *Logger) Fatal(m *LogMessage) { + if l.shouldLog(LEVEL_FATAL) { + l.log(LEVEL_FATAL, m) + } +} + +func (l *Logger) Critical(m *LogMessage) { + l.Fatal(m) + os.Exit(1) +} + +func (l *Logger) shouldLog(level int) bool { + return level >= l.minLogLevel +} + +func (l *Logger) SetFormat(format string) *Logger { + l.format = format + return l +} + +func (l *Logger) SetMinLogLevel(level int) *Logger { + l.minLogLevel = level + return l +} + +func (l *Logger) SetFieldName(field, name string) *Logger { + fieldNames[field] = name + return l +} + +func (l *Logger) SetShowCaller(show bool) *Logger { + l.showCaller = show + return l +} + +func getCaller() string { + _, file, line, ok := runtime.Caller(3) + if !ok { + return "unknown:0" + } + file = filepath.Base(file) + return fmt.Sprintf("%s:%d", file, line) +} diff --git a/logging/logger_bench_test.go b/logging/logger_bench_test.go new file mode 100644 index 0000000..9d92425 --- /dev/null +++ b/logging/logger_bench_test.go @@ -0,0 +1,140 @@ +package libpack_logger + +import ( + "bytes" + "testing" + "time" +) + +func Benchmark_NewLogger(b *testing.B) { + type triggers struct { + ModFormat struct { + Format string + } + ModLevel struct { + Level int + } + } + + tests := []struct { + name string + triggers triggers + }{ + { + name: "BenchmarkNew", + }, + { + name: "BenchmarkNewChangeTimeFormat", + triggers: triggers{ + ModFormat: struct{ Format string }{ + Format: time.RFC3339Nano, + }, + }, + }, + { + name: "BenchmarkNewChangeLogLevel", + triggers: triggers{ + ModLevel: struct{ Level int }{ + Level: LEVEL_DEBUG, + }, + }, + }, + { + name: "BenchmarkNewChangeTimeFormatAndLogLevel", + triggers: triggers{ + ModFormat: struct{ Format string }{ + Format: time.RFC3339Nano, + }, + ModLevel: struct{ Level int }{ + Level: LEVEL_DEBUG, + }, + }, + }, + } + + for _, tt := range tests { + b.Run(tt.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + got := New() + + if tt.triggers.ModFormat.Format != "" { + got = got.SetFormat(tt.triggers.ModFormat.Format) + } + + if tt.triggers.ModLevel.Level != 0 { + got = got.SetMinLogLevel(tt.triggers.ModLevel.Level) + } + } + }) + } +} + +func Benchmark_Log_Debug(b *testing.B) { + output := &bytes.Buffer{} + logger := New().SetMinLogLevel(LEVEL_DEBUG).SetOutput(output) + msg := &LogMessage{ + Message: "debug message", + Pairs: make(map[string]any), + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + logger.Debug(msg) + } +} + +func Benchmark_Log_Info(b *testing.B) { + output := &bytes.Buffer{} + logger := New().SetMinLogLevel(LEVEL_INFO).SetOutput(output) + msg := &LogMessage{ + Message: "info message", + Pairs: make(map[string]any), + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + logger.Info(msg) + } +} + +func Benchmark_Log_Warn(b *testing.B) { + output := &bytes.Buffer{} + logger := New().SetMinLogLevel(LEVEL_WARN).SetOutput(output) + msg := &LogMessage{ + Message: "warn message", + Pairs: make(map[string]any), + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + logger.Warn(msg) + } +} + +func Benchmark_Log_Error(b *testing.B) { + output := &bytes.Buffer{} + logger := New().SetMinLogLevel(LEVEL_ERROR).SetOutput(output) + msg := &LogMessage{ + Message: "error message", + Pairs: map[string]any{"key": "value"}, + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + logger.Error(msg) + } +} + +func Benchmark_Log_Fatal(b *testing.B) { + output := &bytes.Buffer{} + logger := New().SetMinLogLevel(LEVEL_FATAL).SetOutput(output) + msg := &LogMessage{ + Message: "fatal message", + Pairs: make(map[string]any), + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + logger.Fatal(msg) + } +} diff --git a/logging/logger_suite_test.go b/logging/logger_suite_test.go new file mode 100644 index 0000000..965e7f4 --- /dev/null +++ b/logging/logger_suite_test.go @@ -0,0 +1,31 @@ +package libpack_logger + +import ( + "testing" + + assertions "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type LoggerTestSuite struct { + suite.Suite +} + +var ( + assert *assertions.Assertions +) + +func (suite *LoggerTestSuite) BeforeTest(suiteName, testName string) { +} + +func (suite *LoggerTestSuite) SetupTest() { + assert = assertions.New(suite.T()) +} + +// TearDownTest is run after each test to clean up +func (suite *LoggerTestSuite) TearDownTest() { +} + +func TestSuite(t *testing.T) { + suite.Run(t, new(LoggerTestSuite)) +} diff --git a/logging/logger_test.go b/logging/logger_test.go new file mode 100644 index 0000000..92d04a9 --- /dev/null +++ b/logging/logger_test.go @@ -0,0 +1,182 @@ +package libpack_logger + +import ( + "bytes" + "fmt" + "os" + "reflect" + "testing" + "time" + + "github.com/goccy/go-json" +) + +func captureStderr(f func()) string { + originalStderr := os.Stderr + r, w, _ := os.Pipe() + os.Stderr = w + f() + w.Close() + var buf bytes.Buffer + buf.ReadFrom(r) + os.Stderr = originalStderr + return buf.String() +} + +func captureStdOut(f func()) string { + originalStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + f() + w.Close() + var buf bytes.Buffer + buf.ReadFrom(r) + os.Stdout = originalStdout + return buf.String() +} + +func (suite *LoggerTestSuite) Test_LogMessageString() { + msg := &LogMessage{ + Message: "test message", + } + + assert.Equal("test message", msg.String()) +} + +func callLoggerMethod(logger *Logger, methodName string, message *LogMessage) { + // Get the method by name using reflection + method := reflect.ValueOf(logger).MethodByName(methodName) + if method.IsValid() { + // Call the method with the message as an argument + method.Call([]reflect.Value{reflect.ValueOf(message)}) + } else { + fmt.Printf("Method %s does not exist on Logger\n", methodName) + } +} + +func (suite *LoggerTestSuite) Test_LogsLevelsPrint() { + output := &bytes.Buffer{} + logger := New().SetOutput(output) + + tests := []struct { + pairs map[string]any + name string + method string + message string + loggerMinLevel int + messageLogLevel int + wantOutput bool + }{ + { + name: "Log: Debug, Level: Debug - no pairs", + method: "Debug", + loggerMinLevel: LEVEL_DEBUG, + messageLogLevel: LEVEL_DEBUG, + message: "debug message", + wantOutput: true, + }, + { + name: "Log: Info, Level: Info - one pair", + method: "Info", + loggerMinLevel: LEVEL_INFO, + messageLogLevel: LEVEL_INFO, + message: "info message", + pairs: map[string]any{ + "key": "value", + }, + wantOutput: true, + }, + { + name: "Log: Info, Level: Warn - with pairs", + method: "Info", + loggerMinLevel: LEVEL_WARN, + messageLogLevel: LEVEL_INFO, + message: "warn message", + pairs: map[string]any{ + "key1": "value1", + "key2": "value2", + }, + wantOutput: false, + }, + { + name: "Log: Warn, Level: Info - with 500 pairs", + method: "Warn", + loggerMinLevel: LEVEL_INFO, + messageLogLevel: LEVEL_WARN, + message: "warn message with 500 pairs", + pairs: func() map[string]any { + pairs := make(map[string]any) + for i := 0; i < 500; i++ { + pairs[fmt.Sprintf("key%d", i)] = fmt.Sprintf("value%d", i) + } + return pairs + }(), + wantOutput: true, + }, + } + + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + msg := &LogMessage{ + Message: tt.message, + Pairs: tt.pairs, + } + output.Reset() + + // Set logger's minimum log level + logger.SetMinLogLevel(tt.loggerMinLevel) + fmt.Println("Logger min log level:", LevelNames[logger.minLogLevel]) + + // Call the logging method + callLoggerMethod(logger, tt.method, msg) + + logOutput := output.String() + fmt.Println("Output:", logOutput) + + if tt.wantOutput { + var loggedMessage map[string]any + err := json.Unmarshal([]byte(logOutput), &loggedMessage) + if err != nil { + t.Fatalf("Error unmarshalling log message: %v\nLog output: %s", err, logOutput) + } + + if !containsLogMessage(logOutput, tt.message) { + t.Errorf("Expected log message %q, but got %q", tt.message, logOutput) + } + assert.Equal(LevelNames[tt.messageLogLevel], loggedMessage["level"]) + if tt.pairs != nil { + for k, v := range tt.pairs { + assert.Equal(v, loggedMessage[k]) + } + } + } else { + assert.Equal("", logOutput) + } + }) + } +} + +func containsLogMessage(logOutput, expectedMessage string) bool { + return bytes.Contains([]byte(logOutput), []byte(expectedMessage)) +} + +func (suite *LoggerTestSuite) Test_SetFormat() { + logger := New().SetFormat(time.RFC3339Nano) + + assert.Equal(time.RFC3339Nano, logger.format) +} + +func (suite *LoggerTestSuite) Test_SetMinLogLevel() { + logger := New().SetMinLogLevel(LEVEL_DEBUG) + + assert.Equal(LEVEL_DEBUG, logger.minLogLevel) +} + +func (suite *LoggerTestSuite) Test_ShouldLog() { + logger := New().SetMinLogLevel(LEVEL_WARN) + + assert.True(logger.shouldLog(LEVEL_WARN)) + assert.True(logger.shouldLog(LEVEL_ERROR)) + assert.False(logger.shouldLog(LEVEL_INFO)) + assert.False(logger.shouldLog(LEVEL_DEBUG)) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..66bf629 --- /dev/null +++ b/main.go @@ -0,0 +1,122 @@ +package main + +import ( + "flag" + "os" + "strings" + "sync" + + "github.com/gofiber/fiber/v2/middleware/proxy" + "github.com/gookit/goutil/envutil" + graphql "github.com/lukaszraczylo/go-simple-graphql" + libpack_cache "github.com/lukaszraczylo/graphql-monitoring-proxy/cache" + libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" + libpack_logging "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +var cfg *config +var once sync.Once + +// function get value from the env where the value can be anything +func getDetailsFromEnv[T any](key string, defaultValue T) T { + var result any + if _, ok := os.LookupEnv("GMP_" + key); ok { + key = "GMP_" + key + } + switch v := any(defaultValue).(type) { + case string: + result = envutil.Getenv(key, v) + case int: + result = envutil.GetInt(key, v) + case bool: + result = envutil.GetBool(key, v) + default: + result = defaultValue + } + return result.(T) +} + +func parseConfig() { + libpack_config.PKG_NAME = "graphql_proxy" + c := config{} + c.Server.PortGraphQL = getDetailsFromEnv("PORT_GRAPHQL", 8080) + c.Server.PortMonitoring = getDetailsFromEnv("MONITORING_PORT", 9393) + c.Server.HostGraphQL = getDetailsFromEnv("HOST_GRAPHQL", "http://localhost/") + c.Server.HostGraphQLReadOnly = getDetailsFromEnv("HOST_GRAPHQL_READONLY", "") + c.Client.JWTUserClaimPath = getDetailsFromEnv("JWT_USER_CLAIM_PATH", "") + c.Client.JWTRoleClaimPath = getDetailsFromEnv("JWT_ROLE_CLAIM_PATH", "") + c.Client.RoleFromHeader = getDetailsFromEnv("ROLE_FROM_HEADER", "") + c.Client.RoleRateLimit = getDetailsFromEnv("ROLE_RATE_LIMIT", false) + /* in-memory cache */ + c.Cache.CacheEnable = getDetailsFromEnv("ENABLE_GLOBAL_CACHE", false) + c.Cache.CacheTTL = getDetailsFromEnv("CACHE_TTL", 60) + /* redis cache */ + c.Cache.CacheRedisEnable = getDetailsFromEnv("ENABLE_REDIS_CACHE", false) + c.Cache.CacheRedisURL = getDetailsFromEnv("CACHE_REDIS_URL", "localhost:6379") + c.Cache.CacheRedisPassword = getDetailsFromEnv("CACHE_REDIS_PASSWORD", "") + c.Cache.CacheRedisDB = getDetailsFromEnv("CACHE_REDIS_DB", 0) + c.Security.BlockIntrospection = getDetailsFromEnv("BLOCK_SCHEMA_INTROSPECTION", false) + c.Security.IntrospectionAllowed = func() []string { + urls := getDetailsFromEnv("ALLOWED_INTROSPECTION", "") + if urls == "" { + return nil + } + return strings.Split(urls, ",") + }() + c.LogLevel = strings.ToUpper(getDetailsFromEnv("LOG_LEVEL", "info")) + c.Logger = libpack_logging.New().SetMinLogLevel(libpack_logging.GetLogLevel(c.LogLevel)).SetFieldName("timestamp", "ts").SetFieldName("message", "msg").SetShowCaller(false) + c.Server.HealthcheckGraphQL = getDetailsFromEnv("HEALTHCHECK_GRAPHQL_URL", "") + c.Client.GQLClient = graphql.NewConnection() + c.Client.GQLClient.SetEndpoint(c.Server.HealthcheckGraphQL) + c.Server.AccessLog = getDetailsFromEnv("ENABLE_ACCESS_LOG", false) + c.Server.ReadOnlyMode = getDetailsFromEnv("READ_ONLY_MODE", false) + c.Server.AllowURLs = func() []string { + urls := getDetailsFromEnv("ALLOWED_URLS", "") + if urls == "" { + return nil + } + return strings.Split(urls, ",") + }() + c.Client.ClientTimeout = getDetailsFromEnv("PROXIED_CLIENT_TIMEOUT", 120) + c.Client.FastProxyClient = createFasthttpClient(c.Client.ClientTimeout) + proxy.WithClient(c.Client.FastProxyClient) // setting the global proxy client here instead of per request + c.Server.EnableApi = getDetailsFromEnv("ENABLE_API", false) + c.Server.ApiPort = getDetailsFromEnv("API_PORT", 9090) + c.Api.BannedUsersFile = getDetailsFromEnv("BANNED_USERS_FILE", "/go/src/app/banned_users.json") + c.Server.PurgeOnCrawl = getDetailsFromEnv("PURGE_METRICS_ON_CRAWL", false) + c.Server.PurgeEvery = getDetailsFromEnv("PURGE_METRICS_ON_TIMER", 0) + c.HasuraEventCleaner.Enable = getDetailsFromEnv("HASURA_EVENT_CLEANER", false) + c.HasuraEventCleaner.ClearOlderThan = getDetailsFromEnv("HASURA_EVENT_CLEANER_OLDER_THAN", 1) + c.HasuraEventCleaner.EventMetadataDb = getDetailsFromEnv("HASURA_EVENT_METADATA_DB", "") + cfg = &c + + if cfg.Cache.CacheEnable || cfg.Cache.CacheRedisEnable { + cacheConfig := &libpack_cache.CacheConfig{ + Logger: cfg.Logger, + TTL: cfg.Cache.CacheTTL, + } + if cfg.Cache.CacheRedisEnable { + cacheConfig.Redis.Enable = true + cacheConfig.Redis.URL = cfg.Cache.CacheRedisURL + cacheConfig.Redis.Password = cfg.Cache.CacheRedisPassword + cacheConfig.Redis.DB = cfg.Cache.CacheRedisDB + } + libpack_cache.EnableCache(cacheConfig) + } + loadRatelimitConfig() + once.Do(func() { + go enableApi() + go enableHasuraEventCleaner() + }) + prepareQueriesAndExemptions() +} + +func main() { + parseConfig() + StartMonitoringServer() + StartHTTPProxy() +} + +func ifNotInTest() bool { + return flag.Lookup("test.v") == nil +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..e2ea74c --- /dev/null +++ b/main_test.go @@ -0,0 +1,140 @@ +package main + +import ( + "os" + "testing" + "time" + + "github.com/goccy/go-json" + "github.com/gofiber/fiber/v2" + libpack_cache "github.com/lukaszraczylo/graphql-monitoring-proxy/cache/memory" + libpack_logging "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" + assertions "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type Tests struct { + suite.Suite + app *fiber.App +} + +var ( + assert *assertions.Assertions +) + +func (suite *Tests) BeforeTest(suiteName, testName string) { +} + +func (suite *Tests) SetupTest() { + assert = assertions.New(suite.T()) + suite.app = fiber.New( + fiber.Config{ + DisableStartupMessage: true, + JSONEncoder: json.Marshal, + JSONDecoder: json.Unmarshal, + }, + ) + + // Initialize a simple in-memory cache client for testing purposes + libpack_cache.New(5 * time.Minute) + parseConfig() + enableApi() + StartMonitoringServer() + cfg.Logger = libpack_logging.New().SetMinLogLevel(libpack_logging.GetLogLevel(getDetailsFromEnv("LOG_LEVEL", "info"))) + // Setup environment variables here if needed + os.Setenv("GMP_TEST_STRING", "testValue") + os.Setenv("GMP_TEST_INT", "123") + os.Setenv("GMP_TEST_BOOL", "true") + os.Setenv("NON_GMP_TEST_INT", "31337") +} + +// TearDownTest is run after each test to clean up +func (suite *Tests) TearDownTest() { + // Clean up environment variables here if needed + os.Unsetenv("GMP_TEST_STRING") + os.Unsetenv("GMP_TEST_INT") + os.Unsetenv("GMP_TEST_BOOL") + os.Unsetenv("NON_GMP_TEST_INT") +} + +// func (suite *Tests) AfterTest(suiteName, testName string) {) + +func TestSuite(t *testing.T) { + cfg = &config{} + parseConfig() + StartMonitoringServer() + suite.Run(t, new(Tests)) +} + +func (suite *Tests) Test_envVariableSetting() { + tests := []struct { + defaultValue any + expected any + name string + envKey string + }{ + { + name: "test_string", + envKey: "TEST_STRING", + defaultValue: "default", + expected: "testValue", + }, + { + name: "test_int", + envKey: "TEST_INT", + defaultValue: 0, + expected: 123, + }, + { + name: "test_bool", + envKey: "TEST_BOOL", + defaultValue: false, + expected: true, + }, + { + name: "test_non_prefixed", + envKey: "NON_GMP_TEST_INT", + defaultValue: 0, + expected: 31337, + }, + { + name: "test_non_existing", + envKey: "NON_EXISTING", + defaultValue: "default_val", + expected: "default_val", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result := getDetailsFromEnv(tt.envKey, tt.defaultValue) + assert.Equal(tt.expected, result) + }) + } +} + +func (suite *Tests) Test_getDetailsFromEnv() { + tests := []struct { + name string + key string + defaultValue interface{} + envValue string + expected interface{} + }{ + {"string value", "TEST_STRING", "default", "envValue", "envValue"}, + {"int value", "TEST_INT", 0, "123", 123}, + {"bool value", "TEST_BOOL", false, "true", true}, + {"default value", "NON_EXISTENT", "default", "", "default"}, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + if tt.envValue != "" { + os.Setenv("GMP_"+tt.key, tt.envValue) + defer os.Unsetenv("GMP_" + tt.key) + } + result := getDetailsFromEnv(tt.key, tt.defaultValue) + assert.Equal(tt.expected, result) + }) + } +} diff --git a/monitoring.go b/monitoring.go new file mode 100644 index 0000000..bad9af8 --- /dev/null +++ b/monitoring.go @@ -0,0 +1,11 @@ +package main + +import ( + libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring" +) + +func StartMonitoringServer() { + cfg.Monitoring = libpack_monitoring.NewMonitoring(&libpack_monitoring.InitConfig{PurgeOnCrawl: cfg.Server.PurgeOnCrawl, PurgeEvery: cfg.Server.PurgeEvery}) + cfg.Monitoring.AddMetricsPrefix("graphql_proxy") + cfg.Monitoring.RegisterDefaultMetrics() +} diff --git a/monitoring/defaults.go b/monitoring/defaults.go new file mode 100644 index 0000000..f0e7ee7 --- /dev/null +++ b/monitoring/defaults.go @@ -0,0 +1,15 @@ +package libpack_monitoring + +func (ms *MetricsSetup) RegisterDefaultMetrics() { + ms.RegisterMetricsCounter(MetricsSucceeded, nil) + ms.RegisterMetricsCounter(MetricsFailed, nil) + ms.RegisterMetricsCounter(MetricsSkipped, nil) + ms.RegisterMetricsHistogram(MetricsDuration, nil) + ms.RegisterMetricsCounter(MetricsCacheHit, nil) + ms.RegisterMetricsCounter(MetricsCacheMiss, nil) + ms.RegisterMetricsCounter(MetricsQueriesCached, nil) +} + +func (ms *MetricsSetup) RegisterGoMetrics() { + // TODO: metrics.WriteProcessMetrics(ms.metrics_set) +} diff --git a/monitoring/helpers.go b/monitoring/helpers.go new file mode 100644 index 0000000..4773866 --- /dev/null +++ b/monitoring/helpers.go @@ -0,0 +1,173 @@ +package libpack_monitoring + +import ( + "bytes" + "fmt" + "os" + "sort" + "strings" + "sync" + "unicode" + + libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" +) + +var sortedLabelKeysCache = struct { + m sync.Map +}{} + +func (ms *MetricsSetup) get_metrics_name(name string, labels map[string]string) string { + const unknownPodName = "unknown" + var buf bytes.Buffer + + podName := getPodName() + if labels == nil { + labels = defaultLabels(podName) + } else { + ensureDefaultLabels(&labels, podName) + } + + if ms.metrics_prefix != "" { + buf.WriteString(ms.metrics_prefix) + buf.WriteByte('_') + } + buf.WriteString(name) + + if len(labels) > 0 { + buf.WriteByte('{') + appendSortedLabels(&buf, labels) + buf.WriteByte('}') + } + + return buf.String() +} + +func getPodName() string { + const unknownPodName = "unknown" + if hn, err := os.Hostname(); err == nil { + return hn + } + return unknownPodName +} + +func defaultLabels(podName string) map[string]string { + return map[string]string{ + "microservice": libpack_config.PKG_NAME, + "pod": podName, + } +} + +func ensureDefaultLabels(labels *map[string]string, podName string) { + if *labels == nil { + *labels = make(map[string]string) + } + if _, exists := (*labels)["microservice"]; !exists { + (*labels)["microservice"] = libpack_config.PKG_NAME + } + if _, exists := (*labels)["pod"]; !exists { + (*labels)["pod"] = podName + } +} + +func appendSortedLabels(buf *bytes.Buffer, labels map[string]string) { + keys := getSortedKeys(labels) + for i, k := range keys { + if i > 0 { + buf.WriteByte(',') + } + buf.WriteString(k) + buf.WriteString(`="`) + buf.WriteString(labels[k]) + buf.WriteByte('"') + } +} + +func getSortedKeys(labels map[string]string) []string { + labelsKey := labelsToString(labels) + + if keys, ok := sortedLabelKeysCache.m.Load(labelsKey); ok { + return keys.([]string) + } + + keys := make([]string, 0, len(labels)) + for k := range labels { + keys = append(keys, k) + } + sort.Strings(keys) + + sortedLabelKeysCache.m.Store(labelsKey, keys) + + return keys +} + +func labelsToString(labels map[string]string) string { + var sb strings.Builder + for k, v := range labels { + sb.WriteString(k) + sb.WriteByte('=') + sb.WriteString(v) + sb.WriteByte(';') + } + return sb.String() +} + +func validate_metrics_name(name string) error { + cleanedName := clean_metric_name(name) + + finalName := strings.Trim(cleanedName, "_") + + if finalName != name { + return fmt.Errorf("invalid metric name: %s, expected %s", name, finalName) + } + return nil +} + +func clean_metric_name(name string) string { + var buf bytes.Buffer + lastWasUnderscore := false + + for _, r := range name { + if is_allowed_rune(r) { + if is_special_rune(r) { + if lastWasUnderscore { + continue + } + r = '_' + lastWasUnderscore = true + } else { + lastWasUnderscore = false + } + buf.WriteRune(r) + } else if !lastWasUnderscore { + buf.WriteByte('_') + lastWasUnderscore = true + } + } + + return strings.Trim(buf.String(), "_") +} + +func is_allowed_rune(r rune) bool { + return unicode.IsLetter(r) || unicode.IsDigit(r) || r == ' ' || r == '_' +} + +func is_special_rune(r rune) bool { + return r == ' ' || r == '_' +} + +func compile_metrics_with_labels(name string, labels map[string]string) string { + var buf bytes.Buffer + + buf.WriteString(name) + + keys := getSortedKeys(labels) + + for _, k := range keys { + buf.WriteByte('_') + buf.WriteString(k) + buf.WriteByte('_') + buf.WriteString(labels[k]) + } + + return buf.String() +} diff --git a/monitoring/helpers_bench_test.go b/monitoring/helpers_bench_test.go new file mode 100644 index 0000000..84791f5 --- /dev/null +++ b/monitoring/helpers_bench_test.go @@ -0,0 +1,44 @@ +package libpack_monitoring + +import ( + "testing" + + libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" +) + +func BenchmarkGetMetricsName(b *testing.B) { + // Setup environment + libpack_config.PKG_NAME = "test_service" + + ms := &MetricsSetup{metrics_prefix: "test_prefix"} + + labels := map[string]string{ + "env": "production", + "region": "us-west-2", + } + + // Run the benchmark + for n := 0; n < b.N; n++ { + ms.get_metrics_name("request_count", labels) + } +} + +func BenchmarkCompileMetricsWithLabels(b *testing.B) { + labels := map[string]string{ + "env": "production", + "region": "us-west-2", + "app": "api-server", + } + + for n := 0; n < b.N; n++ { + compile_metrics_with_labels("request_count", labels) + } +} + +func BenchmarkValidateMetricsName(b *testing.B) { + input := "valid metric name with special chars @#! and underscores__" + + for n := 0; n < b.N; n++ { + validate_metrics_name(input) + } +} diff --git a/monitoring/helpers_test.go b/monitoring/helpers_test.go new file mode 100644 index 0000000..f3f0981 --- /dev/null +++ b/monitoring/helpers_test.go @@ -0,0 +1,228 @@ +package libpack_monitoring + +import ( + "testing" + + libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" + "github.com/stretchr/testify/assert" +) + +func TestGetMetricsName(t *testing.T) { + ms := &MetricsSetup{metrics_prefix: "prefix"} + libpack_config.PKG_NAME = "example_microservice" + + tests := []struct { + name string + metricName string + labels map[string]string + expectedOutput string + }{ + { + name: "No labels", + metricName: "test_metric", + labels: nil, + expectedOutput: "prefix_test_metric{microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + { + name: "With labels", + metricName: "test_metric", + labels: map[string]string{ + "label1": "value1", + "label2": "value2", + }, + expectedOutput: "prefix_test_metric{label1=\"value1\",label2=\"value2\",microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + { + name: "Alphabetical order labels", + metricName: "test_metric", + labels: map[string]string{ + "label2": "value2", + "label1": "value1", + }, + expectedOutput: "prefix_test_metric{label1=\"value1\",label2=\"value2\",microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + { + name: "Empty metric name", + metricName: "", + labels: nil, + expectedOutput: "prefix_{microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + { + name: "Empty labels map", + metricName: "test_metric", + labels: map[string]string{}, + expectedOutput: "prefix_test_metric{microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + { + name: "Single label", + metricName: "test_metric", + labels: map[string]string{ + "label1": "value1", + }, + expectedOutput: "prefix_test_metric{label1=\"value1\",microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + { + name: "Multiple labels with special characters", + metricName: "test_metric", + labels: map[string]string{ + "label-2": "value-2", + "label_1": "value_1", + }, + expectedOutput: "prefix_test_metric{label-2=\"value-2\",label_1=\"value_1\",microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + { + name: "Prefix only", + metricName: "", + labels: map[string]string{ + "label1": "value1", + }, + expectedOutput: "prefix_{label1=\"value1\",microservice=\"example_microservice\",pod=\"" + getPodName() + "\"}", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ms.get_metrics_name(tt.metricName, tt.labels) + assert.Equal(t, tt.expectedOutput, result) + }) + } +} + +func TestCompileMetricsWithLabels(t *testing.T) { + tests := []struct { + name string + labels map[string]string + want string + }{ + {"request_count", map[string]string{"env": "production", "region": "us-west-2"}, "request_count_env_production_region_us-west-2"}, + {"metric_name", map[string]string{}, "metric_name"}, + {"metric_name", nil, "metric_name"}, + {"metric_name", map[string]string{"key1": "value1"}, "metric_name_key1_value1"}, + {"metric_name", map[string]string{"k": "v", "key2": "value2"}, "metric_name_k_v_key2_value2"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := compile_metrics_with_labels(tt.name, tt.labels); got != tt.want { + t.Errorf("compile_metrics_with_labels() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestValidateMetricsName(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + }{ + {"Valid name", "valid_metric_name", false}, + {"Name with spaces", "valid metric name", true}, + {"Name with special chars", "valid@metric#name!", true}, + {"Name with leading underscore", "_valid_metric_name", true}, + {"Name with trailing underscore", "valid_metric_name_", true}, + {"Name with consecutive underscores", "valid__metric__name", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := validate_metrics_name(tt.input); (err != nil) != tt.wantErr { + t.Errorf("validate_metrics_name() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCleanMetricName(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"valid metric name", "valid_metric_name"}, + {"valid@metric#name!", "valid_metric_name"}, + {"__valid__metric__name__", "valid_metric_name"}, + {" valid metric name ", "valid_metric_name"}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + assert.Equal(t, tt.expected, clean_metric_name(tt.input)) + }) + } +} + +func TestDefaultLabels(t *testing.T) { + podName := "test-pod" + libpack_config.PKG_NAME = "example_microservice" + expected := map[string]string{ + "microservice": "example_microservice", + "pod": podName, + } + + assert.Equal(t, expected, defaultLabels(podName)) +} + +func TestEnsureDefaultLabels(t *testing.T) { + podName := "test-pod" + libpack_config.PKG_NAME = "example_microservice" + + tests := []struct { + inputLabels map[string]string + expectedLabels map[string]string + name string + }{ + { + name: "Nil labels", + inputLabels: nil, + expectedLabels: map[string]string{"microservice": "example_microservice", "pod": podName}, + }, + { + name: "Empty labels", + inputLabels: map[string]string{}, + expectedLabels: map[string]string{"microservice": "example_microservice", "pod": podName}, + }, + { + name: "Partial labels", + inputLabels: map[string]string{"microservice": "test_service"}, + expectedLabels: map[string]string{"microservice": "test_service", "pod": podName}, + }, + { + name: "Complete labels", + inputLabels: map[string]string{"microservice": "test_service", "pod": "custom_pod"}, + expectedLabels: map[string]string{"microservice": "test_service", "pod": "custom_pod"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ensureDefaultLabels(&tt.inputLabels, podName) + assert.Equal(t, tt.expectedLabels, tt.inputLabels) + }) + } +} + +func TestLabelsToString(t *testing.T) { + tests := []struct { + labels map[string]string + expected string + }{ + { + labels: map[string]string{"key1": "value1", "key2": "value2"}, + expected: "key1=value1;key2=value2;", + }, + { + labels: map[string]string{"a": "1", "b": "2"}, + expected: "a=1;b=2;", + }, + { + labels: map[string]string{}, + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.expected, func(t *testing.T) { + assert.Equal(t, tt.expected, labelsToString(tt.labels)) + }) + } +} diff --git a/monitoring/monitoring.go b/monitoring/monitoring.go new file mode 100644 index 0000000..165a0df --- /dev/null +++ b/monitoring/monitoring.go @@ -0,0 +1,174 @@ +package libpack_monitoring + +import ( + "flag" + "fmt" + "time" + + "github.com/VictoriaMetrics/metrics" + "github.com/gofiber/fiber/v2" + "github.com/gookit/goutil/envutil" + libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +type MetricsSetup struct { + metrics_set *metrics.Set + metrics_set_custom *metrics.Set + ic *InitConfig + metrics_prefix string +} + +var log = libpack_logger.New().SetMinLogLevel(libpack_logger.LEVEL_INFO) + +type InitConfig struct { + PurgeOnCrawl bool + PurgeEvery int +} + +func NewMonitoring(ic *InitConfig) *MetricsSetup { + ms := &MetricsSetup{ + ic: ic, + metrics_set: metrics.NewSet(), + metrics_set_custom: metrics.NewSet(), + } + + if flag.Lookup("test.v") == nil { + go ms.startPrometheusEndpoint() + + if ic.PurgeEvery > 0 { + ticker := time.NewTicker(time.Duration(ic.PurgeEvery) * time.Second) + go func() { + for range ticker.C { + ms.PurgeMetrics() + } + }() + } + } + + return ms +} + +func (ms *MetricsSetup) startPrometheusEndpoint() { + app := fiber.New(fiber.Config{ + DisableStartupMessage: true, + AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION), + }) + app.Get("/metrics", ms.metricsEndpoint) + if err := app.Listen(fmt.Sprintf(":%d", envutil.GetInt("MONITORING_PORT", 9393))); err != nil { + log.Critical(&libpack_logger.LogMessage{ + Message: "Can't start the service", + Pairs: map[string]interface{}{"error": err}, + }) + } +} + +func (ms *MetricsSetup) metricsEndpoint(c *fiber.Ctx) error { + ms.metrics_set.WritePrometheus(c.Response().BodyWriter()) + ms.metrics_set_custom.WritePrometheus(c.Response().BodyWriter()) + + if ms.ic.PurgeOnCrawl && ms.ic.PurgeEvery == 0 { + ms.PurgeMetrics() + } + return nil +} + +func (ms *MetricsSetup) AddMetricsPrefix(prefix string) { + ms.metrics_prefix = prefix +} + +func (ms *MetricsSetup) ListActiveMetrics() []string { + return ms.metrics_set.ListMetricNames() +} + +func (ms *MetricsSetup) RegisterMetricsGauge(metric_name string, labels map[string]string, val float64) *metrics.Gauge { + if err := validate_metrics_name(metric_name); err != nil { + log.Critical(&libpack_logger.LogMessage{ + Message: "RegisterMetricsGauge() error", + Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name}, + }) + return nil + } + return ms.metrics_set_custom.GetOrCreateGauge(ms.get_metrics_name(metric_name, labels), func() float64 { + return val + }) +} + +func (ms *MetricsSetup) RegisterMetricsCounter(metric_name string, labels map[string]string) *metrics.Counter { + if err := validate_metrics_name(metric_name); err != nil { + log.Critical(&libpack_logger.LogMessage{ + Message: "RegisterMetricsCounter() error", + Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name}, + }) + return nil + } + if metric_name == MetricsSucceeded || metric_name == MetricsFailed || metric_name == MetricsSkipped { + return ms.metrics_set.GetOrCreateCounter(ms.get_metrics_name(metric_name, labels)) + } + return ms.metrics_set_custom.GetOrCreateCounter(ms.get_metrics_name(metric_name, labels)) +} + +func (ms *MetricsSetup) RegisterFloatCounter(metric_name string, labels map[string]string) *metrics.FloatCounter { + if err := validate_metrics_name(metric_name); err != nil { + log.Critical(&libpack_logger.LogMessage{ + Message: "RegisterFloatCounter() error", + Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name}, + }) + return nil + } + return ms.metrics_set_custom.GetOrCreateFloatCounter(ms.get_metrics_name(metric_name, labels)) +} + +func (ms *MetricsSetup) RegisterMetricsSummary(metric_name string, labels map[string]string) *metrics.Summary { + if err := validate_metrics_name(metric_name); err != nil { + log.Critical(&libpack_logger.LogMessage{ + Message: "RegisterMetricsSummary() error", + Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name}, + }) + return nil + } + return ms.metrics_set_custom.GetOrCreateSummary(ms.get_metrics_name(metric_name, labels)) +} + +func (ms *MetricsSetup) RegisterMetricsHistogram(metric_name string, labels map[string]string) *metrics.Histogram { + if err := validate_metrics_name(metric_name); err != nil { + log.Critical(&libpack_logger.LogMessage{ + Message: "RegisterMetricsHistogram() error", + Pairs: map[string]interface{}{"_error": "Invalid metric name", "_metric_name": metric_name}, + }) + return nil + } + return ms.metrics_set_custom.GetOrCreateHistogram(ms.get_metrics_name(metric_name, labels)) +} + +func (ms *MetricsSetup) Increment(metric_name string, labels map[string]string) { + ms.RegisterMetricsCounter(metric_name, labels).Inc() +} + +func (ms *MetricsSetup) IncrementFloat(metric_name string, labels map[string]string, value float64) { + ms.RegisterFloatCounter(metric_name, labels).Add(value) +} + +func (ms *MetricsSetup) Set(metric_name string, labels map[string]string, value uint64) { + ms.RegisterMetricsCounter(metric_name, labels).Set(value) +} + +func (ms *MetricsSetup) Update(metric_name string, labels map[string]string, value float64) { + ms.RegisterMetricsHistogram(metric_name, labels).Update(value) +} + +func (ms *MetricsSetup) UpdateDuration(metric_name string, labels map[string]string, value time.Time) { + ms.RegisterMetricsHistogram(metric_name, labels).UpdateDuration(value) +} + +func (ms *MetricsSetup) UpdateSummary(metric_name string, labels map[string]string, value float64) { + ms.RegisterMetricsSummary(metric_name, labels).Update(value) +} + +func (ms *MetricsSetup) RemoveMetrics(metric_name string, labels map[string]string) { + ms.metrics_set_custom.UnregisterMetric(ms.get_metrics_name(metric_name, labels)) +} + +func (ms *MetricsSetup) PurgeMetrics() { + ms.metrics_set_custom.UnregisterAllMetrics() +} diff --git a/monitoring/structs.go b/monitoring/structs.go new file mode 100644 index 0000000..1f99194 --- /dev/null +++ b/monitoring/structs.go @@ -0,0 +1,14 @@ +package libpack_monitoring + +const ( + MetricsSucceeded = "requests_succesful" + MetricsFailed = "requests_failed" + MetricsDuration = "requests_duration" + MetricsSkipped = "requests_skipped" + MetricsExecutedQuery = "executed_query" + MetricsTimedQuery = "timed_query" + + MetricsCacheHit = "cache_hit" + MetricsCacheMiss = "cache_miss" + MetricsQueriesCached = "cached_queries" +) diff --git a/proxy.go b/proxy.go new file mode 100644 index 0000000..3ff07ff --- /dev/null +++ b/proxy.go @@ -0,0 +1,130 @@ +package main + +import ( + "crypto/tls" + "fmt" + "net/url" + "time" + + "github.com/avast/retry-go/v4" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/proxy" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" + libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring" + "github.com/valyala/fasthttp" +) + +var ( + httpClient *fasthttp.Client +) + +func init() { + httpClient = createFasthttpClient(30) // Assuming a default timeout of 30 seconds +} + +func createFasthttpClient(timeout int) *fasthttp.Client { + return &fasthttp.Client{ + Name: "graphql_proxy", + NoDefaultUserAgentHeader: true, + TLSConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + MaxConnsPerHost: 2048, + ReadTimeout: time.Duration(timeout) * time.Second, + WriteTimeout: time.Duration(timeout) * time.Second, + MaxIdleConnDuration: time.Duration(timeout) * time.Second, + MaxConnDuration: time.Duration(timeout) * time.Second, + DisableHeaderNamesNormalizing: true, + } +} +func proxyTheRequest(c *fiber.Ctx, currentEndpoint string) error { + if !checkAllowedURLs(c) { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Request blocked", + Pairs: map[string]interface{}{"path": c.Path()}, + }) + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsSkipped, nil) + } + return fmt.Errorf("request blocked - not allowed URL: %s", c.Path()) + } + + proxyURL := currentEndpoint + c.Path() + _, err := url.Parse(proxyURL) + if err != nil { + return fmt.Errorf("invalid URL: %v", err) + } + + if cfg.LogLevel == "debug" { + logDebugRequest(c) + } + + err = retry.Do( + func() error { + return proxy.DoRedirects(c, proxyURL, 3, httpClient) + }, + retry.OnRetry(func(n uint, err error) { + cfg.Logger.Warning(&libpack_logger.LogMessage{ + Message: "Retrying the request", + Pairs: map[string]interface{}{ + "path": c.Path(), + "error": err.Error(), + }, + }) + }), + retry.Attempts(3), + retry.DelayType(retry.BackOffDelay), + retry.Delay(250*time.Millisecond), + retry.LastErrorOnly(true), + ) + + if err != nil { + cfg.Logger.Warning(&libpack_logger.LogMessage{ + Message: "Can't proxy the request", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + } + return fmt.Errorf("failed to proxy request: %v", err) + } + + if cfg.LogLevel == "debug" { + logDebugResponse(c) + } + + if c.Response().StatusCode() != 200 { + if ifNotInTest() { + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + } + return fmt.Errorf("received non-200 response from the GraphQL server: %d", c.Response().StatusCode()) + } + + c.Response().Header.Del(fiber.HeaderServer) + return nil +} + +func logDebugRequest(c *fiber.Ctx) { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Proxying the request", + Pairs: map[string]interface{}{ + "path": c.Path(), + "body": string(c.Body()), + "headers": c.GetReqHeaders(), + "request_uuid": c.Locals("request_uuid"), + }, + }) +} + +func logDebugResponse(c *fiber.Ctx) { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Received proxied response", + Pairs: map[string]interface{}{ + "path": c.Path(), + "response_body": string(c.Response().Body()), + "response_code": c.Response().StatusCode(), + "headers": c.GetRespHeaders(), + "request_uuid": c.Locals("request_uuid"), + }, + }) +} diff --git a/proxy_test.go b/proxy_test.go new file mode 100644 index 0000000..afead6b --- /dev/null +++ b/proxy_test.go @@ -0,0 +1,127 @@ +package main + +import ( + "strings" + + "github.com/valyala/fasthttp" +) + +func (suite *Tests) Test_proxyTheRequest() { + + supplied_headers := map[string]string{ + "X-Forwarded-For": "127.0.0.1", + "Content-Type": "application/json", + } + + tests := []struct { + headers map[string]string + name string + body string + host string + hostRO string + path string + wantErr bool + }{ + { + name: "test_empty", + body: `{"query":"query {\n __type(name: \"Query\") {\n name\n }\n }"}`, + host: "https://telegram-bot.app/", + path: "/v1/graphql", + headers: supplied_headers, + wantErr: false, + }, + { + name: "test_wrong_url", + body: `{"query":"query {\n __type(name: \"Query\") {\n name\n }\n }"}`, + host: "https://google.com/", + path: "/v1/wrongURL", + headers: supplied_headers, + wantErr: true, + }, + { + name: "Test read only mode", + body: `{"query":"query {\n __type(name: \"Query\") {\n name\n }\n }"}`, + host: "https://google.com/", + hostRO: "https://telegram-bot.app/", + path: "/v1/graphql", + headers: supplied_headers, + wantErr: false, + }, + { + name: "Test read only mode wrong host", + body: `{"query":"query {\n __type(name: \"Query\") {\n name\n }\n }"}`, + host: "https://telegram-bot.app/", + hostRO: "https://google.com/", + path: "/v1/graphql", + headers: supplied_headers, + wantErr: true, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + + cfg = &config{} + parseConfig() + cfg.Server.HostGraphQL = tt.host + + if tt.hostRO != "" { + cfg.Server.HostGraphQLReadOnly = tt.hostRO + } + + ctx_headers := func() *fasthttp.RequestHeader { + h := fasthttp.RequestHeader{} + for k, v := range tt.headers { + h.Add(k, v) + } + return &h + }() + + ctx_request := fasthttp.Request{ + Header: *ctx_headers, + } + ctx_request.SetBody([]byte(tt.body)) + ctx_request.SetRequestURI(tt.path) + ctx_request.Header.SetMethod("POST") + ctx := suite.app.AcquireCtx(&fasthttp.RequestCtx{ + Request: ctx_request, + }) + res := parseGraphQLQuery(ctx) + assert.NotNil(ctx, "Fiber context is nil", tt.name) + err := proxyTheRequest(ctx, res.activeEndpoint) + if tt.wantErr { + assert.NotNil(err, "Error is nil", tt.name) + } else { + assert.Nil(err, "Error is not nil", tt.name) + } + }) + } +} + +func (suite *Tests) Test_proxyTheRequestWithPayloads() { + allowedUrls = make(map[string]struct{}) + allowedUrls["/"] = struct{}{} + + suite.Run("Test with invalid URL", func() { + cfg.Server.HostGraphQL = "://invalid-url" + ctx := suite.app.AcquireCtx(&fasthttp.RequestCtx{}) + err := proxyTheRequest(ctx, cfg.Server.HostGraphQL) + assert.NotNil(err) + }) + + suite.Run("Test with network error", func() { + cfg.Server.HostGraphQL = "http://non-existent-host.invalid" + ctx := suite.app.AcquireCtx(&fasthttp.RequestCtx{}) + err := proxyTheRequest(ctx, cfg.Server.HostGraphQL) + assert.NotNil(err) + }) + + suite.Run("Test with large payload", func() { + cfg.Server.HostGraphQL = "https://telegram-bot.app/" + ctx := suite.app.AcquireCtx(&fasthttp.RequestCtx{}) + largePayload := strings.Repeat("a", 10*1024*1024) // 10MB payload + ctx.Context().Request.SetBody([]byte(largePayload)) + err := proxyTheRequest(ctx, cfg.Server.HostGraphQL) + assert.Nil(err) + }) +} diff --git a/ratelimit.go b/ratelimit.go new file mode 100644 index 0000000..18e9f85 --- /dev/null +++ b/ratelimit.go @@ -0,0 +1,124 @@ +package main + +import ( + "os" + "sync" + "time" + + "github.com/goccy/go-json" + goratecounter "github.com/lukaszraczylo/go-ratecounter" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" +) + +type RateLimitConfig struct { + RateCounterTicker *goratecounter.RateCounter + Interval time.Duration `json:"interval"` + Req int `json:"req"` +} + +var ( + rateLimits = make(map[string]RateLimitConfig) + rateLimitMu sync.RWMutex +) + +func loadRatelimitConfig() error { + paths := []string{"/go/src/app/ratelimit.json", "./ratelimit.json", "./static/app/default-ratelimit.json"} + for _, path := range paths { + if err := loadConfigFromPath(path); err == nil { + return nil + } + } + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Rate limit config not found", + Pairs: map[string]interface{}{"paths": paths}, + }) + return os.ErrNotExist +} + +func loadConfigFromPath(path string) error { + file, err := os.ReadFile(path) + if err != nil { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Failed to load config", + Pairs: map[string]interface{}{"path": path, "error": err}, + }) + return err + } + + var config struct { + RateLimit map[string]RateLimitConfig `json:"ratelimit"` + } + + if err := json.Unmarshal(file, &config); err != nil { + return err + } + + newRateLimits := make(map[string]RateLimitConfig, len(config.RateLimit)) + for key, value := range config.RateLimit { + value.RateCounterTicker = goratecounter.NewRateCounter().WithConfig(goratecounter.RateCounterConfig{ + Interval: value.Interval, + }) + + if cfg.LogLevel == "debug" { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Setting ratelimit config for role", + Pairs: map[string]interface{}{ + "role": key, + "interval_used": value.Interval, + "ratelimit": value.Req, + }, + }) + } + newRateLimits[key] = value + } + + rateLimitMu.Lock() + rateLimits = newRateLimits + rateLimitMu.Unlock() + + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Rate limit config loaded", + Pairs: map[string]interface{}{"ratelimit": rateLimits}, + }) + return nil +} + +func rateLimitedRequest(userID, userRole string) bool { + rateLimitMu.RLock() + roleConfig, ok := rateLimits[userRole] + rateLimitMu.RUnlock() + + if !ok || roleConfig.RateCounterTicker == nil { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Rate limit role not found or ticker not initialized", + Pairs: map[string]interface{}{"user_role": userRole}, + }) + return true + } + + roleConfig.RateCounterTicker.Incr(1) + tickerRate := roleConfig.RateCounterTicker.GetRate() + + logDetails := map[string]interface{}{ + "user_role": userRole, + "user_id": userID, + "rate": tickerRate, + "config_rate": roleConfig.Req, + "interval": roleConfig.Interval, + } + + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Rate limit ticker", + Pairs: map[string]interface{}{"log_details": logDetails}, + }) + + if tickerRate > float64(roleConfig.Req) { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Rate limit exceeded", + Pairs: map[string]interface{}{"log_details": logDetails}, + }) + return false + } + + return true +} diff --git a/semver.yaml b/semver.yaml new file mode 100644 index 0000000..be80efe --- /dev/null +++ b/semver.yaml @@ -0,0 +1,15 @@ +version: 1 +force: + existing: true + strict: false + minor: 1 +wording: + patch: + - update + - initial + - fix + minor: + - improve + - release + major: + - breaking diff --git a/server.go b/server.go new file mode 100644 index 0000000..5b02d60 --- /dev/null +++ b/server.go @@ -0,0 +1,265 @@ +package main + +import ( + "fmt" + "strconv" + "sync" + "time" + + "github.com/goccy/go-json" + fiber "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/google/uuid" + + libpack_cache "github.com/lukaszraczylo/graphql-monitoring-proxy/cache" + libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config" + libpack_logger "github.com/lukaszraczylo/graphql-monitoring-proxy/logging" + libpack_monitoring "github.com/lukaszraczylo/graphql-monitoring-proxy/monitoring" +) + +const ( + healthCheckQueryStr = `{ __typename }` +) + +var ( + ctxPool = sync.Pool{ + New: func() interface{} { + return new(fiber.Ctx) + }, + } +) + +func StartHTTPProxy() { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Starting the HTTP proxy", + }) + + serverConfig := fiber.Config{ + DisableStartupMessage: true, + AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION), + IdleTimeout: time.Duration(cfg.Client.ClientTimeout) * time.Second * 2, + ReadTimeout: time.Duration(cfg.Client.ClientTimeout) * time.Second * 2, + WriteTimeout: time.Duration(cfg.Client.ClientTimeout) * time.Second * 2, + JSONEncoder: json.Marshal, + JSONDecoder: json.Unmarshal, + } + + server := fiber.New(serverConfig) + + server.Use(cors.New(cors.Config{ + AllowOrigins: "*", + })) + + server.Use(AddRequestUUID) + + server.Get("/healthz", healthCheck) + server.Get("/livez", healthCheck) + + server.Post("/*", processGraphQLRequest) + server.Get("/*", proxyTheRequestToDefault) + + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "GraphQL proxy started", + Pairs: map[string]interface{}{"port": cfg.Server.PortGraphQL}, + }) + + if err := server.Listen(fmt.Sprintf(":%d", cfg.Server.PortGraphQL)); err != nil { + cfg.Logger.Critical(&libpack_logger.LogMessage{ + Message: "Can't start the service", + Pairs: map[string]interface{}{"port": cfg.Server.PortGraphQL, "error": err.Error()}, + }) + } +} + +func proxyTheRequestToDefault(c *fiber.Ctx) error { + return proxyTheRequest(c, cfg.Server.HostGraphQL) +} + +func AddRequestUUID(c *fiber.Ctx) error { + c.Locals("request_uuid", uuid.NewString()) + return c.Next() +} + +func checkAllowedURLs(c *fiber.Ctx) bool { + if len(allowedUrls) == 0 { + return true + } + path := c.OriginalURL() + _, ok := allowedUrls[path] + return ok +} + +func healthCheck(c *fiber.Ctx) error { + if len(cfg.Server.HealthcheckGraphQL) > 0 { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Health check enabled", + Pairs: map[string]interface{}{"url": cfg.Server.HealthcheckGraphQL}, + }) + + _, err := cfg.Client.GQLClient.Query(healthCheckQueryStr, nil, nil) + if err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't reach the GraphQL server", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + return c.Status(500).SendString("Can't reach the GraphQL server with {__typename} query") + } + } + + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Health check returning OK", + }) + return c.Status(200).SendString("Health check OK") +} + +func processGraphQLRequest(c *fiber.Ctx) error { + startTime := time.Now() + + extractedUserID := "-" + extractedRoleName := "-" + + if authorization := c.Get("Authorization"); authorization != "" && (len(cfg.Client.JWTUserClaimPath) > 0 || len(cfg.Client.JWTRoleClaimPath) > 0) { + extractedUserID, extractedRoleName = extractClaimsFromJWTHeader(authorization) + } + + if checkIfUserIsBanned(c, extractedUserID) { + return c.Status(403).SendString("User is banned") + } + + if cfg.Client.RoleFromHeader != "" { + if role := c.Get(cfg.Client.RoleFromHeader); role != "" { + extractedRoleName = role + } + } + + if cfg.Client.RoleRateLimit { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Rate limiting enabled", + Pairs: map[string]interface{}{"user_id": extractedUserID, "role_name": extractedRoleName}, + }) + if !rateLimitedRequest(extractedUserID, extractedRoleName) { + return c.Status(429).SendString("Rate limit exceeded, try again later") + } + } + + parsedResult := parseGraphQLQuery(c) + if parsedResult.shouldBlock { + return c.Status(403).SendString("Request blocked") + } + + if parsedResult.shouldIgnore { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Request passed as-is - probably not a GraphQL", + }) + return proxyTheRequest(c, parsedResult.activeEndpoint) + } + + calculatedQueryHash := libpack_cache.CalculateHash(c) + + if parsedResult.cacheTime == 0 { + if cacheQuery := c.Get("X-Cache-Graphql-Query"); cacheQuery != "" { + parsedResult.cacheTime, _ = strconv.Atoi(cacheQuery) + } else { + parsedResult.cacheTime = cfg.Cache.CacheTTL + } + } + + wasCached := false + + if parsedResult.cacheRefresh { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Cache refresh requested via query", + Pairs: map[string]interface{}{"user_id": extractedUserID, "request_uuid": c.Locals("request_uuid")}, + }) + libpack_cache.CacheDelete(calculatedQueryHash) + } + + if parsedResult.cacheRequest || cfg.Cache.CacheEnable || cfg.Cache.CacheRedisEnable { + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Cache enabled", + Pairs: map[string]interface{}{"via_query": parsedResult.cacheRequest, "via_env": cfg.Cache.CacheEnable}, + }) + + if cachedResponse := libpack_cache.CacheLookup(calculatedQueryHash); cachedResponse != nil { + cfg.Monitoring.Increment(libpack_monitoring.MetricsCacheHit, nil) + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Cache hit", + Pairs: map[string]interface{}{"hash": calculatedQueryHash, "user_id": extractedUserID, "request_uuid": c.Locals("request_uuid")}, + }) + c.Set("X-Cache-Hit", "true") + wasCached = true + return c.Send(cachedResponse) + } + + cfg.Monitoring.Increment(libpack_monitoring.MetricsCacheMiss, nil) + cfg.Logger.Debug(&libpack_logger.LogMessage{ + Message: "Cache miss", + Pairs: map[string]interface{}{"hash": calculatedQueryHash, "user_id": extractedUserID, "request_uuid": c.Locals("request_uuid")}, + }) + if err := proxyAndCacheTheRequest(c, calculatedQueryHash, parsedResult.cacheTime, parsedResult.activeEndpoint); err != nil { + return err + } + } else { + if err := proxyTheRequest(c, parsedResult.activeEndpoint); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't proxy the request", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + return c.Status(500).SendString("Can't proxy the request - try again later") + } + } + + logAndMonitorRequest(c, extractedUserID, parsedResult.operationType, parsedResult.operationName, wasCached, time.Since(startTime), startTime) + + return nil +} + +func proxyAndCacheTheRequest(c *fiber.Ctx, queryCacheHash string, cacheTime int, currentEndpoint string) error { + if err := proxyTheRequest(c, currentEndpoint); err != nil { + cfg.Logger.Error(&libpack_logger.LogMessage{ + Message: "Can't proxy the request", + Pairs: map[string]interface{}{"error": err.Error()}, + }) + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + return c.Status(500).SendString("Can't proxy the request - try again later") + } + + libpack_cache.CacheStoreWithTTL(queryCacheHash, c.Response().Body(), time.Duration(cacheTime)*time.Second) + cfg.Monitoring.Increment(libpack_monitoring.MetricsQueriesCached, nil) + return c.Send(c.Response().Body()) +} + +func logAndMonitorRequest(c *fiber.Ctx, userID, opType, opName string, wasCached bool, duration time.Duration, startTime time.Time) { + labels := map[string]string{ + "op_type": opType, + "op_name": opName, + "cached": strconv.FormatBool(wasCached), + "user_id": userID, + } + + if cfg.Server.AccessLog { + cfg.Logger.Info(&libpack_logger.LogMessage{ + Message: "Request processed", + Pairs: map[string]interface{}{ + "ip": c.IP(), + "fwd-ip": c.Get("X-Forwarded-For"), + "user_id": userID, + "op_type": opType, + "op_name": opName, + "time": duration, + "cache": wasCached, + "request_uuid": c.Locals("request_uuid"), + }, + }) + } + + cfg.Monitoring.Increment(libpack_monitoring.MetricsSucceeded, nil) + cfg.Monitoring.Increment(libpack_monitoring.MetricsExecutedQuery, labels) + + if !wasCached { + cfg.Monitoring.UpdateDuration(libpack_monitoring.MetricsTimedQuery, labels, startTime) + cfg.Monitoring.Update(libpack_monitoring.MetricsTimedQuery, labels, float64(duration.Milliseconds())) + } +} diff --git a/static/app/.keep b/static/app/.keep new file mode 100644 index 0000000..e69de29 diff --git a/static/app/banned_users.json b/static/app/banned_users.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/static/app/banned_users.json @@ -0,0 +1 @@ +{} diff --git a/static/app/banned_users.json.lock b/static/app/banned_users.json.lock new file mode 100644 index 0000000..e69de29 diff --git a/static/app/ratelimit.json b/static/app/ratelimit.json new file mode 100644 index 0000000..dada06d --- /dev/null +++ b/static/app/ratelimit.json @@ -0,0 +1,16 @@ +{ + "ratelimit": { + "admin": { + "req": 100, + "interval": "second" + }, + "guest": { + "req": 3, + "interval": "second" + }, + "-": { + "req": 100, + "interval": "hour" + } + } +} \ No newline at end of file diff --git a/static/kubernetes-deployment.yaml b/static/kubernetes-deployment.yaml new file mode 100644 index 0000000..ceac15d --- /dev/null +++ b/static/kubernetes-deployment.yaml @@ -0,0 +1,92 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hasura-proxy-internal + labels: + app: hasura-proxy-internal + type: support +spec: + replicas: 2 + selector: + matchLabels: + app: hasura-proxy-internal + type: support + template: + metadata: + labels: + app: hasura-proxy-internal + type: support + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9393" + prometheus.io/path: "/metrics" + spec: + securityContext: + runAsUser: 65534 # nobody + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/worker + operator: Exists + containers: + - name: graphql-proxy + image: ghcr.io/lukaszraczylo/graphql-monitoring-proxy:latest + imagePullPolicy: Always + resources: + limits: + cpu: "1" + memory: "640Mi" + requests: + cpu: "0.75" + memory: "512Mi" + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + ports: + - name: web + containerPort: 8080 + - name: monitoring + containerPort: 9393 + env: + - name: PORT_GRAPHQL + value: "8080" + - name: MONITORING_PORT + value: "9393" + - name: HOST_GRAPHQL + value: http://hasura-internal:8080/v1/graphql + - name: ENABLE_GLOBAL_CACHE + value: "true" + - name: CACHE_TTL + value: "10" + - name: BLOCK_SCHEMA_INTROSPECTION + value: "true" + +--- +apiVersion: v1 +kind: Service +metadata: + name: hasura-proxy-internal + labels: + app: hasura-proxy-internal + type: support + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9393" + prometheus.io/path: "/metrics" +spec: + ports: + - name: web + port: 8080 + targetPort: 8080 + - name: monitoring + port: 9393 + targetPort: 9393 + selector: + app: hasura-proxy-internal + type: support + type: ClusterIP \ No newline at end of file diff --git a/static/kubernetes-single-deployment-with-ro.yaml b/static/kubernetes-single-deployment-with-ro.yaml new file mode 100644 index 0000000..6e634c0 --- /dev/null +++ b/static/kubernetes-single-deployment-with-ro.yaml @@ -0,0 +1,165 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hasura-w-proxy-internal + labels: + app: hasura-w-proxy-internal + type: support +spec: + replicas: 2 + selector: + matchLabels: + app: hasura-w-proxy-internal + type: support + template: + metadata: + labels: + app: hasura-w-proxy-internal + type: support + spec: + securityContext: + runAsUser: 65534 # nobody + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/worker + operator: Exists + containers: + - name: hasura + image: hasura/graphql-engine:v2.33.1-ce + ports: + - name: hasura-internal + containerPort: 8080 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 30 + resources: + limits: + cpu: "1" + memory: "640Mi" + requests: + cpu: "0.75" + memory: "512Mi" + env: + - name: HASURA_GRAPHQL_DATABASE_URL + value: postgres://postgres:xxx@yyy:5432/postgres + - name: HASURA_GRAPHQL_ENABLE_CONSOLE + value: "true" + - name: HASURA_GRAPHQL_DEV_MODE + value: "true" + - name: HASURA_GRAPHQL_ENABLE_TELEMETRY + value: "false" + - name: HASURA_GRAPHQL_EXPERIMENTAL_FEATURES + value: "inherited_roles" + - name: HASURA_GRAPHQL_PG_CONNECTIONS + value: "20" + - name: HASURA_GRAPHQL_LOG_LEVEL + value: "error" + + - name: hasura-ro + image: hasura/graphql-engine:v2.33.1-ce + ports: + - name: hasura-internal-ro + containerPort: 8088 + livenessProbe: + httpGet: + path: /healthz + port: 8088 + initialDelaySeconds: 30 + resources: + limits: + cpu: "1" + memory: "640Mi" + requests: + cpu: "0.75" + memory: "512Mi" + env: + - name: HASURA_GRAPHQL_DATABASE_URL + value: postgres://postgres:xxx@yyy.read-only:5432/postgres + # POINT METADATA TO THE RW database (!!!) + - name: HASURA_GRAPHQL_METADATA_DATABASE_URL + value: postgres://postgres:xxx@yyy:5432/postgres + - name: HASURA_GRAPHQL_ENABLE_CONSOLE + value: "true" + - name: HASURA_GRAPHQL_DEV_MODE + value: "true" + - name: HASURA_GRAPHQL_ENABLE_TELEMETRY + value: "false" + - name: HASURA_GRAPHQL_EXPERIMENTAL_FEATURES + value: "inherited_roles" + - name: HASURA_GRAPHQL_PG_CONNECTIONS + value: "20" + - name: HASURA_GRAPHQL_LOG_LEVEL + value: "error" + - name: HASURA_GRAPHQL_SERVER_PORT + value: "8088" + + - name: graphql-proxy + image: ghcr.io/lukaszraczylo/graphql-monitoring-proxy:latest + imagePullPolicy: Always + resources: + limits: + cpu: "1" + memory: "640Mi" + requests: + cpu: "0.75" + memory: "128Mi" + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + timeoutSeconds: 5 + ports: + - name: web + containerPort: 8181 + - name: monitoring + containerPort: 9393 + env: + - name: PORT_GRAPHQL + value: "8181" + - name: MONITORING_PORT + value: "9393" + - name: HOST_GRAPHQL + value: http://localhost:8080/ + - name: HOST_GRAPHQL_READONLY + value: http://localhost:8088/ + - name: ENABLE_GLOBAL_CACHE + value: "true" + - name: CACHE_TTL + value: "10" + +--- +apiVersion: v1 +kind: Service +metadata: + name: hasura-w-proxy-internal + labels: + app: hasura-w-proxy-internal + type: support + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9393" + prometheus.io/path: "/metrics" +spec: + ports: + - name: hasura + port: 8080 + targetPort: 8080 + - name: hasura-ro + port: 8088 + targetPort: 8088 + - name: proxy + port: 8181 + targetPort: 8181 + - name: monitoring + port: 9393 + targetPort: 9393 + selector: + app: hasura-w-proxy-internal + type: support + type: ClusterIP diff --git a/static/kubernetes-single-deployment.yaml b/static/kubernetes-single-deployment.yaml new file mode 100644 index 0000000..82125cc --- /dev/null +++ b/static/kubernetes-single-deployment.yaml @@ -0,0 +1,121 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hasura-w-proxy-internal + labels: + app: hasura-w-proxy-internal + type: support +spec: + replicas: 2 + selector: + matchLabels: + app: hasura-w-proxy-internal + type: support + template: + metadata: + labels: + app: hasura-w-proxy-internal + type: support + spec: + securityContext: + runAsUser: 65534 # nobody + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/worker + operator: Exists + containers: + - name: hasura + image: hasura/graphql-engine:v2.33.1-ce + ports: + - name: hasura-internal + containerPort: 8080 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 30 + resources: + limits: + cpu: "1" + memory: "640Mi" + requests: + cpu: "0.75" + memory: "512Mi" + env: + - name: HASURA_GRAPHQL_DATABASE_URL + value: postgres://postgres:xxx@yyy:5432/postgres + - name: HASURA_GRAPHQL_ENABLE_CONSOLE + value: "true" + - name: HASURA_GRAPHQL_DEV_MODE + value: "true" + - name: HASURA_GRAPHQL_ENABLE_TELEMETRY + value: "false" + - name: HASURA_GRAPHQL_EXPERIMENTAL_FEATURES + value: "inherited_roles" + - name: HASURA_GRAPHQL_PG_CONNECTIONS + value: "20" + - name: HASURA_GRAPHQL_LOG_LEVEL + value: "error" + - name: graphql-proxy + image: ghcr.io/lukaszraczylo/graphql-monitoring-proxy:latest + imagePullPolicy: Always + resources: + limits: + cpu: "1" + memory: "640Mi" + requests: + cpu: "0.75" + memory: "128Mi" + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + timeoutSeconds: 5 + ports: + - name: web + containerPort: 8181 + - name: monitoring + containerPort: 9393 + env: + - name: PORT_GRAPHQL + value: "8181" + - name: MONITORING_PORT + value: "9393" + - name: HOST_GRAPHQL + value: http://localhost:8080/ + - name: ENABLE_GLOBAL_CACHE + value: "true" + - name: CACHE_TTL + value: "10" + +--- +apiVersion: v1 +kind: Service +metadata: + name: hasura-w-proxy-internal + labels: + app: hasura-w-proxy-internal + type: support + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9393" + prometheus.io/path: "/metrics" +spec: + ports: + - name: hasura + port: 8080 + targetPort: 8080 + - name: proxy + port: 8181 + targetPort: 8181 + - name: monitoring + port: 9393 + targetPort: 9393 + selector: + app: hasura-w-proxy-internal + type: support + type: ClusterIP \ No newline at end of file diff --git a/static/monitoring-at-glance.png b/static/monitoring-at-glance.png new file mode 100644 index 0000000000000000000000000000000000000000..ef06b69cdae17ef22036e35f82703e95dccd1a1d GIT binary patch literal 343820 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

Z7^e!~?OF60%{k93mAc9LkX6cxnO_ zd-SW57ZwxUBufGngRkE$sp(Y07|MAnZznY+ums{LB_K;R;#QXoWx3E-P}cP#mkhyC z>P9a9U8qUY%!4*a)_Pn~YIpf%H?>af{(QRq(YI?pT-o`fPx$`SB7Tu1RMKWYfv#S`OpjN|{EcOJJf&p-e&RV? zmT-CxY4qOd02+eQpEn)iian#UCIYPF1kWxtoP2n>NtRy4AWIRiD|ZlDW9374scnd#Ps}yZ`4@=CMxO7v!>c&;#Cs#NNs{ zm%hOOWlOv+aD}VLmjVe`qGap>zxS+SaFbTL)t|RR8YKW-T=Uz`Tee}SaQrn=+k?0E zRHErqD>O-U>u&gdqxrf>7Z`JGNsx6LERE(ZMgu{E%BR!xUiziv^P-UV2)RUEh-NGS zR4FUdZ>o8tk^cu{{>65m8^wUo9?;Y3E;$4L7d%3HvZ^&-5bR9L?3J6-#2Wctj`xK_ zT_M!wdp#Ab{ORU^+4OKCn*$oabbX);-?e;b&ldz$>;Ey3;C=jWCtjj2=01qOCm+KI-ZXlRBZ6ef=i4P;|dY8Nt245u>>@1pB5*Trx(?}WgocV zomW-+W#bpDU+kA~aS~QS$=|3^ySq`u$4QBzb4vnW9j?sz1Ye@n3av)UT;-?i4{CWo zYzD@iH<{}Vx(qETk~a9<`_1BKy5!Nrk?=n5r=auB0a>OkY4yN!R9H+~LE`lWhLcaN zZq?#YO^PDC>af%Kv^fR(-o89vSNd+XHfKii^Kg;bVxL}Lz(!d9^a^i_+NzaKjHP!d7Hdq*PNOEB(n0;RX*|fLXj}LR82Tv)PwZQi0fS5mLT%bGdmk%P$Fb97p{FZ*`F zH|@d%)C(_mR$*27u4;YA!B*V*k@BZ?9DtPwes)znxurgo!B0y1^1*;Z5#+PNo!ev? zujiBo;ZU7{ zQJ>We5T%11n$+jQUmvYGQ*Pr6s!JG}_md{-ut9M0k zZ=z1T<3L&xh+Lt5xh{v(ZGL`(Kc{8plc$;c>JYjNGD(a93eFjPxQyY^61O_Fb(YfGZKf&ljf4B)BQnMl&>IVVz4_n zUJ+VNLr2Sh4M121V!`fQ;bH&DEhW(ee)&ljA06$o+)%F2Z43=eGlbe*#6@0iC3QCo z9*t*8KyQeb)c$YYG?L*0i#1cvo#C+k=T3s5WeX6GxT^a8YP#WQ z8oP3)5%nhg1T>OqC@+dNQNY!D6xtO_L7V09L+EA9q}E{@X7&DG?zBf1>l9W)ahcfn z%z%~p1a#uBW@|RA!|ho51N%$#s=tk{H1D?$TI}rCTuzGj?`2NvwOP|Mu|^f{;$}ea zahC8Xo8=a#QKwhVLm@jWy3NN&^F}04#!P}jhnJ-xTi~uLp*Eel@c6f321O)c3zT7Y zu)HCn0Z+zh*AA^UU#DytKAtKv5MyB%fcep}t?$FDPnqqB!@Snup@poPO0#dvmgt^a zZ@?*t*Z8m!hdP9YXO&50i!0Zi;5K|c5p@`4ZvR=>r@da(Cwlo0+AI2tc(Q0}A-dtv zo@nhKVxF^=Mdw2fzWqkw)XI-@8k?z6_VZ3lXJ7}}j zxc_bsTm|sD3$XIyG}(!V5!vp+#Bxa{qJEva?}aBlXkaG5+De&4qsr$PXnsU{Hxw6u ziZH$StF!SdP0^EK--g?5+f9||sJxC&Mw7tiZURE^k#}5YmrG^S3_-jg71dUqe6X;7 zw?(1gHVhjVo#f!1uSKW!GQ8dqZAhoP9arBTsj5QvpqiD>82@$~9QW|D4oR}}>55%b zez2MS^8UeZhC`Tl3rGCZlTXhs^907Th_1ga{eX_ZvI|7|jjH|IJ7SV}cNNTAEP#UH zC=u5=^Aph+vzS&}`+5DW`?--_@{Osvk4=JCCw^n*Vkhdo?!ptkmBzWd{jA#>F%!uN z=bd7mD6P8gnLRCkWm(0uOVTSigr^|){3%b^SF3nbs~&r$hv>6 zClFPdO+D#cs^&4c5h+j+F(w23uVE(v)zWAdN*E_i2%~@zMIeYd`;~kslJqYqhPmfvz z<)2a1Gq^cG!^$}P<|QA8cW@8X zJ9M69*NU)$g6Nch4WR|>N+uS^<@KVo9N2ZcZQnXW*<#y*~)_Eep^ww z(;5;6B)O}NpT~bB_^q}bR~}S@NBMC#)Fvt|oYO9_ia^@(=3`SwkelpbYrgZjC0Ivu z>RwMU0qrGMKMXVB-aOP#t`4S_-ZGW_A9KLL_pT->?5)COB|Qy<#fha*^xh2@z04L# zKgb=doaKVt3C}4LcTuKixEkkf+amK1lvDpy;d$)BgKgiU&8t)|_rX{TGeGKX*P@)p z0tUTdXRBJ(jLONv>BrjF(@#$m4L>@av$Gd(!#jUy6eZ}}#~@$p)R|Orv8~QKMR+oY zp*qc+sUI3VM~}ZzH`Y7|v^!D|OUL)EJVoESFi+ET|H2ZW)0C{smJHw7Hwzc3d)MA_Y<|vePk2&1EV7>@@AL>ck%!7HLVrasx;Gj|834#N*LCB%pt%NH#LA$ME z4$IMDOlRjEcZIa!>c*SxH3R3T$IV3_|4$L3ntn@5Y9o~^6j!x9jdj(E7Q{z~K`(#d z^6L^#0b}b^=cH-d5BD{Vv=U*;QBV%k&FLNfi;!Ihr=oR+m^1-yWmG7NsZ)7dzF(C+ z_aI-_tTVVyUnb?JB+26X4Y zqtk87if+CklT*r}?t%!28quCC+Ee_u<#w@JziDCNxE+67H!U-K8A^Twdz)F3QX0j7 zY23Lk?ivnrLi;6&A$DiqH+?b{z&o6oS8@c)*~!!;*z;?UWNG%bEd>Tc(I zd%*o3M6#PEwRM-&lDJ6`@X*41nZ718QJM*wTTm5E%&S4fZL61;N7EFz#iS%q*>yj+ zMZ-+A#znbp#$P?YZ_eQ2pn5sp8?^Br&2Dg=WT*J2FrLr{m0$4fym8!_0U{_kD$uJF z&$)|rfX%Wmb1y`|@J3pXw9CN25<0#Pgrvp1_b2+$2|~01e`9H+%!CVVe}lS~-phlu zIR)?v;h;C6wt4`x1n1r=5v>(sj&ICu!13LsbUXj-ky|nQdUPEcK7963aeS?KJLRgf z>^%CYs_BnL<{KEp?+LEO<;^4LJNq>TlhAa5gHNQK?mr1xt`Sf{SjTOL@N&_H6sW)S zX~>mc`^go~>{?I8H9C%e1;TAGVRkY{WtrA8uJCXu5WCjLu-dx&2pGba)tho(y1FLX z!IZ6B0I%ih;#+Jn-9Fr?IfLE6Q&t_?6@imgOBL8T6tv^!`QZH^k~JqHwR~Q8&2+o# z3=N=7+uBXzZc3D?7VVWz#H20NF(cPh55XDMuvgD$2-HE}S=XHRmY1}hW%f5cRtytw z{)z*ko!Y(;Vu%N91XsAmCY7FhMyn``d}6E(o1NP-H$M!ot3j#TEpQc{{g~szcksnG z%nP{eyF~x>vF1A@Z^WNudazUZ_z6lUm6|ur8pS&h7Tmsf$-PZm^Ukia2U+<$sDISH z4SE&bh7`LV1%*U8@``?Cc!@3Vv z)1FW_H~VQy-RwmfG^DmsUXYhmNUzPjvI`nwCE(;$U01khWw9cFUa-<+pG#^FO|@7) z$c32sKUwAY>&beh-?^QyP@gVV*WH;Ic#N2Ov>MVVKL3H^10-o0cz#)^(#5HX%`DX` zic~ApTUL}2T4NM6ohXQyX*qv0yjG{(snaI$ohCXlW>CnYR#Gh`*?C~!(k7KZgjtti z_(1XaM|^y|iC-b!%EXN5?D1fc|r%Xnk(fO*MO|?<7RvtR- zYM+^CGYQUElFaH?z9jGLsR~Ve-7-ir&y9>QD{>3F#y7nQVv@_e@b;gIJi}-kZ%6r& z&^qXLzPrH*a|ctxuxLYnw~}MW=x(RSa7>&MxzGYtZWL$yGEZpEYn*4^EVui{)JJB1 z$d%)lOLuhc_Q|$fcv&GihXYQg&2pMmM3noQ$hqE#r2mYdEDIm7IYhttU5c52V%~N0 zn!ba> zN|;b@sqt|<#pU{hxXAI|>GfJv1ISv=VZD6p)o?I;e}}qPdcJ#pYm7QL`~zTi{*WuE z=-zz5eR+t#=`yeEl;h~}E zG}L-GJ^M;vw7u9DTsl~7mf$&O>5@cRym%1b7CLyN&s93p&mP@s$053T1k;8bxad$>TChC1a-&byedN+CDiVheI>@|UwxQ&CZjEDQMg04;QDdWC78THe%T(y059 zs<{ptd3o*|UTx+An!B4qx4VJs!wl1#hYtm=?Wo6x$)iX|Cz7tIMITBn?ESPBl7JMP zjj;~`rzRFW?N>p+etAs(k zm9uYwsWd&6nVRL2)Hgb%Uygu%cA251UR^*--M~%Hk<6D3-8`Uh4>*}eSl}G4$(yd& zX3^4?x4cK;@o{a0?d_md1ad)o5?6fF%XDfu$+=+A1fjBAw!-G&X70faaLLVLiICaG zbkx|92CSV7=P3#%d3>DQ9J2uj!-k!M`I=@$Ss%r8(kI(AV%nB~ai_ejNIs-wT8xYA zhJu3xt)xu}k}sWS_doOcvX74sz>5<>Rb*FQ|0ZGKOjSrJ$89=Y^1PSS{mcW^e$;aj z`2XFcDVAr71Z~3SBF^I454O4w7)gz*)93s~92VI_8~fy^pR^wZXZr(Z~X+QF)8 z1M)xPIw!cIJ~!K$-1>en7MGCf5&@Y9YF+oWy(P47zxi6MjKp{MWb-)h4~;M%@ufXJ zP;zWmz_CiaKpx_Hw-#$|ozYUUMHmO;#*mBC|I*<6NS98C$3HRL_cwo8Z=EdG=lT~X9q`ci#Am{fP0Z!KMKJ$v{Xcq0vlFYGBc&3ey-nm)V<=Wqzx*0NB zl-hbLjA~~rn^LOaUJbkF54q=T9g&NLHSrbw2Vy?=QL(1P`sQ#Ep7^*{XDYZpHF>yx z)rjp9ef@0yv1w5$!%rzQ%GhS6ST{0L0`9_n*$L7ZFoja@9xar2-aY3xZT`z47M3uHVfWwJ!z%8X%BPBCz_MY zjhQC+*&UzCh?Zz5c+zjR<&l9KXd$BB7BH-`U#ebsM~6FsZzXsrQ02uajKq*>0wR%+ zJi6kdO3BMn^9ZUm-@(0ohsUeZ7%=e03$K7F68mk0*bqY68)R(?qoDyg3H(}GT0>D` z&x5(!|NQ!FcgrVyYaxgWDDvvTQV9lqhrL zuTBWN=T-7xv{?TR0D9$ZoYwhGK=F=V>8|MrVodiUMt zOG>>>lSTb>`Dnx98eJ3t>kqJ>Ym;Fefq{pTi0er@hsgjJbbLDI9yF>hmB3&K{i#x~ zr56XIGKIL)yUu6Vzmq1+yMWTexb>!LwNi)M9&*ZIw?=HWeC&l83rEJmZy%$e)%u4#CV)HNN~3^7 z48_*ea9=t_P75kN7WJj&##G|$Eps^#dU~M=)?y>$CPk1^Wb8PlC_Z{F=2cu)EEfgp zKR7J{Yd%Xa2O5@9mLA?uw+dl$2LM#;9NyIm=wt38WBShBeXZ*U9Q6;Gl*22mnSXp2#KOhNwJe zzx!ef*@)qZ7I@M1fY{cVZ-lds#HG`eFb7xmSf)73`-E? z>#&^#Z*sx{)yBP>(3eLUQt^RrDVr`euk2r=y*wf zdRjr4m==6dZY_-WU4`sDS}UvI+5N*qgMkemt88&7$Q6f2gx#^3&k3e{xB#=U2bS z(cDKG-9}q#_w`3cLN3ejF3A0={cw@tlRr$HA`&b#RVf#8i*dYCRDQNHzXb&UB=?=% z#D50uZ9>LaI@1lY|UY}$@d)|cBlW1^4<_t+%o4s)z5q(o%K~PWNZ&*Ole3qfNSJ zhF!+w34T-}KPCrKA&+oT@Cr7+D=sJ=7jwkdO7$0r4V4hP3Sw6fj~ii$-hb@w7OO-P z(Af1kAlQ1(Wi@+Zv|MAGNz&+5?9*R|Elxn=Fn6w{8>90ZWO&Du<2gj$ zbag`03|zJ7%PP3EY5t~$$fsZ>rT>=cD&j`KXq06H76#0828USOXDhqR4zI`~frFIB z){XXoR!V_{?`J7;fl>&ay*j}&Z|rG-q#;WC z7%UHIy#fBh-)O%g*$_M}Yj14XuAFk%w${9(iDy^<^{tqgAd=a#g-+c=`pgbRsC*I# zr3i}iK8#JslZqZkvD;4AFECpoX0bnz(!A*r>CfTRyCq$yhqRa=>Kqb$TxA| zqMdjMyaw|3$BJIKo~De0LWPE+yceuqiVsSn3Q4nGsl1wQpKjgKfp_+tVp?3^&NO|+ zRXf~cThN-#{Q(MX?Q%$-yH%UG{JtwVyv8SrS;048y(6Iibb#iwl?1!+Q9VHR5MIl3 zmmReka2u4F zi-48T+YkF!V^uizbFp_uFvBDCZ$5rg=a&vZ{>WG&L&0EcZTe2Hm&+wJdsBa+tyU$U zbS%vET-8`DqnN&=SfTf8lIr{9Vylrd`otRCeOwu8Aw}&_XXIjr5{iUN*L~ax>a<^3 zK~lYo$*O#*P9@OLr480^qbUL9fx__a(BX7Ec56q-Inl>@OpNo5073he7P6eh;(f*Y z(sCQBni%^1Vxh+@R$C&sDI`Ks2rmC&T?zN_X0@TeC~zV>bmM)7DHryN>jm?9>+MKM zT24y*dtNb1FP5GyisHH+3OEG0g|1hb9yR>Tlg;G**$WUNfe`^L`fXZJU^-ns4uZfv zA@@epeEzOTZJD3;S%A!1o=MWy+q*KE(z(YmagoN~&a2L5U9c?oU9p=lPx=te=d}ZY zfo|OP;CjA?uU&?=O9Edu(!AXL7}%2tXq=orBn1LA-HaIJd(_+XmU{RR?P1X_IpWpy zKD|Q%h{3gVT!?IDiep#98@oC9_S_-7-EgTfU~+;p`Q0H|+(ae`-Q4ZrI*YK8Z~H ze9WPx)-0e2SB@i{8X?I4#t+b@I4{aB_ecA08+(;*qe9+__^;WOc1dc#otX_i$>-SW=jMwr<0j zPzc70^PQc5nhx2I8<+v`pY1lXQDsy?$9-AfK&H@(rNM(1Eb^r^P$sf`939q5uM%_E z!GimUE0fw1uDFJv3S634trN8v0 z6SUTw3bS^Oxa?GwVwfVC>C3-`IISipI@MAcrOJc&4w{U%TT@PXrFTGAeCm zm*qE){1r#Df4`LF<_^zKrAeSV#|Hp*Fn9na8%}>nrr1u?inI6xN%nr~d$jTU=-FlT z8#4lva0EMOY>4i!ctN(ybQQYI&e82HHcakyvB!=9g54Os;+4)jn0~%-Jyr^|wT`R7 z5I3dWAbP&{pYcYzF~DyD#qZV}HktaXR`ARbeLuc$^$Pxjb{US3_FhVfScm6~m6;ep zzFdEIHiNF!F!heO@O%LP;S!eS95iiqT*7>=JZ-v2S=8kDr2qQP$z?T^-8;md$i+r= zV4Ht5RcCZk4>xKWR)e2zo+FoyOuX9I*wc1KZ}~U<=$K?6PJ2n^QsWmdOo0^w{s#Sr ztooH^0pRlR8sd(%kfh5|b9FjND@lDYeLcl(o{^veZtrP{CuUq5c^E!P!qJFQ$m6e% zi3F(1-l7Z!yfc2+w@$Yx*W{*)blrq4A%dM8bfmeuYTxo=!n%jw1Q+`BmD zCBU64V@b3a)$x3po~o?T<%bbfHC$5y*jSPwBSI@guHG|;09HPDd3#{_b8y@%;zFcAc*Yh_63wo~>vOd7oH~u!J~Ox( zLLb`Nrn_?{pH|c!#EzCHXg_ zAWNJkedAChPoyGZzfaFna(15ns%v;AmV;=|qer;AXmDx7+rU9 zf$VF=0iyoKVxm{~C@&2aYO{ZgP@Da-b+ETm z<=U*`7aSob<48UQU(4@&>_{O*NLXfYu>Btu;%Z@moIPcvEttE_rxg94n`u$O>K9<-9z_gT@hcdQpc42N@XOyhZq(*_RsX#j-AV6**BvRe}K z-M}$h%pAncD0O6(FYoAS0RNvn?3l^@=_3ev1&p!z-2=FI8Z^0Tlf_?XZ zDl6?{LG8ox-r{hoLQ*;y1qy9`_yDO2h7u&UK-2vsSf|Iw_2GGtm{?;y8HKEk%Rg9S1+|n;Hs}rtNPYODRL&S}Fa*BRNw%RgZ~Q3YJlyM7<$=Yj zmnMGSTu~oJoHCwvUhrm!WUJ3nJix)qqZQjM_^7z~?2OWiS|wuP zR>rg2j$PLMx6RTCL~t_i+ZCxkqS#2j&#fo%u)`%DE6GE?L-u2ygy6Wh)0=e0i9pOKHIPwNg?4FSgW)g3taq);1-4dgH6)eGlgy0 zsfLtprnXpkN5KXWJctZyxk;n+Eevf61EJV)-7MPK>}f_{#6Hz6YI&zcn`sjo`%%c zX}3?VJ2!5mw+;%Mt&?e_KT^dFz9i(e^XlObH0X)hmeyHm(}rK`C>w0FqKbgD zChIXd(J!TFyeN*8I!1rs0FWZu`5f}iN6TWP>*on>J#3tc5PsTc;`}9GB;BgHK&~s> z9boQ?fV=>N%r1OpRKYW%554U#4ByZ%XN_I#V;BXed@e+wd3MeZ+xR0=Lsrq zUhyr_&?>K?-}z*P82JY0lZMh;ig)EB^#9c2en4pr{S>=|hWZjNOi{>3VbIwYJ?*}! zY&T2lJ*}Y9P*KI1*8hG0T@Z6NOMZh$#7=>f-(x>{q1Fh+ZRkp<-BuI^6) z50T2D&D78K9*xJJ92x?|$DiY&g06E>Ta2rZU%w6ipQc6dTUYp3j%SIZiT z2rjciG+Bo52_BUo;j*OnwxG+Sg46kp^dfmNHQkPoKm3YwjK@4jMahW&j%Cp&GnG__ zZ6D?t#4fG)9_~O4P$PoNz3i2Xnvzg-cW^t_Z=%W4d4^d}t|u$-N7y{z&6bS`QHkXr zk>c*|G2*7N9Xd4F&WChux5zzvVc<2^QW#B2?1}G<2?Fm- zI-UBlBDTl|jl9%Nh`#B0%Va}^jq90r^{^kP&U!Z{Abq}$vsIe@^tk}`FArzND~BA_ z=X%QC)krHnzHcUR`!BvQ@uITlws>IfzKqVl3?yfNt}Ph+4XY~*L>29Q`nu*yIB9h> z`ejyymRQjBAY z?Gk@jh5g1R;_pbiD(@*&wzzNq`uvDyXC5^}ap3$DSAE zd#^|LDO@klgX#M9IoAAkFC|Hy6dmeJH9^Be0oh8TVEEI;AuNI_Cr(#i9_s%&R8*s7<>m?x) z3F-DnWOx`9M6a$qD{NRfSFjrC+^$u>*udo^en9$-7aXFG$ml5Y6XN(mZu3LpR!0B; zwfN^+UNij!u}W695iwl_{XRdVC8w=0a&FBFV3y8Y0@5jrI8=~rV&+=%)9kSkeP&B> zKf-V9&|b)0RP?q}WfnSjI)0pdtEDp(q|g;N*Kn*C&uR%fNOkhqVKf+@{B^lTblPRb zyKLn{&*c(cuES|j$*&svAmcJxt&nB&)QYS#j54RMN^WpYEs>s7Gmwu3y~Q7ee|Z?3 ztM(Iq2>WO0%*o2Xb~i+CWF$8Ee?Iv8H(Z(v$STdjfdlNm8&@SHeVXlY)k5OM3YqcE zPw%mw#=CD6!6z=v{ji4gXD&)7hzTO~K3DZe#<^Y@B6bfuT7RiUS z0-D7;al<`aXXD^*@@?R~47kqh2CA$1fDx9b{X;eI8rYB+;#na!SFeE1#!f=8t4RGMxDXS9mSy#u)T0k9C;Kkz)RlmrJ2+j&*-0?*nJ>B&0yU}Qw2I;}ZCEc3CvhwhdG2DEIMcwzH!NC3U zd;aT_(xuls&Euj|qP_Ha@|3uHL#CV7tTPK{9^c%`Pl=N&SRC6;(kpS)V^3i(_~ET0 z>0tQOzO%f|-qH7dn%&@;Q(u)nh`${D-W52~G_uXnP$F9lFRAQKRx zhkKGP(PfkkSWLVnUHds@h$Xw;WEIk?zWFM$ zW6#>|dbvoJR(@&F~b|}d&_jdc>gVDqkPHD z=Uk}P-KMLmep{e(`UZi`KdzfT0??_T1pXl`r`>48{~g=lb0Rj$E$1ut>NdFfG~*SB0-uFC3pUL-nn%F2DG6~ZBUoqT$u ziYVd_F08TED)N1P<}}rx)GRzt>EyVpDxTf$L4r5P`VP=M+<>HBuIn;gbx@OTx`(Q3 zTXB3L3G>x#Ag6A_gw)!bugG&!mb~flyqcasz0Lx9+yua2nl|1&98eqHm0N5S}rBL41N-4V;Y1aCj}?CHDH->ua+$ZSiSv13axD66-barzOOqaqA|- zhL!gb8c3Tz*V%Wd$3r_5cO3rVn*S^h$MpJHzccThfiZnhSa!Oke?snc_2DWAyh!Tx zy%!?R>iUUOvIdy8YMaWw&INZgXzMxSmip)M@?H1yJbJB>tKDJ>vF0dUwf(NU7WC6i}NxHT=LU#Cw-!Y$u^jp(Le>TZr6p6uK zXXYp1$@U!JzwT!>_y~>vOT9bYOttq1cbRP$z+YJFeUR^0szFhwy6LB*ZIdj_N=hUR z)MLTpS)TrCoJdNWspl0_n_swtBKUbkj(-`uX0l|FPEh0T&+lqRi(dXdr}P@60%n`V zqLa*t1RA+VFlIlH6sW+nZZDF7PoLkw0h%KpdpAhBQR-q^#nWnSS7rDeR9N}*`$lUI zHme_C^`4*7!OLDH#(o3hth}Q<7e8M8YLB^S$H?(+s5zQapJsC0K1X=kLf=3#;-sjz z;&8L08cXcE1SeTD=&F@pgu-4vgjkE3s(le0(q*bq91C+g@Cah|`X(VT5*BZzS(4)? z?ACb$ey$I~B271(oK^Y_kz}##;y;(Id6im5#=#N)6cQk=?UDWFa6B_bUSFSJxzo?r zkBD^Px7HZzO*&?9=Z&y9wy+Kb=^YzlFunDTNVFB|Dt|U+6Z=88Ll#!=0LJ;eP6f33 zWEG&*N1v>S?I`ZT{D;78OUS(27xmQf8~njOcS7&G$dr}|pPn?(o%?D%^4*%L4Xx#3 zzrY_wq%;9MiQ)-x173LSd)tyNGrs{`MMqLMEF-}(qsU%j6e&witFsY|&>P%b+A_rU z$-i=ELYtV2)4@|Ns?ht1V;?Wm&Qf$|vX@R^2WN9OsH)Vj3RFq)yVq3&xFU48Ls(V? zmi2MOkrJZO6^dg^nsD72QJ^etNJeTYGm^F)(R>1@baCQrH$!1 zwPnl0%nVf35(sTRy$)CP+A3j=IjV@*UN@7Ch#w zStywZ7$hG-N*-d6>(s%$V~d=mYQOzcXg+1<_3^AS$7n@_*}0fonA|@Ssas3EXaou8 zuWUUxKX-oM$Wb*UNwrE;-_P$NYzL6IAnnFxhWGz-YEx<^sea6$Ax=`q`IwJ9LXRt- zD78NaCubStgwj9r@e4nnDUomKS9BcO1|S-8x{L1N%9xKflLW^JQOKt{h|)s8O!X6$tBbkZ9)oWSQT`-S60;m}Il zfK)+cFnJ;aa8dO6gY|}54bVvPH^fBg@f|MLKDzD_`;Ak&1Z@TA8UZx+R^-6(T_z>X zQWQ2vXtH_*&?pZTHnYE-A!OMO!>a#*D~u;)24NpZaT1;w1F{;9T3|y73E*lkOmnkr zTQcJ7S8NucNylKIvlFl#mjXgMB@Ips<~W6QJ_X3#r&fATdVv`1%mMbE z#=Q4sT9CrkL=VdXK%_*oU8H!LIZj(`@a~Y6J-uz?yRDmzk=CzhK_(E5z6eNtv!t#t zqrRYjd~PEu<$5z%Q)LUAy5?soy;A(B7x&;&e*-OjYF;z)q#)Kp3rZ$0^Jr9Wn;W^t zl=4vowpspb+N5vKVejcA-*T=wp@9-B*u)NHyUyn+|yV z0N=Lht>mgLnJ%h2wWz_RglPB}2!nPW|CeR_2YUCLn5k#IbNUCM|HBylLlDgq1%A$1 z#pN?rIi3$yHS^0w_0`LY9O>7DIP$276qE7S+|_L)8IMZBD;`|cnf0+ozS|A;xv)vvu!ddx40!vqd{vwCN)vh zmsZrMqh>SJXHp7^+%BWme!sx>IO*$Kl-blBPTuI@q5%j*XvsIc$~Sr0;svTFi)z1t zW&AVvJw#3#j;ZapekuAjZFQ^r1vhm!!Xt|DAC1t9ZsnJ)1s=kU%I(JVg`%g#@m2jT zy!;=&%dBjYuR9N>r5Wnwk=GG2hN!lDX#i(sq3zM*5`1{-$ZzcH$9{sTdp*XavraVG zZV?~~qR$L|2Yy*1v8Qq!jE-Gj&sC73%GztObZkpgHOMPc-T~M5QN^`rktED79$+*x z|LCsNC*SEigYK$Mc^|%+3)zs(wK}Y*Ev2`$gb7{CNCti?is%swqOV8A`SV*DSXn@` z^=qMlmd>vCX2dTits|M2c@-+WcWe?X4=Q<%(w_uPlJK~(g>vVh%6d62$zpHyZi;Bh zeA+qYYLdMWJwp>ZQCQx(a}EdTwojL4*1Y3NZ;ZUN;H+-R0*_CRfPH6=026B$-v~jW z)_(Qd1?W`(wbM1H^;oA>X9imojr)<;r{ZX0kmKWC88oMBFHH2gg~aJH|2izpkCsZ7 zj53VOshKuFK)Mky>Ki&|8iqpqx6Z2mr_QA2P!seG#s40;bqp!0lO6w0*TWbw?g>D9 z)+-bgLjq>kat!08U%GZM*|J+s?T*{bswe;}jX$YM$|Ap^DZeE^_bBQ{=q$b1^|TUz zYAuAFX0@z3zprY=*uN}wGxmReKh(<+qCkSxQ%lIV6zYQJQH0)qW+VuMXjwBRV4VJ; zN`K4*x(_6pJHW$^B3Z;#+z308`0ph^*eyDKB4ixXqp*iaU&7VeE&KTMqS@s-TWW3w zr&9AN&MM4!kmpj*9;S%}`udogCmfCU z!n{S<-(z`Zmjd&wLO^370f&Wo?p<6kT1%g)f{F&yw>xw^CN1sdF@cynH%=i?<&OSx zBE2xW)H!O>!n>{%ndIlgN8>CW)*RG9405_Cx7cJ}4PeUblj3;PoS}!k(e!bnC)&7$ zpPpopK_3N$#L@J1c6;9Oqx#GKBj_G9%iiw_1@*g5q4cvC%qRI}bG283VUy;~2D_}Z z4h)lF#Kv9vp8=AUK*Mq8@|2}y#iiSXUFCS2>JGV!4xrLwAGzOZ$gnR+gbuLUm|X31 z2cr!f2Xz2Nz{9osK%QaEN;v3Ix%a=?Fck87eKx7;S4K~P9pLo~oo%LWD)eReFK5vI z#}HAln5_O^65qdTC+>U{W5^K=mJjATSmEXaCjwlk4{j569ukKCmaGGWSW@fwa>@rBfocmhh^Yf$}G97ZYiV zRBQ@B=q&Xo?F{BfkvqcMk?NIbC_kfrzPe~jqqRXQHTmrQ=Vf#n{?Utn+EazaGv6Z8n#KYX_Ll9TtgeH)CVe zzJI)Fa6s9?mQzy@f4z1vz>qAAtW5 zq#wgz>7ZM43u?4l=sv6`ZgMgW4j3w~9X8-PKVq>zX$bH^@e4pl-|-OpKDs>4X1!40 zl_@>-n&g%B2C?G6s@q$v)24%b`Hx8s?N1mx zKq*D~z=9dfI`S!BeQ!hS<&g;B$E(v|3$9{OV^0o_Xp7D*2lcziL7w`m9IwOA0t7s0 zgzSn(EeE`3+exTf6@zS%C6dq~dZv^4)LV;=qIA!w*M}9y(U_^C1LNz0$q!IRlKme? z(g!Pg#8wON&^H?jNsU$E=f0vlN#r*VTja6OHO?&0E`CMG5jMbp5LqtGPsDD74^nwW zY8>srX@IM#sxJ8gGFSZXF!r1_4XatfRXEr>QieZtAavEC*`I6)BESkvRmc2TPA8J! zAbHMhI@0#0a3NrHRwE4v%6va^i_3Oidr{9@YDl^R>cfMWi}OCsaf$9a;M&Z?eq9)b2h#viFW5gLiPwe^ zHjisdz(}rW|8@BMO+MRnCd;=PpT^6#z^UX4JY*enJ+5ux7z<1q;Z{ouC{8pdd^e(C zJJypN3g~tpdxhhJcRt?bfOWpy@m8oP8;c4aGNxB>{chfZ)n2_+SQ-d;f9h%)JSxPD z`1ti5t((7On;mfvE-!Zv|1X@gL<;)4{ZVj$=;TY_Cv*?JH6?N~;pCnvmxGnk`>tmXm2lR66O7yr$m5@m94= zKr6%8Y5*FbEGi*JoC`C{C-6`a8};EMSyFQP_w~~%(!cFY8oAI<0o2ZQ6Y#=wCBA+w zXFkC0v4X&F=GzzQW8Rm!gvh0dzI0VVI&Yi6Z0lUbY~fv;Tm^CA&tId_tT6rUl}eKo zd^aep)xsjI^`Pxht;KLqyWL}bocn1mDaliHAe`V6+Uj*a4R)k&ZL6jjxK7 z8sn|=&?M$qMHQ{UlpdbUo1Gt;Tp26c>Wqe@CH`a+4l-hzEF-3p_0A)Pu&L*sF?^t} zUD_i$3mVm51+WC~ZP7k%W84yZ{f;HsOfb*goGCwPgKdp{M$8J`))x5O9nNuaQCl+R z=a>G0sb~DS`U#Nls25(Fi=r+Wru+}EJ-r_z&gx8S4*Jv9q4jmkkj()wC8QivSI%z2dggy2!T$Tfk28H@UwGtr8mT>Z8RIwpqT$Tnl?|I=AU1drnq{h zf&tg59A=)^IVH+`jGD>Fa35|0+Mp$ITy8w#54?8gPb0*)=C>b@>AcD$tz0qAQ1^G_ zBWgWwc40;Vy3=;wA6fUJ z$#z9-%Pi``p8WozOVs_u92J^j1KnYw;}?b5sb1|~{lpjB&|-;?UoHJ4;>%GIB(ujc z*A|{!C0u`qMOwn#Zv$8=I+%jU7jHCRAN9k1|Je3)(fOy`^tx~5ACD4*iz&{CHN%7J zmq26_xvWfn%$Q&H%NgVY?k*3G0iv~z<~6pMh2fQgiT^n>CZ{R^TfV2VMt%%=?}Y4! zV$`UL^0~KgC3D$z`SH%aX{jhEnV?L7RH&lrxJ~i?7ogyJY>Wyi*;4G+7YNqGh*k~( za-|={? z4Qle&$lH77-&xGwd!u>@L8iun-DJgx*W%V!S#L)Ns6%n^*Wvd&el37_EQ+T zbN1Z>@FW_}{h4H2#;&S`tkxApW?POHh!w_vUb8$sfs8yQ#0*D}b zZQX5be>vxNP~AEe3&7VN5NA_q>#C~ifT@`PkesCK!QDdjxsNHix9HZfAM?7x{*(;oaCh1UUI9y z-fJViwkma*6^W>nc=1S}(vdz2f6sJp$S!OF(VdglvUME7h(!vpWl8>u)mwh571Oc> zHWZ|hy;F7qK*>UuDKCPUF^AZ*n>9|z4d?xWso>mRzDxhUJNuIC+}|=Zd8nxvqsiQ8 zZW;$OuiBr`Vv`&oOH?J2KL{ak==_=bC)Jtzpc^&RC;#=SlX~5Yso(ps|q|;z~Ec@464yTB1M89*iE2sfHGnl^H z!<;}x%SK|uXE;5l0%~&yM~+#!N_BoT4_HL}lj+2OZ17_E!tJN}=P?~fc2{8|DB*=9 z$8MvY{?rr!->*j4Pz3=oYjpyi3G}x~E>~Bk>|Lust+lr05x~Op19-xK+|)DNkk{=I zmQ@l%Q&EByeqdZzWv^X=&?pT+QP|hD>w(H?t5S*+SWM?3;r4WbA+_x^a zi4Br`WV*Hk_4}d{^+I|U@iFrNC*Z#*WBI}`jMjzC#<|?;FvF2;hMhgPnY3@MC&f3i z^+%~VP3HquAx;)dYVb~Zp*Zmz=0!rg?v3OB2ldP(>;(c7l~@Obgcd;D5Ox?G;uKir z@|be%58T8}_ONC9+`yys$6kLE!xzT34{dyp_G{?RWl6;H>(P%H&R3epeYVg5F?~Q= z;0umuqSg96$GjzL+8n*FC0fW?Qq=@H}T7I7bA;M#x~M^Gvy*yMBOM--yQ6aV<11ArITSOnMD8ZHAXMl64RmRJok z6qvTqD`k}jmq_yxDe(j3biB%P7`r`I=BU@R%~aBe!{j&x#53Pc~lq z6;d7XY(zzXMc_#=1oNy33V2C8T@Ab&&%(E!G(kd}xin;$FK`S{NGUmNltLZ9Ui zUz8-v(|n5*5Y)iZ(~+mrDpxH4V;AZzH1Y4yQQ^% z^gN+wJ#ZWoq1BE#EiEmZ>h1X5N~b%U1}WN|4RF+KN!r4cW&h-`Oxb&y2O=e*YS|69PA6m9_J})&?>pYemY!&`) zkqeUFf-9*3iOIm=@`>xfm1A|unTa(pVMt>#aKyvp?#X85#omjyuIvHQf;|%M$0BXQ z_qJDa3RMMbwk$;c+kRria**=D1>W6^>m2DYmeA!wCC{|%4k1%?bJH&wX6=j4k6{;s54n|Fl*fph*;v%&G7BY9~^ z10|=>^Lz8d0aAzn39cRM0-nM|*56ms$AhLju5m;frTKd{&>L}^FZZ-@ymc`#8k@kSA48YwHq^^ya^mOEv$u28k^3yZ zHkN_dS5mvo%w;Bb{u^EJ;!FKAVHQunu?l&l@i>wGJF2p>|lbrvNPgPjTEK)%8zT zQQn7dZm(P8ZU&v5Z8qnvY-83sgYO~nH%Xq*>3a$C`{~Hg?NyEPWg7iZdZ-!|82itJ zBOmQKAVh3rSF6H|Ac+l`Abk7&jL9)m-Z!1~ccl$vMoOH;oXoKsMXfKi=c4^mOout~ z-|;{Ug7vA^HYNFd4zR=IGpUr7<9wi=+QzU0UjVJHo(;6>4cMje*c677xc+SQ>7xq1 zJ-r}e+pHZ;x=tkB4@v6iNEerY4!fW_yUu%JoY3>q8@U?-_R++7sX=u(HRqb#qv_%W zwC5+QFW~^9F##ANsx6L?2&kf+SHj&Wu#bNynF-ovkJfAU*pa6YI%B^b#EH5)Sh-)0 z+QhU=6BW7;B;r5AtUvuq(NAQa`g)RI;f=vJ6Gbd?Ze*0>BbHvgzlN%I<|FPBQF-I~CpVNGV=@x-SyJZN_SwjVN8qh;! z`gr%jP~7{`bG$&M*XwDuLjprIKCS1dkg1hH<>^UdWdrBTW~IC{3FR19fA)eRO&G8Y z1V(sWR)l%uOyspvuC(6LxjX7MIAJ|JUY14OTL^nz{H(T`8Gw>f5fk|A{SO;QeV{DM ziSg%U_~$!A4d)FV)WL5KIbqN!R>IwROpLS_VZ(hqS%fPe)v-&gsd}k0YbYQv}zd2+p!gLfTWYW)#G!rRCDw1Yy??Rnh1(<|(G^^d3PRFr<1$@3NT}jf6)TpE-WV zjzY){T-`Acu23sR9^)#ZVZcJ6^0G)c5)QSUui(-qB^CARb&5MJlR}&*~A7 zWIA0;@9*p0M%Z>X8Zk#eEse&_maK#czAGt z_Gy=iVhaaA=}~l*@|kBg(<=rb(QKWr(V z1;%yxlnFXRI#|aM0+bC#CwpYrU2R8R3nEC32Bdi8k~&X*CKB<=CiOf#^&zhLADx4% z+_FZ^XLLj!@Q&G!AUBxeN#B~4czYNKot7ymDd0gK!(;u4ZpM4Z(_sUx_U2yrcZVvG zz{Lyx)2`?yNXlm8%+9z4?!puQC4~;?o=*79jGkivug&t{+2qRa?YwKK%0a6x!9Pv^ z&-nTiOEF5m-_64I`7rI46qu2$=C7~5IF=_)WP8qRrT>u_RZFgZ>x_yK5BPcmdYS_q z4{1CFS&a5y&H!awfP-ibGbsU^+sBWyD_2n7`K`s!H;V(Gy9vR~Wn_%LQeCuXR|=iW zZN+6&}PBK zN3*yW7UMyyv~Hc`Y;z^|PP)>3G{*g0Z^JC&?~(Ga*%mgoqM^z=gG62ANye9MRt>*T zi>#{e6!wLCll<)_*+;EFv(1Yx%K1T9ZUo94k?{0`%uts}LqDARckjdbeia04enXey z3hz8t_Jl`x3%m zM=WWTfcM6CZx@sN#vbT?>YB27cHXE4c4GP45vLr4I|;A5DQKq=rEc(=cG9FHuo*l% zl~|(uj1rnSBv7W`DxyAVvW>=Sw@_0Zk{{ykayWa03aX2A+a4m=LKJQKB{#sj8DjA^34dSO}x(+UrIAhSOhTpY=BslLc1x@ zxKGqeJnGRd>7wPjmi0|AEyG`BP9|aez~=AW_!zlYHGc(>O?HA@8Od1ZI z8mS8t+g_f-P6LlIBav5u5#&>PBaII5l@@=?ofN6zR%y5i$5Cb&yz-i>T)?eN+J-Kl zME^erBu0z{2YN(UD1=KIvB7U%ut^d-z^xV(Kr<_@AXuY>1++E!Tte^>_TFn({lQkDPKz&@ZG@s3yvSn zajFZ^BI zHnV3_>>2Q6?}~{u@U80$qoK@h=P8>%-QwSV<6l3dkHcv-u!0?TM$UTs0ewnRk+BU8 z)8iQuh%=J;!q7KkMiy(zf*QWx1st^3)p{75j|^)>NceY4R3w4h?N)&ot+sEfiv9n5 z8|BBgdT>E$X~|H^J=^V&X4t@VY-r$d=yU~A0&gC(&cYUse`G}xr>x|nLO-jH zYOHqVx!hyH+mrYM#E+U93!VIDnkgB7CfEGJbY1biI!u58Vz~Y~0LS0d&0g7F698g&IcSiiXErSVgKI@j$tTaudX?+$IC9DA+b^!2bd(= zrA8Tv!TqznF^)Vod52`ieg@d3%xuP(u&EilIR>_;(#ED!1txb>y{WeyH02zwtORtG z;oD#+?rf&1;a~On(H!sp zkcp-|fb&fSNzfj4(zPf3oMR1HN{>$UNI^MGL-wC9dyAR=Z7*MM$r}r2X6{{ar>9E6 zOBYI)#O6EiglZ3)g6%(xl!`F!FejMb?uSxP@ds1vni2qjhSdRuaR=_!XaCp6?N^AL zbd+}SomsC*%(iJyU~j`chTm&4I2(}GlF>ms6QS-YV_-))HkxE+6R99U;GVBhqW9V$ zCdjt{YL(=EQ1)|lH^7-djxN6!d=;5C{pn95#QwYt$12*|_)Kg$x6Q6mK%Qjxd!|T7 zUi!!yHhg@jc`k;Eol~z`Qcm@tBlyT;;Yfa{oGAu1;%_1Z{0OFkP;wl+6J*{QHWVtSmqpLUt&lMkK)8%X%dv6i zrPk!BU#tr3EObF){K$30AJHmdGq5G)KEuMvViGEK;qr8o)^y_MW^_7etzlUwxeyNy zgyezICKiW+S{4f=zD6oC}yJi#h{X0mCPhV-Rm2w@1PwO6leQZ zPS|l>cjB&Uq?gz7qE}7muN8|A$^su8ozWak>9Ru;p{6CqMK_iSl}&Ffn(Og-yt~&+ zgx;1khb`YSG}i7&%IFU8wvWFNhhmbk6BR8KyoCYpv%W?|LnQzLr|GOh%p1QC(S`^TXjrs;azVForC2U z1H%kfkFRaKp&$(Yt?cAOq}iQu5z}5qt7PlIZ5|d!(_mB62%G~@b=@?7j(d-S<15P) zl|-Hc5fF{R1$Rhdc*ImbN5WdCw`(@H{kr};r>nMrk)e%k`&idt~ zdHdGXDAyz;Dy+TZ4bLuoKQ6H!+JwA9X)J?L$AukIH-eEaC}}d1!M9`8UQrGd=%_RI z%vn<^uu+1&V;#h}B0xV(G6malpgk_&619C$&cM5ewaJCH!m!Nu8jPozm<9*vo<7p2 zy%X{3)sJWNH@6E+aC9(M^HMM+9U8rSO98WbhjmJha8_Dc-FQZk#w{M4!g<=qUnp@IQ9&N*gCt5V?}HiZeDIe2P+gf>4EthP#m)OO61 zWZq2ILDfuW4Hw2qYZh8+ILcK;J9#30Lam4{xGTtv*44qnJVkz6+k3?qfzC+RGH9%j zZ&Xc2&3rcjm+WgeR~!pHE8TJKdl()zlY4s~l{d5~S7lV!J0ek!-x~7pfk+~Q84OU+ zeG_lL&yaqX7fd0nZzF%FC0O6gbWib)_Q8(TC2>ZX@vR?Q5%{#3Ro^#;qU0h!UP;nSE7AQ6xc>vnu#k2r;EoWc?-3t!!%N`&>Tu z#m>j}Glr@NCD6H^0qO2wah8u?`IttO6>(tNC-JU+e|u77e-x+Aw>g0BBbWdy9if-+ zVAIKiR7bBYH(^#YMY~L*_hRA8m{31*9X2R%4mmVXQ4pQ;ZZH*-mT-V$m#_-?7xDo< zU3z=loB1t|{aP(tKk&%6cAQ?0KD>h*6w@&Z1H_rP6BA#dPQUEb;ux#_iLf@(!vRcs zJC=uXucCmd9`b}ULdRQ8xzKhGH#6}{2*C!dL+Uy!VW4lwPt1z}BXnhP7a`pSg)?j~ zH-y_04RyS*9|wAj=HSvKjOJ~h)76H(6$w^}?~2c)3r*~s)?cCiW;Cy~w9p?()a(tA zsl#7f+NQCXa?TsNaUYS0NgG|*`9z-_sl8j4+sw0*+F0AS6}Q!;U8_4ci%^3I`9SaW zwC&7JQ2G0os2ON+?+EQjb$P#Buqg{EJ<={c1lw55eGgy~tje>mK@NOGr}v_*C<0C& z&Q1xqmlgsfAbDJ0S6SlGU6s_~64r&8IY$vCeP~hi+X$zf5hX6vm zOv2Yl`H+fkCzND%)0KW>)?#8N5%xrkerpL;xi$O+OwcIpQVvVfS>ca7*t)!MA8G9N1PV2)0Xp$B1mm~{>r{J~BX_azSA2iCyYW?ejI$ybnW`tFON)9yI zloy#QXhJ--%HF}?!n5PSQdsCY7y%vAbCo{6AH0GJ#PHCZ@p84eV2}*ib0DpSLjPuU z-IBc%L^dg1=9)gX>xa}4vx>`6PkQv!=_tWIq!%sQS;Zk<#?=R9K&h}tn;Aqrel^W| z_MN=FEgS3e(Xm{JV_2P)6y0Se=vdZ8FSSi)#usDK(FO%~5J$AH^mmX`m#=G%O|Bk1 znP(gGAH{w2X5Dfo8dO!%I@lNRr(HYXcaYjUMWQ7Fi@Yp85~+nWYo_MuQr=P7fPI^$9XH4jEL`pivZ&9Lev1yInQ5PzH9 z_A1+Sg?((Hug!K zO?!i5%YmK;&UE|is{+0&xVt-=7{S7KCT4~CP$?OS47yMM*4Zj~L-);jHR$s|6*5+J zfe$A_6O?aW07vH*B4A}g0+5?p1pr*n;={a-)u-YkBQZj1I&xRV3>jXt$JG?BWMx`G zsX;;Gw^{iyFa3AbV_Z=YBPP%VDqTts$Mog&sOXFVlo?~U1~zH=?g0<$48!pVX+n_p>@0%RA(6A7vK{mW``+6aYob}WE2o&ww%8;(<=B{^o#rM5?5A*W)5$yCrz28 zwNRw^ysJ1*@@We)FgX=>iz?kNpCPaId%_VSo%99ipaR!ikgkcc6muxebAYkE;7!bvEZmEGj*L8!iF=Zi%qFt zUtCa|C+5EY&WUB94cq3CdGgPwYt~5`w-rmEg7-ROhtJr^AY>Vo;8?q3GwfB}-WtsL z^18}^{@M=ZDe|LZ3*v!R$tAO{Yhm|`t<179E1EJiv)|?J2uMZ1+2fFON_Oj3gG44l z@y60=s1A&{G64LCIuY&*>~bw>g<$tg+Q53El+Y2<6O{GBH+1MfpCL|1ZnovB60?E9 zS70_+IaW*~C@yE7z#rs!2Xdr%YGZEDWY(wvv~OI*1*!jq;>hK<}$V`mEO}WCz&`dEX(OHUhCgc|;N&tk|?Baq?t{ z*>V)$)Behh$uAE(aEYn$exW?P9rM*X*B4^U((^%&;sx|gYP-eq4i`A`%0;as(I|?E zp}2WDM?Bu(mt)Fzk^LM)=m^B}Yb2u#$pDu7vN#N2;Rx?bB`kPJ%%s2&nV|~nIL*8_ zJ9MLnu~b^9UL323)e*;J{rFqoOEhV_J=?q?tKIzXIP3?!9=3*n`T<^D-paRjGNUcIk&?-GGrU0v^LIn-#Nx*wuX#*2zds{I!cL@ z){$zWINUQLlYK%i(L6YeJ;0(IDfayQm9Gfw6|Lcu%oe~S8%TQM9NOis-7W9qKP(1?^5P>8u!INO zBKH!$K4Ok?A)SZ>wD>9VdPGnpJ&tu>=&~o~{suRH`!$m9iGFyl^KMBKm>nDhHV)!+ zV?n+rBpSs9VP=3lQomN$YFU(~y6emu{-zGAN{*ftZ^oK+wLdCPdoAo%O3>J2oCY@7 z_=WX2<0Z1~$b2Y%7-*AwgSrrnV&qr!+NAGo-}N)8zhehbCoxm;!?Tz`(`S^*x(!w zNW+hXnR|3UE`|O*Knq~SNw`yChhVeTekXnmzyES0&dsPy-N%_-lDu0EQ;TZo8cxcL zvbQSLdENS#OTkW+=7;Jyra9#UJ1Bc}?JUNzk@vz9ke9{eK*s0i5`?-fe*zAwBo z!xdDY)q}b#CQSBSKM_AnS`*5Hs>dGQ!^g_<04s+t-x2Or^3_Yr*4A#7RfjQ+#)#_Z*JkQO6Y6qiut?h*QJu!kYlbbpxXIcxOgQN=eqL;r5S} z!q$kAh)S?~Rd~+B21NE83td7vRS`Tg5&XWI`)#I*%*5B$_!>LR_K^a5ZUls{+TT`2 z!Jk)9aHe=ia*PMeSca<91LilnH*^chj?${@CZW!RVj;-5+X{9fa{m`Jrh9{bd-|tR zo42m);$hzoSg*a;^bh(uS7FaVgT%P$%e$wO^k+m z3hkSDBq1v_ZduOvv;TtI_ZL+`@(;~e`fUEGan`3M;@(K~JelJh{+PE9^~!KY{mMI8 zQ1d#`-^GL_#>2MWI?A27^DfOs(*gnpW|3B6ROp=RNF@&6q9^6D%!08#o)Qsn!5!m# zbi}(!q8d?A1s5C5sH7fj`!u;GT6CA@4QYpS4KUTVO?W}e!X%-iM20iWHG{MxiDXFv zbi-Ltg)1Y?20kg(^1}Xh@zbi;s2VPXC|MuZNKXJvgzUovmGD1C=s{2=R|etjiJv>xJ~#rGk=x2&ST~T7eM7V zGGa>FF&c?A*y64j1h1ynyAHg$g)Y}p5k_OB;nb$qO)^sSGKo*Hr$Vy)r)GNk|G9GfOhS$e8uTxo)>N z(2egANZ7I`F}CH3@|-pJ#L$~;3c{|mu~uQIsi-O&bgAd7)tSmu6(qIZEI0aX43zXZ zSwv3dYI^$h^?+~KxB5V!4s1@HjTYOv;Tzuzr7Sl;?pd$77enl|A3X)H1PdGKNE3f7 zd@y7l?>r(i>`0Lbdz5PraA*6FqKhCR8ZMHZQ?=+CP3o$(@KA|88+a;yD?P$ON^PAJG2c{qyybsoM^vdsK0@P^JiR z88o!{#w6}@kPxk{$+klhF`r@Xl4h@KQWCSZc67howNM^7h#gM0RuAbn%hHNe*N152 z8f}4+aJuD(#lI`ts<#^l1=9){%NVt_!5fG$oN4x+XtB0q#0J8R+cFqZ)0=pUl>}>KCJ^iXFuZYe z7mqk7*I4RJm#X8q(J3yqATFEA6fPd7NBc2y;<0>%TKI@~^wA%7nl!n`#pB*UE1p+$ zzF$l37uNwE?Jg~#tM2K`Sjx5ZN#jM^%jfdX+h~GES~BogzwBON{m(@B9g&`sr`-G* zjN8=$*7N<)GTq<>og>9$1%}cgF3qQ}y3-7Mb{vOczV~Pc0q1K+yc(N8f<%8>?fYR! zLv+^WQR`WV+1|PihNlmw>?^%WaKsGoydH6fts9Jc^yZQ9#Z1p?@Qe1HGBP%op9y8$ zH!u{*Kra0y23Hm>OZ7PlwlD!HR8FnpCXfEzrqzqpgYDiUzjF$|WrKu-Jsnq~0CDK` zIp^|;wM2ad-;8`f2HGv{`Y{&f$Vd2!{7VBfmz35qDvYc2m2XNA&zQ@TdK(>1#C7$*gA6#rijjt7N}QM0Ka#q zif9UTJ=AYb<5inz`W@ahkE#Z!RN$LZyK?hG%i1wbd9>QZHOqF(6X|nW#A}ti^AEF74DZVQR#iYdI=ZrhVi#!_H=@MH&{1cdS|VH%qFKa2`mg|^ zs!l(*QtIvWR}&rk8_LP+MkgW8Q;uP8P>cz}byINi6T9xGAeX6!OrGH7VkfUGiJzszKQ|? z_y}?vR*mnQI?Qc*nx3*)w}pM3H=2D9plK2O`K{odW3Bt>s(W9BjPR%--Jwpf#v24Z zEAYqxS}cDKcP!QbceV4pXL^zOQt@2>FPGo?&;&|OSI6tM+>Euu*g3+C?CCo=LUBPI zHp!35Hhs83a0f~W_Xfv>O%Gkke%&6{%K_H0Cor%o4~# zHa=U8;Unn#vnqAQ2j-}S;_T}j_{YeUb4KBizd0Qv@FFtUF1T)U{QMSG)QYhh-f4zZq3271kUuBCmbRe-Guj6Gb{=+}wcZ6Z<7+I{3t zDKmNKAB9(=CO__a4}4yes8g`Qeg*urkIShw7K~Ramf>+ZxyJ*FXW&??AmNeT@U&iS zw-loyC=H3@yozl@_n}5aT%XXZwVCg}Q)QfXWJ;_;<7qD(+y9dF#|T?E|aq* z@M2V%7>NCOk-S=+TD{4gb`ZXiE7)P@4fjgH+^~%}lQl5*-uj~1_=ag&Q~|5k8O6d~ z^j!o0mrR`DD%^x!q1 zB)z6L+gis_Y7V7yRP;D&g18@x<-h0W$0PYx#s@bD6HpBHneG!9tsH;u#o)^_t<9}l zd*4zc%0$H;-;sl5uoQ#hl7+a3PnpbtjMM&qrr6yT{0n*m8e5>vt|uMK6=ww2j0~9y z&LZ{DFT+(EMnt`sR3k|rX!vsKY3n|>%94OqH!E7EDsqxD`yJba1fY^HK^PScjf-JH->eZTXgLaYDIy#0X1tC`4IAu;nnovH31-=QG1N?}by<7o!j=f2NplMsM zKxWme>7C=uQ`z9i(#T6se+dICce~?nGlq{CyDHE4$yTWO3}q`6q}y-taFPkejS|@s zI_}_`s-VmnFYd#@h;Tzzv?-IX2^aybc3FAMHU}OcTvc0{FReOj=%bsv{*6 z%3wX#-HGoEpwHu01rhwXF+$Ma$+oaM;SX5+*;9J>hNbrBEtB_AyX<&922in|wkI~o znDhaOx=v!T(uv)!+&Qi$6r>gb0UZIPyWZ1c2su*KVRQegvGHac4!NA*OZqDv!MxI6 zucTt!T=!L1@SK~;bQkSm@UiE65t|#4tg}HK-#qAm05{)vZpL#=%PYwacq;0RO)KS? z1jjX>i}<@W`D-hB-depQBDh3uyvoLsSpP&*fKjGamzyxdnTr_ZJ;}Wju@yA)S|<(D zU?!-K>r<5Sbo2>BPO{e0Ot*8d%h&>Lvp+!);8RV4CGM@9A?`GmoYHuA3cB-EX;+PI zz1CsMZ`}(Djm;rfGaib#K+r)40>mr??I0%S(`^?8+7`}8?-c+3fPx!KQBt9IZzQl1 zlSh1vV?W*g7QalZ^8K^Wr#_%Eos3?pWLC_no*$1%bhx~FE||^sXA>A1hkK3cF7pbp z{|K}cWDA%;Aq$=J@6CW}Zh+LRmIru$WFmNkbLN#A=PCLCz|hRfI;y|!E}9v^%f*SJV!UB!f(NoBoizvJjO zdGxlsKzMOUiqSP;%c2?4&3;|X&e>oygEOH_x#@yPEFM8)m0JH(msJkr!j2YpW;s!A z0n-vc3BT-T@=(K`1D~JmQbRw>%I}0%dBSccb_O!2&78ZF~8!wtH zf(J3F(uZ*cK%7ftHtL+)!c{T%x- z={EY$ke@endcc>78otXk@TNC-SI-PvBd@JA1I}+qgj_6CNu3bIrp++N^q$S7I`kU$ zGL`riFFKdRJ{Ie^o{)4+sb+bZ`?}Iuz_SXGYp!+PnXssf{E~V<2Zb!-{~hNH5TdXx zkv3FO?Ca`@w=YOds)F76ye@mlS0(~lRkg6k(Vqp2u5}MXmM0Nj-MsVj)2Cxjdh`q5 zos!{sWjQJjqfxS;_nEFyZ&@_$Ykk^6no62JpaRp(sv|b21=(xq%h=OONQw*^DO5fj z0tk!CUd(ovy>)2Ceq<;V_7+x}pa(3f7SeMLv?N4Qc-_e4#0JWPL@?y_a+ozL0c%>y zEM@Y9U&wzo=3HLHfi_LJ2=xXy*mhb#o2nx&F%vw^m$N7u>-+2D>N@;evtPyUWP{Nz z<&*qrD~p?}MNcJ7%SAfwBm0N892U=Q${lnlH+vLZV^|;CRiLmdrU6N5oxUg*u9nTkPoGxb7_jgPLy7PS2u|NoWdY5#MDh z+kD@*0zR3vuu?Sdy3o=^hRwB$3BxpHDzalv9|a%rEB27iN&9$>qTKc`F+F;ZKRB8q znrbv0cU8x|uBdwbcHYIb7mp(D(vW1Lr^Y#h@N1@IrI0Px1bt-2NiYmqZj$@3>B8L? zic0HgGz%&3_34NDr|H%aX3PUHR6pF~7rF5yjx(~Jr~hrhI@KZI2e!@JWUx!vDT52d zFu~sbQ)7JEy4++yatBnjC~b4T)3|%%J&D|ofKjCvn4zb8ai-g0+=oy-7nP(cDd&I_ z7-k~=&xAZXiF(sOwb2%lBe6hfr}2c$ zs1;fhUk(;uJ#q0D(_l!Ull4YMjxwFrxcJ@M;$7t_Qk`@%$Mg=;Y~S=rQY&G*UQ9G>3kY0PXYks`2v|slc18u3Y+WCN-JDdgb z@U_eRr`|GjT0#BZxGgB~@8`U2HJ|-={t~IuNi{s*C9`qaF4&4Y=Gbl|my7y@vB7y9COqMJO|c7wIH*UMfRWoN{Gr$f*N!M#$QwN7ysn^} zxO!nb>V|Uv>*DTm>ZjHt$q#9+JJ`|HHRGP|j?GkkY}$q6#AC;4>&)vHIDhg}tD0QH zuKjL@9CYu$dAx$ho&5uuqxuia4~ng@FZ)owussV|IY-0Jtu)s!G|NSsT-w=#I#W@W zp3_|kfLz#d)el|San%pAc22mc&xxP8QpSGfzw+nR+w-T%ufKOre%*G#m7NctbEu3z z<92Di`#{;+@^7-}jR)o78=kSPMn0b_J22=}07fIOeYE5nL6WRGH}1QWcUGp$xkal?@qP?Cs%_Nuv2&V&*be5$H>P9 z{6e~}`(wUNX$~S^o|p0h9_8BF2^+8o*4x%raKGTXtsR>>1#QQzbFhJf92g|Qpa=#W z%t3}Y=3zn)(@O)&$2vo?K~Clii|M(7+hB{K+t9OQfUJ`fMnC5#{eSkZ13rr43%_)d zKnOLV_aYqu6??~yASj~PP!vHByI2qlO_~Kziemd)5ET#vL_|a^fG8+}g7n@?LP_Dj z-yCl+-c z>s~v+VqE!N$|~#^4v-{&U-OOMael5qvAGFjLw>FJ6edo*;apV6O@)aSngPZcj4}DK zm|)YK0+R52Lb253<(wyzLW6Den~j}ZnZnYE%eV5K&Q(_T%kf^ylI0e5ct!hmVO0S{ zDI5s|w{*{b{aCkPg)*rnR04~cTsv9v{BLEYiDi1(2E3HT`W2KflbaUo-JlVg;ziLi z@sZ~px5v;yAjXv&PK(pmKCrOKR)3hFj`mTdbz%VVM8-m1AIkA|L>oDuOf13tP!!)4 zc>D&TAM+a=aN%_{*OAxPTz|XuH0Lq%xqWk*6FKOoIDXmeC{~_hg3VK0o}KT7EI0MD z(#cE~=jHbsyY~am%YH$up)dxt)<`05j+p@daAxm1TnDdwJ91M&;N(?3in|;Tj|M#Z zB6aF~H61+Y+qizJ|3GTmv;}p!xqU$O=geM6Cr=W6J?|^}{`&<~t!j0;_uk$joSB(H z?b=;Uvu4fE2v5VPapR^mYUEh@`|rPK^_st^-Q`zPi4rB~r9lH|!Gdpu?5Us5qM5U% z(OYl5NyCP{LQS-LKAG|%HEPs^nl@=c*LP?~$B+A4b_-C-B4Yd_W2sexRy2Ob1bTD& zM2lSZ(b%xQgBnzBL=ChJ@6)tDxScsuG%~?z z5pLV6l%dVDCcn!_Cf~Ai8MJsue_i39c)Z>tA9%Ms(D;z2==$v5J;Qa|G0Kt24qPac7I4j^7fyqJ)z$yf zp15YZKIP+9)6%aVk+V1o*KYB7fSmUyUgmc%lXc`snRtPCkT}9sFR%0gX^=^Wa$OK+ zyk)G-ForHn5MmitBoa!w>J#=#qN-h~gJ9lK!!evWb6LO7m zWRfR83m!vpEsyP-ZZ@Wd+Lj?JlG?LZZ8@BB9vfAjAA#<}5)-?cr=7(?-ADRa$9GZr z+!M4qHig#4rRZs)28?pDqx?T8$c_s9(8w4Ud$JB)c=U8#pVjd@>2T~Dp{hcOIhO^< z`D@MdesZ?{6?p2+=0A7(<*C*BXX8~ge{*mBmYOL)Q_GW2>emSHm&JaO+68Y2aJ*L=A4c4)e$aQaG450r*TKpU zRgT?81od;add9wnCZx}_*vv<6Q-~?PNdk5o{bpF$#d#?U!Fa@kfET|ziegP2#z6eB#R#iSdefVOM2=fAKqvFItKs`f-tLyIOODXNZCTW) zy>DY&&Ks&M`Chv)`k(AVnTJCmZpmdB!#uxmyxly*d8~XMSFGkdtNYC2^COEle2sFw zCi8;K)VjQ{yc_%^Y3_vD%3oK{#x)lty(Z9=`acOL|-BXKX6FU#Sv7jXwu zJKDZ0I31rL3uSphk}?~gm;lCeN7WPA^z({i8VRhVP7X7!qoETV%gfI=2RRRqoUG&B zt9$VFgnMvQp1ej!9J@$Z8?Y|$y~4?fER^NTSH|`ui2Te6q5$cil`MWoYBp_-l-oKe z%R#REekfpFl6ea2YaHhztv#7*|KEtct=nDHrORy^>ES}X!aeTkMb}-|fv&msQaz3X zAU1t6c4xhyPGb$ z{4P3@LzJ$~QQQ7qPLmfr9v}wf6Lw}B`b{ghr;P2LISa*={FBPn5B-4Oo$c zk5{^2y!dTHN4=j{FY}k{amOgf?YPsdN;mx}EL*dTLLGN-GsX-hM~5*WV1|!z-f}Yb zgmd2VYW@pVj@cXY27B&F#8ZE)1K&f`Y9n# zKRqExpl;To06B|F_&!YGTaBbD#2e!poTxx8PvnuqfX}?-Pg#Uxzsa#P1LVBFezxEJ zTUPC+xRZ7DI3qSl2q!*X;T)%#LZ=$lPA%RU$Z z0UI}P6>7Gj@_P6TdhGlMscPw6^yQi>Y3|>7FHRsinYr4fm-t23N&Q`1uRG8@VB@1_ zvw?njonFNJd`QPBw_ctZSiXOYw$Z#zv*dHeeHA#$(hnxf@^76V#}hi;nJ@`j9$ORnbBgyg*RgOYjWP8vD$=z%#UtLrn9QW(&)vLeqK_6_+WBIawd!Jr(q-@~$B;gP>)!s^M0aQ9kB#W8KWg z9^-wumdOXW#{6NNs}S$j8D-hcdt8Q@?)e$(ST6khj8T|tKr#ZMx9wHA)GMYu9p6`n zViNb!@-|!YMc6UQ;`v+F{r(HfQtPs%DR)~bLD&VUD7w-C%7{Kp88I1teNMj5t8iLc zX{xZLi69DtNbJPLCMHl^i9|i?$=n<|b}(;_))O2A%w#QC=$q4d{^hpAzLi)$+S#d< zQ!6=Gue7M#oMH~|p%axF()JUF#d^goKa(;s4phvoN+4#-YbmR>k5QJ@c!zwz0e!mp z-_>+d_nYgso38JnPooO4Lj0=T+1mJ&V=RiyboxsoZ{d9aj^7Ca1ZWZb?qHT?^`3yd z1@W$MbV>ysCnm?EY~WZWD~Aqm%O*PlE%LI<&pLx;nBk4}Z!m|s(#(RP7jrt7?UgLA zlcE2$$6rALHncT$V z6kF8gYdLSIVXtI)B_~w2VwYv1!z( zVKjTrmm=!xxr>B<5@wt<7ZnwyS#&xnn;`dwrO)FJ^Rc{!m880eS0)3PUH3C@k9pcUs7^1B`4+mHZyt9+muduzsGFL z%B7uIKAmuz_OUhapb!X(z!m+k37RJ(Gm|niGJ@vIZ-!z)YYb!Mg#5?9LQ>0GB3ss^SoSv zA;?)$b~J5_KZ`Ct`U;iX@E(m??f)wdZ&vZ$hqj%d%TuH2V2yYxRXKs9>FlIjie8tu zsd%zXEd5X-Ui?Up&Aeb6dMwCEdy+Tf`3G*ZALgCN;&RP=e)eb^GO3C4;`zsI|BK4k zI74h4q751y+DXgQGWwBtWvjQL#nrFWvsKx@mhRs0x_%u>;Olbj0%Wz^`jB>1(0*FF z?}!j(e13>Dk_PbXUf(=KZwFVUOre}JQ$kTi&l~+uW5HCWVz?!DLVLH}o$ia-wV|gP4thX6?XigC25tPC{6AJWk1Rp@h#}*PV7I zolgH$>ScMG*<5Ee-mwtlxCt3fHpBUT*~eV9YF{VXaI_9xRBkRM9bMwO@0rO8Ht#_f zvvJcn=Y=xX$@OzPZi3tjLJ^Z=-O6EIqT{4r%lBqcjJ5`2cd~38<<|F+fudu5#1l?q zf6lahl(sg8GBr|D>FSA;s{I|vx_ zdHu>`M5Xvr6u&)h?VM9eBfHhl+hk^0^|p_pw9H@W&x-fa{!(o?I>1l(`+J=vm0kHX zVFjwx{zDwF#4 zOcsG~&M&}^apvO9EQ87Gb>zdGeoysgmhVC=%ZcxmEU)C`w|*Eec+7aPZ2}$ruDXs> zeBJ%|T9+GTWoB2ToL{R`eEYvCwz?CveQRmsoabgO%{V#z?Z8YsCS{?2Ak5C#UpU^0 z&+n6**R3qC1pU5#a`P^UA7U_w<1DhEjS!Vlj#^J}XpBeqP4sg!F5*=P%wh zN8z;f^#bJlu=P8?dx3$`0TbC7@%etscl6-E$K)(Q!^XblMg|Ho%2DI8KlG7^PjQ!oT-ozA1ViSGrGv*#Qnl15?JxM3XY=>8 zq;L0LE;cAHE}!?KP~6RVKjwP_ah{Z%75l_R?w<$pA{~TYjrS3MIBS*t|4@SxvuIt+ znL5D%X;Leqvue zy2gp0sLzQ9^=qrZX3b*L<^w0Gak^%~sYKovL0;%&RVK#+rxcAxhjx~+#kW{w963*Z z{lBRm&5qC8odyH{Jj@Z_v)W|(#D~e+jefOlbM_=PK+^T6L`vz#x{U7 zvvz>~$4-7$cuw^x@I?f9dFI^*E&QE)mbJQhZ6ORXLAv!STU-BTi1`U|eB`KF4zi$FXnM4mgkOqiyim%kiPi z3!Ocsw7EFXTb@h(^!XIXMv=Qv||TlSO`YX(lQnYd1j5quqr&+8AqCqJ@Sh8sVs zLLM>L?*6EXd6tFbeAyXEl&zUaPHdWIf>=&BhNCUy$oX=2I_nQ7rF;jEpQKHHpSo|? zF{)Otghpt}vw8qM|NZ79?TXn#Nx7w{*~O{a+>|L7ODA_ZEBDx*G}^p2OPAHOX#&M2 z`DAU0<-z?ipAMKo7 z+2{>aKUO2AwdtFv%V=JTxa9<%F-HG2z7oSX)1*ljFTSLlqjS&hTKGVJtaSw?(! zYFJ>!T`{)?i`M;h33=~N|CX#r8%x#m(}y$S9fnlE zZryr1u?eK82@}TAwCPhtIUrcAUAu-}cyWL@&hC*%`cvNr@1s?#{s_<`SVU1G7|3;D zvH{|la3H69LEbULrZ^CQfYbX^KBHsX(kZ6Y5xVoP%Vp?73VS7o33qM@ajdkG!$_F> zk%W&Ix`eZj^0>kTD<)NeOl3uSLSC6T>b5;IHvAOb%EA2~y}no{T3vI0TNW9zUmWFl zg|k<3yn0?MEP(sR8=ag`nWW^$%X znyHhRAc%=fOky&UcDS%e!uh!|{pg&k=j-^qHh(mIGVx1R)v<3T$5?+SL@QuedRMxx z&(2!rzYhdc%qW8iW_+I$k`?p~SKD#Z2BCW-wH@f=)fnc59IwWAujBxqucv*kx8?J? z`mFgPK2+zG9533B7cx+PCC9!M6jyxgYwa$zND`ZjReiZ#B2_6BqetU-pEox))vx7% z{H1eK?aOq0QNHcOAO6B&>O$3N)f$51%4O$*}> zoiLl9K?kXNT1LQeKthPczuRuPpEBw_pr-;C#t4xz&YX$AD(7oozBA-YzpxqB^4Hnd zHHf0v)!NrJr=Ind=d=#c$&GamKAM1)k&mO}FTXeKN+y{34|eQ;oh+8mKEAd;O*_5? zKI18409ku$b`u03Y}({5xKAVCoGWfO9z(}6&3vPU0DRGJ_LaNYy~a2zS!_7~mZEyr zudP{cIzZ#st_x@v+$K1`EZ>c#0o)%7=5l#*7{}42*NUZo%U-KT^E2THxs7u@I8FA= z>L#b>H!QaimL>NS#F<%*;I_v1uoXDIHr9{p!R2uot*e}*d)s_Tuvh}?B}4ZOcWo61 z&#qeAa=NUqe>&uM6eKKYz}?U&P81S2*t$Q|PR-~FqyF~m?n|%Zj zCl-US4q+kc3DT9%ZK@Pxog%GIU3J2*9iYwJ&?sIn8|h}phC$Ba^pErn`pv7K z^pm3ta1d}J%gWM^EFN+Ww(m>VH28`(AFN0pJ-r}bI-Pw*d5URPmQLR-%+{fZS+s}*|hNJ>H69jV+h6%9yd@Ic|4KphA@t_lVwf) zD~EROS+5-n7NtG27_EJ6rbRm|)4r26jkgLtsYk#@f>-) zV=JKiT#*5D!euSm(eV~t_4|;H_5Jxtzl*uI@6tvg%`ZIvK%m?8rdi#V?%A*FhydQ1 z*sUv3m$szV6_=O?@$6t3EH|zb?a0XTBg#1?xUp>6cT1L&WpMc4m=&T;fk2mLLLB3K zPJAz9VVx13^{GC8W|nB@YP|w|&1Aj$O5Xa~Nu7|#?A_DNdmdzA491b;?BsVnj&tI7 zz5W?$Q|cQXr^UzHxLyN$CCe*0PU;`2d8$>bp&c(bmP)6TrktEy`eyzddimv-Ow~0K zo8sf+Xx!MzRK2>7zUQkdPHAcBf;6>${W_XB z;eBdC#{5$^H+S>Jcmwn`+m!yGtjzY&`lEVN=cM zo|hRm!(Yq$ zu$i%U>Q|^xfp+iS?KFF_&WCjn{@7|#xGIY|nMq72!${<@Dwm0#Zmlq~T(9K3{nlKu zp2K?Z)=nK+e8|2Ra(M3J!riPj%w!m!jl5RovW#nHtaZZWm}tf8WY&*;t8%$+Zg7!h zdLhTFG0aR(q|Os@+D8U(t4~?TjZ4Y<9nbje(8jJjyN}+t@Q&nm4ex~<`T0jmSj}73 zPr{2n3h9w1$GXPuQ;H<9>8^_!)1~D$+SVGI%5XT|hTWc$XJ~|?Jc}{B*-v3Z&U8vvh@5X?SdY~!SZDvN8Sy-dWEGN9C-%^lGBLsz753hr=Fnu8q^Uc z?O}d?>0a$m`{5pO<rEM%cR#^)+OPaG@Se9!U#Ijl#6@*2_Vv{ZH`VI~W6@M#sI1$hv%t;kGVK75{|I1c^Y?YtL?EY{z6l$qB$L7s7Kz;!kA`B~!` z|GwrMef{vny}bo-hws5UdeHa2{wR-;oNniH-8m1Q6&(7t86KR^%m+zICO3hMB$JT% znFJ%vn8}gL!!dlo1SBtzu&}Pe{D65^mTjz?EW?X1utGbBTeUmel&oh}4)@Jaj<5oL z?0CJ!*X-l@-*==JeS4${tx50cnBa!{yLKI~A0znjc=tb+K6&+Hx|OB-XO3spEPcGs z!KIb2CCGATSKO$P_BPNjS$EJGag%id-`boDkgE+rW^($y=Og6=tynS3P`Y$!I(+yr<>vav)KIF2Bj?JN zEvs2=+7@tITwFXsHjb2=JLh{rY-&*dG`jTCcGUUCK++zL$3x!iY~P$3AkZWNBpaEG zVvW#4Drb%WZ>n*s z+-^9|?0L#6)BroObB2^+!74TxC% zV_VY#DkEQ~ZcTpzFfB1vpYPOU;o+8?`nYxpc= z@b$)>$@K1O--cwP3VEBMycvdk^2UMhI-8o+zesGD!I}m;Sk=If;Gw!YF$;uM4!#d^ zX6(2?T=I|Ixae3min?|kd-X5^L*{?vckPvx4+#BvemmXD$Gu$#`IY13f-QaEhjJVX z!VY!&Eb1=46hGMW57CIkMh%lwWFiB9m9F1-Dlq$x5_G{S%K_bgSYk{zy$1eoKf6}1 zAFO|&OnLJFU&1@xG@ScULu#y9!2lU<-h#%WOnHOPREY?RiDAJ=Hh6YS zsNu~w`5B^`c#g=!mI!=tf!4%i{2^DyAI~T1@LoE-ax=n_H?Y)lqEbUzscojZAtN#T zUTp3Gzk{bguIqI9pMwR#j>`zehpf~(H|QIM2?v%tmIXozNF=ZxowV%G;37sAP0Z4?3(A5^*j2C^D5sSfpr@K ziA}g;%ZI<(-=Xhp=fYBPMFnGb7{cBCP;&V)8o zCJF?&Z?yKeMLaXeD_6ax-}=d{jQqyEpsicYENVHCE0eM2E}jr@3;G|{rI^3%@Czmf z>2M%9!C~&-v+)=DX3h|C&Hos0z7CbF#rK)X0hx*4UARxl?<{s?Czj_{ALs=lm(kK) z$CGafvXzyEM6Qp~XS_KcV~W}H<8&+EOb$qS?3lcSYaSP6f)SI8&Q04YK8E1?;N#t4 z!seFxJA%Bf3`Ur_|2*wT!n8c=c6d&(LdWWFoIIt+-wTD>#|N z=ezdBi$ue}OWHj_*C&6b&z)xW%6Y}+R0IXIr{ zFlNHZ@0}tdLjQIB)c0d+gsNAh)na1P!2<^j)9nwXTt6@5 zcu_AeWS~y}B{{~;5Z+wlXA7S<+BmKttQT-uyrIX}@Xd5C%uPO7H-y8D4`Wi4H?tyX z6N`xf*g$ndEa2zwg&ZT{W}Rl)Vep{Gxd_L8ag_rAygE+P3pqxd-4NrhZ=CaS ziXc1L5uI3;8$IO4<&_+sOWl%)SSLFe?n?K`2k zxsd)v(weZ4An+>|eI9@EyNzFl2)gLhwr9j>QzVH^OpcII+!ei%cExO=q})Nk|8>eUt8SIYZT!?G&WKD#`Ipy!&(xX?6RBiEjqzUNwGpdrJ4(G1&nzk{aWtB+^%2+i=9(z15761`^wC><`Lk_acHs$vJ zJrzlQPCiPdbvYkFN6v6OwI%%+T~m+p`UsVTeLh0T(!D+_LrXLt8-{RoYHp{lH)IJS z69_5mH_oiL@`#+*E^KU@!Q|MD4J za_ucc0$*(G^g){O_*KaH@UuMPQtNVMsA5?kA%|rFFSyN7c}EE0nvbwT818TFSgds} ztZJ|&4WAUB9Nt9D-v2#E`w?@hf$!wS7ySh5@fNJErf$vFb(Isf+$}jN*SHedN3bt8k@0OWNmnKsvy|#eQxJG z+;2ATc{P8!kz=LPzTBEf(uiIggwNzJj8W)QaGYnalviHcx&8PEvdum~`X~EBBnbz7 z@^s773A(Ue$XG9SL6LZN&jle+`)aNJyFuWQNBYypAAdlbH``kicO%<)PbeEB#(Ry| zPUckOJ>k0cqF!FeK>d^)>!-|{%$&pRrnQ+I<3==Z=y6%_d2^5B!c8RNa@c2L5w4B; zB91rM5r*>`AI1bYpzhd+gidxuC&t>XE~tkWG9U*olExjc>J^F{eunVn_KfFwY>Q@< zVOzRus}4SdDdOcAH_+vN#dR?DFW@C_^g)K(BYnJ(13kUiKzAYoo8}-pF)0S*0+}ch zX~!7y9Lmqq%V!1R689Y*0}*FzFI+bJUXfg!$T53f(1XcokwymLIx$vm$#G_KKz@Of z$$GeE!XCnMTgJ5)?V0-(G9Jd|T$L+H>%HcVH=LC?P;Bc&Xe%5-dXF>Xrv?eUb zddp>DPUH6nUf1(FpYyqWuDm{t6gjMi*S_b(rmi>NMx8p{Ov%Z~w0`}+^u&`7(gE!Y zws1OF55UGPJGKVH*YIV+fM2|UJitf3ah{4fRkfqXYVf6F|KFGuek3W#*|@8ee!Lu8 zI*k>{_Q4iUM=%EU-hAb;EiWQHN_c>9XE6OqO#) zxC60}9r|90bM{m6C0UfE8J;*ap&}=R>>K6C^BfeT;&&O36HHpa!$hrhN8Dn}*|Kqix(!);LmWD__hUNNC6n1!2AEI)|? zZ2sBF0xmrp)TXG4QDPGp`W`CXO-TFmIF4OBLmc0^|Ib-eztSWf^KUnHwzYy`qmCei zIw5`7$v|4Qwy9L*tkQb6LtBo~swq45>revdcV#X*Ove*2&3nV2LWAIDhO`E2zGdCaiEvuDQ^o%p&id|2MfBVKIqBUgB>&*W`42Z)rd z&mu9B$h-l{N)c{JcT84gQ6SKYD6+0@NC1MU+#{uLc~vhnIczls znDO&Ln28t&Ga(IOINsFqU3p82b7RFJoPEnkLIFlbCzHOA2JFJ+(5B(!zJqI{o`{nj zW8`+j5whIKVB!q!=Z$TI!{M-j4re5vfaJi>O>X1Ji)UrD27`Dv&M1dBeerC9Js&QQ z2@`znmVjX&J`=Gx&I^(iWHBx*7rLPB7#$P9 z5YJYBx~fjru+KIBnm) zjqdAthnG5=%3=#JPA$Ed3eh4#l8UR>gkq0Oq1^a}^uw?FY24~{R@p7%y^@0hUnG`@;Ak}Y_&hx!NWf_219Cq2G*2#W zm&snTIOEsh_%fWoz|GHByePvfIZPa3z4JpxF_U%s!=JHe!tr3i9F7k&b2w5=c2>uW z8Oc*liBhs`4$I`J51yzmj$-6Aw-&%~zgM!bF(6wT2xb+BbvN_9k~OSQ^?H$BQ2#O#w~C@NOKhd1N!tVo$yhe@l3ik^&q9bChj7xdykn1X z4VlLJmD`tRk&4SiUXY)-9LSetU6lQ`9}&fC3oY-j-=taA+CU&wm8;o`QmXh!Rj^tW zAJbSnUTqm2-r^&E9@L0laLDTw0nE$3CzR9vh5F)aMsR|~E%ZT|^u&ZFV?38@=6f}# zdnE@tdNoFQC5Lg#4+)oL!1qcHWO+rpYjN`>)HY#-P6m80JHa(+@`C%$&Z-u&#m@Oi@wvO*ykAb(+E zgYub#Q4mBKlqVCIf)Ozytu05AKD?0%OyS&;iOl5ibCLTF$}keq5GUu26w;v4nunLi z7_Qciz=Lb39Y@tzvlN$g*!Mecj^@W`#~{JQr5w?4`A4%Jlb1J~R%Jw?(!Zig29c@| z5y8S5n~jw>wr4k1Zp>tuk(P{Q8_7iBCK4ITm&sN}eUUfPtbMWmkw+uK?vpPW^7AQ3Y~*sy!n{6kyS`wV zc5<-J2u5gPIXsTJ83S1c{791{>*iJ1Q)1IAqu-z^RjX+hn|#c(GtWGmUVLE~b-wXB z&1%y=Kb_;^;^~>EU!aB!8`7>_J8AMe6KUzvU;Offa?#@Y{#3ehL)y6HbK0Q&xpmi{ zaRU?^h#iCH_=9L_jYm~Kb zdnNjDS*G@Pl=km9O;^oG%;C0={(DjKAzIY%CNXASw)c11`ui8Ih%qP(B%6hGZ=?;$ zX_}R-$dKiwPN<{Y_eyAE6g#`QKqf;WKk%PTyY)G*MO0}P6X46%qKt`VZLHRrjJ z108F&=|DBKKXwPVt)}0;7{r$WzL`}VWJ=M98gVD?)HfWnW4|L<>w#lZ2f`qB9NL_B zoFd}PT%T??&{;?4(3aR*^w>u$&9XR(1G%C_&U9VYmn9w{SoFa0S8!GKucaD?{}JJ} zi^tG{y&FUrBqO+jvR(nJKd|<*twfx5)Lh;%d_oDEe_mmZIZvL8H!l6$)Z!MNKU+k? zGnbz+o^4glT2r}-J@s~wvS$}9TC~8g2DsnMv6UrGvujhxgLcJ4A-gt(x;3n`Pj3V7 zFRy4?H!hcbY_#){t03pVwa~1+l#iLC%bK(QYE9OBoqT4r&LsC2+|PA1s~ghYIy`}X6R7&61^Qv&e`Rf zVBytRhx)mFcDPq|Udb^deQf#Zpj?y~Pvi*hBXS(VW_^aoA33i)-*7yS6S&6s;}tmu z^1Q&x3@5ji?!1!arj5&E0I*?#l9?>I9h`PSPmKh5CT&?af%a}#hFkTqQe2$+S2n#J z#pGP7N99KSOIh)6>(`+KVzV#sM3$MH@5ekBiYhAae*l3<`Y}1u;8xh!KM{pb(ZGi{ zS6Ev&n>A)KK<W zFWhS;2Xm*q7B#cZ#O1Qj{hY7ej)jpXhwJ0D?mv zVWGN=%C!QXotYdX*}$wEvv%O7Jwditvb>VxrA}vE{}PS*@jRXQQ!#1>oml>u8Kgnu zypjbX;GenW_090IQQKS*3cbW>bMDPB|A>~U*iT4i;WI#U3R(AzD4-X=)5_z0wqx8+&n_w4mw7A zPFB@#llzN2_hR1N72_ikRm`cUw|m@J<&?Z#C#wX^3to=pPWmzQk=F=qcQ2(HS=opd zZgEn5+LW4iEH5It5BqZD)rYUyueJYeiawEt<>tieC@tk~PIm0ipr68uIdHaKe4TuyTWw z5hK>GX6yC=ea1{LqfWSAeulwxlKTah4c|;Jv*#B~Cs`MkZ>Ccu5_Y`QDJS+DuD zA1i3YD$=g+?t6B`sN^ZJ>B|}OXvT~$=*{uIugFo$Su+>VM<2aQ@4okzU!^Ba{D2Y? z5~x@2?sV|rL8?-@DsA1i#V?Q5MY9fFv>%eMvPxF*3IT;cAtHdyP}v>W>QCNFsTxnY z(d*?bl&V=OrHb;#hu=d{7Yc!VA^@_S(VdL;k28)1$RCq^i69j6y*S?G zfHbZg+I-wEo%5wu@h7#wieqti%@0r0Ews!m`)IzZ>!qxGDWYyx2q**;0tx|zKmj9Q ze1Adl@;i*Y4q??mx|7!$`LP_$mbO^7TUlPoVLg;zJP|0YACrp$nhG2Hr=;*v_}C-h zJMxV;j`&OUVE4F9mq|>JVWhovR2|KhJ{$;6a19Jaw~H1%nbfCr>VC-Q z!m;v}=}o_NSeCWrYG!B1Lu<(E3pq6lId?;Sh@7khLbb?{*oGibZ3S^44FH^Mq{%uguQI(w`0K(3vMmM#x{zdx=g1bCMqJKK=Ivh`M4NA1_{`- zfcMRm%)ElO7phNKXCIzNyUeQnCTbn_A4ww|k#`w-4Ip(czL5OaGayGYGq%MQJb|5g z`|Dfair?ZtWOsG&M*p;bb1T+A;Gy~n&nV7ERVNafVc1&N<)J9Viu&g(ilXubc*N;aiq!oO~Hf>Mn8CyOW zYP{k%_(ChDM?Zmc!4SsTSXmPjnaucs6RNY&N5u1znrzcKoNOVtt<VP`CH?KT!8k z05kAVQyaJx4xJ287BT0Vq&1sn5oHrDPZ7hKKR+^9eBSa{N243qh@qruqrUwuMwpCc ziynY9#r@ZpiDs8lSRFVm$TLOQQYyvSOqNp|X?-Em6wZRK1xzC%^S2--!-&g}?&D6wor3S}4+AzlJ z(n8jWH|K#+5a#;Np8zXMP5bvh@n=O@Zn8?hCd-ue^KjQF9_eS4SM)-V!QlIZae(d? zMCi%(stk38$O8M_%_oe369(JZBs}wva?nUATnzoQ#etkzo`8QN z>%2m+omI95_dnn1y2#GfoH=-=bUltgp8({M(6RY+(ggqFgjsRgp>nWy3*aC^^K(4z zOIr{0Br?Ru8?f5%6Wkoxas66trsEE>jZ^1$dEykXu&5yXXVDw@HbpVijC=B@GIJ?r zmhbI~)HA85Vxx%WsUkIG`U+&pe=cH0YDj#dzJew1N&Oo9Cr8fgFppMqq?Z=qML*+Ao#!`q>48*~ zBmxY<;TUq+KzKZMOqrnTYvIlNhU4SobO%`a??4ZquAUC6P6U4?rB75itXCkhYHLhBJA<4%`M2#Sd-mZ?dUcExaG2flO=(Y zE*T*d9|jUfOyYm`9sZB+{1_mr*lM@9xZ#>v1m2ILv2h< zDB-YK6I&B+a7Pka!imM~(;}lp4i78|$jB1(MpV}Cm~4lY9b@nBnH^7NE_g?e%g`ss zKV^}!F5;9m?GTI?&V0P?c#<|eO+NDpJMy$dqsX1e4#4+! z98{?OeUu3V)@#pg=oTQA_C2VegVpB;N0d|Ag& zpXF-EtS9YRWruwOrp{zRVBKS{dvxelCQppx5$H+-TQ#V&vug_rf<7l|;P7c$`%U1I z>r9{0n%X2(-LPcV#`X?Bl+=j1V)Y!d<(tZMh3!eBWwrYJ(5u#5o#)1Wo=iOT@M4UL zjO#@u7UT{+VyI4XX9&`6_k~$2QLldR7GNR`y!*};;fp$xua3<-aY7w1w}Mitj+_n- zVhk>~!>p5OxfWU2l3|L@G)=wK>__EQZB*SK)z-UtQw9S8lLpyz>wWzkDk`sHA=>^} zxaZzAomr7fBcxRVt?pet8ANk*V3`hdr=Viw zA!Vq!diZ|8C4KD;#V*nR_|4$+OtE5;n*D{M)g*>&>Ut6g=n3#`467Mo^Z zWfevZz`ugzWb&RZc}jZZu;y~9Gv{1c%3{*&njM~*?8iuA{55U?s&+J2hN7tMc%ayJ ze|(G*9TS5_0AatJ!BGXiHj>rx9YQKcIHm@_@9G?4V(++iZ!*3j%it0?_ORaR&guQO zH&JFEN6WX(MIjH#0;!y9!e$e38U&{p`Y^mKKl}sHsazKUrWI?78j}fO8WDItBPSTU zwl0AI{|)VZApv5Jg||8YtxvtFEOo|L+GZkAQ);BXJ2Pt+WMo0a7;4H4){&S$_yxzESLNvjxewHc2f;2|m`t_4J zF9UCcm(yHLj{vsfz*C(EtZA;%OyNYkhxmklgjc@eRP43JnxIbnO*!tW&%zxHXx^Nf zi*$3<#>%hb`H5*>Cp(ai@!^FH?`NNxGl^OZ1;qg0Xz51)b$%b#l`v39(e{%1OW5c)Vt+Ozl*k2UJaa*cF$9F4jM z(AOp5T#=Zl6ni*bAY#_~kVtB_KaOD#7mJ|tu^t|m6~*=XbgnFJykPBnOZ7;%JrWJQ z_l0^xCEC6leyhm)RHx3e=;>+t>ytY5ppXTwHz?$LlIVmq+%k4Ap+@9|(gn$0boGAN%GFzh4?9rFe<+nd0F9YkF%=UT0XSC(<{+>`-Wh~x5SJ4#$HY9Iudjfqh|MPX- ztIlv|aLur@y8`Rs$otmE4*RoVb_3&Mul?t`JoCGzuVc*0N78gkYlmVYaj(2(&8*3_ zCJTIZHb>OvkxROg-Ow@a4DNZ4T;gr7jeOGqZy)>ep06=JlJwUdDR-FbymTHY*HAvJ6b&-RqG6lT|Zz*SGU$FlC1l9%4&`}*fPr&$$ba9 z&RU%d1U?Uduy^__-o;WY^S-1xM#s{q6PhnJ+0iht@aw>X=XD-BSy(_G@|!EJ|zMvh5#2 z6iGa-iegP%-k}X0Y=y017%MnawjbR}l#>^?LM9jQ5@mO+j9(BJ79+jQmA>?0SRA^M z-c%{YeU~BqHC3wqNQU7X|oUmiG6dFMr?!v^IPaULr{7f zfNqefd5f|@yE?-uuB_kuWI&XLMgM2h75^6d@*|3j>YUDAA$FM?@&8G*@ZS_UVh7ak z2I1lcleJc=cXHwOyCX>yj9%2O_m-@C$yWwBwa}pQqD0IakT29sb|`az>71ryLUg&j z_?CFvqjJI=f#kilw|)-JQOr7Bw8oviVNi^EGIS8e=_sCEXSOCuv|^pPJ0d+*CPV6WmgH8M-N+I%(I?M~>9+iDvY z6qNONgEf9NhJfVIs(zhvzVrAAv^(u;tjS&PXBtW9YyE!a5Fh6jmx6=)m%N!GdGK{O z5dv(+=b|;x$&dXq!Roxcpj2i|3FYK^BQS!9G>uX$7(7;M1R(t@gZu3ghUG|d` zC#mUg$$mF{R{w&>dNo|t6E{jw=fYuDTYtBT*RoGNGKhh*g%of?w)_Hr@~PeMrJUF2 z8xmFBP^Lc$`NUa=V2uT_huSn{%D^Zy=T2T*KXb;1rdG5Mf{K0SJ|&B2gIAz5MHc0x z8`}^>>m*^49g8w!PiLPIMK;W@f9i&SJ>MnHY_g*MR?<~n(6iV~rYhp>V*!DNZ@ec? zG;Pn3@q65h2eT*CH1}+6AZj4Ys69!dXZGO>WIA zRqhIDlH0xQ>GbI!h=w!gyB|D(uw6zq_zN)=LD>)zzVK4@QJm$+4JZsgC#A}5K_i>T z1nYyGll&JO+ydJZ*6S{W-7_9r1ysE?3aI4aRGM>l*c?@Kz_Td*hiBn}`X$&s%JOd< za1S8?0>;P~tD>bPGZ&#R)md7h_*?>8tLM1V6&70%hcDa+zrIi2p2TOdX`v_Wl8A|1C1y?$5wlxX8~(dHcE_X73LHYLjWcIz64EZTFj+rG!7PXI* z=4quZF=s-1(b=6*rme#eY+1}8`>VnSs>2ufluq~}iATKtbIc?q1ANrs)R&yw2yS1! zhid^WMw4$Gm;LWQX`%(VASi73?-L2a8=g^>1!nOe^-)LHwj1khHR4nzk3vT@Qr{JF zXi}yzx{#|nTd+Ak)2%rT(f#5W!^iRVwEH~jptk94*Z#y1)Yp(0vlTr_W*uUS^LaLg znUfVYnXqQf#}n;NZ~=kQ4-GPS`?0Y6Z(N3STEJyMPx1t@J+XUh7pyfK%|7Sspk2G{!5r&Q&G3zA3ZAX$#n7S(Y2(x%$?k=Aofc3hf0 zpqN!r7rrxv)AYK`KiDd%C95#eM3ieyCsL=VkrL|sL5+%z;Q{79s?y@E;t(+O-=?b4 zH=8?$gx5CRrD=n==>|(h2QcB>9vFn@D35Pk*YQFO;;ABeJIE(@nfJ8!hJz*S4OWZjW%`Lv9ytM4DZ9t5gv8m3r2DYR|V!z=>g$8#1z)N5eXY5SYG;mn3h3515S%id2$gaBS;g*DciUPiw(fPqL(0sr)z(!74^FYo z)TrT-!>plGT-ErW=)_+H{;24nJ%RJyG%Gyhp6?~W1cNs)84v@4PnWG|P_dzL!tkFi zz>Ro$*?+V3S?tvM@C+bK27RoqM2FXp##=VL87e=@9)Sqyx8VhPm7D_NP+Ecr#Wvda ze;1bYh9nvB&9)};H-zMlgLnNFBW$)c!u~#7hE3{6yimQskPsxzsu~Z~H4#x!w5w14 z4egr0`(l&Kb_2JJLo~p<$R-~FmF7cOl)tfU)Al|Cpu{ow?qqw0bfuPm0^G#CzC)&{ zMsbdJ&q@XWtTuIJX~6&+hQ1COqhBzrYpzaq7t|M*<#XNh){h6jyl)h6;(l3o9Qr-? zSg4-rI{l8CIm?AInO2=sxMh0S*870iK_L|m?d*>fgHdX%*qw*(eWO2nu%4O0FqV@+ zFcSQ-t4OBT>{&A;lysGDm#<)`L&@_hh10CV86=x^0nITEQ{cWx-I>13!G=8y95 zC?GC5ZMg%nD8_KD4h;*-()=zMM3V{fn4n*q1M*%C8Q|ZFI9u97Vl~%DBS>Ah5B6FN ztEFkZue8-s`NDH=IJHX&9Cl8#>CjO*RjyekVQKlp;wM(|AUl_NIJ4z@?+kj~LFgmI zes-%}>k7q-NMhKe{4sV8i6v_mvQ3|I-h25{6Nk>YWmU6Q(W*q$GTa_>7Fpr4pA_=1 z#MKc;eneHKGybApYFO0QkOAOe(Pq126AWi7MQH?)8$(XE0?nFK?**T24kktW8=9QN ze|^;qa~)nr;Bqd*H5>O(0_ZEbn^}*Ft!lJE+w?7bHkWEhNWB@SB-voVM@u=V6fdI; z-gBHC5mCx+kjD7JVHs~Trp4q^l-B#=xh>nFLWClc8J!HklW4P*cOCRc5Lt`NEgksm z`31Z>&8glQeQuBZ_M@s0f&_35eWz|ufTL;Z;vnGCKYfRs9O_a>w(*P!JwqQwz)ZWG zQrAhA@K;@f@y(K`M0|A!iBm)iWEJb6vclh$td}Sc ztY$ImjY|t^^$StFZox-?+C}r`iKGpcv}M-S(UD?0xnI~z!xtua0gZ#Du8*%ZiiP~% z^B-bMUPKxqsdm?NRCe}xHp=F38bwFHIxohp7^;DTU#aaoxThwX@S0ci&QP4ho|r1w z1G2X_9>-~33Y#4ip){$sO!f`O^snuFO|9h4dT*}gF!z22>v*#gMBx{LF%#9$dx>4J zCRb=h@Dm{O;Rlb4e81|tw4#;TCq2x|CH5=A$W1QC;Z=88sePIE{oSnf#uz6#cKx@`r*QO6Z-}mzb z_M+)vl+DTi@#S`j9!Pav5Uwb+NiO<|thI*0yl6_{3mT3j?GZ6<6rEe{3>to}=<8{D z9`k29f5&nhx^$LtilqtOUE%t2x_7)d*FH9dy=7zsR)|Ds+U4uUpPZV(4eIa=*L!YoOz;b2yMGRb46`M(Z1S$HlDOL@CUiu|}hEUy={`+TpnrCC)Aqgh7Km z3(DOa{U>TC0uCyQ#SLxj1V;?-vnspreC!3no=sF3f&40V0dQk!lOBZZxz%Zex}cGp zQ6@Ev@w?+i8J%GVhcRqypL=cjMr-mEhu#H8w_=Hm%B|M87#mLkqgZJC@nTLF#v6)H z$lZaj;v9sX{1u{_$v#)ec@nz7Jon}1wPl!}R^FZ5v*5Vh^w!r&%JSH|ZusQ!Ge_T| zLchQZ`+3GBIUvZv3x;8loP#{%t$lB`(IHjX*zGivZoMHc8@kYRf3|3!S)7;y7PF%+C&=LstTGZO$a}~1k&Wc!pxxso zIwj>qoS5P|2yXbD$U(tPA=?ikOI&TJNMXf1}%m?op4q;e=pEBZK|XK=S!Ii866{&?4F@*_0a<4cq9}9nPC}gNChCG`UUcch zEKP_m>TXTat+)2Uxumc_FA~?x%DSq|V=8v?IlRH`>n22A=JeTc`K2-Gq>rpR*(v4^ z#QgNXi2(?Tp-@G%q!o~)JJJ3{X8GT8EdPV*@_)bUQBH)2dy6as@;8z3KM6Dc_fdY| zCEx=?tsvuTm$CgnK~um$&j6+*eNQL%<^K~X_7^Vd@9%yhO!pW41e#k(@i%(BSs+~` z&h!s;phi1^s`dW?d3w(4_#totYQr2S9m$>_sx=*h%g_8-ub z=^yAx;T;M)f(BTzj^7^BKcFYd9d*(wAR-H zb`Ovtr)$(d$z=(M8Vr(59RT#LeArDdw@vTOH~fVF#uV&XwAb`F-yp}CQdRlTTUe(B zlH*5l+T-Ww%X7kIOvMnhUh-#*ijHQ+*AcsJ`yh*lg4g#U<@c&fnlj^8v8qiS%Rvt) z+P(nnr2J3V$%M9VfrZTkrqN8()sxdc_Ydr}sJ7nq8+Q8aoZh7R#N+7Nw+py7#2TX0 zSFN0gzYdSj1F~DecdmlqbgoaX{M#X4u2<|hGb(k5hyYAVQ+nJ576u|ox=^N+yf+mH zcH(FKh)ND+r{{(@PL%@!0=}6ZFKLY=yk0=cJ( zO=q9`g2{H-fMY0~hv;`Waorg-HmPjZ#zUoCn+xg4m7O=_0r2kzRi@&egS|WtX*FJr z;!W3ENJo76lmQ0kY>IfoRUVe=x=T0M&rDc~gbzQLJCbm{+9X{qnq!&iIx!)ciD`oK z!Pyk5MEQ>C#%bU6(R9PPu};veQ?D$cCLd!FPSz7@wJC9aEy4+cVG~_p*SUsMoLNbxjWNF+n7A*Hgas$uW#)HOCjG&foW7wt zI+IZ^_?awLt=Yc`%ZZ5ZLBZ}p*XG2oJ;_ng7lse+BIqb=85 zf$5a*&h_M0jLGgO6_xrhw@A{o# zCohjT$gDutCa#v{Bh4m1MA%ylg|f%%-A2>5^#+pX&w07A8D?!SN+yS|4|~E`bXv42 z><*EMOvYrF12(*T2hlQ*Rx`r5*c#UN=b90Bem$@^_kjN^t)+z=;;I{8Ztz9>!v}a= zPQJ2MR^D}XgL5lT3Jr+4Ru>YFfnvoe!@+0-!`EjAYnP`{LMoNFa}M92!0Ut*Gw=?5tW>9x0&CS{6U>*aY(cY7x0G1QhXl?yt-#qj#v`3330k0OaHw=e<~Q0z_6KdE z9GFlx$#*e6s03(n0U-9_@(XBEi_@J{lr-43L^;`hD;h=|H-+4@0Ipemprw`7&7)O2 zg)_leL9r0F)A3^1%zW^p?_?c7Va)4vcRD-AsetlprSYTI^DdRj(gw;8mm!!WiKki7 zH~lm6_2T^2bEF&*?r6k=aH-(7Oz+7rE9uC>M4r)7lPfEqgzTtR{4>1?2sI`MCxyD+ zocAp7tGb4p9h%JS)=KYA&DN$5mf5V@So{W}p4^s2toBBu9f*aVjW}_KU0{Fd zGUcvg@x}}?kitb>hT*auvRs_cr8zE)Bt%4W(eHnj&w#;b_bR`pa@*PS!JwbSn>}M| zTMz)C^oTIhN?j*OqYZ>nx)QStH_o?b*l&8rP!7IIjV4GXDmBro4teow^L)4UIefH; zm)dc@(r)L1rkOC9Sb60;5lVBbKoKtkLFxQWt{#-aYlr3s>YPTW_0F)mv) z@6?;4xj{nd2*NM>Cv~usAZjK&LVaK)lmyeoH?25%;%OdK+=wGdP0_K`ibJK$5Gc!I zpVo(*75w@WshU*0mWn?==iDZbaMi~u(>y$;ibj! z{z-A3Y9R#s4WV3uvDW>o%VTz5B)HmrBqzYT7ENVrdpMDm*1b^c z5@Zr$(+Q2}4ueMdb11F^qmJ;bRas>-!A)ZYBsLiUX|FMR%W*FI@Y@{BKqfxukozbu z*IO8#wc^VhJPiDk3!r(1 z&YxFwL4cdR7_!acTNGd@fBgzkwF)hsMyut;p8Oa_a~!PuVcV5YTPJT#i=5E_JL~k~ z3zulp3<)AX(t0N`9EwBOv!z1lO9yFX9~DW1$4n2`+jGja!k3s0e7C3Bc8?a_Vo-;@ zmpg2UKvXh=EnoevPP9V(;)7>G{48naJJQkDfP&Hoe&%` z7>ES7-WmLod-hPgKkP-A^yy-!rz8RC=~tb@r2cj0*nm zLit}rwwzz%FnR$sOm%d*(I%=l^s2t={%VXXpMOZ8x%}Ya@NxPe$~y7Q?s#6%3gWsG_TbA2FZh}!{nSt4G)^MrXhUu$vsJ_)UgtCpH#p(`=uX|6z1FSG&)zpEPb@9c$~;WJmr{lh$zJ0NTYsr!*4;txNK#}~)G4dTLR=A!r4;$@d##VW1y|trNT-dG%Bk56;R3cT()79| zQY-U7XpBu7tidxz-YsJyA)j?>5s6R4SRPMjH2t~Jhk9U>Aw>P+P726}2`GCcOspnbUMCP^_`<$65Nu%YkxR1^;+b z@!F@a3zcm`?MIAJBN_`|S#POGOr8&v35vh3H4pQ>g@>d4Tx}ag|4}sM4$_tXYbn-M z6XmB1>?O$E$Xu!F#-2aY<}S*f%z56(jPoqJzJQXcF75p8uty~R{%+0?x)h+h3cfpy zRdtJ|8W)9uJa@1t^pYjD`Sd9euIwDjth%_>L{t=ng>%}yM!c~*$Y-y z-;%KI?Nu1#KIvl)68#bR8Odti1C$hVDnQJ}JD_?WF%u=XBQUR*RzaEga0m@Wmxy?F zATFB^!{xLH&!ZlGs^H#H5Hf_@9&y|f74JIpuElgo?Pz6xWz>W6#m!LLcC>C~Y3uQ;&vX}XL@KB3ozh6c%3yk#Yu=@Q@vro4$Q@jI z`)^10UuBAHzr3Dj61(wT(rAA8eh#@8KU*j97Mu1>E3T4cA;rO|7VHU$i|d&@UPI*$ zGD+i5IX{l;((8VAy@-oX-(=VZuG=D*fH4I3YfKxK5sCfJBv!xP;8Ywa6HmqvjZ&ic zVG4Ou)ME`jv)j2KGQrSfo}e6Z1zPRGLCj#~cr&-HkY}mteNRo>S5VEF!I2Iq{a|wG zpL0VK{Ht9=G83|CetDy|cUnk$HFK-yYNvB{<-umFC`ip-81#obykPn}tPblSD$Cv* zUA>p`qAAOKA+;94TNz8je6=_YRB>Dv&k-Z>^skJ@w`8K2Tr2qTctJlIv1HUGWp|8zDkg;cWtz1Q0zvm{2|=KJnz)UiySceiOks`;*8|L=0U+%BYa?G zohYf-Ddy`fw#Fy2@F>2QOJsvm4t9sdapOAHK_cLVKUG#ev7*uI4GX!YJc+;qE*Igc z$hi#_s8ADdKw+->!_iy+2m3Xd3}>_fWVu9i>cpY!V(D1NkEcwlWy)NrpN7{<>Wuj3tjHkn?b<#CA8`VZ$JaQ}r{Ydg32&BmnB`t(PG z<&LJ75k)QPUEUX+R$3Teiz3GwR$i>q;cew>s(N$B!vYfBHKU()hsprNTODjjFqe6{ z=JtmBYrb4=c{CB^(nqxXx`cakOI&FJJdOJZH@to38JXVd_3Vn?_JM+{W4sPD0B#!u z|3iCEB}^Q(+IGV!3jLbwUU>+0rB|ivz2^6XB?KH=DeF;+vS#X8m5tU#9OoI>3+>5d zmhelju*uCnaqg|&FhWG}3ra@2)j2J=S@mRXr8V9cP2D=PHkyQRW&gows?J$ankwaP zL3B?vfmR(_gYb-Kaor{0o+ zeb~F1Tg%43btXaVto&^>$uv9pHtFHe^&PFxx~Pxnp&UpRD^ay{K_lc#RfvO#T3Dzk zdu2VctEecu!FUeR&G}=Vsq5|wh2`fbe%mbsRD^1xgNsJGClrkGc@i+@sbP*%k zIR!EWmeU#EF@8Xv{>bz?PA?a`k6&7WPBWe>l}uU0mm0%+pB=_%FdiS^8el-LEcTst zP}917Z#ZE}Yof$1&2+lV&CX%X&#}F*FSZ05e~Rso=?M3ee!K9&Jq;^HGzcyp1S+9; zR6_t&|NPB(R=Br_PTMyqhA3El(6g2c6TGXw#<@r9Kv`j$e{Hd1>MNrL{%4q~Rg2Eu z#~Zf;7hCM5EJ3$UHU$mIdUGV!p4sPF`u{~;a~lAo+HyV_LW=90Rxlq3dETW4$gwoc>%K1~_ z8SnYtxYyS4#~=yJZ}7*l<6excy6Y0ZZZ!i@FyFqg}A{EQWQwG-%H5^ z%Rvnrfqg6AbGl_%jf1Ry8lDaLBA2Dn@lJ0_Z#bU!h{;^wq`} z$lFlT@Jevqv+?`&z6+%3-yMBfOiHZ?wKWvnb-4GV8Py}dKuf>e3VxQ({W(AbT{uDb zss-NxXInQfetTS|KfY*Fw3B+Vl{?`bpuO*h&rY*=-f22CEBah%LY5TG|cd2+;N{^rP0P~#QJTN#xXZ4YMC(2_NPRD(%l!w^LI4f zae>(HqA{Y5Z%-QgYhW>`Cri!jByxil^NpDhGI%xj}EODEyVOztO# zY%4d))Z`lNx_lWnTi?Ng(~iM_3ibvk7&5uNd^1sSo46p~^kj?djKuCn2WD0jr< zM<48z4Y|{l$;v5=LJqkSwZR}pS^ly0oR8KnAy_usJBWw0#=E?92|#}(X97jBK4{q_ z+8zFK7RE!+#mmzjl8PpG-o_5{vr*SC5Kn;d`89?j&VDp9XodA$>x0{uwV)gA+2Vx) zt2{jVUP8BfnOlao@L|I9y&;;X^ZDBkXjDp`81tzZKf_aup1h$^CgoyJERL&itv*|s z2kPVK4{MX?>^Gg+UUoac0{R0lOUAI?+%QeldnV#mu&4u8pPF28ku6ZnA7 zXLgB`POEIq;`)q`P_~}YCmed*i_^psO>Qh>qUkXp%y@F0+7N8}f^@+fMwZd1a2=Dx zWY!Ox>vSB2n>%%~t6gZ;G;y?hS)d&}ai-&u=ft*VQs6FU)8R#tT5}rp=sVHZA+CPfHHw4lGf;;wLPuZ)Y}sv2MJY=AVucF6>u_>y8PgISzL}o6mwGy8{>rs}NM zXKEdwj5is1Y|I?oH=JIV+*Qpb?@J^tEU_;p}M5 z4AX@{yLH>6Lg!pVe0fgIDeAK2ncS@KX5u#sV$EUII!w|IMBC$K;ZMeLm-74gk}r=T*YAnfH8&bq=LTKKg; z1=X0Y{N&%e6XyU975MNo9gA}B^?r+qWiJFVgDq?uVu5)8zEu14oT&{sP{BSm>)0I? z^3LaX3&day2OsHlk@_2+kgsqIrAYbdOq-iH3%tMrPX}S4nI=4!3$=`39flSjK_K z2s;8ZV_5G6{XgvE;3B)GAmUZG$5?akYxb=T?qNg zR@f>J3lQ_K%)j6)YlmR7q1C}CemshT`kjNiF7d~#)5tNOAFuF)6yh#fvfp)l)$mHn z60>Eu;$_f>FkBN_rWq>DPjlE+JZmiCx~WY1n$`N+4}?#C*M#KvQ+4MS0bEz9rs}ro zrm9c<$%s3BO`rNfJh_X@VaT5Yj>vo5%9b-vs%|I6licir z5v~%g_9#)>qLUcSvORy|c_bK6!r!fX*pavGv-EIcU7Ux~jA-Pd!UnQf?dGyKsOJX8 z2v@c*Z^dEap*Sf7Q=i*@5*}4|XpwKtrxneJFOs96*vao6-Vq20Jgz0}ABdVh5ug z!qh9(QL1_XclEO?v)L7x%}Ju{HE7?+&@9&ZCJgS1f}mBST%W86aprAEr1WLBxRR>t z@v_g*ok6=dY_-NnO2T$f&m$ykVZt{HYPJymDmz0bppw_OYfj9b)FKjtt)T~OMASYen3$# z?GXPc#+83_S0J?Y{GPfqarV#zW!=X~edLx%P8WfL?*u>!M)A{6%U`DBMh)81eVrF% z{LduAag}CkX+Sd#j2EU>2-RRM*wTKatWeCzr1E|lWFgRX|g>Df5=>M z{<(7?P?lsN(U&D4rekT0-qxolE111?-K@zJy3*n#c#V7xs);g2V8786v6uC>)ibT1 zCdvUbiSj@#$C)NJ*WLow3_>)iLp?ikOCGDav7(9g?XRTCe?`55mXu z51MG{OGxLddj*0tmim150nkA+}>I)<}vZm9|8hY%NXZ@Bt*rmYF#vj5bL0H9S#*8!_EHj7CeEhZwhGMKGl?W~XPc z+7KoFSgYkc(sMu-7>!PJ>&nW9Wl|OGmm)i6e^xh{rb#v5&D)$)G=Fh>D>V zzsw@f(sn@5n57qAOe#g7AnF4MjMffFH!-Ds7iujGg*;VEnXOWtA$2!4DK{yL0tdbC zMi+!5)&Jw{t;6c*vTg6+PLSZi2_(1^+yVqof^FO(xVyVca3^?hHo@K9-QC??ZjpXZ z_j_-j)8~7>zj@eDwQJQ{bB^B_6K561zBtLwZ$>Y4-C&vc8|D}k1mblKDSND8Av>tU zcaqT8>W8XGaxI{5sVy$&bl7uJ&)`2%6xF_Oaulgl`CngJ< zjz3Npmf7pS%OIP~T1-`4w9YUSf=#2}^ax&OmBQ{IJGIu>cs{Jwt*~5GJCzM- zLhdKQqK=Dc;^mkb_i-^tk|?n>{xduO;w6G%Gnf!z_7Cl-dyIa)L#tb`5k;Dx=w$b(tPW*;e`f`tC_CtbgjaEAiVzp)e+m zHm`Su$##asW^d8q_k}DiNFC9fnOl)2<604RY8o`1%P`&c!Gx5L5aODo%tGIqvo2zb+9IzD{}u zrVp6F?K|P?GJ4e_tvm}w*%=c5MhCN`aOKUg(SiK6PZ6p&PTRGq$lw3D||C;Df$O@EVdvyej=4_hX(_cvtw9^Wk zRGy8yAW02K&8T=^GdCn}qttVvepaS_^Zv`{g+xyQm))^<_}w-H1Kb7j`MuD@5ef}h zKrKqQQLIv%c-E}&WviDOMN{?VIYs)%bE+4I&QXWE%iY^!U1hw_YS{K@fM?V@CBYcc zi6jovf*xfhP-Xo}TEwiQR@1Qnr8o8T=;B1TANH0>khaEqMn1e4Pbl2^@`!qNsnH>V zPA%q#o5yyX;yMh5b)PWv7(ajq2vmr^+OR6rqNE~4O%of?BU*2?&n7B#ISAyYZqy=S z%b*oaE$Z_xR!BYAvuGHuvG8N+5s;aIhRP71Y?ZMWc+U|4#IGez_}ZqPv|eicAmnx} znp?!-d+O1)ZG|(_M^&8ht&{~_e$@oryY^dWqMcSCM&@{yTpisU7uVhhJOaD7*rDDc z*T4SpI<3F+g-O;rR@H9p4lK1=rY_zoacLW?KA7X7J=3KM5naeKUM^^K+a;`Li!amC z4X}}4)sD3Q?di)<(K7E8E+?J`2CtX(L?oi+^$}vfw4KXcu#u41M>8yxdn2OY&ZkQF z0hx2)(!u5+nRksc_H(FD{6m>(HY^tF*<|6ArdSvOe^j0Qt_adr>6@tDHyZHVc6Zqc zQ=<41D?hp-GalWhAVZQZgQfud$9O!KoM8tn66w|0(S~U5F9L_vvf;LuKVuzOda2!~ z>i5Wh6f2UxH)%3jSc<&bne3?JC6Jh+a2k`Y|Cada7EKk&SzR45$t;RO+{tQ1)xzoO z;C3i#W&^3d_#jK&Z})(CMB1~z6lf|joqor}gmTYo6}}TsZWAG0NGVqTO()OH7=p4d znIqwZjb@47@s37kod4NzAyms6O7Sr9cOC_u>tn#HLTRv$`n!5|UlDpVOL@nD$NRqMHangMpty|8B_)rz*}Qbv<0WGfaZB^nak zVpL^u08NT&df14T9Xr$CZxH`^tlt+S627_zrCCAt(<3oRdYI=Nx$>#P2lObw)8RF% zffdS79ezq?Tm&ML6sxt_2F}Hdzh-txW8>z8V6`XV%u`4_;71@nV?yLMD%@OF1J+6E z__tiWmDb?=qmTo$zrEK)SnIlUk!VK+Uz4=deM_k!%1lJpRU_uAAg-rAkfxGNMKiIe zIhrsqCd)iy&c#u<2U<$i7Mhva;?Y5cj0$j@Q-Xx^W~sLRseEw$qHa&yr~JT<6_34O zJhO`gEctu{2&6UmvVtv20fu}}H=uwk>+@o2kC$3=2QhzLy-1Xc;gnOf!@Zc->;2Yq zqnTL~P=u&%nl}9tL*_K4O9tlET>@g0ZB4Shpo-#M%Lg-#_0@bis+@$IJLuJpAb!1& zTR%dldGi5qvT(*QjH+eU^%sM@r<;{lw{+cu+w+Kp)=r(M2;ktinq^xYtx=s+TKF2X z%3`@7DpS*gd(GHXlyH&9VrI~)0gtZ(X_$|MjdY>eBSI#Phjc1&1Ns?vJ9WBLQ#;=F zh?XJ$p4?=A;z|N1i&o~;buiJg`KzdY3ueB4~6i&+}|carFD(8fPFqfumt z;b0HwH~8k858%0Mwb5H@#6WQn2`g+vOci2ToP353f^$$a@Ce^S8P8W?sz%&s^hj^9 zYBBB3RqpHPLudvS{%nd;>L%p3Ag31r00wDd*OACNMympcV{_|Tv-&vOyNZBUKA6U< z`1HV-QF@QFW@XQ=a6)?XKt!CYnpT!B$!_!6vIM_KK!I)y!Rz{HezmGNL|iD{hn1RK z0p58n38Kcmp5&~wYonJ=vW2d8g$D(;3NbcawPk)U*NdPvYV{6AJZa>#Seib7En;Ld z9r1-N-!DJIl3=XeCF5P;uUTP)1!2E}_;h=~s)5w+p2|f$SGB6SJv3%BoO4AP(y4+K zo_-@|f4I>pV-Ac^p;oW-Q|0t1V*j9IyXRhi*ctL1C@E-1xCMFWtxniN>%WhWRfe1R z(Ta>TyY&*|O2LzW3(Hb7wN3$k%91*!l(uB|>FNHh_Ubc{$DzjcV*z!6DV7n~KD94n zDEW1r{bA=3?0DHbe;HbW`h2C~C)bD1X@enWpkHD)Kn>in_$E+1_#%p=mAsHu#+qgW z2c)mvFJ)O;sr7^?e$GM3M5;HKBwZAt#UCr^pR$2(wFq4kd){pvZ7s4*b11N*Pc`d# zNx7YLzZo5Zlx-hXG;we;p!?fcVuGWIFpCyRhK#UKfq@$PruNJ zQqOm0ntA{Ed7AWTGYRLOJ?uaUczjqUpojWr+1YS?yvk`WJ_2(Y=DbcByE5dd=o>yt zSxoS`QM7q~!N~#fUyos}`25@M{9*jdG&v8~h+?m{oUBD$lc8}VK7;UmTT?boC7U?g z-7%-){Z{G}M48LM6wZF#VcI<>;l+sJ#gA7=^~{Q`#wj8ydz=ubDH~!r>lOd}N@a|I zNzKMCS@ib7@g!c`PA%p`dP6wt%xR4)?C1NlEH^&!NEJG_vZc{se4=TXg9u zASF=Zt=IWAJ_(x)*k4A}G%bu|B+OTtVO_sYce|Ac}>AEG~J$vBf*-(zW?Ro}M*2c_r0{Vo*3Giau)Z#f0f zYp5D)N`;E^7$6gVss2S82d`AMH;<|o@0`U zt8;oIE$A9*uDv0t3(lA)V5oTO$K=h761(xbJ25p)^Bus9O{nU`L|Len#;8rU_oUB1 zA2lL@&c!ZE`D@*XV1Nt>%sg%hhP-1Q%kPh?Us(gR-3wN?JGH9NwQyF93X0Ftk*hpTXR}kiju6;g zT8%(DL$6MT-c1=eQ&Rpd&-YeMb?r0LzT6mt~Po>MOFSm|lU^ zppfJhX;Ks8Vt0Yn$GB5o=+SYx>5mEoh-lbHp-{an$YByj0VCK;!Tz~P% zX~7X-oeZniSDEtd$6lEG^d$e3Jm3BVv0E6J+M?1fDI)))_G0F8+nLfd zeWSii8vpbBT9Qo8w!CKEHi{j8mHm1G6Y7|^zPM{P1Wm+b!6XL9Faf*c=Z3Bt?Zp7e z+FKKsQKf!e_7*3*LCEyD8-ind|)`2egiBz*Xwz#}R&#{MI_% zh@;0%Wf(4klIxgQ%9>%?XAS)V^?hL@VIH4?mC}X6`vKKFo&l>%@c+c-{%uDMq=$!& z+eJsHl|iZ9QLw ze;Gw9`gW2n)I0V@ZXtWbK%IcBUZH=M^L8s-4CK`c9 zkHlAlilc2w;P||xep^RqPNd>7m&T`C?MYH61@MMPf~s3u*XkrC+FC>+NI0ik2NVo4*89>Vi1u5<>C&Z)v#%4{ ztkKmhUPXu{@KDF76OKHuR24}l59?c@PVqQj?^1Jw6L1KP9k=Oyx}XD5c@|>%=8t!c z-&lKP&cNr$ zmwmiL&7ch9UoE{a4Q!H#?bE%bA=^}mwo~G)@`k{K1%UD`U{vtg0Vtk0o1K(Qe>+kQc*rYVCQ^DeGZ)5gSViWCvL~c zcfG$Xca?hQ7d>>Mls=#1Cj=aMY5BPQ{6eWu?w@qx5qOj;M=O5%I_B7ugJrC&)-gxPu z8p#Y06Ph>SbQh=>m2l=94m*CIA}>-r=@&RW`Q2busU7BM-lq5KYdvMRiK75@Cm*%p_axvc$LrG(dAC z<68+(j6O67pa!^-9H%b(+SeDEGfvB;Mg$kfhW@Hc*ftk^>0_pBNo_f|Q6CITH9@ap zv~nA+Um^R3;+~r<<|guB4y9L1Z2;jk?zvu2yth|7vx(xekG~X)uW#qv=D@lk*S9Ux z3u6fZPRk#G1F&|JEV0S|!qo$7*K5bS}he=qNQbGnWcBuYq7T>~4!_IdoRmL2>m5YVwD8Q0xItm#d#?sLNg1o&xuqHCEV z{l?<79%ZIXiy7t}{flFPQN7W5lF5PUVC4u3aP*_9*(o5TgdKvpF<`RJf6fxq@&UlubBgJKwWC|8`kz<2Hhe~=4_3kdI>sH3(n5x z9TUwh2Tpehs-++tk`bta8Lo!x;aW4IAN8D#ic?qvhe6|_&XaZ7!~vPpE2JJEs_*O= zu-6q!HR#J>kf{6PO7e0faYkmW$QzYsd+8W`O?5H`-zS$1T~XCDwL((1Rpi_FM!x7T z4Mw7IYA!4nHe$8f=XR?XDjZF#yna2SoPADthnfU=VF)G?HtJDk5JEkJywz(kA zkCj=CYB#0BhhsucZ3PUBGRl*^YGRt*+Ogt08a1rtNy9gNEMz6Z@l@hbA1B_zPU%Ha z$(rXchv}CEZ`&WR=3@S}l_F5s`j(;d7lONvNb z_ua$Y6lTj*WozkJ9(czX)SZ~!}pm?UMXGzA8atBCxe7bB^CL( z$7svMi?7SkYn!ZQN)f{P)qW<45V_}Dl?F#V(tx#(wAK;#p{+_pT{X^{|0@AM0;H}& zX99MPI?n?O&ae(jkK@TxA1DaW`tF5@1DDgDFPf)yQ-auMme|4=)NshXZ@x%gdFe7T zHUgy`(Cw+y zkog&@V`1z=#Lie%UCMv5oBXwd0Yr+Z|*`l`vzk)Xg zFuM!@qZdvSgR@&Fq_U__C60e~@NEK1`sO$lnBl$&j zrBN#R^0`Msm4juqS8TcPkKM%^3xd}k)};TFc`cbvXxIY&&H@>*GPw+aD6q_$bRWsS z#RE#h5;_I=m<8_5jI%M-|Jf_^&;1xJ6>X#=cV|MSuIT^6#r_Y@o0m7lGdv{tiTrY6 za^iofr$3TKQix*&6L6&IrtG*gaZRFQA7%;%$|S(jq~W(~nI-;WP?CxXO8`-6Mq#L5 z)RsC!uQ?ZiWP`c)1hR9+Z;r`+Zho$AWPq+%y7U|I*uDdZ^wVXd#b-rnHt=rqNpo;*X_HRFPhC`)qb$`&~m0N`2%3?bW?x-c&ZusqccQNE<@-T-XYIr}f9aGV5e3M%}$C)UW6a^wofK1A$^6psZ6qTD`Y7eU#wM+6w0uwUHs z%p;tFo_D|Ee3>?IcX_XZY&f}?)#%ZT6a=YY%>X)>5T8A%e63zC4mc#xPU6nUed%YvK`vPkcqZRVwNxMT-m*-RacYq=^#WH@O2qEo&f;P?N zij&L!;5`Z7Oe$z%1$*je)q<~H{XSujWj{Z5ndzYT}!-JBS&djYF%4;u&n z@pQ2)5Za5069?xpCkrI&t@63a+O|Gx49h-0xul+Ly7&z}x`hL6r_VU_59Gc6Q^Ih& zQ^f=q_s7lS)4%fo#;{GnfSdCD#^5~$ucH82KTA^*nNe2|X7XJ1KG`(ATFvMBrbWR- zW`hi%_Uy6qOI5A4mwOiILQ#~Y-6}Zw4OM3I17}Kc z{t>vJ=Bq6bH7Ee_eqp zoj(8uH-(FV1g3V8>FHlz0}yfO$?Ou?Y)C5~veBQ5&o`F@}|`zb3I7?XB1$7^1#f)DYiRVtugc4sYbonKY535=v>`m;nF=Dq_z21IQCP9A>!LDrdL z=8r=B`A_SaDPERvJ|}Fy%Q~rBI0my^v*I&?bBhPL|LaT+C)6(qbaij}p8^JyhEh1q zK8V@thA+OAh-IU$Dr?bumu&Am3`nI<0P5KtGonj-*Eg8V@m{Hf$M$&1Eir3;uv>oF zK5Ke#Q?45~dA{;cilOm>c^HEJ?AZx0&Bls_fS3wBiUD6hEY-b6C2 zxK0(CZP7Hl>UM+}5sFL7zIR)wRN7-Rl2jS-Isf?F29x$=wH9IcGmtuy;e2T#fgxjE zx&Td7Z7K>5>YNsDjT+H}TF_xFj1e4e-SL=3I?8I`nfD zPUIeTsWGAvNm8=Bmv8h9s;QOBdCJ>Pco=5%dm_chB!?(MWHfE_&YvHo0s17%Vflk7 zU80uXNa-<}hSYlzfd2Ri6!c81xh!69^l?ts@R57~C@BYU$i-0F;p2bQ^8FNW`GNKg zU|j``)weNp{#bf`+P1pYFUnHC`(a1KFtpFi1g)6(QmMw$OrIEpIrChrQFC7Tsh_Ut zJA%&$?zYpf0Xkn2xEMkuwI;6 z#w?Jh-hQ_eI*?naqd+Amx8}fUE75R6gUSgUt(u%EHn*!*d*)vtS7}_S|5X&LB``ys zP<|db{V8ELo?<;Gj1ql|ICalXK`*fi@QcIISI@*Z5_aPbdZRp82#bh{Kic@?ALNR-w)#bW|8@0C zqnI9ZfL)Gk>K(7kMZ9$u5hz6z)TG125U7d3ru3zbOytZk;iMtiPVlQYO&73H=CT;d z{%$g+FZa4n)x%&`$p2Dcvu3GY``7I~K{ny*yPUo<_ zMA*zqXRh!NX3N>VR2P8n8etdS>{(n=c3#pqfFIDB!crqMj3Ux_8@X!|W zrc7eY{kdbCO?O3d9>{FDpiuA*&B%BHi@z@|j!tkXXC)xN2dqZ6>h2U*BRX{(tUoN} zHx-08rEwf|F|G69`wk+k8wR>;52~_{vV8akj8@P^_@o8hqqbOw^8L8!7$)9X>PX4P zsNPv*%ISCLdo|a=Mb}Q7B$h;dXgfk)V+@ZsKfI<&)O&|DuXp;*-8O}xa-Q4C$wg^L zmqV~<=!nPFnq0*fEWHyVJnbR0F??AE2!VM055ulCTcSskEG0nup60dMv>-X!B>eXf z>932BTps`N9pv=|e&b&xD#2dEVr#RK`RJ9h%WmFa^SsE9a~PzC_KoZs`8;!da0`ps z9JETq`;IdrS_Ab4ORpe?bAa&~%XLaCE?9U|$Uwp;U#KCoFhv@c+kgw#FD|pHT!rjg zMyVGIk_7#>$5OAe6@>qKqjG=Fz;~|Vo5ZDGg~p^cn+JGIW4ILXEye)gM}KU9c%n|) z1_=HoCT^0KB~y%O_~B@HQ|6`7FCv#Wl&iwFj|gtZ?H+WWj0*bYmbDSXvm0%mIsQ+| zG|Zx!X>;~lCwo=jt3q!0YsVkWdO>3mo9jsRPf*T?OfVeoH>+X?Z9~^I!;YsIBYYYM zsVok;pbp2q53-M1$_UtXDisf)J_sb)_JAEF+cQPZ9kq?pX&JRg8JB5Fzm}x@6Bjx~p%har*dRT^qL!zK zAxdi7rB^TSa!DVTaj1F~L#u54gE*2}Fc;DbaU;l7YGHpVD|*^`Sczo|^Ixcta=~V} zG$B%V-kZ~O#c2s(3AqF@VcGpW5s@d<$8L<_zfUzXs5gawq zAR-Lricaoz6Ir68k7Eq=48`Y_4>a?E4Ug&6ui0t?txp*2Jx!S6`W{{po5&u^hSQkPA$juyGaHTVy1!!cM$CzLG5@a*s2x;2|7 z1}|l6-=a-D9E`QKm8Uj+biOFdKJUaa+vyqAbUZ-Gu+q(wNcUAJ;g$O3`;^XNn9ISx zK6E(m&`^gy^>oB+q^ylr?u-;ms~opYc1~!=KgQjv2%2!i?2^R^;lmL{#a;}sYNz+m zRSvz!(|xWuvBUkQ1c7F`qq6|aU$i9;O`6w{zk5rEJXBbtO2=Fc*D?_smxF9 zcLcQTWUp`vwK%qB1~ZDr%D9vm#Xb}~DPl^zU0$!6r4Bz`m?VfdS{kydI4rw-Q0er8 z5f1GaojsT;u0DJ#wHQDL-(>%IdUt8U*9}68VmelAolcnxdtn3GxiD$vwA1Qix1-@T z2SF{mHm0)43b$RLh61PVwpJNgZM=3JZ#N1jS-^*Nne7rDb+#nz%XqH^Rn{%p?ME zkbfL+D!oGi;R^`h3;R3MvVbTrPAITBRxwbfqUS1hdyt%$$349o-wQiOh~)7#!Z}$4 zxOnBIozXhCTz<}#N^G3I`n^d^I{|b$Lp0p<13^EWTln%%ERebF&CiL5UT7XWr%yen z8kXSo*ZBy&Ua$OI7SNJUV$Y8Pjvg(Ki8MN@$k=b&P^YZr6Nc!a0Db)dsP2d`BiNak zRvhqoveVHfS2EEw%SzSH;rX-Ud0k|{V+sW^h7b_j0&3-C?5&8eAdA2)gT0BriG)0@ z?SNLSYtwas89`eTrW!%PPqp1u+zohW?w9jbZEt4;fU{A+{R3y~izLmW#mFZ+(C(+O zZ!JOnPEdm)zlu|J;pd^LJynEv&{z7irW~^MyIqe2KEHd+#U@p{Yg8PA>b5kb8v~}Q zs*q=4#0;3Xba=FRpo0=LV*F?X=e+8NjPcqEAmWNR=s?onR&jx*Velp?^rk@wfhaFD zMo22rx9dVWT8@gl?9>`7?+4NrUc#|Yp6{rz+#e6$*H`tu3y`rcRx>TQKd1sg)<{_R zFE!mb;16idoFnC{h3TBVu%AQVryWge2eU~w=U^0F);ccH1}WOwiT^L|N7AZhW5OU3 z4>~sfZOy}Jz=SV_pl2IzQ=s%e5TG+qwT_ZDbxTxI@=7+#!38HH>)ttk#Pf#^%K8e% ztNFj-KjKk*bgG*CBa+lw#2v_3M+>#>M<(m{6OVz+a>+?x>pum$wU z^?{2aF7JG33vV#{^LiNBUVwg~|6cz^|HW1lGzQ$K+^quzzgswXdzjxE4ktvMuW4&S zVw!8$uJE?*j#2yms(5Md>24lfN%!~s3v(FG(H`LF)q(sC6H;v}jmOE``eZ>7X1(G3 z?vmypYKTx!eDN4~K(9T42huB;G1)2@T4z^zb+tB zUWg7dGxA*yzwfg z2jxKE2mv`DI5bFQ@HyUh5*maK4-F`)`{F}WI1(X9h4csJMgPVI)d_b`?b#n={|gVI zzXYmxYI?&4bVn+}dbM^PY@t5G^h2TrR|9+gnCw^G4zFL5-n0# zq*fq2C{AQluQw{6BH`89ufyg$W5mJaYc?!ePy#;t_qLJ;f4N)4cL@u&KN2Xr=nwf$ zmjVc1;2`L07_y1{^9(bBC%Yrs`c=upLD-N=>bLx-c?WYZtN|cNS4)sdRN>6>Vl1rw z(y~SWHI!ehPN=mCbWyR%F{xxId@JCtiF=wP$P-(=@sA4Ssu5c!CH&4}^S^Z#tfe6! zJzG+J18|@{_F3vNokOlzVc^VSdpH*|mMz{Mhsy;vJ;>GIy+1!CcVcJg)6F1SyHwvztq}U8RY~(xoz|<9+L9G6t+QC0T2l@T%MzhZXPG$+xxLq#cNRN?=d)_vS1-n z*)((*G9M6hE#!Q2r&b1&$7eF(BJ+64TLrtv+Lg3_z)H;0ZO2swP<5P=b+)?=9XKFA zxz_w9pu}}LNEG3<_;nAiB;S=nWz<|u$s+DR4HD!hRJOY@pg8O-`N$uLkyGv?4Eii60+Zbo+AYv z?PmtHvf!Zi!=pfug%dhiT8*%eNtpE}wNo^G7k$tP#@kP!3Waf_SXDPMDRgf<8Ep>d z6i6u-n-25OXPD%E8H8OoUMD%Mf&H*)`^@K?M6k(L+&kZL+3vJWF+`0KSZw4q0R3tX z+lV5qgmo^$_Tsk-{k14Bz}*drfqa2{tiVF6n7-ymci#GhtUD>KL}24Tip_+5N%=@5gdYy}{K0YD|4@0g zEajaq8cj8*CrRk_$TyLXBCJ@dM|OkC$7`DwBFz|bW`oe)TrB$>o3x3MlmA0><9R+r81lRWe;jWsI=<(ph5C*hueyN(M_kb8V)Vu7qC@yz@~tPH_2y z`W^2SmG}mGB$$N2?H-wwcCQL3i&u)rkbTwubvH|Iyhi$E%2%`F5A^`x|6GB}qXg&u z7tlhC-Qm<%Tbbj4se8y#1I10xzg={D;qZ^A)pq_M5oC~bd*=N?Db8=ZIByHb#v3!BEyL8ZiGLpQz5af zpuKpEIo(ELKK*ng#T*oVxb?++-20#Z636La`EJ!P*QJ=IZc(k8%sG>dnn%Q_h4yWs zN&FHs)o}W=nU!?jm{mdP__==_v1+~504|(SE2RE>Rq94@nQ*lXyn7C$?tDSFf65dmqJ!}n$7aHvP9DEuPstDPmyG=-nPSDj^*jlAy% z&W;sw=PLO&40C}G{(|)o>tdJgv>y||S;993{|N@t%Z)4|7=MSy480{@_Uj{CoZ0Gy z-%rsNTJ965(0JK|?|9Nluy*fkSIMv-eh+*y-r{2U_gbH%i}Y2zb<;whU(Q&b3}IgH z!MBp70OH@fPvy4B#dj;4Rkt+y*}OJeZ-6mwP{jzdWXi27ud#d3fqY9L>EsGBTI|)O zPD$*MM3%f+lmSio!mWFum>SLfCXh^T)VulxC`o8XbYA_lY3oo1o%^eO`up@MbBT8U za4hdPGAkxeoswJqY=!B!NLyt{sn+E?g=@*{i~5BkmRSLoJ0lQDAB{peg9~+QVryM4l0@mtWHLqkatujxE4X&bvWm}0|^45#nGKqg!w-&a<6Pu9_9?-7h zVTjcNLKtgM8a$3mTo+L}UKjJ5$)2trf~(dyn{%ZPu_@act(R4*wOXh_k8;PMB+s;H<$_Qv zCIR@Ow8ar=08^OiVS<+dbO{PKkDEZji3Vj8YSk)lHEv5S{=>y~8l+ozE?9d1k!^*tXjL`MGRvwR+@4M#SZ;Q5mM}p% zQkCG}86WCOH6&m)=cF)HTSq0P^Z!}PV5f6Y)fXo3m$w}M?}E&)?3RnPNYJ!QUxpNu1qX-W-0?>2!+cEQm6HBFXHD zxF7L$qV$=`V^G<5Z`z~7yjp@KH_atc-T2~&B~WvP&sor1Ny(#2qn`D`>HmF&2{4lW zTzI^p#tejTRCWb)iSEp{KC~XGV1W#>jtFAS&#ZehHJz0}Qiig7C~ToHJdpXpyrfef z@o@UChKVnbSQs{9OJoAll#q!i7xF1HH!EhT43>?Q); z;e@9KyZ__>zi>2!Sf4ZHsuP|pVPKDyeb+~|6#0JrH7y#LSq-Qh&gue5BI&)*B`k@Vt(HbC2M*U-)dHXm5>#OqvY(&eBI&^!u^bT&m17h|z5C zE$N2a!VZ*0(vNr3OURE42!GDT+&o`_ghtCfLw7|6sZpQC!GT!owlUmk@$@X>_B+y| z)C35j~yuVH78ddu7wZD2>Z)e^9nSpIcsU zzb~-t`kP$p8tyKa$JDyfY@R_4J$a>?!0x#enuUG;25F3HHsoM#0@uRxx<*&2 z{)q6m`YAY_y4k+c%G#6XN_+Nc{>bjtNe(Ysq_HzG)F?`)Bn+yv1Rhjd&P-uvtF^A2 zj}Jl<(kF%ocF}3!S^sYmZlE#rNoPg!%Ej4oFvyFwxQC6JLG5?FImP?@8yVI5he=); zeI+Yz?ft4HnHf$x?D&N4Wf52o>n7ShGsR{SC831X?2!(}N$;4X}|;mRq;`GK<eB10{fx(7x(jGu44SyY>?x39& zxx<#r11F!$Rgsyk=B1q+yNzaf|3GNGdV?0XMI3s}u$g+%T5TdrBGaobb1U}c{bTT? zBSc6$sPyp+PNASE;b4BqH{*JL3g`E-RsivX5&z}RsNZ=5)s}9ldUM~?jAm3RFE&G>7|v7e zXCU8R_q?Tg=HmJfd{O&-N%uzZd3pbK_sPfQLMJ?>;x9#C94GE8Fb(xHqf(G(sm)0y zIS`ZszZKpsi@W;{GxmdAn#|d>&==43ZfVTs(-XNA5~(}8$5 z3Tr1x`;Cqkl$R2KdM6LazbVt)`dHz1Z!qLip_a@wDOXjZes>bDPxF1oGJ@FjIP6CQ zR6oxzr~$R%q}s?_nbgmWl1UMRn$xA%;Djqa=ZnAukLo*SH2SI+$i{8iU>k?wd1Qsu ztx>ZZC|m@K2=oUcl?S+&fe0w~=SPP-=c>F$^Q8vBw!f+UaCEL@cd49<_^sVv8blv8 zI@>(-MV6f~P!Ma^_PLz+zCxY6NTp%rw)`Gvtm+SRalmaiQ}z^RY7UbpN8oe%XjEtq z9YS+5S{FFDT%ugB@^G63=5#(NyP)d88}UeRtqm;2p$bixIh^wl0;-?fw8pwXPPdK4 zpMtu5t+?&k2J-&+YhUa>6W1n z;%?bt`7nyXIAb~E8q%v4D=xBosS)<;;&ox)W?q`JL{rpJi0Y{CJ9X69rse{2BqbzXj33Pk zex+9HeCrZD-b;qF4ILo4p&rkNoeDIHQQyg=1S$s5W(8q(i#j%O`Sb|BI<`2GdaZ-m zBjy;v=rV*{b!W??2J=g)*}rCXAd!WGkpBrzdc~pA-rS%q_D7TQA956$6w>(sxlJ_4 zn@nT^mjFg#R9Tb+a@1-F8fOb`#*S`5Zn+Moq!pJziR6p*af}!=dw`4z`3k-ls6-c5 z{F9UnTFa1Ht#RUkkJdP#wA<+n+^hSb|yVPMcWKEPs%%978cr}M+{JPt)j%`1KXHQ)Eo%}>tq*kQ}A>t@D_b&Y!Q>zyetL7Ptn z`wn;a$2J$IVuSe#g;f{is*U0?@4+`w9C7^(>51oV=Y!Sn0c9G1yy_7?`2*G=X7nxA zR8E&9e7A*^{tYlAv-1Q6C5QgcN)si-+ZP$OlY?W-7f&zJdcqwLa%aRIuFrI`i7=g4 zynB>>eQl&y-Jccnc(^5Sn<)FBP@*PmF8Bz*P`w@b>q2{Nx8a0!eU-6Nh{CVrSc|Z; zD7;=h{zSPtUMA0{(*!5Db6N*78xB$PZ42Ja)>si{Jc@-U+}SG2s@M{ zC8c?fl8o{efHyYs^^i}ie``(v*y9)gs$~}90%YdXg)6wNG2BuzuhjYX|P7m@$Ut&wZ2=l*C@2`)s z#<2Um?3?TlgZJ(2qXM*y4HME2UTERx;pK|p%jJ4ni<`FB5NH1Gw-nK8Rm=$IlS&$f z7;_Z0TNEx&JIQkI~1-oZTpniG&OWHq?Y(ETt zTqGVS)?&T-s^)Rlo0mP@77GK}H@35GaIp{0vOQUeAj6?kb?o|7p{!21*+w?+1$E!Z zrEb1KT+h_G492nZ<(!t!9}7JCvb$gW-z=h2_!Zs!CAX%~n^0R+^x#)%vvsyWEm>vW zr8paKx4A_tgp`Z<8Xj^d?-A|-2sdKGE`|0gL$$xZBD^g_U>>gormGtxewN(*^n%>( z_<9m?XUUm}s40^JKyc02UQ8C`3lQu*8fAaThc1lS_bRxJ;bpM|^hpAqyRUu%zRIS~ z>`FosO7A&4!;izc?Y6tzxWJc?srS8!Bwn49lv2OEXt>bm(6sXP59`qIS$|c(6($AZ z5zm+;1!Yc+r+(2)slTY#W1dKr?rs4+O?O5>nK2E0nJJRz_|-8TRP4pD0OUi60ud@- zzb2m8N)bs2rYJWWvqb9%b&O#)CEx)G7$9r!^qrT)g<~%mVvvBK0HM6H*6l8Rw051t zF~ziyqD4m3yM9DWOhqF=sTOz<%hkQSnS+$1e5kU2M2N>3k$4b<>f2mdvZ_e$?#nwlaCsRE3Em-fU<19VIA^-YBj@4lnCQmO%J^pujfJhpr zje*1hKsJs8#_tAPp8Z5V%Y4sk=)Et^hKq4oe#Bek4OWmelm8W=N>FHL7(se44vqMrD(f2)p4wSN z*&hxeAvtuQF7ZJ<2rR`Cl|8oF?~`3{ocxh9T8(gV1!$t-2_#d!>}D#%6hB91T9RTA z1D{95q_dmpRCqLH+O3rVnoghCml4eaNTw_@KdqIH%_7n7(~Pzg{$6xw-e9v%{dhI_ zmdnL4S^W*?>%`@D?9h>JdG2KkwXBh545>6|-7{Lu&9?wr8#FE*YD} z(ic>~?)F(YAQT&O!;uqMZ|0ZM4pn5pd&Bz2*Be&Y3`~lFp8VfZE zjB5m?Yqi=-PlIR2kg5l(ZYFp6J_yRF=)BPLI0m!?)k_BpSX6ER8q3yb0d=1j!@lWPBO=R^FrKV^YD z!lf880%^R6U(o+0ekHz>j1$5((4K^apzkK)aXbOZ=Q);r*SU}`dH9Bp$>h!nB`H(P ze<;Av5b34U?JjY4zh5fG_f4A~AA&w}#s6SnR?gb>XhE|)3bO#U*;C zfuv>$5>C$EG*!3rwUAo)rhrw{H`&tAJ=P#^Kqb!pEA*tCIz;Xsf1SJ2h8zxvQ#hgA zE^MyvTqtN3p1wikXFgT*Q(rzp+qe480&UA%Lw&BQdPf3i8aE z@1LFS?JC+7!#k0tmHvK|6AD#@iwAR0PNbA0vgJdCSuJxf_H)M@55O`sBzTs2#;Odb zu8s#}@_U(mp>0F9mzbr)4u_1&xPI&&Y&*Vv7$WvC{N>)33INUCnQ}M{(9Oy%9`beV zUi$-r?wgDqM zN%7;II+WEY0Tt+9XPYKM373-ugB2D@QwU$XWuzHAhYetEgNsLB?ey- z&U9e8eNVRq>`>y%Bg$YX3lut|O`ZZ2X+s=STg|CKpriiUD}ex+7B*iKdS!0Kp*Ous zy;i6y_{xobeLyB?d3o6SMQ32_xSU(@{nq(0#PL~?1>r`+cAJFItI{5tX+P4>$>8^J zqki~YZ>4FYYrr99a&T4tn&#^}5w*(4w-J9$=*JP%|HEXSjcnX3Nfq>*&qri*bJ=&YUd$bdpEd74g+HuU)fhm`+g>+J%+R~dC zz5Z6hHA6Xdjp1@Ta17ti7Cz+pQ0s2U-^?DER%x2YJ&-0lIw(AQ@81Uwh3DLU3~t(C6~Q`YCF(#q?*X@f8r9bedck=cjynp%}aa%rRW-!jIo~+ zge;%;BSV1Lc)l)63+i(@r%t9sN~HvI?&I5_lN!fQt=KzD&qguJ_SK!04HD1Xg|aoJ z%+c@;{9YkFZ(nZyRIFQk?b~#w!?o`{=EqN$iGjBXBR~QW-t4_T64Mo(r3)P46P2GQ=o0gcg8S zZeG_pcWw+Na&qKiT=9vMCvUxf&!Zz~@KEW3pn=P)(hDo_caZOj`LEs<9~*fDh(h|| z(ma(#Qe6_}V{X&BVpHqt>)Wtt1Fn+-;`xTh5ug{%+BtVsRHH5n0bLn)J8fnb8%5kE zTSvWt!J4`p2t@vg*OamGQlbrsfP@PAC#0})mjeMqws{e4ipV{*w75wrN?{#Ay3-|WmfQR^Jq%v&kA3;G7!ih+qc)^$!&DV89S9O%d8e&Wv}9+;{$XaOwC8(;ILiKv=-4tS(G^yCf8iqYui+!1Up1!&Pe=ldF)45g=|+#pGDM9{B$F*Gco1Ya z@IeD;5axI}1@J?Bw{H3G%V51&=e>Z9Y3+i`mCLyrczwEKkoQgf_Z?_t?UGqpj|KIp z)(IN{<~!fLyl#{NzVH15X@<6d7d7qFUE=CZ!AFluo;A$8N}Kl!nOKZq6>vF{>Nbp! zLiEF}@i*^0@6x`L6OYKhAEF_=;=X#pQukzIHr=9@)9zJvc1Jb45hZ>B$Dp+tR?fiE zQ74(vGcx_VcNNgtq~!gyVV(~*IqXERK-YHC?3o5+i|=Man!U)9y3Z-Lin#kk<~TQm zspEdpj?~;B${NYRCSp-T4e78884@)1Sl^vFQ_Z3T5H#ohhaVv}$9kn|1Sdn@wppn0 z46g)|)@HzN$5Iis70fne*j)Q&mH0!&>u~@|mv--IYo?5sCeR5a1Zc)30C|=EQQ?d< z87rl zOlf}v)WCUxjN`s&{mV6xV+e2;-|fhA>K@0+3`^qIrabvusPB=Sed&_rg?c&H6e6{2 z>Jqe|Th=c?Oiy|bFGW3la7qBXW%vxE9?^ZF;w@h)O>WZtE6PLe;R%=b)e{e;|LunU zwazuH<|fSDQu9?J-!|j`#j2z;oULI=AU?ak1inb^%ZipjhYz1_c+ug8g4=psTvias zpwdaFH$^yuh>0MXkyXfoNrm_2{MQbX7n}`Yx1 zAbQ5SMWck;E`D5emuVO)Gt_8>I1V1J{A>vBj%{)ET&dn(Ut82wb_ z8+THBK^q7erH(dF5v)#g-!*y9CYsh0kW`bw{Y%$c+I!99v+IqRq5#;J!ik(_&Jk$d zOfx6cBGs`*d7@&tli2$$&N-}k!rK>Lv=c1d(m>vD682u-SwdeI6D#=N3?%F1h4$H= zkUy-*xMTEyZ@d;@TMv{kj1pfTKh>)xs^}d1k;WTc=uPlX5H#dBt@TKYx*}-=jLYat0vTd#Q&+?hNnfDc@%DpeJwOxfLP4xr zpAR;O!x@_i;tKx7ve$uGPl`+x>4~-<%Z%m#^{~w=HZHCX=BCvb_bv1e^G00|39MWm zTzY-HIyq55uZNNQ%r-si%Gguj93KBA{4nLLYIO2qf9hq}s=Z8Y6p(B^ZC+eIs1)ln z;}r|Rqs@WNP@ZkJffS8I#D*Cz~gtP zBJr}`jxovqG~nTzAgYk#r0TGzehCU!x@FY-INND6Y5|#J9ZdDg*ui7_X>{#Gh1H~^ z)eVFjK+YmbIh>Z_!?>G>4aV5lTl!eC_-%e8c?89C*p3$GEDogIoT_~u9+h*>(z--X zO#^T_jK-??(cYG5XS5odHMxM%Drcl$zURxi-*mi1X3g$~sy z@Qf9d%Dgqx+9>9`aYP#MI3uq6S+$M3ua1=wmQTq1>;tL*cIXC;x|hR&p4zqGU@ACr z8TW9zUEZcK#{g|mP|JoatC?T;zyzy zH~3cat;bD8FXWxExnRXHfA9X(q}4qdrCfbmz+5BjH2VUDd!m>K+~KIuJr?BZW$udNYn=ui|HwO7Lb#(pgY|Kgqtu=+Y4;qURr%00n&-?QRz;t4jq2s+@Cx{Ggc*SNG?Qvj&xq}m6c;QlqqHs9 zyRCg<_L3h4?9(%U9OJ zVEgsmwQPJaAza35%@tXIJ{v^o7&qA2e@5y);!1cz7Whs+(>Ab!ml2C3;r*#kN_6QQ zH^-in?tBKLyo<_r1Qhn|aK8dEUE^!1jl^+d?1;J2VEdYJBCk-J;P#_L-l4mDl=UA(|M^>wjXw?gLc_D=15~2|^GLWM zuV$++PS({}$^86$3ySBAer_;w@J`nmfA|Tb6mw|a?hm4@cMZ`**P3P3r$Z^I!*hKY zPX(N1`*NUEL7*4C78{a)_VEZ$|HQBMTI~-ZNXU(AaV6UPf`Qh$8f<=hOK&kTS&^+b z+l;TKC_*C;Ox!n#b#~Sn9GA&cj~JJRGVJOdUk!R9;kIvfzi{PF)=%N>E;cslt4b*(8VMNv&Q(*621PBBI_x~IxGBE*qGZnU$X{| z0f?E;w0kP^)je7`aiQ~;%j{YA}UPs z*;s*>hbF4&IG=IMcUsFfkr!7)jDw3gPCP|D|J8p$M^yF9ym)(-zaFQZT8WYBWd>bH zrBNBuUKLk1Wjjq&@O}vkUT9%jxKIkRP`JEX#-uId;O2`B^5OHW|EcV^QyUJBi?8wo z)LmHP8kE-I2gb|G_khUrqBn+PhmKU0oVdi=yrVh2^fBzpuxNMpPudi1PkwD z)sw2@WqXl3t9*7`+`hl?$zZD(Ez)rdkRs?cW2HBSn+XGTT{e!mrL_!VfjQ^dJ4ZF` zG*iX9?_lZS3$Rh~Y;s)QdvqvLVRR!)LNL{;t{BAWa$@CInwHp) zZt=Q6oHT0s_ul-gx^3N`!>Fz+lWiyLaV|38-V*K16Ezukg@RT&o%9Bh7tqfH_MTxQ z6=*)vVVjoC2F z_6Yk>pL`lp-S!r%G~0vstx1|svb04rbQ!t5oW8g5F8H%5)w~$CFgLz(B(F0z9RiKK z7|B(;67SEfLbo7srf&_)Dervzmik zXR5T0aR%qgj2uyewW1UrP`Y0lpfkVmS#};K8aax+X+Rg9Dp{PKZS_`Op{HtQd)<_ zlbx^#I`F4jhIjZA&@0;d#Q4?XDp7hlEWVVNmf+LIg!AMqJ>!+c3fqo04{&#e(MU67 zIG*>E=9Ey;jiA-J96n3(z_UZ-leM|Z8d?}ev3F9!_9p4c&SjATNLFMuys zxJn#8V?7d#oFPx~;KT7o5w!>rQQNAb2I>nB%G!#g4f{47J@Y3Nz8Xm3_14a-$sbj3 zdUXUkz3)TFX>13)sHdEjnY{_40k6hNO?y*FvjGK#JzgsERQ=KJjDY=XFmF5wu3Ww1 zRrr9gjZAZvrRpxw$F^8@GdQ6T;g)yE%mx*6l}l}p{jn;+);aP%s==5xT@u&+4AFxK zs&25ReLJXmSUoDa*Tf9-EHcNuq-6RL+sK|^&$lNn9XcEw?L&QO0Y-7J~p~A8uFVZ4|;Vy|GNx)bpYB z?dHXCyYAS`BxJZ=BA;i6`{2i;yR+Cl4oUxMhDqt$Z%1-uQ9oq8U0|phKoUyIuq5UZ zJj{z?LBC4Q^!`DlnCE(PmkQygxl22~&u^1C15oRV79G!}gH&!}snh5}&-JO=c^^}_ zf02>6HxWTW$m@0O%C;E#FzsGOgQ#!NGK<&$l-`-+J*TUmSoQI!eyi@y5S_|F#Ywyo zl z*ep_|<0z?5PZ3rm6EVezAUr(8BdW*>5h`6hAp zYN%P1S(axzaB_C4sVNN?cF(Ov#vDLspZ!+G%gZgBY-k>3uCFic6lcb0dM@-lrNrW7 zKE}q>80J3|L^AWXeeQrR4P^abAGDdBw(}_2d)vk!wZhB-XD`)Iix^>v&XfR@bJ+pJ zA*hUN|7`ib)<#z}7c29G`)hL&VaEBjrP<{3iFjd&8pri^cB$rG!P}G1@7>oiOJPu2 zk?A~^vD3Bd;u&fNWs-CMiSq`{`W94kmUZ5pu#u`{<7DZVb>vB8*cz)eo{8#$pre{3 zHJ64=pP`L~_i#Ch{3bkb3Gfqa%#H-Qc`%bj9vyBNHhUUM*fq{d%?AkqT{mhNkvjW9 zCpDc-AqhPhFIQ*G2Zn1#K{c%nezir+25r655~T-9j{^!ULzlO3x93d3>+NaYV*){6 ztT_)|ntVL>-h1zGah7QUPT<_=;a2;T}Ec%o_V7aSkVozFKlviR(dmAI$C%% z#DCOdx>;`xuTOi!I##&ocqFNcRj5Y8ErDjEdS?Xr!4N5{-A<$Op+_ksU}OT=A=S zJDb6KYc*-b*=h1stc*Q`m3kVlej;7ugUyH4aS=u|gG|W_8$)1w1{?buie+8^p4^o3 z-%oDMCw};&iUNUUcl;n!Cnq}NnHvv=%?f(*LoPk@<%#XEm=oe&t2d_F++vq|O#Hdn zH@vx#J-UM#Ffl`);^b2fqQ1%JwjH^|N8B7c`q0blBb$`y%!9F)>DdgC}MNunDBvjjp%CWAex_e zpHuYeaKQqw+pg{g-W1HtBX9x*_E9k*F1U6`ZLtdH28Fz>%yD~)qVz@GGGFYx65PKL zEUw?dHt-$5;gsx-<3%CRgP=q7oh?Qb(PYT|&DIREQpZW!e{gHfa}XTwKS=K39lnou zr|Pb+nLm&}KbfRomlXM@)WyPaNFfjWuKD0}^LXLto#mUS5WT`Vw0$!-8yStmyeW|M zeVu(o%);1u@{k9iQDvZk-;qE%!zDLox=8FOC}|#Mxd6FRM`a|T?S^&)*~K2Btt(Ka zEJ5@4*=V)+vwAj(w1UFKofd}Sdm7N_O1$$&h_zjradkZVqm`{{H(p+G8Pn%-{@hr_ z!>b^}5xcs6uho}0;r6#dC135l~d1Sjv3Y2ItGM}PPxHrD?l9f$o)RQ{-)}Ddq^x`B$U2# zsOw)`J<3ra1J3i2f!u86Tn;GH*)W;!(pgA(A3C6eI6Z-eWyyfo3I)~F42MYt8fw=^ ztUaAatY5PU`-cWqL4PN)SSJ^%VDia|ec{y2>@Y-r8eyw92VUWGE{FlGV)(B>LzQ;&E?k6dz24#KFP^%bi za2h{}bUi=>Wu&b)M&TN_7a~QwrdT8({_OH$K$}q?BX_%7acZ=PatX57#Y8scof|!{le85Zwn;goKhsBuO5RVG z9e~tO!7n4N!k2eqbv=q%AXbe2S9d$_$hP(*^?HJpC@)x}N<*AoaTIIl%=rS5_G(l+ zY<^~G*RL7n+PTjTBb_guS-sF3k+xI+BdD^_P;1ZXwloFz1Y{N#bEQn9|MJf45r@WC zu^|cpGS3o(ju#bDJynBZdH@dQGbpCLbK1v_;O7}L~P^r$45yZW7G^5>L z8NV&s<^Kd_EwT@UK#YfSHpTJ1L{k;ItZ9Q-E;r4PW5)-U&sq*v!#ff|l+$;rPqkE@ zcb)=5A&Cx!YF0c|AN&0g14M?6vjs32bzfqbd736p@z{9pCXKi+;m@iz3gmMs_PgyO zaUHp+?FaVg#+#IVpkm{#Demwz@4<2Kxwq@e7K#6LSyQvM&(#(Z_Nt$Er#`Q!Dv(VU z^z>V(45E;^9THoPm=$>`7WnSQSJX+)6u z_T!J&MVlPi3a0g~AnWISIiX(d+t^24I}Atw;#}k3vPeWuzx2u>-TjVuF^3MJV;m^8 z)G3T=(Ex#0Q$yVm=$e{dG9BCug+t z_EyV9%_6SaL-++&-r4N4W9^Rd&zBPbNZpw9L8=h+6LO_nFtI>w(6fnssNi2W`orZ* zFaAk~Oi_H|^cG){xBf>X$F_c%=S-BnpqT0cI&C7iI|f1nn=nJQsY4~PQikM@sM8d^jAuVjrt zB359M7jNoMvJR9T{d~ox;?Vh|k-%T;HYbh8fQprU>oI%Qh&;RaX5ZrPn{TyQ1hQ#Z zukpEl(e8{DtJ@m=CFU7|7UAkyt>fC~w%9ijs>8$Lyw|V0+!-#4OdYr>l1hj0^o@R4 z%KJNh)alEJKka2U4wPQ{$o372ux%=!>Q}w&KS=p7z522{hERp$_{Va7$C-B{>xoF_ zqm0b5<=4rT_n+(DK_r6oj|AtoZW@PW&+BF%bjQ5-$ooLLm%XUhk3g(P!H68*?%^?_ zy@;S;MY->wG2_Gzyby8jTXVJ{EQ5z%2dEF$@FG^X1Ff*~)96YJ`OFNF!}VtIx2eMW z*L6LdQM%bSu`-nw_X?z>4Tq241_A3E))aq^TG!FUvUYDKMe$j1X1*Yh!z8KE)jR0< z5F)4hPBv-(+xT!vZ^7ir(tA54*oA*!Wn7GMfGDXv5UY~wgDDA`wbdba4 ziVW_9ZjJ(2tztOi3Lq#(QvqzKTuaUkKV8G#0bwaS>qJ|!T$Vq&ssb{7?$FluFpWp-qls; z>jCr1)}ofnTtdvHJaGBTANYk)J%O}%*}il~MTqGwb{b8<`)lI0P&IU27I6hKSA*oeka_RcSUgw|4t7cz+7|mu2Zg|#J!GnawC&n4kW!ZW3YN**E4sN?rkD) zX_C*;JzFe#VSis56{u{eH?2*9GsegdDji^fzzXM<7-e({eL`+&j!HuSu@`)_L+58Xp(tTYN?$WH9WquWWSUR0aiIK=TTSw)bjV8T z7RdQ~${Z+yaLyN5POIp~)Fh!tnyrZ0euOU7t>z=5)U+UGxkZE0vZSW~UIm8_7I*;x z8#6(S+1q7o*`0!*F?s7o;9F4jA?LD}lRUV&`mQU}`mT%|rh_IWAg!j%M@Cs7{t%+G z>eT^$pIJXxR{9WD1U|9IEGwUsJzAW$($|2USW#>|4K|lacJv-amQr^dw+CBlYN?ry zrnJ?s1W&xT+$$1*3xxqGTWMbmVwA>iRi*unBIeZ>urXNFkRTn#2GkQqhusg z)}c(^!BG)D7?pW}g@yBLKI)pxvm)SyzB~73b9C^8935e`&6_B(Yi_nK&U@AAWxDQW z%B{ycl}1_n<>oMjI`?^h(}VHA4C`u7Ag?}4ffONUR_{#ibydRYonU$E)w+TpJhIwu zD`f8)wc0OKN4W%J)#C9MF6F;Fj#_(>j8w!#E=I( z*Q((n<7HX9{*O!E2QKHS$*C~zuSw_FF^d4ENKu1|9$#Phtqlvv;alY+@Y0V%B_J5; z>=oP4c?@E@lm5mr0YR%AA|YGtcP4qc;WLmkwktMvajq(_yJ2EoXxJxj zSzaK~zP%SmL$!An=NU#mE;pX^uD}ZK?AIG3C$XK9Ip$ErSkyszP)iI?nc`*kJ9F}T z2lZvG>CHhR$94nU9myemtqHX2BG}rs`;>#01S$4Z;(|g(EIIXV2e><6^TNA71o*F% zMp(Wpy(bZ5`U@lKi|i{kmlz&1vP(a!2t0TMdG&3o9#`m-gL+P=F-Lr}7L7aD+s0(r zlCjn*6BSmDs$*p?{Tt%tS%Ev7>f)NC7%{i?^BjfwAlV>jCA%}@0LOl8lUit|wa>~p7k{R_hiUXb(UB(#OeAtcr+H)^&jo~r_3yA70)>%IgFfy1 zg`nOY?z(YhOma-$+#n2S?_w9*;dI=7AX{eHRy7mE>F7)iBNDcO|B_bLBBKsy4iJ)9 zvt>3sa}!InB@CzJY~2v6K%r(9Gxirb6+UI#-v0u~{IDO)s;3<+3Yn88Dl_cnJ}rCC zI~RE2vKp=u>mXA`!2KHvXE%bs2Ylot?w+6D8*@x7{MaJ6HPtM*^)s^4#WLIgiRM4H zILh)dK~o;z1M#2t*b$f)uBN?ivp!tTy%eNOZY*FAdgyl2QE_rUdPpkA98=H!`XGV} zqhs4Q#x$}}EAWt_&YV61{`%_^y&%p>_)H0>FL89-iE$QldT07B8Fm27kQkKag2_3} zL5k1H)(%CB_zsCa+i#<0d*(BMyjQ0uE{%_8iYo5?I)xO=+AlJxUFN`d4hg!;llNuA zzWP=h;*1}Lrg)tcBaA`{V|$<_Uc0k7cb_ZX!8Pd(?kJriV6Mzbtk=g9w!I0M$?cv6 z(E_#GqbtS59}qK@y3;`5kK&u9EXbXGqLwbIKO<{**5*Y&7R&ki|APvcbGLP}JzyUDEmtU(SaWnwbQ&OFQ<7`A|Au zNcLnT@ontX2lJWJ0nj++gYT~{P!F7-m8chtXSo`1e9TH6lMDHrOl2mu3Erp~g#iD- zk0_NZJVhGSy~W56E;$s+JiN^nXG9F7_4!rp8jf^ec^s+J{q~UB`6j|*Kb!os3iCEE z$}FHt0Ag*QwN97t`#fopG+v$>fcQe+7PAYBHQt8bCZskBA9v#LzZx=>>6wC?23ruPA%c;Q?7CmHe|WM<-jZlLXrj z(o{P+PK-etThoR(;LI>u;8I1Iw|0%eX{bJ=uCwW8?0D%pVS9Hc4HkRSKm*l{tyh@-8{6|wG$dp}FjslZ zVKDt0{n*vrt4jXj*L4A%VM6p7WJYlIul^Fl4@OlMkAc#?Gpf`^wZIRf?Xz`oN-se5 z*@m!YC?n+K6@Y-_aEM6WVe0?@xF*p0`Dfe^r&p)ghAwc95h08tHX2GcXJ0UqQEVJ*qe`a{O=h}zr@@XMEa~=m8K$w=qM~o$I-YMgg z*QLH~Ongv@Q`$=~lUGw!MO%Nv>%9Em!*3beoeJW!7~Lq`?>Bgk=dgCm;*LYi=I7(L zZNCT5EdbmLdKlb}iAYpKa>xu8lNb@bgiH@UoE4UEW5PM>BjJ0Va_w{=xqDC@lR}zD z;9yE#`F0e_vD8Pb6EtFw(khJ2Ll*pHe=Tpq| zzvd^%j3(a@mEIaFveF?su8rTs{&;A8ePa2<4Vf%tHoT3X*pu=vA}~(U^uYS!cfpJN zx*uF!)3X!67%8pIpG7x3mj<>V{r3EmVB`08ennt4*;@u*78oXvxcl z=*YZh5~LkZ(31Vm>+{+?Q)%WGD)(#QEu|TO@pL9|M}nauE1D&~-`M7WZFDWkLXp}q zOaF2q6~K9>9zFD;|JcmWDX3O*6w|)vpyB*l3+P_DhJ|)Ku?nD$H(%=Y2Z%JK*Gz>G zQ3KQBoth;!f4nt=W}pHpYa5zoRq-+pKTvv--Im~k?ZI2hPR74C_?74B>we*0kc{S~ z4=jkSh2Gmhn{U%-4#2;RMkQ^lUDeX&D!+Ks#KfCgYq;M&HAC#t>z%*(0Nei$qR?J5 zBgYmvV&!pPl~%s=8+|-`6DjEjxC18JziIh$jhma@Fzkp|PCNdt^sacXxy&Xhn3U=0 z{pK#GrFu!r!K5~rI{x!E$DYUd$3>5?+Dw%NQd#nzs`kjVYs%s93}43Hl-~p;+fh{) zkItto|ADdnH%9M9jMD1YDIs%}|3HQQ`_^>q)VawElVN=S_BH=P{r>#BICX$iIo|Y?>Hp$h5{`uEM9?Gc|07KLue;%?0X`A}HhKN02<^|mi@2d=V|cx@ z-t>>p(H}X?6GFg8Y&cT>!zlgRp;PHP)49nc^$nLx|GL!wI6?pOw9N6w$#`Aq&6Y1g zwzVIKo_J$MZt=9!BVm6t-N1NSqXe@NoI|!>RnN3Ra@CL zEeqG@F!l-k!Sy+R&9FNwPmQ3fYsK3?j8WX_sZ$YyL6ZHTE+YR-V?|IJZeu>V- zQ}vhn2{0`C#5$Qst^&YM`3kQDGkY){`qBdpe@`!EYqfJK+7I*rDUlFI1l1+;3hUWO=nhODICeL3|vcUTL~)5$RYS?8Z% z7VfFGvD!ScKk-R};H)gKMp=&!w%YZ~FfzD@O9ML8Re?K;GxC=2B!7WHcjet00$Ur~ z71wzEYIOp7KAUw5b_q{ve+OpV&%hnvJSTQU<$P{x}7iz%|1=vnfh`j*#R|wd&){P{Y z7pQ`NA3sbRom%9E*95osuW0~nJO>B;sw*>XZBt9fSEkF_G7oTXrj3f*)7FLCv~xC# zuF@#)B%CK63}wsnatFy7ASmk?6Uq`*HMdSOmK+TH`b&W1p3RtvX`}yJqcRH}k+j3G zGk$98ce)?-MlII?Su8)d4NM(y_7OC$dOaP4g4a#b*T@}g@NPk2GlC_$R9--FI!WRPqvgp|9A{bl3Yvh_&4Q3&S#_vbvc*wj8}i zf)W|_;H~*$*(Ibp?O<;aWewBlpux4NS??M_u6tE&Rque#kbo9M4>N$UBR!V1lh!6{ zmAmwQjZF|%MjsgzYn!&b{E;tr_}Q>HYx=gZd6Se^mhzda)1pE9kVL?~Dl(;ju!OH> zOK&nYKEt`MQL}*-9+b<=h5Uxpw~$>Q5ET zIUr%GM{tAd=PzEoSg<~tmSHf{wi^#D+}<20P4KxQ)nuZQl4IdHD4Cy=>^RClWjS2- z%*qj?0eER$15y+Tu?knNnZsP0`WprT|CO(S{N))dVPbum(&7t|n>jM!aW7(Jjv2zL z;Cukhn-8`f_z-Kmi8}Sb6#(N>g-l1rX0ZI{YI5AC?PCS(;Wu14HW?;JdA$BB%O?bM zufld=01v8{B9QHij`E4)F9e7V0v1i{n6Z>)%laq|_ zL0OU~kyyZX(U$glO3ZPDExUqw6(lv9INsR#2Ix!^$8fo(N*8eMF4IzkEbgf>oMm4& z6}X#yz&0eV%aH|w7LQeq>JMeh-sV+3y=hV43sr5WT!2#X)g9o?OwcUX9h0r4L5IV` zjZnR!=Ow9jzbHAA+i{-jzWXbZ6xzDi|Ld7Z z?_8v2uCp|OJBCZ%rOLT4!b;R-woZfLwC|`DtCQ(wtzI4p6hfjpwdceUK2cg{D3otV zMYjeN!ML1r83!k35Y_7iu9%H9IF9yB-eo1=3`5&?)4e9vRz^yHJSVl^aUA(|8TVCS zQ1anp@sHV7$`uwZ5~_{#AD=^UR+e|aOFzaG5WT#km^Y6drB8q>4Y<|!(q7&>^2gWk z!6_hF&}jK`ZSrm!AfXdf1=28w1)F-WPrJIE8862uSp~0*n;dUmY+EVb8Y!S;@b@fa z{tYAQ7A4C1xXM0=VR$rNWvP}Te28Xf`I|;Opi!#po_#ytGEZtNvTW1!&M0yf0=u4* zic_RbUW2CbRXB3SB%J#apDT8^o@DF^h2ZPT51558az zO@7-TMP3?|q@s?m?5Qg1&Doaja-QnLN2_sw#0z#^;5wUfZhj=rS_9V>%Rp4=5sz~1D+Rsp zpp8@7YhMf>GY_*WUuk*7-N+quuqhyKhbqT>rw=)t_scTT1NMPk&xz7Utb0$X(NKM;u7dD*9!8D^LPItTrWHlqS|;%GgV5G&w-?U zjdSov!J+!$k8QiH_GP6vO1D=O<6u%C6_CrX&RL<+!2R2J6>H%*pV<{>V_5(`+f#Ah zH7Ss9UJO_pw)d7uV6*n`O3C9jUe<9Vjsbm}{3ebiLC)v~_!2h>G|6+6`JhyP^NFBg zYlQ%S#2)ekVku3a_c)}vy{f92jhg?6pm*45E;}`k5tcY7)JL4Dni^$zSoSyIVlx#h z<$6mk=ISK}#SW@>^iBZYQjdM#cC{fw%zuY*sq5>NfYUOXakN_&iLJl1a7gPoha6{D zHkGs^scKvRX`LmYXGNL)e0+PYx`|CB-2pm9tUe7TCQEi3ue?2zFdSunK~u(S=baGu zX3T+v=#sh-0`Gd4_vC?%YODb=f>jX~>Ge>E)2pX;_=sfVM7(BuqB){%y%kAMDoiaN zT*bs^t7be3wjVNChPN`?2M~7qLjc`kSiE)c-ta^t1M_nA6dbTA0fXCsn9AUzf{7)% zeR3qm*DI09pd6sdWIxBp#3d+NA4NKO;s%pE_%HVcSa-!x^^5f)5vCRFGcn4koQlCl zW{TL)DSOH6jU|1dl53ts#VbeL|4A>g%aV8nO>*Pd^Hv*tE{Q*w4C3bzt(OSid1^>j zvol(im?#WkqH!n$)(iqx#;3*m)ONQ003YBidx;w6LxzjrZVn(LRnI1+iVT*kR|)0m zl>8z`7>*o{0)>o`Whp|5Iwd7$P7)KvRm5v74PJ-_?T3x2N^CrI`-}o$JAWXreltzh zA)*XpwuMv_cs{1}tcit_eLzEI>or5tiaLjkSaR`GWyOYq@4}X?CR}2?=*l}e5g>{x zV|*s4Q%Le7s%~$+t=i-J^E&oJDr+fXrs)S9f!X$lB_*C|{HP9~GmYV24l@&cmV$)E z5r@N-CY$m;rKwHKR3ZFLyY#N8Y#XEif_D{&JRV5f6Ft>LN4ar5@uMUr0Dh~#Uwb8L zVeLzDrtHq;odeuXh>X*mA1h?4+}POi#;GOI)Kvg}ev|=eP?k@2;K~ zn*0^2V4!O5S6^CE6WelB_@^W zZgodfz9P;n>SeWYC!}(;-;l1eJ zTE*Ydoe|KOzOl3~cH@Uj3^(uY6W%y6<^^`Bzw@4(P;9$@nqBVu4&5-X{OV1A9WG~k z)k_rB)epm#yW$lEP^VTK)o+zngsolITp5x)d9@WtR$EpnUfDzpO_0@rEz5)cgo~!M zS8x0u&i;ZAcmSSF-sYd6-`8#eDkBNMKnQV5&%mi=tyRVTpqF&Kf8|IKp8k}}7VJ@$ zjBd#z(}tIJQQv@8uo#w#`4l3*_{VWTXH$L@%etVV; zHvipDhMJk!lx{tVKSVf=F@9M)VPnXTYzzr17-XJ+Q}QvfiLlRuFq5;t0i#jj^{IM{ z_BA7P@A>)qZKPuNU=k$nxP;Tu-}eyw&pB|k!Y3{#pK0Mpys?Dak&R zaO}VVgdV-qiWoS2u}>xTXEE;t(5oc5-vmhH27}cU$5=WctwYU)zbVtezaKsf8%*J= zY==2QU%|~7FE_svaAmAegYKk+-C)X$b_ssAWHRdM!LT5k^F-iejDB%}+3Jpi%!qyI zUreUqKbcHg6Y&Xjb`PN743I!`BIyF#n*a43r1u)0i7E%g4Fohb+(A*|oRuOH=xlj* z>_KX?)%+WTULF~g&B?TJ<2T>=;P3B}!aeEu>$|q7*RP>N%n#S#Lz zjs+jz>B`h)+udV(F5mA(e;vD!LY&KC9DtnOlP_P6J^>pm=*}?6RXV!ezgT5o7)@Ll zRG=HcKX5JoYF2D0E#YJ#FhOy(;|9=^fRj&T~>43kS*(XCJ;o)z&ts+ zDfxg5N$~yE$f2J%wtWF5krrDTesXj1GNq80oCJ_n62h>+E2X5|v~*3nGZHs-47;N4 zss(O|v^$P2dEhw7lkpk|snKu1(O1RFWpqeZOC2fe69JFWh2rS1-FxfH$koglK6{7c zD&YS`LMvs!TZX4ZC}=F+ado1ce{oP*@j_TDle%5{4m-Xfw{2qFp!C@HO^bV+xEq@={q zAu)h09YaV<5|SDIMLeUdKJ&YZO(J<+=-Co>BWDL%;bU^W z-Sqpi_Z>l~5WHE;Z9bG(qRFp>Lp$&NrHt=T`O6)}Bz5RDtjL?GI4+hH8{R;E_49+d zgpatsW2tIAr#_zIvD3RM%(qsVZ%PDDTv%@yFm)}mIpfg~hj=V&;~JehxkKd~Ph1@l z0n%CQO&js(&lY}_FPvLUTi?`;?k%VB)#|`ZXqT#7(xqOl+OLbhx?s9o*WAFQ2K;Dq zIfp*_6)Q3CiIJd_r#)3Y;AqlQgqkH1OEJDrZc!ey1|?j{zuV5a`_!q!c&-`W-BZKs ziE4V2xc;Fh3h!I2LERGw2YMpf!n-wo7v_8TGP#Uxo}1SPtIpu%L}h~*fBF>40qNYI zz3-VWJ;!^lW2V`B)^v9Z2H2FHrChwHo}KO5u#PhkwV*{`gN)nEo$W%}`g}2bVLsoL z#Mq`{Ca20HrJJXa8{5A)CxsU#rq7CdZ@#odq)vqQW$k&?Uuf#pEE(_eoQ;s{e8Q0C zcTh*^%{;#@HL+Q&U)CC~%XffNTjA<`bCHIHl(bCF*=3jO1*NUb>G!}i&e5rRIfi~C zR=33HaSd*%uymyNeQH2+G1MLIB^;h@O-_zyI*5C37oR$fg7t_eEfpqjv>G*- znUvt=?bEq5SY*2$;}8B=%xV5Y9ie8LgUaYQcd)p{?C&Lo6AU&C{dV(hURx3FB@h(U zd)RkrbMMS;Fm*ww!Bo)J3ZL4xS%Y45&=|j8Igwv}KYVf9H65rD>(w`X(}cHkN_Ov1 zy?wg7LfNq2PMSGaCYSbBC+7DRugaJY`4~vVx46J(Q2sEj%^bpeYEu3>si@}oQRlW) z0B$GOeGSQhsu^tbW*bu@XVaA`&?)3d;>S7ElvyFT`rd!atPbcS-33#A&2!#}Jdu0e ze{nQTH_tgKC5IiE()Z8HM{D2vTi0#H#~=Eh&(1A@F)(c*VTC2J?S zroV`MFSGlf>Z#Qoi`SsNDnu3LgI)C+Wr-Rp!41y$x((7t@K#QG^9I&yWVWqh_nGyn zebapTT7cd;{`c^Fq4mHzwLBs=Q~DTwL!JmI?u?8;_r%=yZEJn3XNpx{>xgEBF5}gA zy^??>V51Enhr(8XZ$)22&vIF?E28Gt@|kGy0Dob=9vnt5imy;FvDb@rjQgO|ju{}j zeW88Z)tyNVtwfazT~{VGILucAy}wYNX;&VfaVmrM6t93(MgHsg8aP00KAgHKbS*Z{ zyKWru^&5(oCpD_$CcAfhHvMDU+pTz~oq=t=LEOIFfn5!w{otU66OVuNq<_ z9F`UyeeJGFDz}Cm^6BqJLKRQZJE$Da?P3z_sC|oHO_|p^=U%JwrJ;;H9&7lAQP;s+ zZ^CcP#>{x$;;~Vw(i*99I!dH|r66~28LLY&snU*DaJ1Dqe{yY5&MoZyjqp;iOLTSF z-))U5o@tJXxsTa5*NGSh!SrZTa9jR8t97a@*W+#H#^LU9xL-3wy)1N-H+jd`w%(Gy zkhY@-u1H(AKsD$AaS>AN-oG?ChE~W9d(gHhQf+_q{Gz{8mhz!%T=5$)Di-na8FY0T z1+7nbpL^Js)p6B&Lz7-{!z&me#~$E2)B&i zny)oK;;_G$B4$yJVco48tNXJ04vPJa!{HA(P7e>2$a^%~iyQVy%}z#Z@h{Lj#zWYu z@UOZ30TBLEpQs3nI{KYneV|3{K=dyEl*5K`iA7w_K`v^fxVhG>QClI~)#hGkSge8( zodtt0V7f*6tPx22Cpnl#l}FWfOO31hdB58)2@Xje8Qt00P6a0qd=_kC79hdTsjB`7OF{ImGG2h+PNs~=afuUeo%powX2 z$rsqo$M0>~j4cm7{vs9)ygh@6s*Hc`J36C6m$Le=0(4@Lc?)Xu5{eq|8`__p;S!mw za<8|bHqh4k;1;}IH?*2;cVU)<;9S;|XWD|U^G)T2BjjCQKHd2z;_(meDvS42$|E-W zLH1M@vh82)ya~VCE;0VA?Naa2JJV00cYp4VNzd+GPpxiwgwaby{RXB8he)y!|fXmLS;-Pb&G?Ta5p*%`~u3;m_;w zHXX|g7pWQy?aNenAzcWR6)&WBJdDB>hPG5cRsTp4C`-i(dszz5x7Z5#Afe<}0?^^L zw<>lgN97i~%xc6~)5ErFd8|5beQ>JlTeTMk9pZjPEcdF$fz8ZF)D*9NzrZ5*rRI>} z;MC@BFK_e4Lx9JP8{u|XRQ)Fp2fQaIGgA?Og)}QE-47=6W2lPhTOOILN*-)OiS~Ya zBg9z26Pxcf(b~qRDuoe1VSTfEW-6##=diEV0{q^cb^UHX28IWCmjv;qsVWIBO#GNX zdm1ymC>231yi`Ln=!4An*`fL(P&ZK4;#S(?R^XJ-DEr&97-qml!wa>-&mF@oPqCgK zcarV-&O(VdeL5}jd%H9@(n)=bb?r=&qQf%~Y2mHJ{lM2ks~j?B&0^9RW7(Z-km@+n zN%&z^ab>~+s~~Z^4nQSyD!Dm-Q)Cd>|C1s^N7%#IuhO>$!tks>Dbn~C7meF_SZKSUk9OrKfaGxcVrxaQ-UM4eb zo2?FeS{TTn`#oUe9ZdfkL&HQ+X^)|+@K1s>7Z4@lo{DJ#4Q)46IP0k8@~}xpOM-NC zpH&5`fw`1ANtoZ`a}4q8*M)q{9Ni`wfLY?b@bDr)uS@(Q2puMGrnEl>GdNyf5hy`$ zAxm{?lZED(Kk6|X>+jmKbB2K*b3SzFpE~(x$R2@@yrjtSJC<}gUiX&niqzHLKH z(H?@p=QK{pV+qj=#m=S$sG1B|)B$2SJD>$)x!x>V?zO&FSP$WU{YMh`@Ae;wS_0cI z#-BI>LMVEE8H>0g)f4zvm0A$dOG$umjz;G9%LA5Z26CMixSZwdR^_Y=Z!{498l`ob z+oy;Tbfci;ewtNej4k&2`2*`&M#MkYm86C@pxT=}8vIrA+5W`aOmvJyBk*O2N)hqa zH1Ta}vXoDgWJS3qiR?bn@^0QiZ0-z&%np}jDlAP+c+Te}!4<%uY`2`|_KQ*Y=4l32 zR=C^S7iU{IDy&svSH}V{jcr#?e)0l|#;sL6_Gt@G|3-|)fj(#5M05m-_zF`Ao zvUV?C33;tC)o3@W;QN-&cDBpl_IIK|Q|;1yQ0s?_pk|3XVwb5#0?g8i3^C&Q8n`>a znoIytNJkwuGfarDA=k6KL)t`^JCn=Qf!`(_wXjUYhZw*FE)JI!#<06&QhKw*I`CtW zxoTd((&(LWu69Qs@HHjr^%LSqUMpN{1Bb9$im8i65n9_O@@=st@GiRTnIGg%SBH14 zY_(w?0H)_Eyn!s8E zsQ8t*mtH*Pc0#c?98`}v#>c7ER7zcCsByG!`-*$3o_T;jR~(e3bF8kS@3+yeKDLY z+8;h#yt$uliHMuvY(Xf;4z9(jGSxDt-FooB6f~&H0r#rAQ8~9#%vH3ylk1efYdK=a z_cA|;QDtQ*qy^lwD;fuqzyloSj-c!8Td0`b?2QHyk0m5ipiLI}UG?uo+KYF!s2(88 zr1csOz@*@chlQGJ)C+ak+nlRu%l@RG3Xd-hwCtgQ zMA6HsHXKCxgekw*@foSI&+X!0n83*7#tHk*hpvp*P59s~{&;#r)PF0;#X$576Q9KwXjv84LV? zmsTzha*f<(NecQ89&V&WW5jUG1YvjOlZ{XyR_rGF_{6yvnCV*06>Ck#6*Rlb_;9>}3%))UP}L*s;mGD@%~hSL z)U*lT+7+cP?toAybu6p--Fl;yOvEL+wF2u_GN?YVAF-CL1!F~8prv{}{u$4-y(YXP zI{7m1do1>ue2Yk90wOdDnVkf=)h5+R)9Giw$lhW;GEJ1_M97`x5ELGntJ?LX;z@~C)dCb9 z0FFtJr>9HGjU*gzVK}OexQux)QHx7C+*!2S$lE zSFpajE7ib&LMpeu|6@+0H7^I*G0^s(-$eY5Ld(42DnOFx@`^nM3wo!@ zzY<=yg~##v1XqEww&T?W^y-liU8HJRHPm-|+sU>+=e{I=yn{cw2Mj(*&h@ADNPMs! zbKgQ1t68(^0GRPFp2BZBk?x6!qP-Hj_8%h?$V!1)(sF7{x>H_GbfsJ(;Il#7q{{-* z<&$P2@BIjwtu&0_A&@V@tW^x#fyU7+_w87Pm)8e#3yvY2aKNEdD2DH?GVCg0PEXTn zPpWXCQ`CvmIC~0?W?$b7_TA0W?zk!1vlFBm5+?>F>PX;;hXQB) zg8Ok!Bjp{%{B!@K*HC>2R-m=K^u9 zx5-kTe0tGL(bW0_$HubIieWJ@%f3$xYQ$kBcCnq zKx=cky4b%$IP>wI;nSZ}k{ywEUKjym&z@;R$31cEIFX+>YVPDZeLd6Lk~tY}mn}X9 zigDXcUlfKKfUB^YiMTmC+i}$u;KvH$Y1KY_gOt-Y10GS&OUp!P{BZ$NoA z9%E!b^hu9lfx>>NmgUfk#PY94Q1VFPw?!)_)^&$IvF>+*GIq#3j?%_Hyu1|98y|Fl z_!>W=e70_|i_t#LVRKWdG!ZJu0&6}n3Z~#-0yiB8{#ZV6L6_;56Q!w6jf|$MG+d;M zbk9p>3`RtV=N=3u<*&O}=Gmm#(u0suI-z$dr`}RF>U@m5-2)Y&*l~*pKG=9&NfhwY zy(ha%FGn)Q?fx(qXshAab&}vRBhv+UPp-!Ay00(e)SDming(CvdJJc0#}fc6lPzkS zXs#>v)TraCf@8loWk=kKeXD{JF6nRKFsL6Uyp$acTE@pTqEfwg6$ITExazjLeY3!> z&8Sy0UhdPS2s+J|{SWtU;CnhLqgbS93XJv6UvE|g6YTWX&9i|Yk1J42cHBtNfX6o& z%yYfFYO%Mw+M?8BVmud`Fuw+-Lg16k8tl&>a$mY)yjl>i!^GQ@>v+2O`;>qm0pc`9 z{K@mgaHX2H6p^_nCMmk>AU45>F40`FrS5^x2er(6m*7;1KTNT6nr`i}S|04mpl6Ha zGL_s-WSP56dpI`VD}J5Jq_T(0=Zui8*}JudaJy`*Nv`a z2GMo6qqTCxPK4IrHYLmo9uvf5#+E|4m;WrLd4uWZ_ieo8j){$sgbl68nV*Cw4$FnN zBU$CT8&yR*b%w|1RGyfrJ|lQF8zJsF8kmbv%1FyrO&KNRGq#Uk&XcBkzopxfT8<=C z6l4K|;av}Y5w|RxcEgOhtvjE*@Jz-u((*Ygp8mjV)@}-q=W4ms+Glo$`HW#(M{WeF z?FX(Iv(?@%f3?+J0pHVyt6p}ah86%}C{U8qo*L>Gp?UxaOaGyA_pBVeq9ga0TUWc( zi4)V3Xo=^lGHB0>zCocySK|+gTtqub&?fIozG}VzluQp2T>E9Ut2NoRLYG6EiGm zyKi`?3#p9nZJ}pD{znf{a4GGJhfZ198KN3zOdng4iS3WgQeP>G>o6 zaj1Xym7W2Bw+b(I&!0HVOWdvn4Md;9jx;4kmMT@tdz|j^-yMkN%O6uOqTN^oN5FSy+R4+w*Uw{ z_qWa?ukh41hj|hDty5TQL!eqCnxrQ-0A@WdJKxu^Jh=X&8LeDdqM2bPK@ zKON|GIO*LtvQI6~VO<(TIMo|0FW9I4>~tcHcHhW&c~-AFN4X)KBlc1DdQFZS}eP*U{FIoz&uhpJR0u6t!bs9 zTD>z?VD#h801`OQ?l*K*`!~GX@@~!PVR+MB5*wWEWuReNA~ihBd}cpB{^#>5Bbm;f zA<;Ktu%&ClXdgkNGY@XIn1-hvpsG5SbqE~}uwi10tg$=Twf5T6u6=tJ{pJ?J5%em+NE5h(cF^JSF5 z$BJ{K(Idq4p`8TtZyN{0z7=?8hha$131Hmsl?j3Sy^5 zYJDun#T2#$S@@JjwQ>d;FxE4~jaMJtzS~}J8tIGf5N!zwlU=&*$=`HTlX@TzC*pG^ zyUFT(Iq6GOH6!WpaJXyj`I5!W?DIqAC167Dynil4uPF!J5d>2;9yT*>DUMEbSc0jV zjN9pW)6HClmn1*A7%-JspjPlGr-HnUy)6IH8DFxb@KmFR=CHGn&TlP~cJWUh57>~6 z?K2F}j>HXHS_p5x-}B`oq~{TkwWh!JI1?6@Rs-4hx5sdqPaHAWfS<2WPc_{Q?Hs(3 z5T;G2YRw#akNd(q`X_QX63&Q(yyr+rx%au9-7LJ_e)VnsLZ^r7|9xu~ysz<}>Znit zF1-Wcjr*oIUxs`Xn5``(CB<<2cFXY} zL(?Rv;f(NTr&L(Va5agRQS;q|}2+n9F9PDBZzBykx=^Od%rol%6 z=JNpv4iDg2a(#CB2sf08ID6)dg1XtM2y@7#O_Fn0#Kn)qwHuE(Oqm4s_TkM?-}1)p zWv9?+AcD1~{33bcm*3Iy*>dWzcJU(>%FSu{4$zM0xBUanZ86_aKCUpVp;aWhwS5~H zNYy`-Nr`irkwbv7u{^_jzkV&$g4!`AD~ln44-17hpWnt{U^Ztj#R9L`*o6h2^2akm z`TFVQ7jGUWj|T+!)@_Biz*PkHgfMw`sD-QIKzkqJV*m;G7tvnl29UWW!_5h==|6p1>;7cYV7})_*|gAQCTH`fbcEvJfn>SI7!#OO z8Op%SjF|A^i-qNeBY>xz`}k?%4hix)gkL%ls3VGc+sW)3iKx=;kOqF#sF74{fUa#D?IIo#nisBtV-9b81M zK4uQePZZP5H@5;c+YxN~YY4Ej4@o9`ue7-vkb@tSF}Dota5eiFy={LLW<&=gHJklp z7Duyl9l+4>^Mq&M!(~^KvHbBK%R}-&oin?bOSD*9h+9}>-~$GDh7l!|Zs^OP6T3oK zo6*c_r*VlgiO-%Ti66=g#&C4Jm*fgggmW@5Xa`?oD=F2=QwL)^UXhZ`-7+n=Oy{*7 zSKi;Deh+4GYG{Gs@fkzHo-3@-{q0y&e^J&3|3;&5bQ*0=1PiG2s1#qFkF7L#wHa$Q zUR$$$-~ae*;Fvw%tm2~wZJ9zC(e^;btL}4>J998K>t!3KzN`dTr+Y;u#H-Y(_Ggl^ z?>ec*qyBlBDXY;{hton(#fpLuPL1 zzV^t6$4z;bneir6>|h@3_qN1|exsH_oRVl=u?ZBzR|6Si0$vjaqYpCgva_zj?On~1%A%C=^HO=j=>t6<*CebF-$*{w?Zemj{@dNX{WBjsOx zS_`a9xuV{^dxCKpQbengEHQle@g;4jAsn|K;=Vkn^evP!zXq24vZM_wQZ&?)CeF2* zLYl6BTzEV7dmoIa>Y4eic0YO?xhC<*BuDt|%pHxt8Q6wgOCf!w$yH@P(3_NLN{L1l zPy0b}$r>?^16bfBzbxkY^ZTiMji%SW*aIixHIJ&kg|!x?22bj_`J~d&i`Q=Gf24L> z88Y4RQSPL~`D{OV=`TusEgx2dlwCD+jHT!@T*NKCQEN_eH-}Lf+it~$t>MP1Auixz zb{b1;j4G_NvEw9SPt{5%r^gXhd9#H;IJQh$NVHA6!r%#s8Zx1sV{rOfE46L^(@iKZ5WCPBqb?#C-a1#6Pzw)t50Vt#8#2CcXbu~A{K%6jiI-0oy$-7(qP*?^li(1_PSpwVHg z?H0;hM5Dt_g)M3hj~pD7mfYf0LLjouht1{|X|w3iR=a-lBn>mONRJ(BEodFwd7+ zdygAyVacya`rwbv`aShyONyWD&#V8)aX#N%y(@M{l5DzkHuIg5$117G9{<+xhK1{* zX8HEQo{2GLp9gI_sZy+4Kk<6mn))HyHP)g7oCf` zt5aT?w>XWgbf@h}VC?Pf1>>RS@})%{rHT61=ZP=Ix3)fH(J6@W^~#Ud8?5j_O-&&b z@S}aes3DTYtrySFw*x9^V#u{vM7*};<*RGa^IeAUFC?OL(r?0}1CAO>mp}M7IsbUh zgF{_=)0d5qS5yr4H@Ox$eGu=X5oE4ssGW)Za;bD>pkxh)Bkn$@v+vCg9}5Z%CC{IQ zh7?Y0nD@y9g@s(J+p{A@O$P8Y$DIoBZV z(b89l{GN#=^?%LSg`y}z4ox#&J{1;+4G)Hk*yB)PpW9c${#|~G4EO7dNW#4x9=@SL z%v)tY)T;%0h;U&vEO~MmlkQ6A`+zg^PT2XBtJyHfVar*PQzq=Smb?uuZ{gwYJp)}y z>FCe4k5A2revjPHlWVzy5$^veibG1|YqPArRmFL`?xwfC}%X=8Xs7`Z#+WDZK5hkcGiITfzljYXM5kp z=4C-qYRyp~b7j|f55?jysA@E_(c3M{XD4yZklpmK%H0OeEIsd5V3qb z8fn=0OGzUDnI~n_o!r}xs545esU53gB_(~64Shr5!%JVq^DQ9`UZq{AYivSwUtcx( zL5Wva@?H=RK@1OdNQLsdU-gHX0{%xE$bRUy$9cjD_7)l}_rj7!g$+!Doj_KGvJ~0Sf zB@m1o&Z03=p^f)R$35sC3VfVHB++ODhjnk*`gbh|{^)cI>8j9B@;sLFnfg^Sr=ycWFYV z**T~B!nt-X(5oBws z&Cg#2XB%yeB)<*(O7P>y4>B;o)P;EUE|u!M&&T8I^K+x5veu`1=$)k|nUEm$01eQtXttm4K{%~JG=c+dz)IV&u{rJ-8|a-p?b5v zu=~QGNP9RvEL%1uK!2bbU0RhJP??j#%_5&mq{TCmB-}tZv)fVXVxR5uLZePAPJ_+S>e8nY;g5}4t%;}Za9YWcE~?ittPEoir$}ZxEvN=V70Fp z^}I@Ry?bEi)uek&wF-L9j#6{-V4*`s^T7kj0-c3qAFq-UHhPK*32AhbwbqzP%Z#aR z)oPP?0F&=!sP$`x-*KXkRL|giK96>&r>dPALR2PUvT~64F8i|vTBhXx%73(n|EASz zB^RQ9%v$)1`u1lq9w>V}&vbI2ERq@kc;%Tg>?dDTPh|j7R(j9vx?-u19|hORC3|ojCS<3}Z60GVgEdT7!N6t{%aDz0;J{YjUDB zbGY$}cA2?VzUk3rOeU!5k~{aFKJ&${9yM~&%QmvAiEUK+(z2cS!Hhp4&}HJkM75oP zEHW6QN4$T3oA^BYl!k-JxuxAHY3}z&TWfOOzMF)U0*BWyo88-5I1WRGtDp?G^Qp2)rI`)YcF$#CWb7y!Sx0QnA-KsW z$cHM>^ck`*08y3JnRb*=X*(wAv$MnsW)^zM3R2PA50%7#gdWkVFk`tje~J6a)CJAl z?&9d^Xu6Q=0gK~a_04Fvp35+|jWUqA6nFDFp8=bCnu7bb?P70c4TDMT?S53!N^;A6 z={+OiwU(BqaTnjy3i5B`>phk`^4qnz9fr-&R2o8AjuT_qUZrhW`eo+j*xZTDZ#Od; z#SW9fcz9_lzWs*qy@fA5*^^q(Qqa#7JMDoRJ&2%Y7Jcyuu*VN!!n6J_sUg7ec#{tSU|0v_H9m-aaV8gx%P9 zPR_0`@AT&NYj}#+p$ap79o+fn)G08%IEzMQsYOXwkY1j{L)OPfS}k3`4Sr(JD{NI@Zfc0-J0*%t`(Oh#+gi-sD!);Y?`Z06FAma=QW7;vus z$s>87!earVmN;Ntx$Nt=+2H^k-;oY$&)OB;m#2kb@R(M*X#|dLb=1=xj;RtEuFAy& z2|9(%E-ByGP8{HL&+Eqz$C%`M77O_dE$Dr#BgxO71r_)BnB7;=Z3 zeA4DHwtR)J$UKcP%2&4U$vKT4GIQlaPn|loB*O(jnf?MJ%>W=Da zNRme6*1GxFXf#B&fO@3b%(8?uv{ROqfVlz|CR?yxZClnQ52gJy3E{80(gQa)6y7XC zq9Q(bIE3R94L8D7d>&#$TFF>(RTjYFousHIavQ|N(N_W(*S{JS*hhR2D_d;urQcs9 zy1c~P7LHr;@4Il_->tOpnpSdfDR5uI0=R&+c8JFiUrfNX-5<(^-kN5hT8;8{yKX}f zH`ffPb*0>HJ-|0pC)2Uy3&=3q=Q9fJSJJS+J-)ZKwe88&mfph6mA|lW$t?vYDs`$3 zKHE_Nig{az1cM4x%Tj(puXo`OL`;{SgGTd!{HEERvwp2rJ!Nu%1rp1aK)$IKZ&7&ac`*M2{LV?4Tj(RG0 z8u$h#N9Sp1zi@OFgOPv8)vw-(LiY$CIah6LRg3D(a|S^?yvjn2A%+-*>~W*I=8VxQ zh)0gP1G`pb@PiqI%#H!iEB_%BhON=ck zdk^d9j2Gp0u7zn>vr=My#Pl-YXhQo!EXTjy%~aSIy^(#rqc%)K7S_2t6_cPi3cZoN z)9!E~vs1fo4WT3}W=q=@&cSzHbE1BS@!q}f3fn=^ptn+Ck{K&Zf8F@|89$p%cL0&v zBz{huMv}Wi*4A%N(D=y$1IP3F3B#4vU5fas`~Y2ysqEsx9W7prHB=T$t9dx@*Z5IkTOsa@X=m3| zoX&WA{G0Y~D$8?{%w_pD5vX!UFc)@@)#WaZdwol=4`}xcs@=mx_F8`vr8cUUjO$Cw zsDZ(FOxXHAY74_)Ty-@RLiFs#kE)DPsd*n#_?IaIk4`Rb^VX1Qs`RC;y|ks_k&1c6 zs)7U(9IW|bnxBwi#JQcFA*Bh-<1>Ge5&pS;N;*@N9K06W`(>_+xO9<_>D6^o7yVJM zC+Tvmnq&97W0*^_y+ay|3k|---qp~g;COWR%WKinnX}sgs?pJ$%;c-1RnmzQSOJU& z6285tWd*va@%mvwzh9Ag8d*k4>nLO6!u=|%!f$I3K3DC1`#O@tX70z+%Nd8YarjUi zozUz8fwN_rM(edTvIEI$ERb|HU<4l3R{%YrDV3B==x$-72JwD*NhwW6-1Z% z+uW?P>nLviCtIDBIqb8d{M#P0IH7cwFQ2r9xkHwBep>WpDe&wq=F)y0>QO7uE@1^q zQdJM#Zv!FMuew2&`1yjh_Oo)3jM~K7sq9WFK6(X0^uiF(-9A0j{)w+(wmz^b zNM*=ye&9a6RW1*=R;FygP=CFOuTyzv3ZPpjztu0Viht14Ej73Uwq$eTk-Jw;yhrI> z{ngLF&k`sT()>N3nn_9K}KLHhk1kXHnbxyzD?(fwG28KzWWxR7?EV z9B!6ddUZw5T+f>)MXB)f$A7K%sVIYt83k0waBw@Oamcvr%jL4GKB`x-Dko~&eE#s; zn#WRcflhX1KJ{Yt>cYd6yazBQx$rq6ZKki|Zi|phnC|*}X9%rjF;ES$j z;tXapUV;2jH6JFsHrmZO%YP}wZVQ~*yvDB0&M0uTGyGrIku#yZLk8u^PZH<>6fXvPjO54l&HlxiT=~j?%cHa1U;Ox3P^$g*)Ie)a z%eYSxyFsJWb-D)+^ubmjk?OaZaPd_+L~b1wfe58^b6}&GlRW8hZ}*)4<+IgsaGIdC zr^j@Xo0C)Za4T)2e0Piw#r~OZf{m9!`}qs$l0_p`sgoY_w`VICW}lk(ON-J*S%ZQZ zKlL84w|y}mtPuR0h|(5$clVeW7A@6OU&puPY>$*L-+FM{N%aO>u4)Q$gL>2}PPcee z(ah|QOvSM>cPTDW=-Xj-bVzR=y>A>&-V5xX$HZ) z=-~1@#-?F6N zblb9n29dizWP+EVFecSlDOJtB=V!Lu3BKzym&tLExG&Hh?GI_W&OUhw#@@fE1a5Hq zJJ=dA-6mO;e53qe6FqT!$um%(J2Y0>xLJ=Z4QOEBTc=Za5MW|LvsbTrWfOyOi+(E> z34X0(bIw!=OrW#uYqjlutNdwg*|Msy%tFr_jP#kCO2?IdQxLIpwvk?(42TrV*8B?Z zdoL-SQd{Xb@hD%txjghHCQd9Tx*V0rs{)VZV4h?*WdiQGQQyMWT}%p>X7V|Z;jB6Z zN+aBJJ7v9@laa44-R#)kS+>Ni8D|(R54A|sE)OizW{?%{uHh7JVix=L@bGVhj;q7B zMj=%{P_6}ELu7ld3T+M=#=}0k|0aIyyY=UUh49J;?b~v44t6u4W?8bfKHf-m+^_g( zSk_(>2Tyi8C?@@fn^na2w&@Li&G~KYG%2)!CJ~jv08DWxs83O-ddLgH$ycXtwKdKDP4c($6?wy#scy53;4xs!+!Osun-(wl<>6l{*FxlW+_ zdaq=}JKl-9wa;A5gKf&<$cpQrV5XzHtn}nb-nch<(w!id>REvYFwuNIph&p`zXwgo zQo5cEdTxA-8DsM`O80ktjhqTe--E#o8L-+LRUKK~E+>qk&&J8yds}lNBh^P5tunED zKz;8DB3A_3@3v??Ci;leV3FbH!HP18fG+UOUS`-Nk5Ne)Y$#2aHQ2OF;P!|H9aq7? zibY@Ti~w}MVRO06G;7V&u|G)36uKvlu|o2CUrXtYLuTZ-Z*4VnX5>JX(uTFObkLI|<*WFdO9@Lsf#AQ|650r4AyHniMPUl7!O;4K}r5iK@4^u;c zq2Md%);iQ&7F#SscHC6b3oFEW|4{zwdduXJszZT#dImX{iBz*mwJxS2GM4-IH56l6=Qxe=bnS$2fN28Yas75j z+FU_JkI0M=xl`K0FF9JNlgVlv8O_M4R-7Y+J2?0{IcW;gP6fqHMw{()HrhuQUSD%J z_1u~d^U=;RbSh>BRaMM05aNo*_z`6@ZJ=4san;}%<}1c>GF#;nB(&N$TTs_j_L3%9iM z(Oi>ra*3k$B^Rl=HLHdD$2g8AHK|Q2pWHWS+P^6p98E>d%Fizgk741e5c{QVH&oA^ zVwCgh0GgXtn&xIwq-qDM+~jdrrHP`?6KzO$7csHDS%7Jm$`xyBecr=j>mmw!lV}#c zE7%Q{%rMY3J@rQBAp|v4L-Bqp=`hfDR$7&+*zJ*#7+%taqs|(8K_b{pqS$t}^}n$Yh)9-02aatUd`?EzPAg7WVcg;EM@mZ~Li zD>xP!_9&S7j8Kj&3WyFEmGcC{9*G?|zZSu5SCl&}+BFrE1$wTdvWJ*eeT-B6%mcv` zUR|g4@jBz?qsN6N0=Rd(C`+4|90ALY>1*9jzoE6(YEptu~gS z-pn_1oe6C8%uS-+833)VZ4~q`PaR!r+zPN@5T;kkw?d)aWxOwWKoQA@ zKC083(g3@g@TjvXtvN9#Tw0+ad1Tw( ziFw4pVuHQ%6s&RHy4@b^F8ccKcyMEk01rlN7Kn+7X%^|b2(Bq#kM}1&&w6`r>#R_J zo=mnl!;-#~)Vv$!8VHKa*BBWYtsbaR3j@17)nK^uy%6exP=^y2FlH5j5nZnkN>*C^U?T2b zAn?@tMERIip5JNYb_B+|opNV!f!3!}uPro2!6^*(ka>gU&?q@Cf0L-?-V}*340dAv zxgBzv{D%mb2^;)Kb?DZWk%383!4q*KKCbfUcp+ z4tddsu7?%oS_!I;bWMkhdXxX(0g?I)Ko2bwEBU|O!EG->2Sr7NZrT!32UZwsBxH`* zE7^ffYpv2`E%;-a*83k2`XcY2S8d`HeoQfGJ}8RgvwBnmsU5a-7n+w7q^{|sqCF2d zQ2{(}Dxhktq4~ev$=~Zix%cvtzR_>HnGe~CiHUV%75K=KLbbi+9W>9g1Dj*xQ)w%y z5`P}=!f_)u;twx?zh0`5{rJB^<-H-flUJV(cLiFm;lAt-D9IdiddwA;9G#Xus!8z@5*L4i(fe->?V&Z}vP^RAJ z8o1tbLc;qX;mo0XkP69;-@`<{1~oteGXsj(bqM-Tg6nnr@!EWmgYTKZR58+FqFUiw z1TB3>N5}rVVEciBVnQM!c+Ijd0yOUK9Oi3T!yEdU5FkQr@7h~1w=eOrS#C^oc@PYI zzr!U=FQpT&m8%NxBtgMA3_05dihUcl*LL0kS8}P{^+~SMaBqd_>FI4gZY=4QNK>Lq zev(VbIvq_-&FvkVuGA9gG+)nmK%1yk{HR8LWBTh{uBdp&a2b~p5Orq-eCsUf5<9^{ z{@brOT=}J?A~V#lV7COAX_{DDbT@Thy0ncK^~;wp70b-O?sx*Ec=qdcS$QNd2>WLZ zwCMNrkb`AYwT`Gv(sC%4q^|@bw>Y#uxv#&sd5y7AUIW8dsGZMgg@`%6u+Ad{e11Xu zJH57`2sJU#iCc#RLq_2wnE_8pwqvxjJee+CIF!diE$lgv zsaEVlR`6*i2VKiu_+$%&V{G;sJwt&5cD&4`D=fJT5F zk;1YR2W@&7@jQ#5!F`cwxdp2$8~Ri-t;S=Fh`3ph_X}U&`rQ=PZrFsu4i5GJ;Up~36Vwm%1uA+k`Jp}|+-fPSZbjBULWwt%3 z6W#8vNpm29f(IVHn0rH8-ELnR5#8E)gE=biy~AiF3K}+tXz?tysp7U;VxZ*TzLp^1 zRR)_PXY9{yVFPkG8CLDQ1!YP-e09j*wTLec$A#S}{Xe7@vsBg_#Ts&X{ zgNgt3%T1LaN?!ld^EC^*0>Pnj?=4x|u{D#rok5>I&}l*W;OCi0JsE|F^ z3QRLI)s#Rj=)UA<)ykyz@#o{r*@a$uyi$LpB_KjI&XroL^vsu%m^%y%5Ai`sF8qK! zr$t>Sb4&M&k})dTC;|ZC9RLWopZWgv2{7a+`(k=ib-p9;Wfz?iLk1m@6}wnIh0 zD%Nd&hJjUPAu9*FW?zi(-s6l9hy&m&k|!^JMK*$l`8pR<2Wa`xdh;Rt7^%>)3}xT| zfq_+zzVT0cmhlj2qF_ZTAE(hb`|hzTT6M11no=`f8}36j`Vk&~=wW+aU1;ejv@q#g zmVSLXI$z5MkDl)Ajtp0DTd&AB9qs~g@2|ZdoAbXqTsLQl1p^Kn8B7%i+y12rYqCNx zjWqejA8IxET(TK$xYJ$tWxLWP&2K*)78;W`!;QTU2+vKkp;SESnNk_EU|9o?ofan6 z)Bu8{E21hXLi`TDD)zba)9zO+my?{d)I4QKc&Htu!rGf>h`HQN?)KPqu@7G(xea|O zF&54mX$o+265o8{WwTT*A-WVrrO;88_b?sRL>u$|JptGp-#?U-tGm3|8;?Avyd>gL zibRx`#=qUyYJ5s8%B`iT8IAo}Xdw$99+Jg3^X!nj*`2R1iUafi;433M?gu}s^wbxaWMMDW%O)vG^vABpW3FV1 z`V{T@`0IwhPA&g@q*QzlcHOn86JOu$DaKPd8Czt$H76ikYQ+dNZ2TL=uGg#T1-Zku z9(0y)kATGc8)wq8_No!lDZ<9vk)>wo<}4<)BsI0C&IF&)>(|#x+zGam)I2>loU|?|0 zqI!2MD%y)I$**%j;y-A&y**`&?{;7An)rWMd+(^G+HQOFwHL&Kf-fQh(z{ZnTWFz5 z7imhB5_&Jejuh!dx>5p!-VzBV7J8Ll0z~O83DO}X+$VnDH_kct_m1D-+;RVq0f~F> zWS3{HHP@VT9lChE`{fweME35@|HtG!}C@_8MYnTpS1NpLoO-`x$@#d(; ztk!MZk)=80ws&vPwR6wxwc(nH{|>uK8!wof?<^>@ng{m3Mw$UPShqA5IwnpFDs3^s9qAlt8{xbqc0DSJ?@t2j z8u?^}15x{a?|M;0X`(BtgNYY6|o4~cO{Yb(6y4l<<-4+ntW|4f`H8uUyUmnTBd4lG*gk$v`oWo zzo#+nfc2Yzb;0PdsFT?XKbzN6*yPe1v+DzbW7B1PR5Rs+azTBtw*h)Z^^*u28RP0b zCeRmn1;(8*^_4wkH^#uyS0?M#^>y{hdp-+0OX3b{Scwa&jWL;=B5OJO)8*V$J!@Me z9@o(&d8*^*d`O;<;oD6rsGcMwXWb7c&X9@RezPS!S0g zhzZ}5oit()HeTS6gwk19Sa2`qHJtmm8kN-ffl{t@PEC$4mbV0S;rutUbxP0m538a* zdl}OU46YF=$0(d21aCH6VmFTl1I-ulfOrlRz!=D@le7~ycYIf$11}TZD6UN$XJOi< zoJ47Mx%y9eKT_{J4HLbl`(dVsWg((&pgHJ*IA83w-2&?Z-R@Ssq?tk`i&hZOXV(2P z+e=$uZY|J}GIP^e+01CxE`mLqOTBnEqrp;C5=748;D~l>lmcPE=E9jXpp*X?P3$^* z$Ojx?NH3ocUrz1I9w;$TAzmZ5-`QppPe>~j)ihZ;44w6_|4wHeo|&<>+sL+-f=zvx zl=oW7ff+zFfBjPGA7`IZeXZrRw~%d|1%IX2-<__I32t&_trY1^D3G4z^h}hm2apjk zr;*R*`nriJwnYE0C&Z)rQG+Wu!Rt#Bt`|J;HnO$_(Tg{I&x^CN=Tn zm%FK9*&TrDQ;I2lz4P|b>=n9eTnJMMK~R_>3+`l179s_ajDXiu#hJ>G@N8+>$_R@r z*3QC7pdNux6BAbU=Z|3~G7^N|A1F;5n-W4FsU`_4#zWs?Z|A}iL;!(Hn8iL8> zk=lIiE@r;o@fH2y^{#ajS9U|nUs0j?U)oQLi6~D{iUX~#WtiUY1U>`rOubL{Pi*_! z@_!F`ADi7>uM%Qg`N~P~UkP1&($nRws@#kM zsq@x#?VH#*SIYkEd^wLxi$r!#BS)y_JNj~-QvzE~5gaEtrIOzcmkRFp@+)P^3tH+3 z#gKv=bg;-SRTMN&5K^x2m^4h>9~pUcrm_f%9pVO&8~d-5KJw>}f1lX}RF@W#5xlvq zp)@k$lDBi`pwi%8Pdo8F4>n=}C0FS6?-Tj|!-X)}j+>jpUA!b;13Ivc+3-vnPvwEo z$Ve&+b#W%Ov5^LShp3SuWLCSOp(m!7meTy6HW?&Oi`pkc=SnF|zm2-?ru;}3qPaaW z{Fv$*@YZxX<7>}_I6%k@yEb7@d0Eq~s>w@rP2CmceT{(f*|%18evs-)eoHuJywfDYjG#-i?egyl8f%MdnW?vDS%QkA`zw3o ziib;8lINte?^go~50&_2p=XRv?7VK;;(O>6;S1LbQ;3tl&4#O=$2UjmBao+xiq{jj zm>;Wx))z7~)D3=NFUWp!%6Zr!(T!TP1D!bik&FTod1t} za;CCpu62(%^bQmJTv17?A4P63EftabQJyP$UmqeBEPqee#CGl{TC()e)yN1(G=He( zSynbS`Bx8?IMU9Y(!;k3lnj55g2Zuk&E#dmu2?826zJ9pxgT{zcl0ZKySzYh`6+R%mZtYLpzwqL4U;K713&mHo`jW6S&K!7Vt?MsI zYO<0nQ?}Eg zPs}5>pH@n|JpY@NJ6es3?*CnM=sfZHb7s(~7}dED|G)5M(mk^Se{4tpLhNX5q_BD= zzOA`BZMw~?lymN-c6?)VQ)`K%l+>x7@-`k3!t>X!--c@1NH50o%I-W*ll5Ue<;X@# zLbj<6rapd#H7W&@!NfPLj4(?qGISod?;ni~|ADN0l7s6$I=}s318d7}bexP-pIRz4 zyw>YtQU495V5tX+9Hv5burMl@oK77>B2e*x@)e}3MvE)H)m^FuBl=t*uwZ#X00VKy zX(p^}kzBf@DHy0dg-DMmb!L}$v1^2&THr`cOnQmC$S644@maoFCkHG6SUA~q6;DtP z#+3R>a}(5KMM4eSw=dVQgSR)Z1c#cXFl=iDjD$GRaOA>&7`{LK&wyGlG43i-t@kBha;z1pjaTMr3@>%~)o_0ti&{d3 z@6xx`-_NP_xSgl5^gsqG!+b7vl9{9aC3Q)YGaEir3lZ8qF?Ul9Z6c#Ak&yTBlgUipyAxSsp9R$)Jvkvo@1gqb z0b*2_quuS;*;PN%$s)0^m6+$nxVW^iT-;Xv(KBeoU*s(V`yz#p3LYjK^{D8^YUUpiVGu>I52uSxH__q79PPco4Bh^UW&$`e6iroxm9UDMGMRlPveY>1Bn#v~H zFl)1ar!YQRXq?u&*s-RHZJ(;$_Td}leGrE%T=!TMOvaJLEB;Te@;@T#mT@1Dz}ead zP$n1{QQBeYv+Kx$H-++TaDSOV+c!Jh+>Z>n5?JlxVjQrcc2OqVeznRqTZWuDMHB1A z2Ago@5%q>PBZqkW6Mqwb{AUqxz5(?*wx3Pmv4M-i^#BWp#2Vc0`7*@tQj;e;@E_YS zqr=60a9CNxa0;)=g?LW(s5cJP{?abACfqPgoM81E5i7VO6oT7GIv1}hod%loD)d? zl7Ik{gKB>Qq?;NrlHfguL9`O63KN6ndQB+AQlGZ$C`x}aq4;Ck9Y0fZnBTj!J8q@} zUdj=Fvs2ltpz4mx&yQQ{dw135C&t3H#Oxj*QUG~v9T%wa8QbT_ZUX|+uJ^<>`>_pk z2;@?-*l#cU)#0~P?xy=pYtI_ogYtX2#nZRvMt!bQ_z+#R0%irMUqE7Yv#l*D2LSwi zs|SQc(3eUi5nY6R#wYL6(M>TI`n&V3#h+u6c!aGXB###MXiByL86gCu`BhCjk}XSi z`sIMrv)0-FDhEcEVP1Yjg(r-+HtDG3v2%WIIWrwe+QWb=MU=LAlsGy&dT1}o=k!O1 zp^aRdV*e45f@5A1$EWOB*V9niK0hYP3zSLX>W zEs*4^C&6;#lFa?>tKw$jXPks%V>#8@mP zsH94`{Lrf^04le{wOM|(VIeu2%F7n4JP(^A(|>YEiBtWr7Ul!?3hwP~L;~aG2N&eP z2n>7@P@1Z~l3`Lipd>{ZARZa>8P%A=swb&5<*MrcqA431D*b3tOX3p*cESnt{7Ew1 zK=p_vhL;j&_r(n00fZVV`<%zFr+ML0N{X00SBzHQz!u(Qa~Bc0MVPRvyKv;67n6T@ z7-!&LyqL-%oIO;vDOo@+?NYc_x-@SPNv;HCBy&doGPi~s{3DIldXaIH_&!FN?&3w( zi2%~fl!ze@!6rW_y@#@W_=5X!MY}|8nM#8Rsrh9aM?Io9T(L05 zR;KXt<7!8lYz06Lw(tQ3a<{)m%6fK|Rt+jB&>0wK*Q(cJmzEoIuh=!*+2r2LicJ}C z?@p!@zWw{9mV++VAx!q*ui$DhmU+05v9}-=5a^LE2b1X|o>coQ%7+i4d%$SzG}&L8 zuTRcFH|C~N&Vgu;+GxdWdLx$wFvsE6UiiGBFd*hx@<&?zn!majzUkOom#`eIcsF z^V90;YSFQ}Krz7I>OeM0uv-LMocVpBJd9UNKtsRA=`q#lO3zKP-Uw>qB-#g+5$ojR zN^o8NFBC)4cmf%OUqU@d+rCEj9`aJbq*q5ttNp>%K|aY%EtuNCK=; zC-aEYV=8<>D=v+OhC|6nXKXiS!s+oqKvz#s5B#R_h|4L+n&#g&gR1&Wy11Irs(cu> zr^3IoSbHjf&LbS;3;-)x^~0`!1_Q{9@_W=I1OZ|{R`y@W2P8;4)^e1HauU@gX9M3$ z5A=n1+N#m7clf1*Gpbh|!`owUWf|{7-RS%kPtqb>=L=>`5yk9 z8%UiZvan)q$nb>(Ymf2$()c>B_L=XRxhWu6=Z5w;JP=2V(Uv+H_A*8(DJzFruct*A z)xZMi(sar;)*o#I#Y6uPj7cU%s$;fZWWHb5N^>PN_s$!HaXlm_Yt?{?XDa~xsje8C%ARvISG0&+`S12-Y2FVbh3>& zsL=Fuwq7Z?{_Sv=pc z>v7+=2#$DbWl{7}yW{MHPqW%*@^fRJ-3~B(^19^9-m+i}f4a1{x87(1ug8Y!Pj_4Y zn!38cWXzKiCeFn@XSuTpSI|mK@9h-gM?b;=E4r1@u8Obs8^2)@k6ggyl5(#9)bIJU zUP+ul^uWLs$+ZDdZ4A%t+vRll6oIk~|TTBDnk>*`e%*Uufcwf#bY z_o3wrP2~RVY;M)KW=)&5_%kAQ-AQXdULBk15N2qt9-d^?Q`mh}QCWGu0FDWiV0HL1 zP3{VYDz?>c%ku#LkGP52u6$k<$YribpX|#)cbqFU_I5-iX-)49eb39`U)y}zKSaM` zGSKsV8wC`V3D^O2O0?^KDLZotDmy4nc>_OUp%HX@_wf{qpIY z^XbEeZ)pPtPl%63kO>D#G!7}Nz1$)*2~R7$@eYi#JAOV9HXSfJ8P3VV2X8x|s0qJD zH{-)|pSfN<2ze;|HpQ!9gGk53#LdeKA@B&F4^%e4wvjea5`;*;1`QWpwOep})dZu@ z$)2sm76MV3O6I%b?C5 zXiVO0M-n6?GML|&_ki(sXAphS=j{gh6X7c4(ZyI~1C&XkNM^=&TK>A=RtFro=c&!B z3&#T2lMW;lL*-=zk?7^Ozja8wVymUeKk7m2mX@i2O2;JwLei{Q^$i+B!$z@qYdI@1E379#s?d zioYWdUCsP;^y~>W&qjExJSHbcQ8ax*lr!j2FFn8GpZY=tGSK>hp{xCw9#+DKzEl@aXWcDmFCScgo*%54@p>N9fU&DmtU zd*bIdi5C`RUncu~?c?U8EXnH@Od5X`;QDA)%Iu#jhIUcAOFAyPJ~YanJB7Yt@9i-B zgyHg!^7+yR3!!JyK<4-|S&A2$uwS<=DpkLYd1b6|w`Ee=?YUy+gntng^{hwnUu}x*_RAaC#x)O zi3(#yOdXU_ai14#|C}=I6r5fOc^P{If4Q?W)w@Sx8=LGt#+&r7)BcMTjM$riZ%RQX zD*e?pstu2fo)lfLFdl}4hbx@5y>x^*;QN}6u8>^%JO^w51WNk3j2Ra@IXt(~Gf6?* zEky@?0*70I_66uym|A6zv!B+~saB_o9>`I0?TZ8U8`zlq0ob(IFI zV(?E9co*l#nQ_ndu*E|pe7~d5C*0OLtdi^v^R>^2+qqo<4cKgs8rZ}xOTw1(hPa2g z;8`-|C0DBt4gCKMzW@9S{2;WW6LM%s*VXdO#Su?SGhv7D!!BcA4qk(y9|xC{lH(5T zu=lf(jvp z^z(;xdvIMlt9YC=Fd0C6VAOay3d1UDrxo|9ls2)f>{bnNlG2CXl!}duW6y4>;|C+k zWV86pcm22O3|4^5NG%~25+@(akM>M3ajr368Ytk`R*-u**OSSf8}%TiKr>*+~a>{qxw8|i5yzwz^Og(SOKO6H`lr6pg-3@-~rO$|8W_NrH$zaD*w zcutaZ;HpSIYiJ7FHE^f+L1%xd?9~`R$I{qQTTAQ*=*pxKXt4ddQCnwcbY-`LAcnk^ zTDLprSYeAV6>>=y$+;Qbc@e-Dx5ngEM>xC;DoASd2=UWiHQ*+MQ~RWIjWe zUnti?CW+2&^!G%MkINdNzRi{$J|=R#_!u=J2CSxi8q^c_S=X-y;uB9b+)*4uVH6t< zelbB*N4PG??R*}2^6`5dCJ;65Ub`XoLIp!XtHE|{lK0m$gK`bK^v2ov48puSlDr#q z=V0UnAn!3Z_x)ufb+At}3#b_}V2ENkZJ5Kvp0H4Gzil2QiN7Qfm_4akPHEMUkutNB z0ec-4nZfa4^w~2pA(^su4k2~U4>6y_oCZcvg!spKYCsP<;aH0=4VJ^55bw$mRYw?= zvONfCSY%4DZ{`0!BYXMEW|!o;*{a^tQ%oN>2lRwL+|9YZyT5L)E838F?#2dM#`$B( z)^*|96*XYVCegAtU}VH_Xu`!0o{JloQn|FQ3m?>Fmuh6*HlVT%FI~FShcIyTo9QLl z{GNK#JyRbuonwN!5fTwLlhAW=u`^XtB_n`x#wQ|e+(}frKR4v>?b-tiK+54l5r9!k zLVJSM#4<>yq~_BwHP5qON*7sRv`(H6Y<|zRIT}%8d%U2hOI2IAmUqLPX)lrSHt(PI zD*&zDl%A7uk7eF%;~%}02zmy?^@CPS>_%+w(3N++YWl11*Nh)HQ?1QFbPgtqQj zDQjPp35(N2637IMo?+2W(_kSWTvJsCD16W7F^?+3A7hY1Ee};R$K}cvU7ye8p*bEo{D5<&Ti*vKc45{hy`I4P{a$-wn&k& zh;N#J@(Q!ic#>hC*wrb-9-$Hxs%fEs{!Yd*W$? zorHskOFpgTXU%>6=x5fq`z6p;_P(K^r(S*EPq@?$C2b{3#`=lctBpleR90}x$!T(E zMd>;?q{K@ed7h#hmi+B^l+WDn(oy;TlJ9uoGfD{+%2_x9Nw3#5^vr=26NCzQUuhTFa^uBz_Nnvc+UBYVE?LOX@sF!oee-x}++y zbN^l%NAtdXZ@g2cXIR!*n-6vzHL~2l1C&-g=t!NKdKEn!?B6vp&?%-W4lMWYw8n$M zA7d`Vh(*lgr*Q{6LTgF>az^8XI^W(y%Y*aP3VOo|&yCrYP*}^%BYiFb7?0{r2-shP z?PDUUG8>>7W^!1U`H3^JidLB7;>?ZV?mFa~8HDN^QB@2af&fNk_& z=|x4a*dyzt0&`4p#q(@lo9%Xa(X~@+CGOL-l5JE3VAwQhnvKM=ds74X;qFbQfsLqO zQ!a_m`8j=naMCH}H9Iqx>LNNns=OlR_eRgx_k--lk7=&4%7}3{l36pG?p$a3tiXzJ z4fl=U$^8)jsQE25?!NtKiFBoH3_eI{*VZWnbOy<`_J0K?zv$gkWDPVRomc9owty^=3o4Z+Pz# ziQCaLGuPA9gLc3csSe(ygbDl#ZOY{~~_8hiEs8vO}>X?-kBsCM77 zK!@EtHkPhSYXA1Wt%F0DX^^H}`MiUQH{Xi&_JzjHRSyB4%*?}I<142jPvYc$cXmdw zcOR_YO=lO`Z^+6IM18Qwja4tR78({uzw67*)%o5GW}9baGk+`Y>F$oDWg6P;@(^GO zKkL93D9iN2tE$RMs*X>?JpO8q{@N2lU%DCGU(P^ER zDdYMC6X9$3S>wa%Q^-}1&KA;E?TOfwt(JHCErEB>|3i1@pBunm4xWEG!!%-D+66Av zc+%W*mFq7bON{EFKOF1Bz!zmxcAVUYrf8mKy?E`&(bW6X8oY-;%EjlP!a4FJ=FmfTzj!Idl4n-&%A;Cm(=US*uW6Md#LGp@73;xq0|R0nzH(j_^N2Pcx9zi`p`nHb zdDeP)t?v~eDZ72~$nF~jaOp*+q;|>xxk8PrpdhZe6(Q)q6c>+voTDby>p8J?fHdVm zHlUI9e!aC0V>$($?_(2F_)?av-6SG1KQ{BsCMD?J=2A9D_ZF7)LTF*|r7Uw<}wOy2k z_jg>MgW~+!y8taZ2KW-=lfB7mUGxBNY#RETcGm+8q+j$*aSw#jiKBh##Tgfur0(fi zwSedsj(t@A2^~PExeY~Xx0_#bPHEbCBK}^%YyAD?jRCvuuvyDDViVl|pQSZcdw1$);c|X-fzsfDZAI{J6y4Q+ea-Z()D(Twq^vx7uKds$i!aDb z112u@u5mO)^sTGoXzSZpI3R5HyZBD}u<5T_=(m@S(lI3PZFcweH=_jT?2Qe29}Ho_ zI}cootE3!J!JQYK1=o}AKy-(Y)j&&eYU}lC`;IVQzf+_sJWoDGSKE+NE73v6A9i4b z7*X{mKH@Xr&m^$K6)zocw_VZG%UhynQePuT>2j*|kiF^q;oCxwvJgZ=9vS&JS9K8N2}AGDjJ<*Ae(Oy_RF2vpgVMm(PElBx!_-j)guXp2kne5* z_nYTrf#kXtd2T7CuHBJ&j@rKAe19!Vw>8j25nO!oFfMUyp-APc2c2e5EPfpiB-H6o zg0l>dOOVf09VD17RV_0AC~!6w(@hzu6{2Eqe#_nVa~PP2!C;&G4wQ<{Wq1QrOyr;~ zMNme6&>pr#(^u4OKxZfamjh(oNcf?R{w>`&I5MZJ6^YMT7(|p{D+Ip}R^tukKH1g= zY}O6KeC;Z)M4>7F@d9YHjRy4&EB)KPLELz5eQ_L2OaTbn+Kf>d+I9^ z)CE))U}{ehIs=BC?i+=f^b?V<1zA#ctVO{|xMuIq1;OBZ^Mi3k#o7QHTVYwL!O8D! zR&HYr5NT}huTA=HXs}IIIBcrJbl!Y$NblMm1&$l&Zpk;57S6V z=h7f*44BdJ{VzV(af$TjP*$Omr8AEl5r>9A?*2Y;|JsN?2KK-nz!Oafvr==P)&OGA zqoTSL5zUN^nkpz(=$S!SSXeAbq!MchXyIozu1^k~MMa|SD)R8VQuAEIa&9}~X5IJD)-oyntYG)_xH#UZA? zzi<<~k>sCNHKdGSH8r(Z&-x|lnvFgo;MA^^EE$Zf+Z`Eo zip**a&9CpokFc_&QZcX~(xfmj{fSQCPoz%jvz~6I6(M}@UE4Crw*HkxoFU?++q0B# z9Y2OocKFSkvw{>KOb=_Xu(Eq8OIrDBw4D7b^Za7osdl4E+Z1UtUI;{Hwjhrs?i7Wn z)p94V;E=kgyh0RN=c=~AUH3=tpp5@X8l#tBNs+)7I9wT0Ql?e zuR6F|)*N-Anw58Z%+MxvVcLXnVM+)vgM0;$pY~%HWck3{J=al}npy9~(d?xCy=A`L z*(95_p5QGn8|?cgsXT<+;JDk$?&gA9;r>p)B}8k|=4iHalBh|>tnu59FOrA8qvOxM zc4~wV78uU%ht}9hCc{)*ab2nss5QSn=LhLRIcD8CWcjl|=HAbdb ztAL@h{dvJt%0Ya-%$F|xIvN35u0@?9>~}{unwJk^ZcMnuNO?4febqApnl_J_n)ysu zth;;VY^+uidX@%@+?M4|`n~Pa57!1wWDAc{Ta8Tq`HXGJ-L~VgyZqU!6oEr4g+FSt zTEEa#O7{gro^teBx}7;VYA3YX5;HS{6^DScsQUYBzt@^NcSD>U!Mwq=$+{XwvVEzG zSUwmGl{!1{U=2!wrV_m#J^F0wW9B18DjE`UZA zsBl@qo%tDTuYl7?+2pneJNtwt#Lj4k|2yizr#JsX&8$C(zYsJmek@+b{4%XJa91H% z=qn;cA{(d;YJWC7NhfAXIeThEjQHEDl^Y*X)!IaH#r)Z7Q+T94S9!u@Rg01n#;lo{DZF(xO+CdatFQz|pRJ}h&J>9w_r=-!hR z9^CHi%*H)>)YLUBwo^I^yfZlR@6aL_rvPFAGhsQEIdlcmqhA`oh z=yBlR0AT;+wZWBS_g`=DxkNMac`0W%bJ>lugCGg=jqCI-a5NU}RVia#jS%Nxw<DFj^jR(=djq7+r}^TXsL|6GB*m+eG3u<1Jmzzw2Dz#0zBo%yI&Ug*O*1lLxYs9{r=a^Immz}9* z9?*O;9xmc{WA@AUTcrJ^g!KY2_2KSR!TGejb9;cPri`C>e zDvk!^Ldc2B>FF66)VVu#vZT&0>}r+SHwF>L>W{0QaY7)W?t=-#?X|>+gw@s4lIbRo z9`Q165q%~Ww7tX7A}~rNIxX2;NLbinPWZ#JvIDxoB#OPAKa{?pfYh{I;`w{88a#v2>ss`liLz+Xj<5VX?67gN_s8nmY@Z8Q0ox8P|GOpS!VN*Fov5 zJT?>Z2G>r@RR)F(n6Ckpmvr9h73!!IH!jlllWRITLC2RR@zCG%2iK~KzV>Mc+dS*# zz(pJww5kQ1Fib=1U6zGxx_Hz8buBi!Y{DoUbPE|s2GJk|il`G?kSZ`E>5Eol8AO-* zzS_^PY&i@fIy5CM=7&amQ{g zj6`7zE@o*;@z30SyT)7F24z!|q`fNwH#Y5ltx2RVE|e_!hsxmO0{YBY4ZlVr_wD=P z?U%J>R}3d`L^fVoS_`+H0$0DSyTe|~x({E=s07>Wh*TUJV zE?6z@|}%rTDslH~R0L89MvV?~mBT zDppJ##R;nhaKMYd<^PRbN{g>%SAQ4d^Poa`lY*EhvoK#~R|sm*GDw$zWpZ>;vRYcr z2Xo%%a+G9bU{MA%aSNz=obv{xNvzLPn317eea#&~pFQ`f!;;7e(4TtTkhQf*2ojl7 z%xo`K;ng<>exd)n$q@SVszHt`B-%98NGIu~S`LR$HcsfvopxcL>T0`&Vu@vkjGBP;xW$_TC-%b(`V?bT3 zbJu%3`0X2fbY0!GmiilS_@mSI!x7#fmVYyvU+5T{bQzOBFp&ZgMr$nXR)tYhaD_5J z1}UQHcTL!}KB9F8FVcvW!fftx!mI!=RXV}>2b>5PyKZkd&u73=HJ7ZQJP37jyrnJk z(Tb?`n5$+ql~aha_I!)Rc5ZyoibJU41!5V=_w==+`d3-v-N<@}b!z}GfVSR~G_!`Z z0qNz$yU$*w_-r@x>LN_dLPHfq^JkB{uQoDntC=o_93AD%_lp|};-gJX=TA^y-aR&| zyeMq2jk`3ketzaB5~`v?xaH+=wbl7vSLzix<0;FOJ{OV>lI-2THw;PmTkgnDMF$gG@clM+cMevY@(n-+ zP!mVYMAf7hn^Qd{yn^ig$UH+V9Mc)xe9^hO=P$sLnTx{M84h~jkeurQ_U7w!1?J^X zPnSjQ=yCk&T@bsp3KhICR;`=2+@Aal#Gh<596iI@8UP3nF{J zbPSYvYlCCb(u#G99qcZ2U350z{-iHknxH7z=YdJTW&3epDR-XOW&%%!)xIqWpnDGp zMT(yXjJ$;*PxMZc=)$C7N8N%gH*)VNWnZ$Haf$(L)Lwn=0CB6XkZVA|{In7wU7U2A zaN!-I#N=hZgNobjLetCNS`TD+pAK_{7PuuIXt0et97}b27zdXg?H?ZCW^Og*PEJln zELp$TuOHS3LgwKg8s@caV3D78AV?h2%88sDz^@P@2iJ5&E+L}i7)iI(0MbKwF;3yG zfJ+;5Yi|C6Va+cQ8>ua2#%C%9SU66oSzOe30Tn*4FOInPTjVY4aK`@e?KDNb^c8zY zQbbiic1cg{&1}<$dK%(#_W=FbOHV)Z!;*q}WPx54{0o=O=Iz0ljV^V4#?dcaOcC%! zv@v%^+@li*khqxQf@TsFyw+wP=hmm6IpNl^lB;Ksr`f(_7359agNVH!v3}+K=NnQ# z+-UHVSAGrYpAdjQ?G)tA>1<$AHFR78P?r~){VBW?vRpa_nG0MB;YA&JI3(?ewi;y5 zI3b2MRv}q;q|H+#?XVUxAWWrQKMG)@>%%dX|5xLh|IQQnnL{O0<)G8j&Ke)70?F$rr&iP zpNZ2h`(QVK-n0O!tG5l~*^{Kwb1YjWM6a@|S=rgSRw2R1YyXan`lqn`Pp{n#J*^nu zsuSrRou98}dCbA-nnYl}n8UyhkCZlL@WMXMYOTLV-TLPjyYaU3uw5yLx;o8hXr`Mo zzu(UnDzo9j3N*&aR}PKbeaWQf>HPv1u~Ab$IQ56}q^8#Hyq>U9XcW#+JhQ(()?Q?6 z@!1QtNni&cM|9DO096;Sk#=XY_yP@09EpGzE;h+zKUH!5gHDEW?UOV>ac^JmsTYv* zH=kZpQdDfs-);u`CdRyejd$-=!h`Yg{^~XEF%{N}_Zda3KhDg~*3_AmqP2wTtHSm3 zAL`6?WeU2~Z@zU&?Qk7If#zT9gA26LOCS*hwCS0_Pr_{g&@d|jLqt?dO`PL)c6>n^ zTU>W%l^0+_Q6qbDJx;G6H>fWY4DNzcCMdxMs>mc+CZsHFnbsPp|l?V;< z=j{XT1&r44-!m*R6#_)7*DGDBNq|=)*1u_x*rq4%?k(rP5nFWZly#%b_X#(r6K6>P zHfvqDO}y5hT;lA45m#%V_#>-aaFCnq{H;}BSjfuf?OaG)zx5ZZ9@PTfTN{4>|3Az3&%nXwPvCDqUmeFA^#xI=oUX=LVC?72Mj zfI(a{`gdhO-xJUAd?ih?2xkRfFPE*H0Zb!dG*GPU*<;n z5ARu8P0y68WF~UN1vf_&^bxYoyZhisW|M`r-f{9lghb6$i4Q+N5@*_lhFj%)6n@HR z>Clv7w!ms#auev8JddaYH=O|+#~&YK3UK^fpY!8^Rf0OOjAV1EyZsGXxmkHneb{W+ zj6q=MF<*+m9I5jO3Q8C^CfGgpPtlNkp)DZ}^-d8JYvF%{{Q58z60F~e#+9GI4$2e$(7OG33rDF`Oc%$T`% z2%=MpBKViv$M`s%Sftj%#Zr{({vHGjDW|pyk zzP)CitO@g9_p*{3#LK|;|3fWg8dVg70KP{>t;b>B(#ZXNK$#rBz78Fd(IOyJ(ww(G`Y-o?-X9P zfV+fM%rTC z7%w?<{_Je4#vygRFmXMI13=smf>a!c2kjog$3-&dH$z7tqyb^6T?Md$#4je0k!Ko! zz$tjv36suZXo;Gj(D2V0dXbU4cJH3`CyM310_?T`Iw%=1$0r31Z7^MAV?d4?(2^j;%$8uuN9v$kgX$z? zb=pD9hBc|M939cNjhe%BeJaL9%D%-speIHg>&c}y0v9YPLAO^c$sPeC$&M@Ye`!L@ zFkLuKxzv#VAc+iLahzoHSa+_;k51MZIN48=`IWe#Z2<6*t(9%z*0$EKXV#KNxI+L@ zwf5|Ai5d3zF{pcofo1sllV#bwUPQO*k%E4-X|KV`fTuTFOr}0nQ`~XLTd!r*hUo+F zNHDIjkQlX}2bjZ5yq-ht@Cn=vSWaeRW1||FLIpyQb3zMCuoi`Qu$S^Yo9uig??Q7V zU+a=`c!h9t1mj{uAQQ}@Y*}INS(>TqJ(I?*gtyd^qRqhm zspQ`~)@HZNod7eBdFzNCg-Xm*8u8f3eRbQUAxUDj;B(u*nQ>fq3hmw0X;J z^^w|$!dwyfjj@aa<7-f5!Y-20^`4#@z%boyyyTAQ54&J7>b7s^F6=i!nZ_ch^ovA? zK>-5eafM9g@tmx0Fw)OJgRcq)Q}JcEs^#&{?=LTgRa#wKUjS@XvYD<4i>rX<&6P*q zLUpCI5*69Pa=&-hg~E2mJ$iiD8R@(A%Jy5oYrjhetEIA-{o3)ijt*dT{8;Eon-*$o zCQkSRYwq9neV+?7GB%P;gS;fcxQ_Q&a(PRJ`!Y+w2Jg0Xk^TqMKsS}-twjMz&hZFF zy{-kdMLH9yA*e--!y9U{uo6tj$;)ex)0P$Pkr({|MlNC+ipn<^1B?_zuMK{^ zlr?!>UbloeRGF_QWt}eP!!$Ec=NEo6@02WusPN%a-R`5D3?tS()6`zi(qLgnpSrkW~j-lM@2R zPRjm7){9OZyIKZ%!|c4W{&5}p3#rVS4us*#99+rh`1!Lmox06BiTHBk;R&Pa0 zg;=!H*jOS{o!YxDo0MkhevJ?C##Y!Uv;n>Q%plwMStOPqz$deO=(G2b{ioc(!@ms7 z>iu&~z5gHLzB{0)ciZx3MMbfJpfr^xARs8cDpEp~E`%aThk%p>DFHi5l^W?vZvklu z1OiHtCLlFH2q+L*2q+McJ|EtD-@9+--5Ku8AM=+&a&p2s=iA@jYp=ETdeh#{!rI+( zl#40z57=qf3#0=*eKwPlu<85|jO?$+3o4 zm1k2=a1!k+D|~$T-KT`nU|N-6^@E)2H#033i8xK5GXE<2kmriE|L5$C9X24E1g5IY z+HZ}+xt8hxjm_8To0op|YF=r-)+v<3!2(ya+JFS_ncrCD2-DxR=``IKk=oy14|}Ur z$pM+;VmtKlP|}O1Y3Cu@BE}4IG>0!-$E5*u)U$;GE3u*xH#tF)bS~)OlWG-$M(Ps+Hd8sN`(h=5g-3 za6FdGvR9z85Z;*WTU7K@kp~F=cKgUvw)=)Pr9=-ObCnS-I$yDWQBjb_p57_1y`y#C zU+t02vAfAE{h9I2%9owiLY{n4Vs7``_bk+mbpKE8)8y{x>uI9`t5g&_s_Dt;C{KIY}aTTHn`f@x46tofnxcq$w)-hTYLwvlV+yfI?@GeO%R z<+^#BuzXka31Qp0B2M}XMq65AHFtX?u=;=COxVD~*d2e1Tm7vvOnn8Pv>E`e`Pk}+ zwkn`d)6bF77jbNfMM?55;az`;Ut}u(0JLs*2Hh;2Bz98b6*BT9@yR{$KBihrf|YZ!w;*t($6Le`Tc@J@+R z>2hhOJ%?x;3>VY|27?g&Pi2e)Da7ACy1u0vymL?e?zMN8nMPMN%)sz)G_n|gA{S1t z9ByfPtmJ#xL7l<$?R>?k0BIuOz0RJ5C`nNZ(Q{Y17PeFYMNVk z$FsW+1G;`7``l*vd-@10HCJohz;A$ZAN&gBqd;@vV(<0M-Cx!TSZc1;+DiljN6byw zBIQ3c{FVQ=$CzzWWB5Pvf87*&duabHfiuU@fEW9LmVImARE;y&UV%fJl&=08kk|`j zu&lrN8C=?bkYZH+o=NX|f}S4r?%kkG^2VDu9zer-V`Tkr$QnhDB{M$Nc4r9EU8u&^ zJ!JVkF1Ylgvb9+$-JKw8;tikfE1z}R5Ks%>_a=DknbO@e5f_(~RaH~~d!)N)#JV9^ zBSSS@*)sUSh2P}DgmWBcueb~pGT4iHDMd$e)WAyY9RuuG8gsDZlYAj&f2#N9^jMn^}J$tIS- z#CYJ1{OW&#Rqvjp>n(<(c219S9Y0RolL!FQVOqQ=`Kn!O&r31e7A24kdihpc!c)xs zoS5^#R{S+d5jyHeIW;DM`ti|y4-&c%a8q{}yLfGV{VJHgcz?%lVcpZ3xLRu0uIdY) zxol$JpKsK};op-PIXPEE%JiBRoV4;8y=rPY)U*91DGNqKOwrH_GposFSOqmtPfy#G zcRt|_TG*WrbZKw@No*KXN3gE=!L#oN#qaRW>tj6ze0AddR<$)Xzc!BLc{#JM&lM!} ze7yZI&#WTRJ?$usRij}{&2`V~A`58gN&!cGX(BR|96?-+-UHl#GLlTij-u>465Erj ztRR4nd~$QG+UEJY`4-*&0>V#r5jnYfe9^rR&l7d`kX6M&m{LFao4GqinL_u@r=atGGZ z>wpoWoFBRqq3p6lncVx98vejkZajpD# z@}sBP4$0m6d%)4N#OqT)l=aoqIjZPkb42`dC;n4d=dHHM_Si0kG+y^Q`LBmBrPxK3 zG#?RXVtd$9AX4=Z+`Q{c_Yq`(<&b975!{)7pq%aU`vA)6)H;q5*c%b7!!E1Y%&_}t z6D45OrX}Jqg}RwnmAA~3A`EX|h2GD5Wc8qT6-FG{=<6dbOoPc&q^`-#yUc}?mTark zUR}QSaLJMDIf;4&8>FBd;_%kWg@x%okft3*IdfKTcuj`i3FTgu`%?X;fYHmAmUIaw zmy{GST>}Ak)z3Zg|Jl%l z;sc4qmZ@)hro{eMz~On1VGMo+Ndg_xHSscO(5X!+2M)#lRyUKB|CHHlZ~(^aZQeA< z9*gS#>KODk;^Ss(*jFuJhVb*Kz*9Cp^9Q{cDlyZ%O8Jm+8%jimr8&aB58vExlM_Jk zR52+r=;h&E5X+DlddP;0+d2y^qq%dE^*7H<1#`|is3Jn*fHN70 zf+T@R#hv7>v0YjmpfyzMB`dJVwxPx%F4*4}}`4FHze^_!6c%GWh|>MU|JAx7c|X z@awlrR#ukA$l_wdgjLdWadlAxseyj|iw37*)Zre-zpeYxg2paotW^m@VU<=7a12&{>J>6{CRN3C!#UDwX9|2Mu~MJqyK{iV zR^Qt5R};J4a86mH&O=EMQ;b!`pt2R5Ew={XatG%r2q&HDx zg9se@_q_O9mb(;%9nwgu!{yS=N34gU3O7dkyTUHRKQXOO{fzQNVpDl6f!?LR{XS9n{>-Y5Z2K3f4UGR7BJ9e|fxLhkm%s{nP);K!myxq!- zk8N|rVc&@{<;i;bUE$uGjwo5TI!#{Pgv(&Z<>BK~Q`nq0kk0Vc1moY&@(!?wyAS1J)mV#H1C)l5h~h?A-AQ<`v>?cZhMyw76YP^ z{YplP{jHqmyXm4RLr()Z)=Mm#ln3u z)SAA0!ClU)-(;#7*)tXD2c|+X4D7p8{_=nt4ou3$+ z58278T1=c4>GaNzh8smi9@&4Gr%!(SRQpJ7Fr7G4q2iXqkH69yutR!@f2-C3?Hg%R z7otJOFfU?!&p>Z8zE37-8me#ih;83zc*g&zUR#A(>tw{fiA;YTlmWGJj|Y41k$opV z#~4A+IPc8S(0!v6m)|Qt`NYdkcxv`<@Qkhi(ZmVskN+{y^Z$IP%wY|q2y`l71NYrY zQgZFTaKMP4$0Ok|PFLy3;dj#i!Lk_+yY5uUtzOhfm&;l+&%)GYoD>79KPFL7APxhQ zK74UP#|P?m_CFDPtZk3ffcF3`4YC82nTwJzs(MBgI!1yI226qkzXR*bzZY4K{&h|+ zYL~)YQ$PXfqQ+u+U=a|6{=P>MH?^U%J&v}N%nk??3^PCR7#t6*qBj#kRSHNv!%?bS zP(Dz_4o4Yh0Cf?xBIc?}GNZ$1$YGWDmZ8vtGHz`qYntyj4;v~yNr(Y1Ierc!*Gu}% zj#fJSEOoi5F+?kyqzfLJXt+%Kh+1*91-qg|eSK3MfX)J%ZGsY#iUu&!gPoYShtqE}=KRa2{*{WBI zP>DtvlYJT;cMQ{gMETdD^iu7S=fw;Wu8DAC8m0#++bvTQu__g+gaoa`q zt{7*S4*w{_l@^*&v0eJ?b{&9GCheQm&9}%`q&CMdh^h*tcw!nG(@aO$pQXAJ)2Am5 zRn}lh@e$EDw4e3lbXyMZ7WaBq{a1&Dq^-#lR9=4((|lJ8qck4s&A+sf`ug=ba|)W- z+^Z&bncxp9>ees|#lVxkU&v)_S#dLrx%?;1R)WBm(Wx^4sDYv9ydI*wD zep0ZWw6BeOd3mOG_SJ6jLyI?`neLWT74!3Bw(mWBo(P^#Kj4$VyvY8cWDN5(9x=p&btDBvol>sKiFw$HDuYbPMi_k#l;~^ z3T8shXfcSjJcC-62@X&j4)oU@2m}t@mz;3!Wi*Hd_IASAE-V%P3pKwClZ^`ldb(eM z{%b{PhVq&N8v)SX<<-JIE1uph3w=Nd^4IHKlz?&q%GmG%MB*Z!b{7#Lw9(ObP0cyk zpm(b;ro7*wIUSZCCK2#awW~?sY2u5xPoh%)jy?XpbO3zDUE|yDfLZ_xJdHjBqAWL~ z^wIqX=Ga^Sknps%e%?2%p!A;mu+Fc+qK8j`$;qIo2dJ# zRp7nM$JtKV#kk#??b-JwJH1jWdy@a((Q?XMy3^y5gDjy5;uL!90%jDM zX;IA(9hU+Img>fVNl(=@Yin!IG5mHGoZ>a9G;rW%Td(8#czf3f20M0%2Y3{k1NWR6 z%hP^NxdHO-n4#VCc^1_Xz|rBaJWDjL5Q^2YWZ#Gej?}$mFy}XX1xJZeUpB$9j zp#KHSY4VhQ^dJp57~TGYlp2OD`t`#-XR|Ca@4O5VbKi0GrJyCbMMQKpP{Lf1rym<9 zO2Fni9r`vGTgIE{y6SRqW&5w{PaVw9PGAfITZH30UW^i{iOs_b%-y z*z_8iSPBB`qqktVWejJK`E{R>K-A!OuB4;Dqe;elW*!6bRmkp6WNU&@mnRWu07Z-@ z@~iAN(Rvx#p3YR<0*z9P7b=M>XaPf#n~t8v+dfNmbq0(X=_$c;7mPLRyHa`fa1F^( zmLC{)Ds<1BNx3Xd8&TEHWYRt*#L6S;kuwrVOz~Wl-?kg-Qq*V)(OUxDNn8NtD=7JI zdwO|I0)_{qq}&EI6+N4#emWR^nV%+ufiwOLxm+2Pe(j}!wGIZIbEbu zr{1IIxoIipL*z?U(+hMXwOxvegNJ=9qAsRs$AHE(aJM?T*H#*lsNJ_W;(H^dj@R0V z@@!shofwBSfAgu1q=}lniswJsGlFS4NmutW*S`ZK+E=e$)p}y!0y zOH%OqRi0#WP8zE1_ijX7UYeFz^9i5o!Kk?T7H#BWZ37;|C5M+-5dz`|vCo>}Jo4is2S_(;`gg&a5FO`NO1IM%KhfR%*E#RAFj&1w2 zg)r4s1^^>-&jV(wEX){~`?IJvjWrGnlDZz(*k+_wisz$AfoePy%Sy^M6sg;KhICRb zX^UrI_K5~i_tLq z_#m~ok;dzoXWDCDOE^Lgz?2KJu%r|-x3OBS^{-)%R#u4(U%lPwBK6L&*e%z&XIl#D zTw43F*^{v;#QW1R;<(uGKL{;f``l?+s^O27)ad0)Y80i20_w#6;qctpv@{W5TH2c=i2^FqjqJLz zrx5?uOdM<#YIn28Fef5qkKI@a2jajb>gE8kxcz*jq6jcAmpq^Y5%Zi3GC%Xqefkc4 z6qA5DKv?d$9E27E!qfxV!_rvxSq*59J)>TBm|;F6I)q<_*US}vD{D}|=$28lmbB^Z z`cf~Wen^tuY)@84d8%lB7>KM1v$rEp4#=m*f%FDssMw?EZ43seqI5%Ut(lNL1s+>{ zKQr~s-f(#9rgH4@c|xpfodw3wRNUxkNfUoSl$F%_tkOdviRP5iJrGTBnA5u=s=pjdq zRSpQz0Ro7}mb1@ypP zdykcakX}A{Lr$)F^I-6eV2O;^VrS1~%?HZn>l=Pc<8!dJ*RWSrRWGVZ{FJ>%gE?Ph zQH^26x76mIgL>b%4Lnya;%A&o4BFvU=>>y%v**tHROOA&2k!A;dP|4wHkHJPVGm+c zQtX}~@7f5MUp0wz#qxt z=4uM4q2=Y3=CBB1Y?8RorMI${HecvE6q72fFK-L_n3sv}oY#Cl zjXd_ZKLvSJvn98jphnnUJ^QK5nU_fcgx}`Kugg9 z0&;mg-M83zdHsYFxIa%JAb3S(xvU-md44L;*^Mr`{xLaQ=JGxw;Z!p=wT|&2Vvza< zi>_DNt<&5&+s90w>**o4F`d)g9&I~_hnw8mtqQyJr+b+OGdr&F397ALs~#JTsZ}E_ zPd;=aI4`ixJKjO8@W|rsf&As~4o+NZE~R{%|KP(s{wF?++uf_OrQI>Qwcl0e9RG<9 z<6x^Mjl)qY_S~yy$96@RIj`^*6~rD#guIEH?VVDuMliRJRF&Cv4ENigsPS}51Ik#j zUTl9fm~dV7;4`{p^^)>L_xx&Z1FxHCVUsep-nB-6MLR4_;_|Ht3H`A8a=$c|Pci zcwr~bwX@^vuC*4^5U5+e_uex)NZS; z4o&~X+GMvdVl(UC0r#XnxgBqrQnYYWsn!!a#JaXoZ~?PAEV}WfMWgZ1zVqJJgC(0Hs%-N|pY8PP0+9*w)rC3sX$q+Oe8v81 z5hV;}>@waOVWYR~ziyXKL{zs9nv9inCwj-`fe~d#tL^qZfn4eOktNyrZ_XU!K4aR^ z$_ylv;c_RJ3@#xnONq>(kI>-PJF~aobBxva#8E3mQwWoo`)EyLYS6k}DccZL$*J2$ zh~VM$6mEG!Vs1urBWF#jUq}0gQ_EY*o|V}n_JZM-SpJ}wlC7tnA8q;0N~ckcptLX6 z?M|s0w*}JY_UgzAM=8`4KBZ%?F|kRC(61QVspc7ooC|!*>g&vIf_aIc&uTo1eV=hX zC7?|dISOwR`S1LdDaNIY|Nf? zkcC=lmyNBtN(oX1~7C0GR=Hx_hFb=&nTr~zGZk^KQe1hl;-8)8K^; zm_C)`$QO1a;Va zz^lojImjzbF=^37?80rLc1xy9Mp+yeaK)JFTW}JUV51vo^XlMzvi>*3!yXHhi!bJ9a7_)3F)@8r>JpmMuvr z?yCAo2c+c)xCfv0-`HM6+3xi4oXc6`_Sn=)r>>UI7Ps(P2uFS^tm)uH24r~;u1wy? z^Syy4tt|VT9yQzT>*5M!DP zWjk@Vjf$7n8~GpFIZaHJ;Eu9j7ynwh^gU8h{!;ZCF@~?sicI6PI+7b#9M(ejFCX~- z^@SGHU+hUsJP?I{EXn^)dGTL=_g3G0sKRQUg$TcHS#wW<=;~!!l76>y0iIay zLa-FB`hCLG2aga7+L5aQelz5{-EE~?X*=6=t*jGY+CI~9{#aecu26#FLjAMa{gp57 z&hm-54u%8W#8O-v^I_XIDAvURttm3g1?^L+6&S=zu7NmHR` z4Qy5?R5>AiVe^8eHtQ#532R49UYYe|LJUlOQfTH|4dm_9qwx&v65tjEehJU-(^9y& z8#P}yw%huYj-yn6z5YPL(ts3pM9v&BWOsV~`|~y1AN5B{w^wC(qY>WNS)x;pMV4LY zg{7hCG~B~MwFNehI<0O05Uqp_)XLVhbWwoz^;(Wz)lMBg3x%bY47-7OQ!(p$LMf%l zAE#r9hY{g~Qy$nIqNU|}muBgs?yFUZPiOK>ifdsL2mYsP_%DBaT6w~7?zQ$9QWB!D z8&E?EStx1d5Q~Y?k?Bx9C$mdFO+N^xU+dew}C#sP0Ev1;@a+HvRBu~vu|;3n3wb0k=v6z1W^|X z8WRV@kc6YAKo1@zQp>Wany!(yHrc9(>&}*dXYgAHE!J+vXZbs>#7Kk~ylw5tYUlp= z4Jl+*G7^3no&^h}DK54hpkqsBso+=7SO%mYp|gvr)ts?H5@W*>Sl5zxRwn0oVE=V~ z>C+)DbZ`8I=)RG=GY%ja48}6pG@O!EPm+L+i2F;U_JxjZ7Z zKmD#2=H|A9=axss#>ZE7r%myxKJST;U7lj%8=tKQDP8oy+Q>t9>G9VKJ(a%_-~0p* zZ58yZPz9XYm)RTOqgj)siMpS|Ye9fc_M=Tqn6oHT(6*;V8G1MR1`B<$M!%e1i{ zb3r2R_V#XI>Q)R1A$;uqq+0^BR_Fzx*dz284i}WV4(aDv)!nv{ulb2(?dN~DlemPM znbDOY!Pj7(9sPt>1KnCz>~{5Ht^Z_K-OjOe-VRor-bY}d@s2URT?07DdcLvz;Q{9^ zCTERS<^N2H^P=;(nQwtD3fDL895>M@2kbTtcF8-?e?P6|}+FW_ji8O#?%mE{_H(VX7ElR&!>OLzdsyaUm zBPd9~E*0ZoRK(ZOn8v+h-hYVgD_V+-g>)r@9HN9YFl{#Sx0fm}rG>@_*m9VR+h@YB z#Zy-=)r@r-Q!n8t+#X4e*o>}Yi`a~C@SpIxz6hu44tR7SpKdWQOLbZGNE{zQcm*Lp4YtiO>Zvu^%17(NRAA! zAOg5k_dh=R!1^M$P*GC_^VBGi$xp|lgz}CX!0^*N?9+v&H|@$gBy?XmIixTNYD{Kw zsPWW$TX!Z)d(M1NYpLDbN+W3$fxwB8biF@d&CmZ7+70ahwr99%A(AEt1R1cq87NCO z)FmO$?GM2V=eOx~hn99|SSulCz>RUeb{^vX&xwE1+^hk`#M7;iMrk>SH6nL*Y>;q=<4 zUKcW1thR;dnain}q1lUaJH=kU-n92TVNL*}D}<2w9wcV+l#vG^1?{!S^|@`zYj!Q8joP#r&;+2BgC1N$_*k3%%S5b|Eg+>{tpnEU}p){{}y1DXBoaRz$_z3XSY8 zCI1SjvvBBO!4Q8*!_o;5jg`==!_+Vbp2}MGoHX;UZVC}SVT47KB8F(Nq8Qu40nAe{ zQVsbG?cp!tM1X*|C8w4zS6cF!RvVopQdulw`nsYtL&wG6sgGC^%iSpASSf8kq^uit za_)DZSxRF;w6U78^FU!jcu(dmm{NEOgiMPen@}{mtRqT|O(KOMLNB)a*^4ni0N_ThYLJ*bGYa=)J24=x}@tZ-tb4mPR%^yN9d=m9>s^V_2*hYq?j|lpvcv9`KvN8 zZ_{Vf3vZcxeCLrWw76KC#2w6rD`tN_%HReXQ*Q!Zk!0?!h`jSlg=>6tTvk3!lq}&; z$o^TXI-~gB^>F?loFvCSZ&TCu%fWqvX`<(IS0c1O|DsDtd&*KeR1RCHr;h0@6wlanC}z{p-+}di&6M+xX4UUd9}pyw}v-G~Tso zN0r1cc^uk4H1!;*IH!dz&Me0-1To@vlhej}b+C;=GE;kCoU~VnN^PiiRN-Y0{~bv% z=BIE@f{O4)_9LpxjrHc|5Xq^#W%dC)I_l#q0ZEdJ{&|9wmajI8;yfE#COTnKc#M$X zwsOdHrxzVZF+d3N)i$==0VG*Cw+fJmOzA~Pse*Ndb}7I8BE%6}mMYPW@ZShA7DIMo zhq#QF2HqHaF<#=hx#$K#UljAPEIFKZ?Phb~h>(XLthXx~%x4KFZBi3D>VC*W;+-#= zA3F1&P0Kz_0Lcuvq#x!t;Ur7ZcJhDu%L zYf)jn4z07xfzf2mOO?0|@lRT4pPwXHa;p1AV6}|4N(Z!D7_CFsSaSKyv2tg!u%k{I zN1<5P>Y1L3aezYNQ}|izfIXOLo@&>@(0rJ`?eH) zqI3x?n5^4Zj{VEp+*stDBFv)QB2vJDbKZJL3 z?yPF}L2~GAix4o>58!(=w;cV^N`x6*&s$9|lAH(vPG+ zG%WKo`~yeMWbpnQ9G%Ped=HL<6fG4Fo!@s%J{D{6Bto0~;_AO4V=_zycR`>lQGVZ* z%M&H(BOnIpTuc8q4j~`!E2^<5;X53CBto_*IN5ugUu>sWH#2Bg5cZHdTtVKTxJ3gi z4>RX}foM=@X$x2Wv?_h@d26!K4Q<_^y3`jV!uN7_soi!K-Z^{080*GfwVwT>^w$>) zfH+JR6f49C7S-I}vw!ZH(Eu~gY`>jqum!bMB~ob5gT|1<*W3H*D9{vNW~SAen^l-W zvvt#Wu_xKDry*A%HvTHYjjegwNfIIZ-R1?O=lH zRT!~IP>s_|J-v3mX1U0-waKJ4(WQt#lvyOc`*gVH^lHgn-v)YQ@x^Ph3zbkQ^&Mo> z%Ox7Ys!dR&E`_m3&>(w@AM`24HGc511(kX)2G#(VA@!ZLy4H9#4-b#4mSeSqb_U|2 zD=kb*yn7#^Dt%{|oLcVAD zez(pZ-nFq-J&H$Eyakl5_6gmvC_2s>N%NQI+Qln`$u^7xTx<~sk4_=<+nOdAZWfbF zD>krOnoO%tI@i7)pxrhgq1In^_vup2+m2jdn7cF=G#Ztjes#!wcapI7NVN1K+0$IF zn7L|_N!UsuzD(L_rvQo)!#Ar7`Lj{(-BXnhGO-W`?seA8RS$m)ErE-5ILS3Na>I}# zC(%4|rHu6P16L+;^Ay$V3;1GO1dB1rS-BS`rTsbi8(R9^T@~hGCw^53adwsEPB0Lz zSacdk=BJZ7O`x&VN>9-cm!w#c9M{DI&v5#qehD2lxPdJ5uyHe#0FI>a^OG|0TM|B= z>$)=@+SQWcLyBj-P-t7A)=#StV$R52Ex=uJ+OGV*r8)WJ5JS{(1>tQTn4|F$@81f_ zvS;}*x4QODCV%1tin?w*ijja;r?jvAH1KwT1db|w5ZU=?(TloIf($)N|N#&#g@!=!G!MGM>c zTjor~bkST@*B`^$!C~-X&4j|T{?I?H)~43@^KQxFlYR z-!HoL^hbjlaBtt~gB<}GkC)*RbKGE1 zO+0I`x#07oYuLK8J`9?7t4878Ll2%a>|uJ70NIH~V@Gv5W!KUhSARZ3IQJkede_F5 ze(`a(Ipxq|J0yrB>3szzW3@`D7IN+QkCyf+_AB&ur;2Cz-E^=F)drXPiWg-l=GVE@ z7*GIHGKi30%@lwS2X3;HKXmJA0H6Y`;212GvZ9UH4SYU1ofhEXPJ#vqwO>=yg)I{L}ua_MYw&euN&HE|73+sGT<;NbsT&p~O zqTf6$7RX55l~X%<8rL-U7V&~UI{*VwcB|38AZ&J>*~+s>Zg=bE)JK{z@o2S1VsnXh ze%R_Y<_sx}n%_CWVaV76=N!^H{RLbnj=6QPM5IcmKBo=f-ER$ppT#BKM|p~eJhW&t zjC{77KMj=?pEb8T`Bu0Gwjro8s&&uRrs7 zq4_)-imjke*pPl0DZg{2na%6YINJ;6KWlH^cyH~6gS7{Pr#U!-)z$!4N0s$+kt~O( zh}z-w+=;fPYY{O0+T?UV+yP@R3W>E(K*wF%x70M}pGTXV*A`4k%U#vR|VQu)(uE{p^UQLn3_ ze|xKVV0)8v8EipbiUYM8Znb2;q54zz9{o7dn)3L$S2S9|cp7{th3MfLxw2)XYJmeo z%c+|;w>e;qudPetHdF7)E@dZ`_Va|NQGHW{>jGY=TtqLpK#K_20xyd;TXrLn`e|ta zH>18QLRPg3WAkQV9}B=bjyaeX2=b>Y*(S z1Iu<@h4o_Avop%3#iWf3YW|g}$kp|9`9=8L;D?y)#5F2*?5)ojvVDhFXKNValoJo2 z$+qnf>&%+n9pVz04O+=O@nLDLybiW$=28*u)min;@6Oz7W_DROZg5;R)&1vp7rVsJ z9CmhZ8GsX^&l@a`PTx=Pv+|BWzRCT;ByUs^6Y=1@w|URQ?Uk}$o@65wZnSsePu-qy z(?P?e?$CWB=?d5GHdVqLad8ZilVMsmwKM=NhVm5x3$@GORy}8}Dlo*1*tmo;7ah9v ztsHW5-M;6=U5g$WaraTV(9O@mi@f!y>!#x}+TyX+*wGo8DINj@ebZ~YBaY}+ZCY+= z8KkUO05xm&?$HY=x1?PA3tX05;2 z3%}DV>1^bbX=O9lqUWbmZ-9}2Ou%KASh}M=d$$en&LWU8OTdzsC{mz0Dq9bafX)Rb zl;Yhy*+tq@T|fXAz{%?eFI{pP0ZY@YChnS{QfzE*P@96+=@4D+xZ6Ab0##z+gAO~#+jr#MVGcu#YW;{XXRD%b-`Zw zmr;(6JS#Ao)1%8`9}?%J<<;o)=W*n@|3>QfZBFj__L0^`t^nf|*R-2EzbDr2(si28 z85kjX^Jb3c7MM~hKPFbeRTY3}C;R^; zq{~Lk)JkJ6uaRNzr`|Vh-hQ&0lNPvgaI4vTPAQv0Of(XPK{0;uhLv)~h534wLT~&y zY{fLtkskQk6lzs`v4qn$O)|ENR1trAcQZANRdOJZrS{jCq`{%iEU=6T7*_(;0Q{94 zv#o_QxJ;S{o;H9w>oaOUmLb1ee{*R`u&dqDpPJ-l5#nbFeZ}!J@< zatf7pt)CA%lAo_^CbUdjmq^j;^T@gck|+)2Mo$U9(F>Z!*>8Hh*;?x^S;$%H*q3ns z4u$WF__ZCkyYESM&o+N%8^{tqL&j`5eF9EH-NynCt!V~Cf$@EosX+_kl3`OF=?$H8 znRUOrp^@Ihm1Qvhr8mr(Z#KmnK;*S0o>r_$qTK7Tz>IjZO5W+aaACE(nRC#bbFY+}R#NOJ!0Y+>qj%dFc<{)0k@No;i5yb5WchaTeKbc>Dd72F|6lQlYa=WQVy}(k!c>qX2T!h_pHVTc#XXMV?(39 zgg^ApBNOM<^Dc>mVdGg6Z$WG1@T(*7BRNQS`I@GRairkF14l9r=qhs^?GY zf(-Tqfs3Su!AR$)zHEQjMRrw{8J-61CEaI)tp{~gEGq0K`Fa>aXl~83C8I{UBm;#x z{p>ZCJOz(J<)_%uFUob7dl;H$vjh5amSl`_{4(QztU>e^W4adLX5$y0$Z&7VEHqzN z4A@OciT=;kc*pkteKksKhYgh46*R(IzthDg>Vj|SXP9C|EWOCI;R00{b#bxi7Rg$< zS!78Zpp2=p!;`CfKZoP^bNmdMs8LKk^0xl9cFt;^>+|s-X*J81!ax!fL>zr9Vj2@1 zR->m={WYdKg$CX?#%e;N)h9316R_fPD2x!)pJ`OB7G=_ZZ6tnjN$wbH)WthyL2pQtS zu7OPTQ?|`~RT`nhL_ZQVmZ3jJT5<){tA)|!(Vw`wosH0Be@k6LUgM@+eOl1=m7uD+ z(+ZDG=Bg@#IeI!sF*LWmda`t9gGxQNI*1e@BshhYyVreS?HKn4wv18Xv0l5|d&FG@ zM)p><29yv~*lXmP{%0^g`j*p3WfU6Y)ca+@io>;c9Sl?%5$YIz@jTzQDm`T--V_bx zQ_YbapZ11cl3ek7TFFMm}pkvG&0=Ir_UdDC6GhZ{XA|BP{ zu8+zPk+e91p04E1Iw9>sHnurQ2CkuxYcp@P{j)hTvJpPH1goQjlZKlvvg3^T;NKYD zc-+!hGw&5!n;%lXAY@q>x)5OR7UR7M>OT+VWlP>e#WZ21*=03t_z9>BlD|=cKSgiO zi`z=|)fC4Gw0w_eaZ#^YQ9~w1kV`czT5oNXU$!#BlY%C8;)OfJe zVVUSxJ_(^w+3f=tg2U@K)lGsx$Bf)zV$l<&*Q;%vcBer3$3&|MQXtacab5ohj@e%? z#`r*_s8`rfvO>H98+h>2w4V%xp$QuHM<9$CILMwM0&{jOKlV6+h#JtBz3 zAsTMzONyjuSymBOMT9V?L`eRhv^7cZ3V6s1)T#n_yE(PyC_Bar!BwDh61`bMJ?P`` z^x@78VN9Sy$B`n9Q24=Y|=58@cu}Ol>rYt|n)_#esBy$LL`o?wc(PBfq^`(#7wdo(NO9O%=C0Zr!@~@VQyo%l ztXMb^Gf_Bx(G-6-cUClnVtwN!B{O2aztDI2K@7kPltu7*)j%3Ot!at8&UpHp=P$w z4;SD!y@D5!fnKU)z?)dff0Hrqd?(uA_B?Mrn21RMRJoTstjyNIKYH=K5(0Z8_BR5_cd>l>aPA?V6Ds*9U`Y-sso zC9Wd57#EvY1D0F<-g0w5o|tB}WRQK?P@pM)qD~hi(k~;ov#($^StU=*0lV6pqUjEU z{P_w^)SD0$IYQ4xra2d=id>f%K6_d}a=g!N+J=#~bu86|EP7&muI9{erg`$pyV42> zK!@xs)#3c8Y4un=DG?e&_MKc!Jt{)D80IIWuSV>(Qe30>ybM_HpDx`E+;w2H&dB}8 zW;f@Ol=>a~G}^^2X#5!4&#^h&?%DqBGlDou$HB&LAxRXQ_G8RhRj23dk8n(0k|8h2 zKbd0HdijmliH?=s1n#&Z6MPm5HPumf7OF1smOT8KU1Z{ z!J=oni!*5J3f|vpYQfVZdy`|yp+LDECDN5A9Jl7nzFL$^4yeVbSd0}x;QXZZ5I^}7 zn|GVS4cJdah!b#(ne<&5dpAXgd2RJ|g!YfJCeJu}$J2MIhM6&l+-{=sEVa0tOK+~6 z=;lAfjJsV~dw{@!2cweWID=MHJG#&MJcFo}Io_+Y{+ReqH(Ba)4;+Clrq4blIdX3& zbrp-=S?N73;a$GUH~?AP9R@5CgrXaL?z7!g^7qu#%*)rN+;^%7G9r##kO+R{p!Id<>QhRwdM^>h8f;JQ5B6$8>!o^fZSOTRO$ zYA8H9%Be@D%p*tmO^08*XbO1o`)C;YBQ)8yZad-)mFS93c{z*8f0tkHSd3qd+Ur6y zn26+_<_JLJ_4-5oJ#MuTyo98Hl`L*lrUSXu8f4dlJwI>alQ&)aZ8$UkhCunMdY4CS za=CIIMo8vT#}3`Ruo_&kMOA8xinY=&i0CZYPG4p`y?Ce$?Y7BwZK{j-* zvt!3%X3ANM4zK%Z|80p>YBAvhzIOAOIndmS&X--9{r0QErP#WCc2v{;W*e=tRKD4( zqaZzH$rROQFz}x%BneiPO~@$NG#5;QVX8JuJ|A&2Y`5ywSLx4@0;SmNX*1uj6SEaU zX@3w)liXbJa3W;)O24)yb^M7u?Y1UJq!7{niTP59SaA`YpE=`BzUI0cmKF^EEUp88 zCyIQ9MD&t>G8#qZFS|g&svRz#bIyb-2Yr`q-=2|p7vj)`AY_NmZ+M@{Uf%ooRrU5H z7ujeufDiruBKceaRg-7i#Z`RMTUK{$R|V0v3{T1%f76^sP=+L?&;1Yf-a0JGuj>~_ z5mAs(2|-F4DQOs5q`Nx=>5idOL<9t+C5DCpBqWDMO1fi!A*30)JI>AbIq!AOdEWPV z58pq2=lcEeT-WFf_uTj1v-e(W?X_2YmcK-C1RUnf_IltTT|gqp#fLww#1=3FI?iEn z#nx>%w)#@!&2%L6C$xD0r?mf<} zOMHuk&v`X|MqP4o4NpCx`=I{Vso=B&ApB(~AKl18K-Y6IXDKlFH1ZG9?}k|GV+;qa z){tptjJ^VK2Mw_IsIv@ib(+k4D}a4Dwi);Ok_I04aD9opg`#}x5^&soa}oEEz@>xK zExysGq`Y4qv^kBoYDn4MUS2rjkBtNBXn2H8!rNwNp99gZn)9VQ+dNR*d;$IdeQ95> zerrpGp^OZ%JUncgwd=`%ya$lgEt`(voqr`F`VU@Yt|u64o*;J&VK3w3d*N? zw`PkQ3HVQzomYs=>v+yMp1~3?fGEM~Z%LB3CPE|Fdmuz`X1WM(4zujXp$A}%xLobA zfa|*^G{UEiNI8f(Ehx-yHoV=~a^u&wDqTX8l>yUpye0x-XZOMZ(7~kv!@-BGFJvWT zFl?&XoCCAsz-%5$oe7=b&n(&849Wk{|ANNJEU!8fMlR0|mfM~yL|LOxTa?5Ar}g*^ z8-$ib%@8IS17-`j4#bIq((D040&phokw~xKK5t~VaPT8?8UefZ9})i9slQl}*@@9u z*O9PA)$(S7Z-mb1?Zs*nOz!QsUxz}9r^JyH$8s~QQ5ykJ|Cv??*55rtGm+)}bLTlu1E~#5J5A6j!iM~T%D0x0ht-)=QFKcpYf#R%_>xnDsld7?R4TND}!wCeae_B#- z*uf-iAA}#iKF2_pW|=m2$bono?3VDy^;^wo$NG5<^E$u3TGm^uNI+IqKzNtFwQgM) zG&DdW>Z{rb5S^H;Le}z91*%o9P=SGsZmWd-v;XZ3w#us8Aa&G5z@q@B=N;GEQ2E9p z5D6*<+RGQ4#pRr*G9?O-!J}u!XBb2GTX#Onv3rp4e*f21q|O)D+(BJ#xrUPU(T`+CAqy#Gfx@)s&L0cn?vL$W%76$BDK?+}0UxVmb88QMdHqb#5gTwHpCKBHOjj zsPKSwGt7(Sgf4>kFG|grHN-&!P1yI6aI!L5KLg2nFRQ)2%~?qP7=R6BNz&MPXpKn} zi_3s8C#R^xs|SS8I}ec~ONr)bol~?(ae@Zsl@Pj=*E6*qq#1{oIK%?KjtQ&A{dr;1 z_+w;%^X>%L5Z{aQAs@N42IIt)4>T&JdM}_^mu$PjjMar9DuBMI^_RW~tA^(Q2Feri z+qhVWxzAt}b;;=o*ZzF)4uW;LLe>4$XL!AyHfKERAC3mR>W>6166XQ(Hy`sYN6g9= zvFoU(0pBVeU6xHpoE6@YXB2ZudObcL4*zl;i)D@KoJsb1TMc$j?Z$-&WM4mxbATSY zt`>Rd-ntW$IhAdct$y{srDj++20_RJI9VrTN&s3{9rL4Q;2${iEjHzNJqj2vg$6PW zMlg%?b;hdb5eHP7S=BJ~;MvEmxz;6HCO!EQfJGAQYujU{mRDkX=6eON&B!{$e(4DvBi))9 zzM$OEF=089daeof0*g7u~mWVa8 z5pdZLfzEWNjWC@b6ex+XBgDkCxahxReK;?I;w5Z+R|wJycH zkDcO)2UvYw`Beo1xvs3{m5lUJ1ZAxc)8;tqjBzvQ8z`>?DJ2X;Zt@mid#%v0=Jfmk zjpnF(*p4_iIm~Cq6&K}jgzgs?m)ItLb+={oGC+cV1I}}|a4$RUbX}b|Pqjx|QDh%# ztGEkpbvOU@LE8M0g3){OgsB=ix5t#0GtDEX+A3=Js_6N-mKGew0SP|rh`+kPYG8(3 z8+kN;Jd>U*mhmYBx|kx%0qk!OWw2$WMh?sJ0^Zp6(s{i?L490B*=vc!VtaT6;B3G% zEg(dCv$tdIvwQ;K#d7KEV^rnzbKFisp}%;N3x`@X4it06z#HqN%IoaRgZg-J_yQ4l1ppC%mj^Gw+t>9qO~oEs?iLO24&1T{ zc<@q*S?lc_TQ45x$ab^R2O_p17(8(H?7|B~tl_ll4X3vDNN)KlOKLm3%-Yv-AL}@V zwlNF$gxdMS*)K;yE@7sAw1}`~S;bHA(aC~kO7C9S<)KCZ^PG~mKAiYzxA0TpSm-v~ zDk3?d_EN^BxKjL}s)n$5@9;^iEl!7yg<%jldeC%h;=_h-_3`Ck-#A}0v&GFLRuOXj%h9bQ1*)@oyE?+T0r-M- z2b%D7d!>Nhb$nX;@v!x`&sKW!W*Z=!aj>GOJ8!D)aZ3@k=^j^8_F?UIcT{a!+_2Z) zbZSvy(r^BEem5+QriISTy2dlEx%o_ux$v7=(K8NBim;MphnotMsCKwJ@g!?9%hZRx z>1eVq53RIa#@fpdYYRN6Teu=FcDC9gOD+zF(N-19%K;?XcqYHT?nrYl$DV87o|cBK z=4;!g4BpzYui)k@q|heYj3;HW{Q}KT7XxF~%DAz(o_(;TOW9TJn?CNSCSBRK%E2;p z)@lBfbRo3(X1lRYVegVBRf;p3^bi<7!oK#j>ecH;?TN?x720~NV?vEQ>Y75UcuIt@J ztGCSq#?L zVheK{IBXY&o@_fxLzvf0OgFLJ?iS$d$tuk1hlNo^c=p((X&l)ypw-$?78|6a7q$xG zKHcN0O$)-@2MM@)Sh?d4^l&i6^Hn;B%YxCJ;{dT@liD=HW95qTN>{-xu_?FOQ-}l;KPX*1gNUe5(wn?tm zV(%)&W>S&-!Q?b!QPWz^$)cxrY|#V9dTgcHJ&W(I-%D#4q3wI{z_RawX1SlvjiOwJ znAiN8oMjjE>cHWd8{9Ck{xJ!2xTz_T)v?AyOddwoqAf*^-ArvySbOBo{;2>MpUL}VjuA!=yck;y^xT)Ef^P``Q!FSBTH_QQ_y}}4O5KW9Rv9|q-T~7y@LO1DCO^k{ zBOt*R<2VC3Q?_Ec$LZHM8#dvz%ojj!&>N8G-iYo=U6I|OHh z-7Xz#1mWaIu+PoGLs1yA7qN|I{T*$}RsV3oOWC%4!J3%0#Hdd854ZJqyP?yh34(glaNJbR z_fE$4GsA0v!Z~f2PAB`v<4n2a17#IU@lkPf^=TA7lm?U#*g*O;K??mx1VOjz0VWey zW5Td7iEt{+7nvUR&u;b#1eLVI@5V4@Ik2GT$3i zZ^QXnMXPO(V%&RtKgi=xMHYEXy7SRiSsV9h1~%w5fDs8nryB+Y26{cM&)n-|VZ~=t z1{EaNb24X^vj$8)Pmc332~r-7KG|#%$8f6Zs}dFC{7Bx#?XLSA$@DpaebM)_GfCyd z?B-J7YPw$kH0WSK`$LI?Z*`9A%Z5W8Cf3S!m_v^N&naeerfddcCh%kcL6YsJ+r3an z6K=D2`FMaU?KYbzgmj_ zbj_&p0miz93%5)tncW+!&AWkc5p_C==cL{hhOi-nv}7+jZ- zmR7P-aPes04MM1i>me^| z;ut5I5XStYHSv8xUK@7X;|+@kix%7ZUMa*I(bE=mEe;I}BQck(Nt3UC; zc;P`vF3hRc)__l6Cu}v7$R2l&HOp+(d|UjWC!JsMlh+v?8T0g9WZWns(?@}+GM)}k z5;j$e{CF|Kd;Fr%+MC~%aFdp`I9`#?KAC1Ofh#eNiv~hkvXb>wZ`z2gZS3+=em1(e zx~a`o_e940yzd$>zE5y<( zKYLb|!+GX)ap1N#Q17H=O!l9a!bwn(Zqy|ZOw$|`mZ>rucw(NyxjG9bQxw+ zTjt{{SMs~`?Fa@mViswJtp&mhZ?3W`P_S@@tv?yfP@X%2C&LzP;R2j?O0=wy&sG9E zT7rpfLbk6aa`&chDCC@FV?|H?(Dtd^xFpAEF1qhAP0TH{57l$#y6G9b7A^|fC{yrF zbQx;7O1b2XZ)ECZA5+IH3m#p3D%OD!8sCKjyJU)@l?x${Fn7GX z+9*q#MMU~_W;n1KGDa<1OKwpj^kUGHUC1y_7S1LwE(Qa~{krWDTp<@DCTym{4MCQ` zta^T}(jVCe`1=F?`LR&U~*} zH#I_V*-`rS^s0EBU3ism%|_H$UppCBIjtBdFwApfd`*K&>>e+%R#%6Bb@#gq!vLj5 zs5JE%t9QKy%1v`s>+wwTp?h z;?vRswpl6^bq_dx%GZt!qt#eHVGY{ih|QomxZiVZf+%t@XrU+yGe29ewH*&uI{WC7 zp=R*7d%QPi-iC^_N6&>5!^R3Z7pzo?os&^B4J9-q$RI!b!)1gY8CM-N9l%aE;`Q8Cg3(PC3K0r3W~7N7;tESCQYG`VLR<+HO%$n` zu2?4vG~L!ukEA$DlXc0G0)HR1Oh*@c+~|K#FnQ=J4m`!BOH$aCGLDm8@kX*RC-Hhwj>T1Wqef>|dVAi-?Rzze%8m2CTu>8)>fNjOo zNEbW)iq37(RhE+UCk9onr^K6aiqCVh%TIkLXCz>gS2f^pXa_;4J%QzdE~Vdz&J+8Y zmUHz4APYb_D~yUfhYs`3>lYzwI^$8T)(Li#Io?q z^|iiEkJf6EoAC_B25Ai1dhPEq_iMR>ky9P)6Gi^PHQ_7rcNo!DUkC^KjzpMJ#ScL# z)nLqV0X<)k_9`hXuS1K#@C0d#)1Q`Gex+NjTlUq3$5%kU7{R1>^8AyR^G0$epM^Vp zgdLZOzJp&aj+Y!P^~;atk*;SGy}HLLy4o|oOom~rRqMVpX=Xnh!Ro6a)m0kDe}cTk zvDRbKq++y%td^D+q0WqqInK#t?Xa(UzVl9nc!oXe5DH-qx5MiQ`4(XUEQ|WUCdTG^4mq$)cP`^JLAlyzh%Si&(|kwLUWdXG<2!_D5%I^P1$ke)2=Amy{zLF; zsoy4m7LXwCB!~anQ`1`>V3e7R+y--#x=m{X=(pw1+Lq+=Phh~Yd z3W)};NV+GGcBQX->*v&Vn>p$M8`(}(L>24DAR;I-stEZvzcuzfUU(zGkzO_4mwSHV{A6Ei-M(5tq0!MQfTJZO z#c@R%0ErXuI8M}z!2n{HZEFB0;Qlefo;s-fBg*|TF{1>CTY_~l(b`mi>9~Si>ScM# z%tpmmbSNa zLeVYk^w-(N;WKJf_bZVXXV*AF`&E976NfmEB6F0peuo?ZRh48``;)21wE;wKcZ?Ju zVCZZ|az~ecXmJUsMc)zrp}eoEQ)?oOwf3!y?8wR9^b9zAbJ(=}t7RY-=(QfAYU9C= z-aDlM8*!mom6)B&$Q!7XO-tyUBcbFrO(RhGxV}0&yB;7wqx5pO6*e9vjk?kgp}NK1 zvBeZl@zqiE@R`=quCP$%orn*{A*Y#oY`i|oP$guu=HPKW30;bkB#(V1W*5T@o0yUa z9S)~U9qrpPDQ>^Tg;}6+XbG8lhW46)p>$b1jDWVK1@onCMd@b4i7)$lBR^1!_Zoke z8WcT}lW-gbL=wr>x(Tyb>i*2kY8Tw!?)3fb8tY+#GkKICVVwyr;=P zT#Lg+btT-6Ihb@@-@tv3kN?onH<8KE?<$9RrxGqS+<5hw-K-Z?nn{vuNFVTU(^E-2 zk;jPC8S2NHp?okcr8lixzJ}Ph@xem51rJBnA{LdkX;6md9Yj^y`k6 zb6>elsVL&OtW0< zocJ}Dp2Uk6tahsbNzQ}a`us-M4RoanKt-=r~xI(R6Y8(WPC3$1?t+4br zvD3f|j!yk;lpKgj-?K}deSJ1K2S`6;-<@h6uH1;1KI={9QLH5er@s{3)=&lU&fZ1p8AF+f6s=Mxql020h?9P&2+ z;Nb&8_-u(PS@`PEWWSdq62U!F48ZL9QgN6oz?T2a`(NEC+BI5MewU3P1(XVhi%nJ7F$UV#b0an4b!|%H zY)ae5op$p0nfoT-*D-46+cS|dY&gP3u$(9M?k5ef51fk8MU(2);WD7*gEhiUmbzby$-UXsFfS5u_iHci&{H$}7P1N3m z5X@ND{hI1R6eYkSzy*TKn5h~QpIu$eTPxCbC$Cn36K3I1y6B?sa)f>#mBNKrsQ1Oz zJ0P1mJBgejIQVWGCWXI9JsObEUq678b9Zn)35?N#D9Jw2zu^}w1Tx9-Hb%te@2mjj zCEj|e5?3Kv6925B{HsN#7zZ8+Jwj$U+)1|G3b;`@KrtKZJ?K}y^I&kTfk)cU50!ZD z?69-S1$=x~D;}$PciQ=H$NQxcc*NMplb+ZQNjnLz>3N&P@ZqwpJm@TC35&zF*Ek6XGnM67 z7U#V)>pr6Rrkt(-spYO#@c?nhU1+#~RGveU*pu##J9h5@<%5bFWsjW~n%fcbPcJ@j z{GHzaUt;3Qu;0le_?JWq@O})48^h?Oz`O3}DEB>{19EVdzUyyb=j_t}0@_Yi93ENJRCnG5a61!3;@oUN%S#-lOZqr6K)S#ISgy1SYB`eYY6CswrQ$Ztq`33e z1)%r>txee`%m5{w!8xs;Nimxlq3m{pje(?qwz?gq3Zh)qG=h@4MezsE(ba9$n)`Cl z^4#&~24FnT(|^yGz_Dns3SqE@Suq39-I9E&cnTG_dCvWy3$;Yoxy;n04yuT%SwSj7s^@rUDf5AP%5)7v#tLE$^`Lb4}+0FY&FgJWh z9ryjvNhq0hi|O|n0F{3jn;pY%4xdjti1A>kCWDu{g%Ub5f=ON>Z0iD_)Hjygru4)Q zqKuR=z2XMJ{Ti3C@K>hNcvX+D&J!A~l-=ABqvJND)!cY3zmrlaZ2tDLAQg9yq< z;rIzBU={?;BzA*kHBpL7_fWj7vT~H3%lvX#Wx37h&!|g5+kz)GNYELO5f;+$fuEe1 zp2AZn)(|Oo9h%n zZQT7uMd8k^*5GcSf&uL?I%Y75OLdB8cjEq3wWew0%yIPR;`*5gq0YXSQ#CX78}TYo zJN?9s^&!ENh;Jvt?vkD|V!ANXK=yDl$_D5LogG^RCp*_qedZ)%xrT zZb&tr3Gh%T<6- zCfk)I9@edRPC5i)vmfm+45$10@a_5sE++}Wb&`O(LS5gQVN;E_pcUFbjT$$%LBI9f zuXC#O`F~FfZQ4y59~9}Oa^zIIS20_ES7>(JYDA=?}hl#@m?_JuoPVe^&z$jn({YeLebI-ou};v23|Wy>Q9Bm4H+q4 zyBeq|{q#qRqkQz`Fcsw$^(day)k>lXt5*WT)$Orc-Hp0a9e;T_N0a$Ae_i=Nf2WTh zivfkN2?GmD>coqBm+jFp$N+@@fPprb4dS`~sP<$#_(Qd_&D)Q&W$h%VLvc zU0n>@YNX^9uO$JTD=CZ6X7?(9<86^~&eE3&_!1Zcn%tb6)XPgzGy$4;L`ug!qhm zIH{5DQ0iZnFn%Fba>w8y69g*7lO48Y-DJEH`#hr9)axkOv@xq|d9)tqba6vl&my61 z^^Wz+)9<|4{5jr-&78>1=Vay5J^{WIPy|)$KNG!R#kDLc;zVXgh z-1q=uzlq^Q@M;Njxa%{i^MrP2g3~;&bK(o+7fJ608Xh;s@Va%+g{Y72^8zjwbJD*T zm45&V3d;$jpI!B}29f#YCv(efMd3%QDKC;P`z1$noOy`a>B+nElw>@;SsI)e_9}eK z%boMQU|CD_5($PL86<2Wk_{m1Q9e$awUEOx_(*3b8Hw-O;Hz4_uOHj=k}${Zuie=7 z!OLK$rQ(1N5;{fUi#q$YCi{9OzDQDMtgX2nV#tqthjZhOte19;GUJ3~aUqa`?=+Er zmaN~e=Df78nIZ*nF`X?NULcXnA&bk6=sSKXr}dYSlIJpn{f?V*M_uORwUQVd#`-!s z#JIX{D?jhwr$G5TuRnwVo{lyPQ7*9zv#$8t(Ic%_`#pMz%`|?@@e%R@&tgtmvN**h zCBvfpxTT@A*oHMuXb`VvA~NO4W+_Lj1^;;Fvi!2L;jPoQ)oYV$dzax5sLU!sD#6yD z=0GRT%mJb6(D7n@u{2+yxOnCp*<3HR$k~0j-SiB~Mr-N}T4`w~a()el2zgy+o;pYp z@3(<+&psI>Nn4hn9$2q6nPzdu8mvQRx~@_4askuD33e$ku> zPD0#47Vpyro@Ltwzxo%GRPtX_+m9DaTTbCiCHujW;|oDh(hrhV914%`0_vFtkUa$)#NV57l>5yzxTE=Nv4so@?A_cq8gR8AJU+z- zoZ$M{1oC~N;>|+^VHvrs-x2cbL6YGM;?mBW)v}+;M+D}7i2(ptQDxh;9-P)BP+CN@sV3t6in!1!Q{1&gFjGHky`RF>2N%_v21ZxQ(Jr9U^T95 zM=mFfrCv=zx%%dp?^KoG0M#`7Gts!lx9BmAb^$2{3mBHF{{9>1v(*rJ{A|iRqNp^UJV`HeW$%;#@*>pJP^_k{c2j86X#WCsfp-Yf4asA~b zY1>F!DTlnYG@B2L1iBStiY<^tNiF$!;jl544Wht@bg4ytM&y|U_qSzTr!bQEx+hg8 z+cikYNv{xJb-`;q=H9X$kwm_0>tSuWruOr>nm_3D7UEF`DOK8zM0g&qAzeSQccb(B z<`bq)DU|5-Twluq+5N7)Tuw5L5ytPRSzzo7_==cr=~`c7sZ+kB`tGCYz3Qi8 zDTZ$j8&LquTb|Bd?7Wx~kX{mtEb4gt^WNW)<$&PFb-7=85RLWS(+xSy#z(i4iqIL59TE011l77bik} zhsRu@7SxhoqO+ukVmGoPp`9+LktLREcw{L9C5@@Hq#hHK!H^`^?OtdwSxtwU&@>%i zy|G&HfY6OpL<$}e5d$Q=%63ko?qt)Iu9d{TahaHJhK%2@l!LIt$8A4(`)SVC&AmTV zZX@05H%fqKfJWEV${3p6k>)Lxx*86n?T$*&fmw-d8nr8!3h0c!D{gH-$M@<4)Dd7CoAUzlJk8#;|t0a6yy3ZBQ#+5K5ncDi< zYie|*9WB_L?a10KG~Wj}sJuQQ66EI(KHi>@-NaqAmR3;6t*&-QzF1}y(7NDF6R!*N z2JP07CoQ4(SK0WJ&k}!xQI&DvS6SXzk)q2GHkXRPIx2!78%H?@=F! zp!}WJuWuq|W(3QCp8kUM18MRT=ZHstcRvK~e=qr~&_q=Unu*CT)A{D_?G3jZiHOuQ ze-l-yB(%i)e^Z2^+%tdfZxU0!6*(OEcXp(I_j-3``1cIog|hxl!*@G}{+pWqe@#ta zFuL?!soLF-dK!iIl!yVQ>)Q9N>8Y_{c|`s;Z+&!=9ge!DW|t5Dc<509aBy)`Bb{YM z-9vxS@(slW_p^jpe4qjf=n+VT`o-r5>49%JiOWb_?ppx|VPd+GRi6fa?qgo~FgSR6 z;GSO*bH@Qt>eJ>E|Gi4=-4v4T@psp#uTYpDyv6C53PN!)B9zS}3BdKozMm0nvh@}P z^fQb`IYQ)-2ztO^YpLt2^znP=3R}kNnta4Lxu#>^8B`QD#sQ1ah@4v6V}B7Bm#+dK zs29Nz*-(V>LKXuTzw$Pa5ER?Qw%2$yRNs2Cgg-KFidNwx|EjcH)zqU#fGk4c8S3?? zH}}>kK7|Jsa=o~>mfozi`0kY)Fr@d36xLW0B16so+aa80?=Wb7ODYB1Bs^DjOx$Sw z+{OzJ#+^;v$2LrJ`?A8!n9*)669R$kdjFAsg2<2&8M>DmBR2N7Q&a4zKLK$lXaDnV zV0x6Qu=mH6fDfN3!w){O1O3Jp1=e%n0sUr3Eed4F0neQ)h)v3zd-U}7W==Ef9#VxU z@p2PU7Ti2y;=S&!*y)Uj{Z1zP7+K6(5(qYPbvHJrZy(C9&HYRuugee<9HaV^o(dxJ z7?`tXZF{a1D5Mk3K*^1Cpihy<3-0{5Mx?uh;+o-dP%|RsQ6^?kUb}HHo$F^663FS< zpFYdFCB9v6T(watCY1XYIQRX7#cahx{jqIK74Uv5-Y-Nwgq%NSeSi~aqWXwf9701t!g|?`{L@q0b!5cP+4T;5&6Hdd zMc~(<;F}*#_yUxUt7Z@UHczpzPpw}7ijw&KR%0U^#YM^9BFf>#Qze&QhJ-U-HP#sG zz~e;o-vyj`8m^8E1jt_mymfB?zV}~U@)Y~}sr3^|1upI9x7-_Xz{4f(QSRRXLTYJK z>NIP3=8wIO&-x7K`TarB?=LVW3pMYDVh_6MlzfkrT5zxdQBH&_c4u18@in(!B0QAIjwZc_8|a-le+d`w4GyN^q;h7yPJ;K98L^t^`@e}+ZV`b#;t zd!MY&FjP~)&U@U(FRwTcPOd5}#Mx4DE`z=-GCdXXGI~r%iTI9EVPvwHX^{&|WUlh7 z%y8hHo0}!nED4}}VtmS}@4)*%ZuklOJ}Y8PEs0OSJSG*XN3INKXPmxIY*W_%%G!3K%Qr5XCRi2M-UWqOs+V*k<(JyeSJDiHX77 z^TvJhT}mtlWeQcE^O3`gdlt>_A4)p<0FQVZH!B1Kt4LzzfCs0$xAu#UVABQQhF|}< zL1Zppk*NytxFZ*rox$tbG5Ji8CuE4_lT!`^-!!yV485m<>eYh>Oh8La_r+Xvfp;5A z4ZLnjpase|?_f0d#+$jaMl5@YEZ-{wHJ3mFjLcvw`V{x9G;ri2>vi+tbB**@cBDXCq|_7hBETG>hDT7sU!jnWihd-; zrv3#|S_!a^Cm?lwFwu-WV#NSR-!&ey=t-8~BW5oO9?1xl3WueM#oi1t;AfEPQqTcc zT>j-1f3Sb!$Y80Y*m4`79e>c%TgWnFk4uSu3;0URGASb{ezhzrCwYb`C$$XahlEcQ zs=m(oz@1UlOK>*fqFz&dhNCJvWdKh{DHZY&OZG-f=Ecez#2yyO{zV!#c z*%G$3a42~v^EK?rhKgIKot)SO?Qm7g>u!VxePPeXULGcZfv|dBnq%tUAAGwu^^nh+ z7x-bVrN0y30`20~R*Ci#1Cu@IQ0SODNP|N9so3^g`DdzMAdxxC);;V>j^Gpz}J&iuW0ns%iQ6W!tMKAMWA^iNh+R-?^Y==X4dwMWOc;f+Oc`J>dD zH9-c5E*z2x2YP@q*(7O(2nQ;#yn<;fACE+5TC|l)S2#K2+p^LF`xPfDxO{tHYtiIyoK71=`qYsqp$*YoMMi+hl z-Dt&pvNycqPncgGhUy5~^B=auAF*vrWbmj_fEh#Cb;z>?`mbW6!FYxO3R$>4@Z=KIY?)E8WlCVQ`nC;l=rmq z*~olpma5}>A{w(c2Ib3ZbrKkXyrDck{<1GL5Oqru&>Yg_l_@uXMc(Lj_;dwK&v7|} z%m)a{hCg|)B7arLQ6N`D=bZj4v3*QNB!o!3!d*Ran)}LokSpRG96>lTPa|axz0Ab0 ziYGg*m<(MUPaX-|^-h1!58amV`lHs9H9h+v9nGF{!u3|hg=k17HUPXk`|jt~J789x z7e%mp0sK}UVuT2JpoQMN&2Jj9+17?F7Y2`20fVuwDpx49xA>QFBotqMf49bu)sPhO zH2Ho-$S?{6wr~J7l$V3~(i=-%Lu0rqF2L`s_be*!=@_Ke;gZSJFf|X7weYZj1{pKoQnYhiv9D}=9!Pp~Xc zeD$k8w0BXUB&`XxU%r;BjVNI7p39%FNyUaTK=*?sulgSaVW*>-kGfo`lwp6|Gb-lg z6l7yC^wm}~q{?yfo$Nw3vk~3n=XFdqzj8$Bmp@l;dz$grSTZw{>8UFKa?Yczio8z> zFipu4Eyg3@18qUbR@Pth#aI(^|1B`85G&&g`och?BglT8Ph) z_@_oyTPFvbVYBBI-?#;J2azzGU4Fqzv5|3n{!4|9^#JdoKZGx?B*59lt-8u`^*0>P z`h6|gZ1s|?xiG)jQ{5lD_teEG3uDV%IURNF*Fb^EkJj4{8u(KKEl)NRqaVCqfvbd8 z*3}Hmmr+asi4q&oBt<&LhnPTMb$-#R^Q9XDLlhOk@1#XX&)QLQw6I!h9Jv;JhWxr1 zegB#l&j$)-EZ6SH?X0eDliVOPa8q=W!HA2E9mR53OZ{Ey=+dkzhWh$ynkQi-6ZykZ z@w(A}xYW}@ULH$5B%i6Oge7}z;)T^LLwG1=&!<4uim_~fw+YmK`jT%k(pPyiVL$Q% zxTELupy(-lSwsc}nD~HE@;VYs;Dc*^Pe5!Sc=t@T2vzzcnRk}IL5hw@%~{U99Frp> zU!hZHQ?{$J>`#=GFO}02_B3J~I)rVFp6gUy%vN*T87;__cAhP0!6D9Cb zRI*~Mq0v$iQ@SY!kBVN5men}Jn3Kn;e>`Nwa(Q~1N{66{JtFWvpg+4W1Y!$#@S0+R zb}Kkt{&cl6=ne)zb23mB$rL7yN$XD>uybh6c%H4F*NbuPN61%rcG#pVt5Q*Yj6Xw+&5wBoHXVVs_tm=q7Oa zV`;heD<)XADjii(Rt!B=Af*hA)Ln-dw7WDP%e?k$WOy)u4hL#eiha4oZyKCCu)3L5 zN4E{Ae)5b@NRrpPehSF9`a9PuOC(b_N$~2>kod1C=*R#v_b66uqpQprKXMj0K+;Is zmnGegJOu2+(q|hHg<0`q-c*e@fQnzc3H+1WY(xj;S-)RPXL(+?6M((0^8%FD)3)uU zg$@t3oUW}5&uKfAl5JNf>m~Q55?)Hhi7#ZynwXGklCTGKiP#8bS$JG)O_xgeqq$BC zob@T}Em3`1g$Y#V(Mt9TrG$`~6A^SU^AplhQ{xlCK~a57F?4X^gb$9361)ZYlh$<( zm2|Z7jmuxR{=~kIDLQU~^xFv}!bZ&Nz6`E^rs3+w&pidqR zx#(V#S5_YK<>TR!j?D4lZ?jN4yVwX3h{mD;XxiRd1ii6l0?g|22T=z6$>KVLNqSnv z7X|9K|6^+h5qQ5O*4gcAFZRknor@2`_I57aFj)HqGX?(C+pHx7?b>Ru{o*04 z+XlYVB>??(t2X)?f04ERA!M9Az(kcfEWK#&jb&@`rnn`aJQK7_K|VRb9_FnlM*d56 z1mA92ku=iQ5;?m09#L=dIybxg9kT$H82XfJAa=#%1oH#XU=_Vfdf#umSAzjn;u)y6 z+^(4KNd-8$V?ZBxs`M(74STbY&S6aC@ImyS&LeX#dn^GJDqf*4i~DFBFN^`nq>MQL ztX9R==VOuNa8j{!G}|9)oL~KF zfdT2Hxn1ExLK#d5D5!G=g8@S1b)Vj%pT~c~K0KSC0s4ngyQB|vuw=l~lBEN^Xtc>%IE8KzL3Q(E{4DvbI)2S5JnhCgEWYGZ@mbTH_dh@!6m$#UNp@rp~e&^0a;4%{>c zp~Bl(KXBgSU(owNH6$A(=NO)nB**IDeVXh1BDF4Di@Aq3zNuoMBhA=|ki&!)WhW&O zmj?%_36Rw=NIZGk;S4ETEu5s`qY4-wm=hfvQ#ZpAt$za;F+|YcE6HyNh5<5MX(x{9 ziNgQ7R2XP20S(I^Uyw^jv2cNOTs=`$;L+>a##%_8I4T6-<+9)QmK__e3JNQDU7rSecD&bmOaQ{P2Ljh6rZkX>dWybdCAY z;h0+eMS4yd9foAy3S@+tG_W&65#_(@R~y}=U#i?CGiRfF4LG8L3a7Q_B3}N-6o=N9txLE%fXOL0H|SXVRspE6wrE|#2736Hm$c9ow*L}? zvJc6Z$L?8;F5Kd&#qHACPI@+Vchm(sIz=xlb^4NuVg@HdLbF>te_S{^jR?hzHcT_#Y z!LoI=j3QF&>!T_lVxbq6 zrXpQHq!S=?q)YFh^xiuJL_nlV@4bYM^iHH1siB7+0qKMep>sd>Uh8+xI%n^5f9J0I z7qddXnfWF&?>z7GKJ(69U?{K+!#lw^w?lEGOXZ1o`!nq6l3p|4QWv^Pz-sq1_-Yb& zckDW^<%3u8Sur^azUD}-73&k#C$K+r{_eV&vFuPE%~LW`ffHE(bAJlJ_thkTZwCRu z7q0G}@#PkwDjhp25F!YSR5i_-TL(w;HURcc0Zx$g?a>W$efq@Rc@j3-KrHcFz9-25 zU^Y^ZYhJqG0I=wZ4odu3A6QC+C2ln@4{?B=|J#q&L2f!bn3H;C*(c{FEFkVUaRO})Qg)8uH8;w>37GQzna`w27v40PT7<&pmE zKwTOo-9UWn@+Z{e)s8dIT|d}3t!F0&k4rbfc zS)#OEGv?qiz!-%GxE+L^WRv611nQD2=?9Wqm*cbjmx_Qk!X6@D22^x%0yqiqHkN~rU`nQs{2owNXe<9aID)BWMXL~p%&@A1qY z++8nfGC$sVw(nkl9xu;sdIeP!>Mg)Scs|GfuaHR$H#70;Kj2)ro8!#S*JC|(Tb7Cs zKliZ8$7_tiAcFAJsPJz&vcQ~y8Vu6_cx;rs_K%gFWyk*o9{bOge=OKNLYmRnQ*9Vl z@nJG_sq63{(1&f!Fh|>x{_Zv?swhfbUwd_7Qg6}oBD~LbL!~G_t&P4sLNo3=jm)$` z8cWDBp90{hW`6x2;AgY9`on!D1#^lPKd1coEVqwlCRL(y6C&c-eXei0;rv&pM z(Dx)hDEb{GkL@zxX=fL-66_>=Uc-{_7H54ogM~T){Didd42<}r_VPz;r>n-uHUP5q zi~kP@4)`A%0O)SNT8706f#NyE)YFv9R&kvxkr4;nbOPP~Bx#yoV6~n&IA>`SEsjHi z2ygbg#y1<=@?U;eV>{KQ+Dev+qM+oW+j&z+p#?+C#;(HVJnV zA=>0ZGH;?0n$vnbp!53MtrWU@b_SLm{{{2?%K`aM@X!pPq-rbNbZ|8b^W-C<0UFI9 z!kK_7t@fw3<1c`P%17G_c&yr_zq55;E1!{~H=YsZdIYzb=IOc(uZZ27dyry~jb*0_ zV6-&H2LbrC7R?z;;R?k&yD3J_6Iv!hbE{(yJkru~qnD4jtnJnH z2T5N2`9}037fXoNG7S#_Jkd#ytvg&#x)b?tVJ6TF+e2#0nku~quGw}f>;Gu5Z2j|v z5=A9*9p$~j1MQTkC=xMLu|vG0?ZIHMC%bx$yn-+3O2w2FulE#uIR~r12M0b73oHPl zkCxgiU3j!VvXs6G?f{3`|1>+yF z;Zd(2R>=e4-$8aA0J;X^w~oa`J;T<=g=%8-gA&5)kK_<2%Zm~Xe%|7bMOtIdvEymY zTY5=kA2jNkh$37`9_Ia|H>#w|)crX?Oowf0b}GPS^-8G=V5N@)Byj&NVs`&+Z(G82 zCPfx+q`uL(7U*G4nzGat13<0Cv!C4M!(rb}L{T&V0zaE#Ce|PiAEpkJ61cxk zg7G7s1^YZ+|2g$#IDj+HG5Rj`Q$0e#x8|ffoWLRqd1TnycUfz@I3(Q*Mvz7KEMS+d z-$C@AR6J7RpsxL>kLBoKI>0n%B8vNqJm`O@D;vw?kvQ(Jh)&_JWYqjkyQH)}_cg~B zr~U?Jk?o)HQ?-cxBnP-yy=0=`2e)D47ot@5K&U5xZQz?Y_Im&&5x&50zyXL;Wp#Npuql8CgbvFrzPF^G72I(wU{_&S@+)S7}4k~F-( z_u0nDu>~zFU(dXnX>#J5827~Pk7RhZKVG5iGSg5R0kb>E`F*V-Hg#bvAcOCgq-W(4&@nZ)xRhF`P^xs5bhA#Gu>SSrbN zroDN1c_!=i%(vByccvf}g96l!W%~0LJJDNj#`Z%b7Q1+s5@)wj-`?b>U$iActnzU|K)fIa=HX!~;Rm@`o}2Z#xs{Sk}-n^&ZcOMGe6T$IaIO6o5&!4Wz-NJcdY{ni?FsLifu}UvcU=i z<3)Tc#I3Lu1HYM59`m1sDO*jv`8xD8UOdesek>h~8Ow1-U_Q19{u*1)DUR3sprmF_ zuDY@)k{i_cK+xrV$#&M@ywzpYkaY?XiZ9@g*UqUDYTt*Xd+;U{NuxMDpDIV5B~+Uf z=8Wk-{`UL9IZ};Lwc1fcHE}8TU8*+vph*&H)cLBSEou_J4#9Jz>8j5<{c`qVJnE=pz9tkg2#;!a zcrcjOJV#Bz`ht)N&_R6o!oPrz{VMO3<)5?pVq`J3sgvK0Ck|Ui-H-t{7xEk*UYoC0 zLHkF<{8T%)X_q}m1MJ|mt5%t8WpUaw--|qk&d;8x?T!hKZfjBW;dAeBQ$ltLXX@NI z%Fpad1$D{-KcfH>1RGftr6gIO705J%RX&m0Q)|{9dX_1N%!-p;n{UX(k8jSzI}PyN zTt*kmikF*cggRO`Z^uN|x{FfJw1EY{lYUchx|<&eU+3H!s+}3eq(f7sr;>!t>;UhwfV1YcNH(t+{ZZMU+jPzca4Aq&8wMkg_~#(_Q5%ajGNzY!@^wJ#5YA+ z*%X^h5`iT9^5=g$1QvRYDX}ksc;0CwKJE-4yZA_FlFKCa^5=YkLzv%~2mohwDnv@H zGDMWw$If_GoY<;dhQ@R{1rc5 zy;o#9Zw5YMN20&mm)Z0(#M>hb%JGB>Iu~j}&xHM!%;3Y@bEP0!CU&2FZtD>N3Be~` z@aL}kDx<|#`$!whgL2g9(lZ-oPT{Dph9k4ob#|!L(c)0pL&z=)HCm=c{yPe#(m+5A za~p(r#b!9oR*e>K%xbNU;d%@fwkEU`js2uLQd<1rwpQnqK7lizW6=I{s#Y~m%K|%$tfcd+0M(5j{k990L zpPvTMZiK`AbE~gHEZ0{c`D!JxiRVZK(Af2QVy(@gVT$>u9T96J;+WX?&zW$MlQ{RF zmra$RY+0bFDA2&?y~U4d#L1polseV8H)%3m6?#%xzOb}WwcPcNsyZcEjNyW~i0Dn0 zqr!8RrB;3(qX-%@F^vAm0AoUW+0$k{eg%~VvWb-lM>C$Wr@K>fZS3whRZyRq-L@45w$*ba}>@`UBv=0r} z6?oG(r3qym;^1C;eo}D8t=Ag&2Sbim>}0GNrY*Qw(hfwm6aJP)%R^{@1*!u;0v*+F z7J&F8Afr6Ix){&(xBIbT3C5~CT7XVMkf*9<#HyhzhSuw#NO2C{3rSw+4`wWohcfJ5 zEzHv4Lf=CfXwEhUK*-U!xcdHRC8L%Wo*u~-k$cqa(#SOe73DTFHps<=$R=uVMc=KD zQ`S`<8ZTk%NbCgfy=LTyc~JU@;IzTxHUbDgtmsP~@Uhak)+Dd`^fgo7RIkCcCS;XS;Zyi0yp@I+*}|jdO3u>X=vz!qKhkv3X`WS!-%^!`t3OD z3{Wu{O!TZV=CJzrJ6*JdKW_DK;*Xrra-#CM`U}KP8Clmy9NafG3Du`Oxse~pxscxO zA66*rz_14OsES&bWtvpb21iq6|3~DHKXTrEl_~CpCAo67xGL52i|zppX&wrBhETWf zd3@9?OCZ!V7>{^*5qEj>-K)#moAjoR^Y#dMEY=ZjX7(Z7M1Q-XtytVpw3OEWxS zAgDBlsZ=viLsX5CaD?v1#HX+KaS${J?N>}1_ccFEed`8&+;FfqpMtwy0zFi`y0Ecp zO2{YTdyKcfDS9M%d2s#i{IV*`fWEYiEtdB0)A_?>$c}~ymkUh8&GWCRU)56*#+~qP zUgZ(z%}6ZE>9XTm2?hEH9PBdU;qiN}AIa#KLO21-84(#$h8y`g5#Mt%*nibNm$?Cv zt2%OHq=s%*UL#3sTjLzN$z%VPB2vh*fBl)Foe?;urmm$qQp|S*Z3Bf{g`U2reDpvY z;DWU2zn;1SZvE6q2~(WLJzc>)C7BJ{wvDrL^~Oros6uWr-@RUZ86l~3Z&^EH*#?8T zh!Zlc(J1jNjBZl&@9!ZCFGTLox0i}XZ67V?ULABP_XNmpfFh$K32Bq1!>rc7ol->` zr76}(fp;!p^5amrHx?nFekb@1n%v1r_Dt%Ej{Z2-QqujSYAo}-XU$Ua(hSREvY9#) zAKGjdyoDpKs+NQ_((Qk}v90c&UB3**IHn(M&n%9_D-_gOzS82ms*{+empS79Q5vM0 zhE(!`6UfVCj7L@YdSO$ulzt~rgs0C&uC~^6n^f3(ywY}}y1V`-d)YSX9+IB$4Ygw* z3J*Ghwh7Wo& zCpqS%q4PrDHE)`O>#yyu3hD(3v>R~m{vXD8JE)7`hk2s8l^cwFRFIuv=9pj8p!{Hy zFs6qOmy})!PRFwUZf9fAwYQ7%ui(w6kfN#N2b@m~&|F_YAIe;RJ3-El(6n`V*w?jF zJnxj?+uF}~fY5*^{wciDidiOP=GAH}*vIqv{@ekdNJDT*>%py68+uJRaPo0q%SJyB z8|gU-VXy)A(ruT@8CCP0hEtt18M&t*r`K+@3tu%aYeSWuBBM{ee6KB7$9q4`cVK@h zz*+ylOf**!_^HOO^R={sg2~ zlJF1L&9AJ%FVd<7^T!%)__C~h=itSN05=Q3#n}`=@OK?B6#(+UJJdy6fojO|qYT|o z`3i;wJD_r*Ig8>5xypRZ-{a|O@bQk;LD9?~kzg60nKqc5pgc4DruOe=ee@)+;k6&^ zBh1mfB!|`ztBz;2Rc>hVULF z^`=>tqwNv}a>O{OFiaw$cF(`ma_b<&_%GCRroHg{ke4G<71wz4nb&5QD^jh-hPvSB zRdw7{y+*LN3L*Iy0He7`2jCAlJw$3h$-$!0oExaitUQ}oVhEE_S0&#$T0++*vO_g* z8?*~D1l3e#C)by==M(H+M^(=hgyNUZk2|PaqzqKjzR_ZYw%}8*FX3H?-nxl7w;?Vb zO~}T_x~I}9u#;yDUwLU3I70nyT9WMr!D@b2U1g~jl4H$t5;Rku51Wy@cB~lT3qv&g z@#g#T>gWBqo-6&Y@RvWOp6Ro3narthcsmCAzFni%JLtFS%d^`k-w3X?YFQJQ$HjIk zXivV+8UDU6TvTFu;P%H~Gc0&uzS}SruyNJWo@dLc0Rl%enlTJ>uDRwZ)1*sWF2<(d zpaY)CEmA{Y))MXretabHXwd)>=}bWVl*l<)0(X@^*rx33aKP^9|+pr)^a}D;m{vm zY%|O6y>X)HitN{8rlU{YY0PixjxRWGbbX)2;HrR;G+xW^DLs}^4I{=v>@g{vNo8JT zr4e94+=VVXn4$@J4(jWG^JP5y@Ep5TK8DOq`+;T3g047l{Zh=%g{$-N;K|PV`P+ms zt3(>9K2peKKkZaWM$@H05_L2Xmj0{>B>T(*f?!r#4^bRswXu~WRWwtcy2?b)|2H=Yk0u-?bA^YOpJnB%1P09 zJ*iiI#oN2obOvz9sP1WnZe7{9f8*a>{$Y2^+X$n>RCVb7RN@a9Zh0Nuq1lL@y|Y`n z6wfP_VPckL2+He5F{WFe8QlV&{{8X|f8{HM4BUoX_bc5Gi<51}CF2SC_Tc#CQwnrI z>ix+atns^qKa~DthBn=TV(;rjDon@r_inm(=sC0(3^6td+0*uslMleDgW}jh%=GnX|EJ_nHp%^sS zK8p$7LRO;8F;6q!H$!6lPGmRKGY@!#zg!cOo`!71HQu1 zvEIgwitWe3jK#(|if&pN7o%|;<;PF}FEeRH>_A3&uDT;AIBR`Ry!%M#l&si*c>ZsG z7qIIeg_gYWOkmwA(@Z%t1YFLOjYD1Du<0-Qy!PfvXn?)>P8)Qx`BWLzHY#r+0JLhF%^F@kZm&ecvGv)Rz95LrY8{qb@)A%fScqcQaft z-kk3u9J>h#rY_{W`xb+tg(lEKz_U1a)Qq`=26^4s&#iiA+fUcJyB6jLy%(T5OuTF+ zm~Ajfr%x})-6&jZ7^cM370{rUd(sbxI;Tq#TyqbUP{{fN#~s$k7uyoqfZT`+Bcz$- zGkp)(D#sHAkH22mdRTClpPO-Icws(U^fn1M<@Qn^~ z*ya$lv*m{LkUBaj%rf7#pIzB3Z9Dt&hO<)z+m7Q6bPLg+-@qXD+kLfa~sE&E6F0tMfVw<<( zBgmhC+2@om$%iKzy!)%}iVDSz5A0bPZ3BTTikh{sX}zz%+b z`tT(}^Fz4OGxh40h(_T*M3=hXY}zNkoQQ@;Ku#opkTk@)=7#!qKV&ZWo7exkKEGMSXPMJuhIaTI*JQXJ9_p=B z34@YZ!!INmw33+<_g)QuK>uT#M6~rmh~ju%UV0ri{s zUqSfWYS1fYJirv3syS@CKi>Lji(*c?Dv6odO&_=2Y0JgAI<=-KA-ZWhauR_Y&Ke3X zS|Z!v5ndG-<6D95%RY4{A%pblJ;B{Isd$8jZ%lFO(6i$F zFQ_H@VY8yLDL@`{i$tpDSu*S2zC~pB8ww>@v)HU(fh>Ao?hBMxNa&3ffpAB7IZ17I z#|)yhV34B*kb&VdUs>g*t1aCA6%G(r!P;xJxCl?qhwr%)sNE}K14_X9lle#daszKM z^yl`pbn?ZqUDn@jIqN{e^3*4(dvX9E;crIGjVMOU8X;S5c)DQD3t)XFmaL;c@T*fP>K1gR>F%=e*KZ+)u zXi*bNpw6tePjYmBrwgn-f9-9>?3{ntT_Ljc5f=w!j-BOc0%Sb|d6Qpv=+ZlIMXvW9 z{P4N2lQIAb1<)uBInEH7m%nY}#1(sBC1EU_Y>ozc%k4#g5Oc@s0b5Wh{?zL57XzDi zi>7QW^vNB;y)st!1?vaY0Bio~m-H3*a`6x+I!n}M)vf>_U^yq?-5b(jYr$g<)bG=- zXI_H8twJVpQA{<8EE z9D3#e=Gu!fe>A+IzHs^w6FQQV+r#a?U+calb{6f0I&)^1Vxs#=#rsoh8KmOvO!}nR zyI>Dhl*1}xJ{-AbF><)OHpU1h9Z}oe$yUy?QdSO+R)mq`s|pyaLo4i-*DHAnju5|Q zblPU>EftofEgmoyiqF(u5f*Se9s8f4i8;XJD?|mp$3$L8EOyqLF5;(mBc}2@95x0b zpTBhTfSNNt#HHIxaJuRgIzQ~6aPG?rSo2`aWZ$WDm<-L?X=qgLB;cqq(tBa}E=di{ zsX+z%u~7ZSh#!1KzsyZs$(w8Zidi>;O6u>q+(i;5X|#}jw&z2oV(3`8kh&BK)(%{2 z4*sLW*P^GkDVWbQ=xrvVLFL?5cIt9&J+0z1{Rk=tuc-6?+q~>kSqrW|F$?oZ_Pz~H zo`hRnoqqXBe+k49$grt{jM|>KM^TF7N+Z45NkCMC>3g_jwlnhcAq_D~iY?vm6}cEL z*^PcUr5F8*$rT1tcHH@E$Ax~Z4D#sIfLL`xXrtBF3yERpzdZCitiyDuLvT*eb+5T0SMes|cDs*yw}uRnFRL=JWZnr z+NEN36d7poA_8}CgzFQ3U^j|fXNRBp$knc*Un$*?&p;%f0p5%sw(_09mc%w z`B%a3D^l0dC|~NA-$h_CPRqt;KqWm@Ids87Fqq~&O*fH=>%p4v6Ro~ua3y{ z!P|UgY-Uo8*kvr1&?{Sg-=ZPsU#SGWMBhOe((^Y>Zsidrk*Z_WzP>ive9r$oHCto< zSrH>C10SAnh7RP$n+Q5$vEoVEgy*30&=BFM9jMImbv z5dS__sQ2R4_*H=5zDS zT;;BlA`v(siB|Yy1IXW0s@6o-v%Arv>vo^kf2MtdH0a>MU}k;~Hz|#N7X-7`g3j7x ztHxC0PT#Z4Xklh{sF6r_TZN#Iu~_1evA@xYCrPL6iLCqMuxCr*g$#Hx2lB6v{mR|0 zmzY=>ejO#rTslUrVX>v=w83&_vD$oSg5wulDkmW^DHm+?kQF|ih)m!2b3W9hbb9By z!!)P4#wf^>;$X+oyLwc*KOEXH?9W{P+#=Xko3OS9N>yBMjF5Go?iDy@#8^^?_6l_S zR_ColLrNo6D$cI*yKl`X(E;L}*RWwRw2bV+H&-J`i4ozhH0Tx|HzJ4zHg0uOJbxI?6-VQub)1EI^zcaacH=XcEP&m zm%6L2Y4<* zyW#u&j+skJ4!s6VYVlr?5za^JL&oaSa-41%?iwX;IFR!ApI!pzz4z>1{8Mvo_nz9) zU~9q_BEZeO*q)xsKcE20%kO|~kB6LyDq?R92kZ(02NG_jN`oi^u}i6#N_yD)paPk( zr3v~2*36#LI*^+$HG%+fEy&~Ky&01{zCp{xvpbwm1t(ZBryV$ZdPX*~NbqbxsA&02 z^d)sB?8!9E`uri&(NU&P-A?$xoCEU)E9~N-T)ajW%#y&@xaxT5>!>*Y#^)#aY*WoM zNMCt;9FPgapL3 zUk&^5nrTR3@v-g5k;c1=%h_uA`*rK1@l(CMKGS<4HlF;uS5x@6rxV*V{s&Qs4(?fS z0!@_S_CR-ym;TY!cb9nc;*Oath3)R=0s9TY9aZuL6< zLX7aQh3WCY*;O?Yo7~;;1_ujC`9SX18f|^z|XbuSrx|%B)|ro-uAn( zZDP5v*##Wa{y1+u9N=c2LV6d4~jsB~yt80t?*p1H& z=;zDQu+YHMi;mc}ALr$Uzx?}({&n@(=So)%-{UTn29>06;S7Q2Jron7&=fBIV7tJM zA(6shwyOsEsWSxocvKKjeDL3j+emst1wISA)TR%tSO-}50*HU|4ElfhxCUngnIRVTZBM!%wvWTfV55=CH?#5|2jJkf7EB-4Auj9j97r!|0=i7KfWxU zc?W2CLGt&D|9!*Jz@zCFF7Z$QHyMTge`)oy*2~qE+rV#HW4uJeCZ*GBO;zaWQ{KAw=cF!V8DD9jU5+U@OvkFh~ zDLCdn!E3Z{i-2*%#0aY^@k9TIZ?P;dyM=ZgrX z_r;?swz1|XKJ-f>Cn6HV%pbmm2)PNiU9Da$YQKiBKC|nI;e))L&80I+-RldO&wR2O z-tLj*3dw-)+oM9;paypx`l6Tr$WaMixx;}X?HEcPWZRi{jiD*TJV8< zm|8xhzh4&ouz~FOABjHa-F_XSP1M6J|EF5MGS_9OT~r#;JCqYe-TVMv_|lw!px4tP z%S|M*$yopW>XVde+`BFHB%0-c?maA3!{O1*+gXauZlJJhkP~r1L17cggJ2*X+OFTX zUj~pskg_(X@@LT1Z7#M$+;7Q^D91%XtvMOPmsFyc2I69%>s8^o=tDO)E*7=jg)38( zvaT-r7_px0_;`%~+!$Tzv^UHt??q+ou_v$;f?9Eqw_(nbTd-p{Yx&-L3~7 zf#wsmi87n1Gua<6g;G4ywyxMCv$*b4^98x1S+Y@n7ZZq@KR-nAPR9_R>tB7?44|bQ zsAt-n3vzh4fMm{#o!X!NSu}9{)dwS+CJQeS9>{;TCHL_%v%03HyW~)G-2ITx2AhXx6p^_lC7XXl#aQZJ|FAxmTk`&5lgKt0 zEXryDi!fCO4F>O{oT{eJ(Ss=x8Av--OpRtDXN&tUzHXPN_|MEVDl zsuJKp9>W}B(D+R5hecx^T3{$j;dFkEct2^}ygI9j*-5f|s(XrP8s@zT>TG7CSXfv# z@|1-_zO?S7rWHG&@-{YY*yG?0c4JETl+(3VI=3+OAST=!LfP(J9tSdK$JM zqawul4rS`WHR#{BEnpY!Anrt!%=A*Z1Iz9^({1Hp)}91~MhOM!0=;##F`r<+56DW671 zbkR}%DD+f+5rfTkC^D1#jkWkavmS=9>`bb$0-SB zu+uI-f7$nYq0UZ%b%W_*u39;OX|Pu9p~66t%Q#3 zFZItUj3}Y!ThcNqZz3*q5xp*yEEKR(ED%IMZVq94*b+Fdbgp`5VJL#wR&|D*MctM>k@qXiE-SF*X9K} z-1+9ZBV>2j%eXeGJBT5kwW0<{)s4F^j0j#cQb;lo)SS%JGV+sIH)Wf5hb!j$U8{`# zaL($%OC9+QW=#r@d9C!_4zWc4Xazz|3o<)^#>dCMxq+PQKR!nsDh~r$8XXDAvF<4g zos{|f{bY{mibjR4^=PfAc=PTjl&OI1R{fUX0yS3Op9*so%RbC-8)E*7A?!Pjt89Sd zQ%SC@tZ-W$q#5U@{Bj)$PExg9=GUS46}~q0aQ|_6LvoYR;zpB_$cDO?a;?+YxnS=; z*Fimcw#Y0zvGeW^9yHOAf3%1bID_k4Tp$+@B{OuoDxZ2jLh4nf3zE~z_b@Vd_19&T zm|BecJXse(r`LwdH;NbS=d#gb!o*dRvaZIQJB=F5KOAaHW``a4o>BdF`+DJ2tyiB* z&Q{%XyyfIo*)dj><$SO~N=d1VOOV*-2jzgJGW3%Scq8y8SF4&Dqo*bO!?sE>#_B`5 z_HGw-y^dCBNQ&%@Vhv`2PqNf8^%^N5XykIGL1qT$90pY0CjYB>78 z3F#t#6IqPl+z6`KQy!amLJjpOe8KPo^^lwP6?+wenLZJXc5@^8b4eqgE3?##F--o{ z_(i+t`u9a{_kp@45#eAKR#vO)PSi&5zHO=dL8=3?#^UFb~trgO=BLL zL@K6S;;|XE$7lL1GB*+Ue~}Tb%zBw@7p#(2G5%z?{>xVEd-HZMOx|8*;S&~mq^-{v)8lI^)D%SLch0@x8;ZR}k#C!$H8+zI zEU=`wIvQ75t*qMC)M`&)KnC2BTL=uk~NhMtJQk?;{lRM`MZ49EX8)9Zvq58%Zn_o-NqJCR>MNhrswahjVp4)xCXoE7jD>ml-~2MSY`&OssWp+roLfjU-{bD>Yml8alYLZAd6an&nki;JHf9g!ef)K5MazJ z7LgBcX1}Zs>dG-fBO(fS_^s_*xdfPHt0T$O>JE&@Gm{12CecEQ+rL|QJh-k-E;vSe z_X_rJ640qac-P~P?lRVq=tm<2E_`RI-MGqiTIAecLG~8b#?{yN_!bF=yjxdRLG$So z=ssN9c2=q#;$XT9x_6gRvpXal2C-HLJ`TNhc^91RsAVF@^P?Sk|A8CDOa$ar8FujJ zxJ_rk)Q|cZ2&@X8U+k`oc0_|$J9`VPn{Ahz+So705$3?8)^nX7@^{7)w$;UsTM zV{#;vi0yt>9xOzLtkAmjOdMQt1?PRM_B}*vjTUQ8|Ed%T5ODE#1zo-cwcp=2HYM3r zkDRgMvJ*B6ITL95@k3MyeXcYc4zuPv8VJEHaLUM{B-I7X?~|-tvNj2QD&Z z8e~p_bJBacllTsl^GO@?SqJ;t14P5ki8{9yTo$t^ zofZWZGQ|WJKT&DqvT6{xl;dw*B(#5l(eTgp-trq33Jwdnfg4tx-K-(`+vMX+XhQMe za`Zu9n3^)p;@#1r^oM*kSGwNl_y&ed-)-;#PH)tu-NAHQpeMfn{+X6AcQ@vPE;>*~ z>_?95M@;6ceJ6z0yL9Tk_RM{7lYJqrL`IP+MQx_ZbU^%t!Axj#*-`O=<2>8JOw#Mj zmtHEH4d?CJBdW_&+WTMr$m}aL27i4b5YT9+LSBkF=yQ^WSwsrzos=HHmF6(hlD_JF zsfz@}9y}jmnRxz>AFM3%eDH>id;)c3-EsY^HuT0Y1%^)q; z@sd6sYN@tceO~TylRL4>;`O)PwEad+r0>tknZ>@n{eCRC2TsX(KxaqK0hrv^7gaa9 z&Dzt!s29Y;n-&k;^*)UkvbsskYwp!etV1;3?nXxJpw9d*$JK_i3;M6)AtjfYrk95# z1v~?P>)K~?UuZs(d0pU^@y=y8@4o4{baGR)W<`@`<`dFE?B&$T`~pm7^ww$5O|w3J z)KAQL=n{1Y`ldYuNQX}6l)Mo)*#djmO_R>I>&IIc*$JUuU}Sj1FXvk8Eakbk*wQ2A z9V)bRo3UQ$aXbDRzI=PHIkfPmeewJ}6xM9QVQp9e+cDRfh8uXKTy|z*_O#NYR?=^k4|LM!bP`}LA#+*=25d6hAEZsaj}scY zDHtsJiIh#TFY&DIDU~4FA9Kn^u%9HX|1eSXKOfOL*c#WfZ&8wlT!T3tG3L`aw~70n z39kgoc6ym*_>iXIEi;C_G@0$)J31;f9F6`ycA4M!?hA0E#pA8z&AGpJZ9PSHjXN`G-N%iw zzoJ@#jL3mMhxy+yC|p3#mQB|FaupE7^xo(WGEr;xOS5>z^{dF_ty;S|8`H0j04iL-9A4E=3dfH$ozm0z(-+N=(8J&uNw;0GF!``~r=6CyPn@b_ zgfU4L`O6xKr|&m1_`u(f-TH8MS*9_k6D=HLE#Tw#*p$uKwH0R4m_G}l&o_c(l8T9k zY_%d=)FT`XUJ+ygmnQ5GOvE3Z`rU+8Q81@1tt2>pA2|&oqOCniDgslA?fw=Qm)qNt@ZAY~q|nkg-WXnKUDLIw(#p1hdA?tW4GII#XOOew8&ee3EV>s; z%dk_k98CTD>a3Z=vJLAh%OQ_mJ`W|72XZFVNxhoX&MmNHmKzPK zFU}y=F|Cq(fcI+ma@E{^$i*3v(bRu<$x`L>UPeOjn;;+*ApGpV1Mcdx6cVt`hV+f&@c&=LX67DW+ zcZS$1)d$+qwBfEN)5ADPW}_@}W~#JDttuWj*1&+i%nz>;O+G?+Sz$jf$VdkJw__dl zv2v{39ju?lt!tP(Sw&%(*J>^@4?Oi+T1mP=2fD$H-v#Z)m0TZPZSa*RFf@hU46(f)ZPIM85?8KBeyp)= zK#NF0|NUW6mi{+mLBQk}w+-64s?K~q?RtTGw?`pEa5^Kp#<^fF;@OyikpU$1O%i>Q z>|$%OZz>rLav3kkbkFoXCn~i5wJ&hL#-W^|x8dZ+HXH}qOmd>v8Kijy*dF$%!+CNy z!`}CgSX)^O=4_m1j*(Xj;LRvMM;ErzAHr6nvuB(v^Hdm2m1d3^!5RlcMnQy4@C-9% zRO-g`#Aqi9B=e@(IK4psScZ4baru%*(YurbO^ps@R5|11j#?zcy9DAb-|G!Er;mXS z*bj6Oyvdo0PYh33d+s1~u(w=>bLCe4Ws{`>*4jFsE3_dOv*OU?6!fSI%z+~TyUK*E z_TNaD>>MWPUmjn*f2qyRpjhQ5c1?ehuiX^&EKu%ij~7+IccD0Z`Xk8YVHSsUKiC8= zf_!so{4O-fUoM6(Eb?Znryz)IOqxeN;z~uK9uE?}tKiKFtOQ56E>59=UvP++? z{Z?*E`MV#aY25fsW>ot9#1AGedK)eT>!$QIY#};P+xd;%Co18+N)Msh)YPmnrgYW$ zHcX^*OOM;7`nNRa zLCb2MnGOQPoNp6Q?`b9GT-C&LqafcPbuCQQ&qx>xiA7f}5L?q3|FsNOvq!zCA3}n} z558TJx-7$Qg#Gmn2Z5{Rr2(#OAlX&mK*QEAA3l9RWQWDb#4BaGRP8RxTU?ecvkJ+q zDU3Njje%3y0@s?EyU$(2&5;Iv|4)108P!zQ?yraf0?G^m76he=2nYyBXF#QfPLL8A zIsp=-2vPzvf*@7t5UTVNdJ<}|&_eGe2}Ei{nv{eR%8m2B^R9J2+!uU+Ri-c zoT0F0B^H+0c~?`+g)>txIS@S`CE7H)h~D4Ij9;VB@lOA?XfV@i_agjqguOpK@Tp4I zX`Lyo_4%CLQk_q?#v4}ObCt#}_%hPv8P~vdT}1MyvW5D?rYmSYSE~WL1F^bnFYLC| z3taO?2GF6KWs5e(%?DFMJX#LQRQq`~947;hdQzM>>yf2?fav*_udB;oGv~rIHzEdB zhws28ruq3i6_I=;vS(@UMH~x1z`58fz|k{Bx{rs=fzZN zGP_Gbt+h6FUfV5qqPfZh@~VwTp|i(gi`Z?~sI!?;Xkp#U7CD$mplYo9=vi%=%&+Rx zzII@lJ5KVtC#1mpIpmqGc z0D#^p34uukIzX#!YA)BZWZ{`u9w$&s72SLU45!>QT;gG%6kQ583g+wvj z>0;z;4v=tou^CnMja0DA#@s#+xQ+jNhYw$!@%Bqh3d-xfQK6Uc%eG{ z<#R*;y*fE~8z3q5*d3NGURSdub&L>PNL7*$Qd~N6oE+ZT*4>WvBw1cXFr6p|H^nC= zI#{^2cQ5y>dfuKZx27A1iUL8%#Aux+aoqvYg9f2qkF~TAq(lC@qwKT`-owZ9(F8hU zF|Vjtx5H-2inveA@iMb=FLIj(vN=B8d9r*@;`Ad*i8Zj-i*hD+G8d8WloEE~*`!fr zh+>Y9%@pWW$}@#Yo5*X4;>CJh%bFr&H7Qo|^Ef+`N8(tYGQD)y@`h{DP7T8K#U3f6 z^ge$0X$-7=vk!ruaZsov3=@JDwA{c8Y{gT%*~oyc^-8y9vkRE#U`Oj_Ux4rU$T4(0 zv%?c^<+QE+)HaOIx*a57-QRg-`h{D;NN?9j+^U!4KAiqh!w<422KB9Ix|vaF)I3V}APSnesTpWVD64thx| zyyuk9Dkn)IZ^CU@MR(qa;ynNp6Y4J>7lK`ONy07n2V8P2*OfQWVu>PSi?a>jCuOUS zS(`Ooyfvcosgje=V0CpE;f|uzwX!N7t6QvK*2*%LzLR}^uFUTaA{tmkE2U|pBqZ-B zTLF@RWI;vAlXuy-2erN&FT^`HA5t{jw3ojyMI#E+;^NTlhvtTbV< zJBO>><(j!hI3}!lauaCn**4DP1mJevdgA^b&YKsV^$IijP6@cu?60DiYt-XaxUQSF zXvYg!FB^qbEX&oJa$uG^MBFQ+0c`%}uG#JNqqqG(w<-FwVA-_WYDgARrr$ty&LNB6 zx5ZFXBan!Kz0$#1fARpL%)wkwCD!p(+cJH^3k$6a1}fKfvh>mwuoZO_g@FVV5Yoe8 z+^O}ZeCL@JWPx*x+nbQLk#esP_+d)GANH9!+WegmQSOUn+bHL1LikbI*d7KAp4HV; z&{(iM__(HBlBIJ93ntW6UPdomQY2BIXK)+w9;Q3z=madaMA2us&n$ynxcS92A~1Px z*-uyQaYG!$pxUyDct_IA(W+E@tyd9K+i@_acr~YIVJ5%R;V`~&Yebj*^{nY$IT7DV zOGK6lJumr&$0~P=|Au59~IQzO^CJ#LE1>4f8eu;JsIffRm3}LJ1s4lf>@+9ggi@sK&%aQ;w8W>c%`nY^>=|=3J2p1Z58H>-(LT>xUlMAeRm*&2U4GopHUA089qORW zkpRQl5pO*hB=usSyls>OS@~s=w}IjIodx#2K7t2x3%HcH@oVW&-A5M-mJH%ghd1J9 z@GPxsck{(Gfv6}ZhPBgZIu-9!+3GBEFp0&d=P~YsOI?3c^V{9{3i+T$2HvKwOfO?z z>BSLI1m6fN@G7yLJ7{PQxUvGKVZyws&=k|sdVlq8bGWK9MYC>4zq#>jvGuo3ZWlCP zf_7W;2G1NNfwKI$SfaLDU=(QuA)njnwKtH8SKTVXcRHUQU-Fu>@!2}V+xKhNH1Mqs z`#9WVcE-JBYp?HmmLmS;&BicQ07VpvCIq~mCKMsyv~3jKQ_Ii1T&{_K;=xL|CUKT4<*fwA_uu#x)%*#P!cG^8%nrDw z#Z**l23e3Pa0gBboU*y|Sf|kV&@}iGuUBm2#-S+aVR1I0%SFOPM7DK*p5AZ$heo^% zPGMdVh_#I!Aof@tF(X-}UW9DFb-pSxGy^rCb#eTH70!k(NOy4ueLG3t(kC(|&(xo) zxAFS#+BW~{;^Dn+CI?5K?7Ufl3hx%MEpxzkV-!Z&Qx*L(-B-SRair~uyQ7|6*)A$a zKhAmgth7nBwNm^hqLyn0#%h!HdXl^5XXC1_(#0C5Smnds_woCEyRC`C^uf%G?jt(o zewLK3o5%oVhFe3*%Y1pcG=E?2Td9Oh%0dQXVH40nrP6``(W0zNNHVQZAiu_yfXYla z$;}ap@Apj}zdj}>!ak}0HwK<%RNB@>(WMr2lC%0YSj2X2vf%{&P++yz*zt$qjLdc% zzORbPDkVp}|5=0+freHZu+2+M+nV7chMo?j`WBMjcI8|TC|IM$D=Bo6qs1Ft(i#^k z@c;?8Qr}~;tUKqkJ6U<22#4SEeDALK^(0|YL`vT37jS|l-j>(D-4puxFqB~vKm!;Z zdWXDVI4^V+WjIF3%kzwBM4iHY@;ts>j#qN1ck!y8@d7RBSYg4;QSVG<-SW~pm25(e zIlOa`rfmeTUflLC0kzxI5H$ICq7^0CWu)HrxRypZ5LN|T1A2Ef3-f>WA za8u#jh)GkseNdd_88Nh%=b2!c8p1gmsp{4*t5aj?8P3uF`1PBq#`yOW>R;l3wl6`7 z7k0V0<}}wo%UM`VD)qr&__W@7J@R}~xf^E@l}wN3q=208e;iB{1a zG{$}1D_!4w+ppp%0qUJ-dqt5~avL})4sPC$=!_9xP7XA?^^V6ma)l~nbHi1%2QpeN zxttT@T(k4^09K7CN8YkdS&LVgPeqBJ5Lzg>UVSEmIp=-^N1)$CtL5Qx-TV}1W$|jM zp+7Vl_IgMuXVCdt^>EOvNyu^CG%*f=efSZyN0@_l#b|dWt?2B0=|YO#G;)vX1~3VI zscIO@bP)ESW)2T3=P{XC)ZJMuSSkzskmXAl7de9+WKn!xaet8~WZuB{%n2T9))wZ| z=qJ<)d2^|)J*(F^5?BoLO9W(nP6R%68R=65&J z`{7H382AD3l>NPC=JO}u-nWWRW}Fz9FZr^sZh!FeTdzgQ7&PBM4IAMr7v{>$iLSa% z2_|Kww+(7T*jQ?ZL-O zDA*)xy~;^DFC-$c_z1>SdhSA&{UK$=V_?HOyIGu>#~dV05D5?a9u=gPnFRQnG#c#q z=5i~J8#cBj&a$Mf=;k><@|OB~utQ20t!(Mj=gV{wufM48W%-NjenRL*(3_bI@1e(} zM&FO<^BHQicOr`oOg8Te(z<@+blYAi)QfDxerhW_{SbX9pF}L9Dq1}*fmKLldE5{n z?D9U3V^*cu=uNYRlda0zMFor2(L^kQF@TzduE1^<&Mel10HbQTSQ^?90;xpdoPf_3 zT_eY7Kj$D_IWLnMgT5h`)AGF!ii6mNlQM$D>-%cnkcr7}59#yT>JXn=w{P8^3 z3C^+oJXba;>Fl7QU#|R4MPhGCm!**T4e7BbBE5)VObg|({E1a4blSe$T6K{W_~=TR zyS(n@RJ^k`X1~a)Qkq1$`Avu?73AC4@*=)~cYv9_fjVmhgwA~8K8ohoLqli!r%9PW zuk0|~N*YS#X7kl+e8({yYbgx{%vcro+m|IbzpH+=_CBa~gR})m@UPxZ*cmSeIWKJO@^7oU=Fg=2 zb~pXbjLQR1jv(OI~&RM$m*$tj>!viy5araJ2hE=fN>m7;c3to2_aAh@Vy@yU1 z@msxI%$l&7Ph6K|RBKWSAE3&Z&L93Bnf(ieZJcmtD`QuV9ohn4#hdnz}hl~G^!1FzVeP4q;(y>V~;39FFx>{ zDBct~=y(~93naY{5oEv9E@iq|Z1Zb^NDo4yS2R_Za-$X(=cAzBX1|!~1BYMowtW&< zDBm<)=M2AeQz6X=C~|YMmi}Vw&sol$Vt|BmpK5tfghT$-0}$s$O$TDV25G+>TLn_pPnhP(^n(Rw4Om(=@W9-FxT?}Q{P4j$^d&L0w_X}r6 z_UeK~3FJxQM?KU}X^R-6Lf*|4znZbdOZQ1`D)YN&T_ zb3}g)SVhQf<0TI7Bm1YieBbFiy;!OTecbfmadeRUn$Ro$W0qiKH3YUX8)pc&E~&h; z%Q8976d96;z^m+kJuZn*cwsq%NTT?|gSF<6b)u2XGv+hpUiqQP_qcjIkgVRM;CTfG z3)`?tF45*ZXGh%A@>dCo4EaxHJuqpiL~gJBv;91K_#)ZnhHsiRl!7Ebm%s^`He`_* zZF7g|ZTM^lGUc|KqLKWh*HTRnQIApk&tbm_W`yTszHS-&Ow2siy!3>#b*$**# z!@lm`>yK(KKwCs*3~F&J#<+k}Xb5cq;ic-hLT8IE%o}?+S_CMaaMQgcERd7J{rfr9 zcV?-{e6P8v`VQx*;ar_0CO10@bz)@Y8P(;ZH?{`eq8a!>rKr=^sXrsVg}_JBdF^l_ zVp8PLS^iW3RzQ3G^;bQ)>RQ~QdeiZy_jxyDzRXhrB0iE-#$f(^Xqb}fETbFj!kwS) zOyys_@qGdk-4brE%>S6S;+{zNzG9NgyNsH0oU=su+-rAYIiHQ<1=+{StjS6Fzx_%Z zm$gCF?ab@p#+|trl?Ra>^cS(J7TbOE#Thw{FkUj>=>r1*p=tt}KLK)vl^dSqw0t&J zNa^XS;=I?*9n$N%IXd@4QSVl?Be2!xPP?7dV?nZ`TS~9`ijEC4R>A@TRmfcQQ1Tr=)qWi&3)a(Hz|I@Ru8PvJh&ahq7O*-@AvD>n$gn_o85|xLD#BWk(xAQTW@+LSpw{xjj{+r ze?K7QK-{$58+Pl%WtB^(#-3l(`a_hDjbGDz_YdI9how`C=rU;dBKb~A&5+Mj^n=EjAJyB^DdImdSSSsrXgp+mAayqVUeG$yTrZwS889dom=1+QY{jlV zNVoE6!yX+-#LGxsADO+t7;|&41s=9C+*&;2Tas!qUHQii#SIUqsij85-Lm)X6E8Jo z=C!Z>X`VA@8Iv?$p)g8NB{L%ZiOkmH(L!DGx{hV#SkwnA@BYf=8EhiKH-2YX3>5<; zmI_eVB87i^ir`H=`(^KQJxsW*hhk zF+@)OVrT+md-t&5`F)!o_jD;#vdU1*jnhmx|B1w-JOIXcB7-!wE~arQ8gN^k5JB@c zO`Qk#&w_KE(}ycKQ)r4J=XGcW$Ntrlq)$`S$4SZPl>W{hIVYkZw}~D%FZWAg(N)!P zL4XTV{Z&gE;$D zXI#P4_`E1(yZ$G8T2mogE!L!|09;$G`UTqX?&1cvcQLTWMzQCiI#tH`Ki=j(fmK&j-5Yy1^%569x3pb z6n|;`zjnPIeI^PJn@lX={8zU8kM#dLl^4IA=_qRI7UE<28>0QEYiK9mD?1bWCAre@ ze>vnoE&nSOH4vK`?TB~nTdkk&__r_r@2w5~4q=>EUk{+q@ApS$e?2ORUP`s2MiJ!+HImG@!)B*xCryT+^0j_t?pzOy5Y zNM