From ff5c30ac51cba3050bbdc872dcb8742d7e46b256 Mon Sep 17 00:00:00 2001 From: Zubeen Sahajwani Date: Tue, 7 Apr 2026 10:27:05 -0400 Subject: [PATCH 1/2] Add role-based HR data agent with field-level DLP via AgentCore Gateway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Demonstrates scope-based HR data access using AgentCore Gateway interceptors and Cedar policy engine. An HR Manager sees full employee records; an HR Specialist sees profiles but not compensation; an Employee sees names only. DLP redaction is applied transparently by the Response Interceptor — no application code changes needed when switching personas. --- .../role-based-hr-data-agent/.gitignore | 43 ++ .../role-based-hr-data-agent/README.md | 247 ++++++++ .../role-based-hr-data-agent/__init__.py | 0 .../agent_config/__init__.py | 0 .../agent_config/access_token.py | 57 ++ .../agent_config/agent.py | 215 +++++++ .../agent_config/agent_task.py | 56 ++ .../agent_config/tools/__init__.py | 0 .../agent_config/utils.py | 32 ++ 02-use-cases/role-based-hr-data-agent/app.py | 538 ++++++++++++++++++ .../app_modules/__init__.py | 0 .../app_modules/auth.py | 101 ++++ .../app_modules/chat.py | 78 +++ .../app_modules/main.py | 154 +++++ .../app_modules/styles.py | 50 ++ .../app_modules/utils.py | 14 + .../dev-requirements.txt | 24 + .../docs/diagrams/flow.md | 88 +++ .../docs/screenshots/employee.png | Bin 0 -> 569629 bytes .../docs/screenshots/full-architecture.png | Bin 0 -> 160579 bytes .../docs/screenshots/hr-manager.png | Bin 0 -> 538083 bytes 02-use-cases/role-based-hr-data-agent/main.py | 44 ++ .../prerequisite/cedar/hr_dlp_policies.cedar | 76 +++ .../prerequisite/cognito.yaml | 197 +++++++ .../prerequisite/infrastructure.yaml | 242 ++++++++ .../prerequisite/lambda/__init__.py | 0 .../prerequisite/lambda/api_spec.json | 106 ++++ .../lambda/interceptors/__init__.py | 0 .../interceptors/request_interceptor.py | 185 ++++++ .../interceptors/response_interceptor.py | 263 +++++++++ .../lambda/interceptors/tenant_mapping.py | 64 +++ .../prerequisite/lambda/python/__init__.py | 0 .../lambda/python/audit_logger.py | 188 ++++++ .../prerequisite/lambda/python/dummy_data.py | 127 +++++ .../prerequisite/lambda/python/hr_handlers.py | 207 +++++++ .../lambda/python/lambda_handler.py | 144 +++++ .../policies/dlp_redaction_rules.txt | 68 +++ .../policies/hr_data_governance.txt | 74 +++ .../prerequisite/prereqs_config.yaml | 53 ++ .../role-based-hr-data-agent/pyproject.toml | 31 + .../role-based-hr-data-agent/requirements.txt | 7 + .../scripts/__init__.py | 0 .../scripts/agentcore_agent_runtime.py | 190 +++++++ .../scripts/agentcore_gateway.py | 201 +++++++ .../scripts/cleanup.sh | 214 +++++++ .../scripts/cognito_credentials_provider.py | 164 ++++++ .../scripts/create_cedar_policies.py | 350 ++++++++++++ .../scripts/package_runtime.sh | 65 +++ .../scripts/prereq.sh | 172 ++++++ .../role-based-hr-data-agent/scripts/utils.py | 61 ++ .../role-based-hr-data-agent/test/__init__.py | 0 .../test/test_agent.py | 86 +++ .../test/test_dlp_redaction.py | 178 ++++++ .../test/test_gateway.py | 116 ++++ 54 files changed, 5570 insertions(+) create mode 100644 02-use-cases/role-based-hr-data-agent/.gitignore create mode 100644 02-use-cases/role-based-hr-data-agent/README.md create mode 100644 02-use-cases/role-based-hr-data-agent/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/agent_config/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/agent_config/access_token.py create mode 100644 02-use-cases/role-based-hr-data-agent/agent_config/agent.py create mode 100644 02-use-cases/role-based-hr-data-agent/agent_config/agent_task.py create mode 100644 02-use-cases/role-based-hr-data-agent/agent_config/tools/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/agent_config/utils.py create mode 100644 02-use-cases/role-based-hr-data-agent/app.py create mode 100644 02-use-cases/role-based-hr-data-agent/app_modules/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/app_modules/auth.py create mode 100644 02-use-cases/role-based-hr-data-agent/app_modules/chat.py create mode 100644 02-use-cases/role-based-hr-data-agent/app_modules/main.py create mode 100644 02-use-cases/role-based-hr-data-agent/app_modules/styles.py create mode 100644 02-use-cases/role-based-hr-data-agent/app_modules/utils.py create mode 100644 02-use-cases/role-based-hr-data-agent/dev-requirements.txt create mode 100644 02-use-cases/role-based-hr-data-agent/docs/diagrams/flow.md create mode 100644 02-use-cases/role-based-hr-data-agent/docs/screenshots/employee.png create mode 100644 02-use-cases/role-based-hr-data-agent/docs/screenshots/full-architecture.png create mode 100644 02-use-cases/role-based-hr-data-agent/docs/screenshots/hr-manager.png create mode 100644 02-use-cases/role-based-hr-data-agent/main.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/cedar/hr_dlp_policies.cedar create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/cognito.yaml create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/infrastructure.yaml create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/api_spec.json create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/interceptors/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/interceptors/request_interceptor.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/interceptors/response_interceptor.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/interceptors/tenant_mapping.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/python/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/python/audit_logger.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/python/dummy_data.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/python/hr_handlers.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/lambda/python/lambda_handler.py create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/policies/dlp_redaction_rules.txt create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/policies/hr_data_governance.txt create mode 100644 02-use-cases/role-based-hr-data-agent/prerequisite/prereqs_config.yaml create mode 100644 02-use-cases/role-based-hr-data-agent/pyproject.toml create mode 100644 02-use-cases/role-based-hr-data-agent/requirements.txt create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/agentcore_agent_runtime.py create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/agentcore_gateway.py create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/cleanup.sh create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/cognito_credentials_provider.py create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/create_cedar_policies.py create mode 100755 02-use-cases/role-based-hr-data-agent/scripts/package_runtime.sh create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/prereq.sh create mode 100644 02-use-cases/role-based-hr-data-agent/scripts/utils.py create mode 100644 02-use-cases/role-based-hr-data-agent/test/__init__.py create mode 100644 02-use-cases/role-based-hr-data-agent/test/test_agent.py create mode 100644 02-use-cases/role-based-hr-data-agent/test/test_dlp_redaction.py create mode 100644 02-use-cases/role-based-hr-data-agent/test/test_gateway.py diff --git a/02-use-cases/role-based-hr-data-agent/.gitignore b/02-use-cases/role-based-hr-data-agent/.gitignore new file mode 100644 index 000000000..825611dfd --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/.gitignore @@ -0,0 +1,43 @@ +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd +*.egg-info/ +.Python + +# Virtual environments +.venv/ +venv/ +env/ + +# Build / packaging artifacts +dist/ +build/ + +# Test and coverage +.pytest_cache/ +.mypy_cache/ +.coverage +htmlcov/ + +# Credentials and secrets +.env +.env.* +persona_app_clients.json + +# macOS +.DS_Store + +# IDE +.vscode/ +.idea/ + +# Logs +*.log + +# Backup files +*.bak + +# Claude Code workspace instructions +CLAUDE.md diff --git a/02-use-cases/role-based-hr-data-agent/README.md b/02-use-cases/role-based-hr-data-agent/README.md new file mode 100644 index 000000000..f00aa16db --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/README.md @@ -0,0 +1,247 @@ +# Role-Based HR Data Agent + +> [!IMPORTANT] +> This sample uses synthetic HR data for demonstration purposes only. No real employee data is processed. Review IAM permissions before deploying in production. + +A role-based HR data access agent with automatic **field-level DLP redaction** using Amazon Bedrock AgentCore. The agent enforces data access policies based on each caller's OAuth 2.0 scopes — without changing application code. + +**Key capabilities:** +- **AgentCore Runtime** — hosts the Strands Agent; receives user prompts and drives MCP tool calls via the Gateway +- **AgentCore Gateway** — central policy enforcement point; routes every `tools/list` and `tools/call` through interceptors and Cedar +- **Request Interceptor** — decodes JWT and injects tenant context on every `tools/call` +- **Cedar Policy Engine** — Allow/Deny per tool based on OAuth scopes +- **Response Interceptor** — hides tools from `tools/list` and redacts fields on `tools/call` responses +- **Multi-tenant isolation** — tenant resolved from OAuth `client_id`; no custom JWT claims needed +- **Cognito OAuth 2.0** — `client_credentials` with custom scopes per persona + +> **Note:** This sample uses AWS Lambda as the AgentCore Gateway target. + +![Architecture](docs/screenshots/full-architecture.png) + +| # | Step | +|---|---| +| 1 | Application sends a prompt to AgentCore Runtime with an inbound auth token | +| 2 | Runtime obtains a scoped JWT from Cognito (`client_credentials` flow) | +| 3 | Strands Agent sends an MCP request (`tools/list` or `tools/call`) to AgentCore Gateway with the JWT in the header | +| 4 | Gateway forwards the request to the **Request Interceptor Lambda** | +| 5 | Request Interceptor decodes the JWT, injects `tenantId` into tool arguments, and returns the transformed request | +| 6 | Gateway evaluates the **Cedar Policy Engine** — Allow or Deny based on OAuth scopes | +| 7 | Gateway calls the **Lambda target** (HR Data Provider) with the transformed request, using AgentCore Identity for outbound auth | +| 8 | Lambda returns the full (unredacted) response | +| 9 | Gateway passes the response to the **Response Interceptor Lambda** | +| 10 | Response Interceptor applies field-level DLP redaction and filters tool discovery by scope; transformed response returned to the Runtime | + +## Demo + +| HR Manager — full access | Employee — all sensitive fields redacted | +|:---:|:---:| +| ![HR Manager](docs/screenshots/hr-manager.png) | ![Employee](docs/screenshots/employee.png) | + +> Same query, same agent, different OAuth scopes — DLP redaction applied automatically by the Response Interceptor. + +> See [per-persona request flow](docs/diagrams/flow.md) for a detailed sequence diagram with DLP redaction steps. + +## Reference + +### Scope → Field Mapping + +The Lambda target returns full unredacted records for every caller. The Response Interceptor applies field-level redaction based on the caller's OAuth scopes — ensuring sensitive fields never reach the agent or the user unless the persona has explicit permission. This mapping is defined in `_redact_employee()` in [`prerequisite/lambda/interceptors/response_interceptor.py`](prerequisite/lambda/interceptors/response_interceptor.py). To extend redaction to other data sources (DynamoDB, RDS, S3), update the field lists in that function — the Gateway interceptor pattern applies identically regardless of what the Lambda target reads from. + +| Scope | Redacted fields | +|---|---| +| `hr-dlp-gateway/pii` | email, phone, personal_phone, emergency_contact | +| `hr-dlp-gateway/address` | address, city, state, zip_code | +| `hr-dlp-gateway/comp` | salary, bonus, stock_options, pay_grade, benefits_value, compensation_history | + +### Persona Access Matrix + +Step 2 (`prereq.sh`) creates a Cognito User Pool with a resource server (`hr-dlp-gateway`) that defines four custom OAuth scopes — `read`, `pii`, `address`, and `comp` — and provisions one app client per persona with a fixed `AllowedOAuthScopes` list. Each persona gets a `client_id` and `client_secret` stored in SSM; the agent fetches a token via `client_credentials` flow using those credentials. The Gateway enforces what tools are visible and what fields are returned based on the scopes present in the token. + +| Persona | Scopes | Tools visible | Salary | Email | Address | +|---|---|---|---|---|---| +| HR Manager | read, pii, address, comp | 3 | Visible | Visible | Visible | +| HR Specialist | read, pii | 2 | `[REDACTED]` | Visible | `[REDACTED]` | +| Employee | read | 1 | `[REDACTED]` | `[REDACTED]` | `[REDACTED]` | +| Admin | read, pii, address, comp | 3 | Visible | Visible | Visible | + +## Prerequisites + +- AWS account with Amazon Bedrock AgentCore access (us-east-1) +- **Claude Haiku 4.5** enabled via cross-region inference (CRIS) in your account +- Python 3.10+ +- AWS CLI configured (`aws configure`) +- [uv](https://docs.astral.sh/uv/) (recommended) or pip + +## Setup + +### Step 1: Clone and install + +```bash +git clone https://github.com/awslabs/agentcore-samples.git +cd agentcore-samples/02-use-cases/role-based-hr-data-agent + +uv sync +``` + +### Step 2: Deploy infrastructure + +Packages Lambda functions and deploys CloudFormation stacks for Lambda, IAM, and Cognito. Stores all resource IDs in SSM under `/app/hrdlp/*`. + +```bash +bash scripts/prereq.sh --region us-east-1 --env dev +``` + +### Step 3: Create the AgentCore Gateway + +Creates the Gateway with JWT authorizer, Lambda target (3 HR tools), and request/response interceptors. The Lambda target **must** be attached before Step 4 — Cedar builds its policy schema from the registered tool names. + +```bash +python scripts/agentcore_gateway.py create --config prerequisite/prereqs_config.yaml +``` + +### Step 4: Create the Cedar Policy Engine + +Attaches the Cedar Policy Engine and creates the three HR DLP authorization policies. Uses a two-phase `update_gateway` approach: Phase A attaches the engine **without interceptors** so Cedar's internal schema initialization call succeeds, then Phase B restores the interceptors once policies are ACTIVE. + +```bash +python scripts/create_cedar_policies.py --region us-east-1 --env dev +``` + +Default mode is `LOG_ONLY`. Switch to enforcement for production: + +```bash +python scripts/create_cedar_policies.py --mode ENFORCE +``` + +### Step 5: Deploy the AgentCore Runtime + +```bash +bash scripts/package_runtime.sh + +BUCKET=$(aws ssm get-parameter --name /app/hrdlp/deploy-bucket --query Parameter.Value --output text) +aws s3 cp dist/runtime.zip s3://${BUCKET}/hr-data-agent/runtime.zip + +python scripts/agentcore_agent_runtime.py create +``` + +### Step 6: Run the Streamlit app + +```bash +streamlit run app.py +``` + +Open http://localhost:8501. Select a persona, click **Get OAuth Token**, then ask a question such as *"Show me John Smith's compensation"*. Switch personas to see DLP redaction applied automatically. + +## Testing + +> **Note:** Cedar defaults to `LOG_ONLY` mode — policies log decisions but do not block requests. Tests will pass in either mode; switch to `ENFORCE` only when ready for production. + +### Verify DLP redaction + +```bash +python test/test_dlp_redaction.py +``` + +Expected output: + +``` +Testing persona: hr-manager → PASS (salary visible, email visible) +Testing persona: hr-specialist → PASS (salary redacted, email visible) +Testing persona: employee → PASS (salary redacted, email redacted) +Testing persona: admin → PASS (salary visible, email visible) +``` + +### Test the full agent + +```bash +python test/test_agent.py --persona hr-manager --prompt "Show me John Smith's compensation" +python test/test_agent.py --persona employee --prompt "Show me John Smith's compensation" +``` + +### Test the Gateway directly + +```bash +python test/test_gateway.py --persona hr-manager --list-tools +python test/test_gateway.py --persona employee --list-tools +python test/test_gateway.py --persona hr-specialist --query "Sarah Johnson" +``` + +### View CloudWatch logs + +```bash +ENV=dev +aws logs tail /aws/lambda/hr-data-provider-lambda-${ENV} --since 1h --follow +aws logs tail /aws/lambda/hr-request-interceptor-lambda-${ENV} --since 1h --follow +aws logs tail /aws/lambda/hr-response-interceptor-lambda-${ENV} --since 1h --follow +``` + +## Troubleshooting + +**Cedar `CREATE_FAILED: An internal error occurred during creation`** +Cedar's schema initialization failed — usually the engine is in a corrupted state from a prior failed run. Clean up and redeploy from Step 2: +```bash +bash scripts/cleanup.sh && bash scripts/prereq.sh --region us-east-1 --env dev +``` + +**Cedar `CREATE_FAILED: unable to find at offset 0`** +No Lambda target is registered. Complete Step 3 before running Step 4. +```bash +python scripts/agentcore_gateway.py create --config prerequisite/prereqs_config.yaml +``` + +**Runtime `CREATE_FAILED` — ARM64 binary incompatibility** +macOS packaging pulled darwin binaries. Delete the old zip and repackage: +```bash +rm -f dist/runtime.zip && bash scripts/package_runtime.sh +``` + +**SSM parameters missing when running the app** +Complete Steps 2–5 first. Verify all parameters are present: +```bash +aws ssm get-parameters-by-path --path /app/hrdlp --recursive --query "Parameters[].Name" --output text +``` + +**Runtime returns 403 after update** +`update-agent-runtime` resets fields not explicitly passed. Always run the full update: +```bash +python scripts/agentcore_agent_runtime.py update +``` + +## Project Structure + +``` +role-based-hr-data-agent/ +├── agent_config/ # HRDataAgent — Strands + MCP/JSON-RPC +├── app_modules/ # Streamlit UI (auth, chat, persona selector) +├── docs/ +│ ├── screenshots/ # Demo screenshots + full architecture diagram +│ └── diagrams/ # Per-persona request flow (flow.md) +├── scripts/ # Deployment CLI (gateway, runtime, Cedar, Cognito) +├── prerequisite/ +│ ├── lambda/ # HR Data Provider + Request/Response Interceptors +│ ├── cedar/ # Cedar authorization policies +│ ├── infrastructure.yaml +│ └── cognito.yaml +├── test/ # Gateway, agent, and DLP redaction tests +├── app.py # Streamlit entry point +├── main.py # AgentCore Runtime entry point +└── requirements.txt +``` + +## Cleanup + +```bash +bash scripts/cleanup.sh --region us-east-1 --env dev +``` + +## Contributing + +We welcome contributions! See [Contributing Guidelines](../../CONTRIBUTING.md) for details. + +## License + +MIT License — see [LICENSE](../../LICENSE). + +## Support + +Report issues via [GitHub Issues](https://github.com/awslabs/agentcore-samples/issues). diff --git a/02-use-cases/role-based-hr-data-agent/__init__.py b/02-use-cases/role-based-hr-data-agent/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/02-use-cases/role-based-hr-data-agent/agent_config/__init__.py b/02-use-cases/role-based-hr-data-agent/agent_config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/02-use-cases/role-based-hr-data-agent/agent_config/access_token.py b/02-use-cases/role-based-hr-data-agent/agent_config/access_token.py new file mode 100644 index 000000000..1ba1796f7 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/agent_config/access_token.py @@ -0,0 +1,57 @@ +""" +M2M (machine-to-machine) access token acquisition for AgentCore Gateway. + +Fetches a Cognito client_credentials token and caches it for reuse. +""" + +import logging +import time +from typing import Optional + +import requests + +from agent_config.utils import get_ssm_parameter + +logger = logging.getLogger(__name__) + +_cached_token: Optional[str] = None +_token_expiry: float = 0.0 + + +def get_gateway_access_token(client_id: Optional[str] = None, client_secret: Optional[str] = None) -> Optional[str]: + """ + Return a valid Cognito client_credentials access token. + + Credentials are read from SSM if not provided directly: + /app/hrdlp/cognito-client-id + /app/hrdlp/cognito-client-secret + /app/hrdlp/cognito-token-url + """ + global _cached_token, _token_expiry + + if _cached_token and time.time() < _token_expiry - 60: + return _cached_token + + client_id = client_id or get_ssm_parameter("/app/hrdlp/cognito-client-id") + client_secret = client_secret or get_ssm_parameter("/app/hrdlp/cognito-client-secret") + token_url = get_ssm_parameter("/app/hrdlp/cognito-token-url") + + if not all([client_id, client_secret, token_url]): + logger.error("Missing Cognito credentials in SSM") + return None + + try: + response = requests.post( + token_url, + data={"grant_type": "client_credentials"}, + auth=(client_id, client_secret), + timeout=10, + ) + response.raise_for_status() + data = response.json() + _cached_token = data["access_token"] + _token_expiry = time.time() + data.get("expires_in", 3600) + return _cached_token + except Exception as e: + logger.error(f"Failed to acquire access token: {e}") + return None diff --git a/02-use-cases/role-based-hr-data-agent/agent_config/agent.py b/02-use-cases/role-based-hr-data-agent/agent_config/agent.py new file mode 100644 index 000000000..cf54e7a3e --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/agent_config/agent.py @@ -0,0 +1,215 @@ +""" +HRDataAgent — Strands-based agent that connects to AgentCore Gateway via MCP/JSON-RPC. + +Discovers tools dynamically from the Gateway (filtered per OAuth scope) and +invokes them over HTTP JSON-RPC. All field-level DLP redaction is applied +transparently by the Gateway Response Interceptor before data reaches here. +""" + +from __future__ import annotations + +import asyncio +import logging +import re +import uuid +from typing import Any, AsyncGenerator, Dict, List, Optional + +import httpx +from strands import Agent +from strands.models import BedrockModel +from strands.types.tools import AgentTool, ToolResult, ToolUse +from strands.types._events import ToolResultEvent + +logger = logging.getLogger(__name__) + +MODEL_ID = "us.anthropic.claude-haiku-4-5-20251001-v1:0" + +_SAFE_NAME = re.compile(r"[^A-Za-z0-9_-]+") + + +def _safe_tool_name(name: str) -> str: + safe = _SAFE_NAME.sub("_", name).strip("_") + return safe or "tool" + + +def _normalize_input_schema(tool_schema: Dict[str, Any]) -> Dict[str, Any]: + schema = tool_schema.get("inputSchema") or tool_schema.get("input_schema") or {} + if isinstance(schema, dict) and "json" in schema and isinstance(schema["json"], dict): + return schema + if isinstance(schema, dict): + return {"json": schema} + return {"json": {"type": "object", "properties": {}}} + + +async def _call_gateway_jsonrpc( + gateway_url: str, + access_token: str, + method: str, + params: Optional[dict] = None, +) -> Any: + async with httpx.AsyncClient(timeout=30.0) as client: + payload = { + "jsonrpc": "2.0", + "id": uuid.uuid4().hex, + "method": method, + "params": params or {}, + } + resp = await client.post( + gateway_url, + json=payload, + headers={ + "Content-Type": "application/json", + "Accept": "application/json", + "Authorization": f"Bearer {access_token}", + }, + ) + resp.raise_for_status() + body = resp.json() + if body.get("error") is not None: + raise RuntimeError(f"Gateway error: {body['error']}") + return body.get("result") + + +async def _list_tools(gateway_url: str, access_token: str) -> List[Dict[str, Any]]: + result = await _call_gateway_jsonrpc(gateway_url, access_token, "tools/list", {}) + if result is None: + return [] + if isinstance(result, list): + return result + if isinstance(result, dict) and isinstance(result.get("tools"), list): + return result["tools"] + return [] + + +class _HTTPGatewayTool(AgentTool): + """Wraps a single MCP tool from the Gateway as a Strands AgentTool.""" + + def __init__( + self, + tool_schema: Dict[str, Any], + gateway_url: str, + access_token: str, + name_map: Dict[str, str], + ): + original_name = tool_schema.get("name") + if not original_name: + raise ValueError(f"Tool schema missing 'name': {tool_schema}") + + self._original_name = original_name + self._name = _safe_tool_name(original_name) + self._description = tool_schema.get("description", "") + self._input_schema = _normalize_input_schema(tool_schema) + self._gateway_url = gateway_url + self._access_token = access_token + name_map[self._name] = self._original_name + super().__init__() + + @property + def tool_name(self) -> str: + return self._name + + @property + def tool_spec(self) -> Dict[str, Any]: + return { + "name": self._name, + "description": self._description, + "inputSchema": self._input_schema, + } + + @property + def tool_type(self) -> str: + return "agentcore_gateway_http_jsonrpc" + + async def stream( + self, + tool_use: ToolUse, + invocation_state: Dict[str, Any], + **kwargs, + ) -> AsyncGenerator[ToolResultEvent, None]: + tool_input = tool_use.get("input", {}) + params = {"name": self._original_name, "arguments": tool_input} + result = await _call_gateway_jsonrpc( + self._gateway_url, self._access_token, "tools/call", params + ) + + result_text = "" + if isinstance(result, dict): + content = result.get("content", []) + if isinstance(content, list): + parts = [ + item.get("text", "") + for item in content + if isinstance(item, dict) and item.get("type") == "text" + ] + result_text = "\n".join(p for p in parts if p) or str(result) + else: + result_text = str(result) + else: + result_text = str(result) + + tool_result: ToolResult = { + "toolUseId": tool_use["toolUseId"], + "status": "success", + "content": [{"text": result_text}], + } + yield ToolResultEvent(tool_result) + + +SYSTEM_PROMPT = """You are a secure HR Assistant with role-based data access control. + +You help users access HR information through the Amazon Bedrock AgentCore Gateway. +The Gateway enforces OAuth scope-based authorization and applies field-level DLP +redaction automatically — you receive data that is already correctly filtered for +the current user's role. + +IMPORTANT RULES: +- Always call tools FIRST before responding. Never fabricate data. +- Present tool responses directly. Do not invent placeholder values. +- If a field contains [REDACTED - Insufficient Permissions], display it exactly. +- Never assume parameter values. Ask the user if required information is missing. +- Only explain redaction AFTER presenting data, and only if the user asks. + +Available tools (shown based on your OAuth scopes): +- search_employee: Search employees by name, department, or role +- get_employee_profile: Get detailed employee profile (PII/address may be redacted) +- get_employee_compensation: Get salary and compensation data (requires comp scope) + +Role capabilities: +- HR Manager / Admin: Full access — all fields visible +- HR Specialist: Profiles + PII visible; compensation and address redacted +- Employee: Search only — all PII, address, and compensation redacted +""" + + +class HRDataAgent: + """ + Strands agent wired to AgentCore Gateway via JSON-RPC. + + Discovers tools dynamically on each invocation so tool visibility + always reflects the caller's current OAuth scopes. + """ + + def __init__(self, gateway_url: str, access_token: str): + self.gateway_url = gateway_url + self.access_token = access_token + + async def process(self, user_prompt: str) -> Dict[str, Any]: + model = BedrockModel(model_id=MODEL_ID, temperature=0.0, streaming=False) + + tool_schemas = await _list_tools(self.gateway_url, self.access_token) + logger.info(f"Loaded {len(tool_schemas)} tools from Gateway") + + name_map: Dict[str, str] = {} + tools = [ + _HTTPGatewayTool(schema, self.gateway_url, self.access_token, name_map) + for schema in tool_schemas + ] + + agent = Agent(model=model, system_prompt=SYSTEM_PROMPT, tools=tools) + result = await asyncio.to_thread(agent, user_prompt) + + return { + "result": result.message, + "model": MODEL_ID, + "tool_count": len(tools), + } diff --git a/02-use-cases/role-based-hr-data-agent/agent_config/agent_task.py b/02-use-cases/role-based-hr-data-agent/agent_config/agent_task.py new file mode 100644 index 000000000..2ddd38a43 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/agent_config/agent_task.py @@ -0,0 +1,56 @@ +""" +Async agent workflow orchestration. + +Wires together: context setup → token extraction → agent invocation → response. +""" + +from __future__ import annotations + +import logging +from typing import Any, Dict, Optional + +from agent_config.agent import HRDataAgent + +logger = logging.getLogger(__name__) + + +def _get_auth_header(context: Any) -> Optional[str]: + if hasattr(context, "request_headers") and isinstance(context.request_headers, dict): + headers = context.request_headers + return headers.get("Authorization") or headers.get("authorization") + return None + + +def _strip_bearer(header: str) -> str: + return header.replace("Bearer ", "").replace("bearer ", "").strip() + + +async def run_agent_task( + payload: Dict[str, Any], + context: Any, + gateway_url: str, + session_id: str, +) -> Dict[str, Any]: + """ + Main async workflow: + 1. Extract OAuth token from request headers + 2. Create/retrieve AgentContext for this session + 3. Instantiate HRDataAgent and invoke with user prompt + 4. Return structured response + """ + user_prompt = payload.get("prompt") or "How can I help you today?" + logger.info(f"[agent_task] session={session_id} prompt={user_prompt[:80]!r}") + + # Extract access token from incoming request (pass-through from caller) + auth_header = _get_auth_header(context) + if not auth_header: + return {"error": "Missing Authorization header"} + access_token = _strip_bearer(auth_header) + + try: + agent = HRDataAgent(gateway_url=gateway_url, access_token=access_token) + result = await agent.process(user_prompt) + return result + except Exception as e: + logger.exception(f"[agent_task] failure: {e}") + return {"error": str(e)} diff --git a/02-use-cases/role-based-hr-data-agent/agent_config/tools/__init__.py b/02-use-cases/role-based-hr-data-agent/agent_config/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/02-use-cases/role-based-hr-data-agent/agent_config/utils.py b/02-use-cases/role-based-hr-data-agent/agent_config/utils.py new file mode 100644 index 000000000..e2b29de9d --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/agent_config/utils.py @@ -0,0 +1,32 @@ +""" +Utility helpers for agent_config. +""" + +import json +import logging +from typing import Any, Optional + +import boto3 +import yaml +from botocore.exceptions import ClientError + +logger = logging.getLogger(__name__) + + +def get_ssm_parameter(name: str, decrypt: bool = True) -> Optional[str]: + """Retrieve a parameter from AWS SSM Parameter Store.""" + try: + client = boto3.client("ssm") + response = client.get_parameter(Name=name, WithDecryption=decrypt) + return response["Parameter"]["Value"] + except ClientError as e: + logger.warning(f"SSM parameter not found: {name} — {e}") + return None + + +def read_config(path: str) -> dict: + """Read a JSON or YAML config file and return as dict.""" + with open(path, "r", encoding="utf-8") as f: + if path.endswith(".yaml") or path.endswith(".yml"): + return yaml.safe_load(f) + return json.load(f) diff --git a/02-use-cases/role-based-hr-data-agent/app.py b/02-use-cases/role-based-hr-data-agent/app.py new file mode 100644 index 000000000..125a88fdf --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/app.py @@ -0,0 +1,538 @@ +#!/usr/bin/env python3 +""" +HR DLP Demo — Streamlit frontend. + +All configuration is read dynamically from SSM Parameter Store on startup: + /app/hrdlp/runtime-url — AgentCore Runtime invocation URL + /app/hrdlp/gateway-url — AgentCore Gateway MCP endpoint + /app/hrdlp/cognito-token-url — Cognito OAuth2 token endpoint + /app/hrdlp/personas/*/client-id — Per-persona Cognito app client ID + /app/hrdlp/personas/*/client-secret — Per-persona client secret (SecureString) + +Usage: + streamlit run app.py +""" + +import base64 +import json +import os +from datetime import datetime +from typing import Optional + +import boto3 +import requests +import streamlit as st + +# --------------------------------------------------------------------------- +# SSM helpers +# --------------------------------------------------------------------------- + +@st.cache_resource(show_spinner=False) +def _ssm_client(): + region = os.getenv("AWS_REGION", os.getenv("AWS_DEFAULT_REGION", "us-east-1")) + return boto3.client("ssm", region_name=region) + + +def _get_param(name: str, secure: bool = False) -> Optional[str]: + try: + resp = _ssm_client().get_parameter(Name=name, WithDecryption=secure) + return resp["Parameter"]["Value"] + except Exception: + return None + + +# --------------------------------------------------------------------------- +# Config — loaded once per session +# --------------------------------------------------------------------------- + +@st.cache_resource(show_spinner="Loading configuration from SSM…") +def load_config() -> dict: + runtime_url = _get_param("/app/hrdlp/runtime-url") + gateway_url = _get_param("/app/hrdlp/gateway-url") + token_url = _get_param("/app/hrdlp/cognito-token-url") + + personas = {} + for persona in ["hr-manager", "hr-specialist", "employee", "admin"]: + client_id = _get_param(f"/app/hrdlp/personas/{persona}/client-id") + client_secret = _get_param(f"/app/hrdlp/personas/{persona}/client-secret", secure=True) + if client_id and client_secret: + personas[persona] = {"client_id": client_id, "client_secret": client_secret} + + missing = [] + if not runtime_url: missing.append("/app/hrdlp/runtime-url") + if not gateway_url: missing.append("/app/hrdlp/gateway-url") + if not token_url: missing.append("/app/hrdlp/cognito-token-url") + if not personas: missing.append("/app/hrdlp/personas/*/client-id and client-secret") + + return { + "runtime_url": runtime_url, + "gateway_url": gateway_url, + "token_url": token_url, + "personas": personas, + "missing": missing, + } + + +# --------------------------------------------------------------------------- +# Persona display definitions +# --------------------------------------------------------------------------- + +PERSONAS = { + "HR Manager": { + "key": "hr-manager", + "icon": "👔", + "description": "Full access — compensation, PII, and address visible", + "scopes": ["read", "pii", "address", "comp"], + "color": "#1f77b4", + "expected_tools": 3, + }, + "HR Specialist": { + "key": "hr-specialist", + "icon": "👨‍💼", + "description": "Profiles + PII; compensation and address redacted", + "scopes": ["read", "pii"], + "color": "#ff7f0e", + "expected_tools": 2, + }, + "Employee": { + "key": "employee", + "icon": "👤", + "description": "Search only; all sensitive fields redacted", + "scopes": ["read"], + "color": "#2ca02c", + "expected_tools": 1, + }, + "Admin": { + "key": "admin", + "icon": "🛡️", + "description": "Full administrative access", + "scopes": ["read", "pii", "address", "comp"], + "color": "#9467bd", + "expected_tools": 3, + }, +} + +SUGGESTED_QUERIES = [ + "What can you help me with?", + "Find all software engineers", + "Show me Sarah Johnson's profile", + "What is John Smith's compensation?", + "Search for HR department employees", +] + +# --------------------------------------------------------------------------- +# Auth helpers +# --------------------------------------------------------------------------- + +def get_token(config: dict, persona_key: str) -> Optional[str]: + """Obtain a client_credentials access token for the given persona.""" + creds = config["personas"].get(persona_key) + if not creds: + add_log(f"No credentials found in SSM for persona: {persona_key}", "error", "Cognito") + return None + add_log(f"POST {config['token_url']} (grant_type=client_credentials)", "info", "Cognito") + encoded = base64.b64encode(f"{creds['client_id']}:{creds['client_secret']}".encode()).decode() + try: + resp = requests.post( + config["token_url"], + headers={"Content-Type": "application/x-www-form-urlencoded", + "Authorization": f"Basic {encoded}"}, + data={"grant_type": "client_credentials"}, + timeout=10, + ) + resp.raise_for_status() + token_data = resp.json() + expires = token_data.get("expires_in", "?") + scopes = token_data.get("scope", "") + add_log(f"Token issued (expires {expires}s) | scopes: {scopes}", "success", "Cognito") + return token_data.get("access_token") + except Exception as e: + add_log(f"Token request failed: {e}", "error", "Cognito") + st.error(f"Token request failed: {e}") + return None + + +# --------------------------------------------------------------------------- +# Runtime / Gateway calls +# --------------------------------------------------------------------------- + +def call_runtime(config: dict, token: str, prompt: str, session_id: str = "") -> tuple[list, Optional[str]]: + """POST to AgentCore Runtime and return (raw_chunks, final_text).""" + add_log(f"POST {config['runtime_url'].split('/runtimes/')[0]}/runtimes/…/invocations", "info", "Runtime") + add_log(f"Session: {session_id[:16]}…", "info", "Runtime") + + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}"} + chunks, llm_response = [], None + try: + resp = requests.post( + config["runtime_url"], + headers=headers, + json={"prompt": prompt, "sessionId": session_id or str(id(prompt))}, + stream=True, + timeout=120, + ) + if resp.status_code != 200: + add_log(f"HTTP {resp.status_code}: {resp.text[:120]}", "error", "Runtime") + st.error(f"Runtime returned HTTP {resp.status_code}: {resp.text[:200]}") + return [], None + + add_log(f"HTTP 200 — streaming response…", "success", "Runtime") + + for line in resp.iter_lines(): + if not line: + continue + decoded = line.decode("utf-8") + if decoded.startswith("data: "): + decoded = decoded[6:] + chunks.append(decoded) + try: + data = json.loads(decoded) + + # Error returned as JSON body (e.g. missing sessionId) + if "error" in data and "result" not in data: + add_log(f"Runtime error: {data['error']}", "error", "Runtime") + st.error(f"Runtime error: {data['error']}") + return [], None + + if "result" in data: + result = data["result"] + model = data.get("model", "") + tool_count = data.get("tool_count", "?") + add_log(f"Tools used: {tool_count} | Model: {model.split('/')[-1] if model else 'unknown'}", "info", "Runtime") + if isinstance(result, dict) and "content" in result: + content = result["content"] + llm_response = content[0].get("text", str(result)) if content else str(result) + else: + llm_response = str(result) + add_log(f"Response received ({len(llm_response)} chars)", "success", "Runtime") + + elif data.get("type") == "response": + llm_response = data.get("message", "") + add_log(f"Response received ({len(llm_response)} chars)", "success", "Runtime") + + elif data.get("type") == "status": + add_log(data.get("message", ""), "info", "Runtime") + + elif data.get("type") == "tools_discovered": + tools = data.get("tools", []) + add_log(f"Tools discovered: {len(tools)}", "success", "Gateway") + for t in tools: + add_log(f" - {t}", "info", "Gateway") + + elif data.get("type") == "tool_result": + add_log(data.get("message", "Tool call completed"), "success", "Lambda") + + elif data.get("type") == "error": + add_log(data.get("message", "Unknown error"), "error", "Runtime") + + except json.JSONDecodeError: + pass + + except requests.Timeout: + add_log("Request timed out after 120s", "error", "Runtime") + st.error("Request timed out — the agent may still be processing.") + except Exception as e: + add_log(f"Exception: {e}", "error", "Runtime") + st.error(f"Runtime error: {e}") + return chunks, llm_response + + +def discover_tools(config: dict, token: str) -> list[str]: + """Call Gateway tools/list and return tool names.""" + add_log("POST tools/list → Gateway", "info", "Gateway") + try: + resp = requests.post( + config["gateway_url"], + headers={"Content-Type": "application/json", "Authorization": f"Bearer {token}"}, + json={"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}, + timeout=30, + ) + resp.raise_for_status() + tools = resp.json().get("result", {}).get("tools", []) + add_log(f"HTTP 200 — {len(tools)} tool(s) visible to this persona", "success", "Gateway") + for t in tools: + short = t["name"].replace("hr-lambda-target___", "") + add_log(f" ✓ {short}", "info", "Gateway") + return [t["name"] for t in tools] + except Exception as e: + add_log(f"Tool discovery failed: {e}", "error", "Gateway") + st.error(f"Tool discovery failed: {e}") + return [] + + +def call_tool(config: dict, token: str, tool_name: str, arguments: dict): + """Call a specific tool via the Gateway.""" + try: + resp = requests.post( + config["gateway_url"], + headers={"Content-Type": "application/json", "Authorization": f"Bearer {token}"}, + json={"jsonrpc": "2.0", "id": 1, "method": "tools/call", + "params": {"name": tool_name, "arguments": arguments}}, + timeout=30, + ) + resp.raise_for_status() + return resp.json() + except Exception as e: + st.error(f"Tool call failed: {e}") + return None + + +# --------------------------------------------------------------------------- +# Session state helpers +# --------------------------------------------------------------------------- + +def _init_state(): + import uuid + defaults = { + "selected_persona": "HR Manager", + "token": None, + "tools": [], + "logs": [], + "llm_response": None, + "conversation_history": [], + "is_processing": False, + "session_id": str(uuid.uuid4()), + } + for k, v in defaults.items(): + if k not in st.session_state: + st.session_state[k] = v + + +def add_log(message: str, level: str = "info", component: str = ""): + ts = datetime.now().strftime("%H:%M:%S.%f")[:-3] + st.session_state.logs.append({"ts": ts, "msg": message, "level": level, "comp": component}) + + +def _switch_persona(name: str): + import uuid + st.session_state.selected_persona = name + st.session_state.token = None + st.session_state.tools = [] + st.session_state.logs = [] + st.session_state.llm_response = None + st.session_state.conversation_history = [] + st.session_state.session_id = str(uuid.uuid4()) + + +# --------------------------------------------------------------------------- +# Main app +# --------------------------------------------------------------------------- + +st.set_page_config(page_title="HR DLP Demo", page_icon="🔒", layout="wide") +_init_state() + +# Load config (cached — only hits SSM once per process) +config = load_config() + +if config["missing"]: + st.error( + "**Missing SSM parameters** — run the full deployment sequence first:\n\n" + "```\nbash scripts/prereq.sh --region us-east-1 --env dev\n" + "python scripts/agentcore_gateway.py create --config prerequisite/prereqs_config.yaml\n" + "python scripts/create_cedar_policies.py --region us-east-1 --env dev\n" + "python scripts/agentcore_agent_runtime.py create\n```\n\n" + f"Missing: `{'`, `'.join(config['missing'])}`" + ) + st.stop() + +st.title("🔒 HR DLP Gateway — Interactive Demo") +st.caption("Role-based data access with automatic field-level redaction via Amazon Bedrock AgentCore") + +# --------------------------------------------------------------------------- +# Sidebar +# --------------------------------------------------------------------------- +with st.sidebar: + st.header("👥 Persona") + + for display_name, meta in PERSONAS.items(): + is_active = st.session_state.selected_persona == display_name + label = f"{meta['icon']} {display_name}" + if st.button(label, key=f"btn_{display_name}", use_container_width=True, + type="primary" if is_active else "secondary"): + _switch_persona(display_name) + st.rerun() + + st.divider() + p = PERSONAS[st.session_state.selected_persona] + st.markdown(f"### {p['icon']} {st.session_state.selected_persona}") + st.caption(p["description"]) + st.markdown(f"**Scopes:** `{'`, `'.join(p['scopes'])}`") + st.markdown(f"**Expected tools:** {p['expected_tools']}") + + st.divider() + st.header("Actions") + + if st.button("🔑 Get OAuth Token", use_container_width=True): + with st.spinner("Requesting token from Cognito…"): + token = get_token(config, p["key"]) + if token: + st.session_state.token = token + st.session_state.llm_response = None + st.session_state.conversation_history = [] + add_log(f"Token obtained for {st.session_state.selected_persona}", "success", "Cognito") + st.success("Token obtained") + st.rerun() + + if st.button("🔧 Discover Tools", use_container_width=True, + disabled=not st.session_state.token): + with st.spinner("Calling Gateway tools/list…"): + tools = discover_tools(config, st.session_state.token) + st.session_state.tools = tools + add_log(f"Discovered {len(tools)} tools", "success", "Gateway") + for t in tools: + add_log(f" - {t}", "info", "Gateway") + st.rerun() + + if st.button("🗑️ Clear", use_container_width=True): + st.session_state.logs = [] + st.session_state.llm_response = None + st.rerun() + + # Connection info (collapsed) + with st.expander("ℹ️ Connection info"): + st.caption(f"**Runtime:** `{config['runtime_url'][:60]}…`") + st.caption(f"**Gateway:** `{config['gateway_url'][:60]}…`") + st.caption(f"**Token URL:** `{config['token_url'][:60]}…`") + +# --------------------------------------------------------------------------- +# Main area — two columns +# --------------------------------------------------------------------------- +col_chat, col_tools = st.columns([1, 1]) + +# ---- Left column: agent chat ---- +with col_chat: + st.header("💬 Agent Chat") + + if st.session_state.token: + st.success(f"✅ Authenticated as **{st.session_state.selected_persona}**") + else: + st.warning("No token — click **Get OAuth Token** in the sidebar") + + # Suggested queries + st.markdown("**Quick examples:**") + for q in SUGGESTED_QUERIES: + if st.button(q, key=f"quick_{q[:20]}", use_container_width=True, + disabled=not st.session_state.token): + st.session_state.logs = [] + add_log(f"Query: {q}", "info", "Client") + with st.spinner("Processing…"): + _, llm_response = call_runtime(config, st.session_state.token, q, st.session_state.session_id) + if llm_response: + st.session_state.llm_response = llm_response + st.session_state.conversation_history.append({"role": "user", "content": q}) + st.session_state.conversation_history.append({"role": "assistant", "content": llm_response}) + st.rerun() + + st.divider() + + # Custom query + query = st.text_area("Custom query:", value="Show me John Smith's full profile", + height=80, disabled=not st.session_state.token) + if st.button("🚀 Send", use_container_width=True, + disabled=(not st.session_state.token or st.session_state.is_processing)): + st.session_state.is_processing = True + st.session_state.logs = [] + add_log(f"Sending query as {st.session_state.selected_persona}", "info", "Client") + with st.spinner("Processing…"): + _, llm_response = call_runtime(config, st.session_state.token, query, st.session_state.session_id) + if llm_response: + st.session_state.llm_response = llm_response + st.session_state.conversation_history.append({"role": "user", "content": query}) + st.session_state.conversation_history.append({"role": "assistant", "content": llm_response}) + st.session_state.is_processing = False + st.rerun() + + # Response display + if st.session_state.llm_response: + st.divider() + st.subheader("🤖 Agent Response") + st.markdown(st.session_state.llm_response) + if st.button("Clear response"): + st.session_state.llm_response = None + st.session_state.conversation_history = [] + st.rerun() + + # Conversation history + if st.session_state.conversation_history: + with st.expander("💬 Conversation history", expanded=False): + for msg in st.session_state.conversation_history: + prefix = "**You:**" if msg["role"] == "user" else "**Agent:**" + text = msg["content"] + st.markdown(f"{prefix} {text[:300]}{'…' if len(text) > 300 else ''}") + st.markdown("---") + +# ---- Right column: direct tool calling + logs ---- +with col_tools: + st.header("🔧 Direct Tool Calling") + + if not st.session_state.tools: + st.info("Click **Discover Tools** in the sidebar to see what this persona can access.") + else: + tool_labels = { + "hr-lambda-target___search_employee": "Search Employee", + "hr-lambda-target___get_employee_profile": "Get Employee Profile", + "hr-lambda-target___get_employee_compensation": "Get Employee Compensation", + } + available = {k: v for k, v in tool_labels.items() if k in st.session_state.tools} + + if not available: + st.warning("No recognized tools visible for this persona.") + else: + selected_tool = st.selectbox( + "Tool:", options=list(available.keys()), + format_func=lambda x: available[x], + ) + + with st.form(key="tool_form"): + if selected_tool == "hr-lambda-target___search_employee": + search_q = st.text_input("Search query:", value="John") + tenant = st.text_input("Tenant ID:", value="tenant-alpha") + submitted = st.form_submit_button("🚀 Call Tool", use_container_width=True) + if submitted: + result = call_tool(config, st.session_state.token, selected_tool, + {"query": search_q, "tenantId": tenant}) + if result: + st.json(result) + + elif selected_tool == "hr-lambda-target___get_employee_profile": + emp_id = st.text_input("Employee ID:", value="EMP001") + tenant = st.text_input("Tenant ID:", value="tenant-alpha") + inc_pii = st.checkbox("Include PII") + inc_addr = st.checkbox("Include Address") + submitted = st.form_submit_button("🚀 Call Tool", use_container_width=True) + if submitted: + result = call_tool(config, st.session_state.token, selected_tool, + {"employee_id": emp_id, "tenantId": tenant, + "include_pii": inc_pii, "include_address": inc_addr}) + if result: + st.json(result) + + elif selected_tool == "hr-lambda-target___get_employee_compensation": + emp_id = st.text_input("Employee ID:", value="EMP001") + tenant = st.text_input("Tenant ID:", value="tenant-alpha") + submitted = st.form_submit_button("🚀 Call Tool", use_container_width=True) + if submitted: + result = call_tool(config, st.session_state.token, selected_tool, + {"employee_id": emp_id, "tenantId": tenant}) + if result: + st.json(result) + + # Activity log + if st.session_state.logs: + st.divider() + st.subheader("📝 Activity Log") + log_icons = {"error": "🔴", "success": "🟢", "warning": "🟡", "info": "⚪"} + with st.container(height=250): + for entry in st.session_state.logs[-15:]: + icon = log_icons.get(entry["level"], "⚪") + comp = f"[{entry['comp']}] " if entry["comp"] else "" + st.markdown(f"{icon} `{entry['ts']}` {comp}{entry['msg']}") + +# --------------------------------------------------------------------------- +# Footer +# --------------------------------------------------------------------------- +st.divider() +st.markdown( + "**Flow:** Client → Cognito OAuth2 → AgentCore Runtime → AgentCore Gateway " + "→ Request Interceptor → Cedar Policy Engine → HR Lambda → Response Interceptor (DLP) → Response" +) diff --git a/02-use-cases/role-based-hr-data-agent/app_modules/__init__.py b/02-use-cases/role-based-hr-data-agent/app_modules/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/02-use-cases/role-based-hr-data-agent/app_modules/auth.py b/02-use-cases/role-based-hr-data-agent/app_modules/auth.py new file mode 100644 index 000000000..bb97faad4 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/app_modules/auth.py @@ -0,0 +1,101 @@ +""" +Cognito OAuth2 PKCE authentication for the Streamlit UI. + +Manages the authorization code flow, token exchange, and cookie-based +token storage so the user stays logged in across Streamlit reruns. +""" + +import base64 +import hashlib +import json +import logging +import os +import secrets +from typing import Optional +from urllib.parse import urlencode + +import requests +import streamlit as st + +from agent_config.utils import get_ssm_parameter + +logger = logging.getLogger(__name__) + + +class AuthManager: + """Handles Cognito PKCE OAuth2 flow for the Streamlit UI.""" + + def __init__(self): + self.region = os.getenv("AWS_REGION", "us-east-1") + self.client_id = get_ssm_parameter("/app/hrdlp/cognito-client-id") or os.getenv("COGNITO_CLIENT_ID", "") + self.token_url = get_ssm_parameter("/app/hrdlp/cognito-token-url") or os.getenv("COGNITO_TOKEN_URL", "") + self.user_pool_id = get_ssm_parameter("/app/hrdlp/cognito-user-pool-id") or "" + # Derive auth URL from token URL + self.auth_url = self.token_url.replace("/oauth2/token", "/oauth2/authorize") if self.token_url else "" + self.redirect_uri = os.getenv("STREAMLIT_REDIRECT_URI", "http://localhost:8501") + self.scopes = "openid email profile hr-dlp-gateway/read hr-dlp-gateway/pii hr-dlp-gateway/address hr-dlp-gateway/comp" + + def get_auth_url(self) -> str: + """Generate the Cognito authorization URL with PKCE code challenge.""" + verifier = secrets.token_urlsafe(64) + challenge = base64.urlsafe_b64encode( + hashlib.sha256(verifier.encode()).digest() + ).rstrip(b"=").decode() + st.session_state["pkce_verifier"] = verifier + + params = { + "response_type": "code", + "client_id": self.client_id, + "redirect_uri": self.redirect_uri, + "scope": self.scopes, + "code_challenge": challenge, + "code_challenge_method": "S256", + } + return f"{self.auth_url}?{urlencode(params)}" + + def exchange_code(self, code: str) -> Optional[dict]: + """Exchange an authorization code for tokens.""" + verifier = st.session_state.get("pkce_verifier", "") + try: + resp = requests.post( + self.token_url, + data={ + "grant_type": "authorization_code", + "client_id": self.client_id, + "code": code, + "redirect_uri": self.redirect_uri, + "code_verifier": verifier, + }, + timeout=15, + ) + resp.raise_for_status() + return resp.json() + except Exception as e: + logger.error(f"Token exchange failed: {e}") + return None + + def decode_token(self, access_token: str) -> dict: + """Decode JWT payload (without verification — display only).""" + try: + payload_b64 = access_token.split(".")[1] + padding = 4 - len(payload_b64) % 4 + payload_bytes = base64.urlsafe_b64decode(payload_b64 + "=" * padding) + return json.loads(payload_bytes.decode()) + except Exception: + return {} + + def store_tokens(self, tokens: dict) -> None: + """Persist tokens in Streamlit session state.""" + st.session_state["access_token"] = tokens.get("access_token") + st.session_state["id_token"] = tokens.get("id_token") + st.session_state["refresh_token"] = tokens.get("refresh_token") + + def get_access_token(self) -> Optional[str]: + return st.session_state.get("access_token") + + def is_authenticated(self) -> bool: + return bool(self.get_access_token()) + + def logout(self) -> None: + for key in ["access_token", "id_token", "refresh_token", "pkce_verifier"]: + st.session_state.pop(key, None) diff --git a/02-use-cases/role-based-hr-data-agent/app_modules/chat.py b/02-use-cases/role-based-hr-data-agent/app_modules/chat.py new file mode 100644 index 000000000..1f2a4d5bc --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/app_modules/chat.py @@ -0,0 +1,78 @@ +""" +Chat manager — sends messages to the AgentCore Runtime and streams responses. +""" + +import json +import logging +import os +from typing import Optional + +import boto3 +import requests +import streamlit as st + +from agent_config.utils import get_ssm_parameter +from app_modules.utils import make_urls_clickable + +logger = logging.getLogger(__name__) + + +class ChatManager: + """Manages chat interactions with the AgentCore Runtime.""" + + def __init__(self): + self.region = os.getenv("AWS_REGION", "us-east-1") + # runtime-url is the full ARN-encoded invocation URL stored by agentcore_agent_runtime.py + self.runtime_url = get_ssm_parameter("/app/hrdlp/runtime-url") or "" + + def send_message( + self, + message: str, + session_id: str, + access_token: str, + message_placeholder, + ) -> Optional[str]: + """ + POST to AgentCore Runtime and stream the response into message_placeholder. + """ + if not self.runtime_url: + st.error("Runtime URL not configured. Check /app/hrdlp/runtime-id in SSM.") + return None + + session = boto3.session.Session() + credentials = session.get_credentials().get_frozen_credentials() + + payload = {"prompt": message, "sessionId": session_id} + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {access_token}", + } + + try: + with requests.post( + self.runtime_url, + json=payload, + headers=headers, + stream=True, + timeout=120, + ) as resp: + resp.raise_for_status() + full_response = "" + for chunk in resp.iter_content(chunk_size=None, decode_unicode=True): + if chunk: + full_response += chunk + message_placeholder.markdown( + make_urls_clickable(full_response) + " ▌", + unsafe_allow_html=True, + ) + message_placeholder.markdown( + make_urls_clickable(full_response), unsafe_allow_html=True + ) + return full_response + except requests.Timeout: + st.error("Request timed out. The agent may still be processing.") + except requests.HTTPError as e: + st.error(f"Runtime error: {e.response.status_code} — {e.response.text[:200]}") + except Exception as e: + st.error(f"Unexpected error: {e}") + return None diff --git a/02-use-cases/role-based-hr-data-agent/app_modules/main.py b/02-use-cases/role-based-hr-data-agent/app_modules/main.py new file mode 100644 index 000000000..b455b8496 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/app_modules/main.py @@ -0,0 +1,154 @@ +""" +Streamlit entry point for the Role-Based HR Data Agent demo. + +Handles OAuth callback, authentication check, and chat interface rendering. +""" + +import uuid +from urllib.parse import parse_qs, urlparse + +import streamlit as st + +from app_modules.auth import AuthManager +from app_modules.chat import ChatManager +from app_modules.styles import apply_custom_styles + +# Persona display config +PERSONAS = { + "HR Manager": { + "icon": "👔", + "badge_class": "badge-manager", + "description": "Full access — all fields visible", + "scopes": ["read", "pii", "address", "comp"], + }, + "HR Specialist": { + "icon": "👨‍💼", + "badge_class": "badge-specialist", + "description": "Profiles + PII; compensation and address redacted", + "scopes": ["read", "pii"], + }, + "Employee": { + "icon": "👤", + "badge_class": "badge-employee", + "description": "Search only; all sensitive fields redacted", + "scopes": ["read"], + }, +} + +SUGGESTED_QUERIES = [ + "Find all engineers in the company", + "Show me Sarah Johnson's profile", + "What is John Smith's compensation?", + "Search for HR department employees", +] + + +def main(): + st.set_page_config( + page_title="HR Data Agent", + page_icon="🔐", + layout="centered", + initial_sidebar_state="expanded", + ) + apply_custom_styles() + + auth = AuthManager() + chat = ChatManager() + + # ------------------------------------------------------------------ + # OAuth callback handling + # ------------------------------------------------------------------ + query_params = st.query_params + if "code" in query_params and not auth.is_authenticated(): + code = query_params["code"] + tokens = auth.exchange_code(code) + if tokens: + auth.store_tokens(tokens) + st.query_params.clear() + st.rerun() + else: + st.error("Authentication failed. Please try again.") + + # ------------------------------------------------------------------ + # Login screen + # ------------------------------------------------------------------ + if not auth.is_authenticated(): + st.title("🔐 HR Data Agent") + st.markdown("Secure HR data access with role-based DLP enforcement via Amazon Bedrock AgentCore.") + st.markdown("---") + if st.button("Login with Cognito", use_container_width=True): + st.markdown(f'', + unsafe_allow_html=True) + return + + # ------------------------------------------------------------------ + # Authenticated — Chat interface + # ------------------------------------------------------------------ + token_claims = auth.decode_token(auth.get_access_token()) + + # Session state + if "session_id" not in st.session_state: + st.session_state.session_id = str(uuid.uuid4()) + if "messages" not in st.session_state: + st.session_state.messages = [] + + # ------------------------------------------------------------------ + # Sidebar + # ------------------------------------------------------------------ + with st.sidebar: + st.title("HR Data Agent") + st.markdown("---") + + # Persona selector (for demo — switches OAuth persona client) + st.subheader("Demo Persona") + selected_persona = st.selectbox("Select Role", list(PERSONAS.keys())) + p = PERSONAS[selected_persona] + st.markdown( + f'{p["icon"]} {selected_persona}', + unsafe_allow_html=True, + ) + st.caption(p["description"]) + st.markdown(f"**Scopes:** `{'`, `'.join(p['scopes'])}`") + + st.markdown("---") + st.subheader("Suggested Queries") + for q in SUGGESTED_QUERIES: + if st.button(q, key=f"suggest_{q[:20]}"): + st.session_state.messages.append({"role": "user", "content": q}) + st.rerun() + + st.markdown("---") + if st.button("Clear conversation"): + st.session_state.messages = [] + st.session_state.session_id = str(uuid.uuid4()) + st.rerun() + + if st.button("Logout"): + auth.logout() + st.rerun() + + # ------------------------------------------------------------------ + # Chat area + # ------------------------------------------------------------------ + st.title("🔐 HR Data Agent") + + for msg in st.session_state.messages: + with st.chat_message(msg["role"]): + st.markdown(msg["content"]) + + if prompt := st.chat_input("Ask about employees..."): + st.session_state.messages.append({"role": "user", "content": prompt}) + with st.chat_message("user"): + st.markdown(prompt) + + with st.chat_message("assistant"): + placeholder = st.empty() + placeholder.markdown('Thinking...', unsafe_allow_html=True) + response = chat.send_message( + message=prompt, + session_id=st.session_state.session_id, + access_token=auth.get_access_token(), + message_placeholder=placeholder, + ) + if response: + st.session_state.messages.append({"role": "assistant", "content": response}) diff --git a/02-use-cases/role-based-hr-data-agent/app_modules/styles.py b/02-use-cases/role-based-hr-data-agent/app_modules/styles.py new file mode 100644 index 000000000..ca12f8d71 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/app_modules/styles.py @@ -0,0 +1,50 @@ +"""Dark theme CSS styles for the Streamlit HR Data Agent UI.""" + +import streamlit as st + + +def apply_custom_styles() -> None: + st.markdown( + """ + + """, + unsafe_allow_html=True, + ) diff --git a/02-use-cases/role-based-hr-data-agent/app_modules/utils.py b/02-use-cases/role-based-hr-data-agent/app_modules/utils.py new file mode 100644 index 000000000..d945893b6 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/app_modules/utils.py @@ -0,0 +1,14 @@ +"""Utility helpers for the Streamlit frontend.""" + +import re + + +def make_urls_clickable(text: str) -> str: + """Wrap bare URLs in tags.""" + pattern = r"(https?://[^\s\)\]\"']+)" + return re.sub(pattern, r'\1', text) + + +def create_safe_markdown_text(text: str) -> str: + """Convert newlines to
for safe HTML rendering.""" + return text.replace("\n", "
") diff --git a/02-use-cases/role-based-hr-data-agent/dev-requirements.txt b/02-use-cases/role-based-hr-data-agent/dev-requirements.txt new file mode 100644 index 000000000..2183d6b40 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/dev-requirements.txt @@ -0,0 +1,24 @@ +# Testing +pytest>=7.4.0 +pytest-cov>=4.1.0 +pytest-asyncio>=0.23.0 +pytest-mock>=3.12.0 +moto>=4.2.0 + +# Code quality +black>=23.12.0 +flake8>=6.1.0 +mypy>=1.8.0 +isort>=5.13.0 +pre-commit>=3.6.0 + +# Frontend +streamlit>=1.31.0 +streamlit-cookies-controller>=0.0.4 + +# Infrastructure helpers +opensearch-py>=2.4.0 +pandas>=2.1.0 + +# CLI +click>=8.1.7 diff --git a/02-use-cases/role-based-hr-data-agent/docs/diagrams/flow.md b/02-use-cases/role-based-hr-data-agent/docs/diagrams/flow.md new file mode 100644 index 000000000..1fd4a8ff2 --- /dev/null +++ b/02-use-cases/role-based-hr-data-agent/docs/diagrams/flow.md @@ -0,0 +1,88 @@ +# DLP Gateway — Request Flow by Persona + +```mermaid +sequenceDiagram + participant HRM as 👔 HR Manager
(read, pii, address, comp) + participant HRS as 👨‍💼 HR Specialist
(read, pii) + participant EMP as 👤 Employee
(read) + participant RT as 🤖 AgentCore Runtime
(Strands Agent) + participant GW as 🔒 Gateway + participant RI as 🛡️ Response Interceptor
(DLP + tool filter) + participant CP as 📜 Cedar Policy
Engine + participant LM as 💾 Lambda
(HR Data Provider) + + Note over HRM,LM: ═══ HR Manager: "Show me John Smith's compensation" ═══ + + HRM->>RT: POST /invocations {prompt} + RT->>GW: tools/list + GW->>LM: Get all 3 tools + GW->>RI: Filter tools by scope + RI-->>GW: ✅ All 3 tools (has read, pii, address, comp) + GW-->>RT: search_employee, get_employee_profile, get_employee_compensation + + RT->>GW: tools/call search_employee {query: "John Smith"} + GW->>CP: Evaluate — hr-dlp-gateway/read scope? + CP-->>GW: ✅ ALLOW + GW->>LM: Invoke Lambda + LM-->>GW: Employee list (EMP001) + GW->>RI: Apply DLP + RI-->>GW: ✅ No redaction (has pii, address, comp) + GW-->>RT: John Smith, EMP001 + + RT->>GW: tools/call get_employee_compensation {employeeId: "EMP001"} + GW->>CP: Evaluate — hr-dlp-gateway/comp scope? + CP-->>GW: ✅ ALLOW + GW->>LM: Invoke Lambda + LM-->>GW: Salary: $145,000, Bonus: $15,000 + GW->>RI: Apply DLP + RI-->>GW: ✅ No redaction (has comp) + GW-->>RT: Full compensation data + RT-->>HRM: 💰 Salary: $145,000 | Bonus: $15,000 | Stock: 500 units + + Note over HRM,LM: ═══ HR Specialist: "Show me John Smith's profile" ═══ + + HRS->>RT: POST /invocations {prompt} + RT->>GW: tools/list + GW->>LM: Get all 3 tools + GW->>RI: Filter tools by scope + RI-->>GW: ✅ 2 tools (has read, pii — no comp) + GW-->>RT: search_employee, get_employee_profile (❌ compensation hidden) + + RT->>GW: tools/call search_employee {query: "John Smith"} + GW->>CP: Evaluate — hr-dlp-gateway/read scope? + CP-->>GW: ✅ ALLOW + GW->>LM: Invoke Lambda + LM-->>GW: Employee list (EMP001) + GW->>RI: Apply DLP + RI-->>GW: ✅ No redaction on search results + GW-->>RT: John Smith, EMP001 + + RT->>GW: tools/call get_employee_profile {employeeId: "EMP001"} + GW->>CP: Evaluate — hr-dlp-gateway/pii scope? + CP-->>GW: ✅ ALLOW + GW->>LM: Invoke Lambda + LM-->>GW: Full profile (PII + address + comp) + GW->>RI: Apply DLP + RI-->>GW: 🔒 Redact address & comp (missing hr-dlp-gateway/address, hr-dlp-gateway/comp) + GW-->>RT: PII ✅ | Address: [REDACTED] | Comp: [REDACTED] + RT-->>HRS: 👤 Name, Email, Phone ✅ | Address: [REDACTED] | Comp: [REDACTED] + + Note over HRM,LM: ═══ Employee: "Search for engineers" ═══ + + EMP->>RT: POST /invocations {prompt} + RT->>GW: tools/list + GW->>LM: Get all 3 tools + GW->>RI: Filter tools by scope + RI-->>GW: ✅ 1 tool (has read only — no pii, no comp) + GW-->>RT: search_employee only (❌ profile hidden, ❌ compensation hidden) + + RT->>GW: tools/call search_employee {query: "engineer"} + GW->>CP: Evaluate — hr-dlp-gateway/read scope? + CP-->>GW: ✅ ALLOW + GW->>LM: Invoke Lambda + LM-->>GW: Employee list (names, departments) + GW->>RI: Apply DLP + RI-->>GW: 🔒 Redact PII fields (missing hr-dlp-gateway/pii) + GW-->>RT: Names & Departments ✅ | Email: [REDACTED] | Phone: [REDACTED] + RT-->>EMP: 📋 John Smith - Engineering | Charlie Brown - Engineering
Contact info: [REDACTED] +``` diff --git a/02-use-cases/role-based-hr-data-agent/docs/screenshots/employee.png b/02-use-cases/role-based-hr-data-agent/docs/screenshots/employee.png new file mode 100644 index 0000000000000000000000000000000000000000..17157c5ef77d1aba9b2e351c4518bbca7ec4d6a2 GIT binary patch literal 569629 zcmd4(1yq&Ww+9T9f|8Qb8ak zPMdbm``wy%8&<>MVVZ)OV}E8Q!7NK07_fY~F3MiU!CNP2g@prq^Ss(ew56q{qazj| z;=7sE)$?l}Wa6QzzMj0%laZo{LV*#;1j*ueBf4PTAi&JgMRXs)f+WeU_~`WtzL}Aa zMSg)HFHu;O#7+CNU_i157NizC@2*h^O^WFYvjMXrb9;`zO|Gk{6qR*Hr#bsh} z*RQUg;upz1`SA^7GjLhrM_V1T2&_-Rj7^0&7Fpq<4X;VFSBNQtC3RmY;XwyPr0$F4 z9O~k87s0K)319cm&s8p*KRn~7lf|XhP3*%6Rc!a$h5zA=0tbUa^GXVdO@IT_>4@X_ zE4_*w<^nuHCj!O7({HHZ;>25#t@;c!2rWWS;0_{fuKk)QF>opdHnE+w^+o(o5vJ<=5Pr@Oy=F!UIQ4FdXLcGehqlkz8Au9 zQAW)Y@kjub{N6(g8~7Qd)30qmhA=pcQbOHOSNGe~_YwvroCQ8hu2IaSFfm1i2HCQ% z`CbSL9ZTHQ^L@qz+qXk(tn$!fZKy0^h^_ITV<4TrXKQ#F$AeQ_L06neKf{hf($X(gj^FPQb4+1hqVssCehtV=kS~hh*tt2 zWW-1i`V&8srxy&L`wd>z4&Wez(nh&F+8;zu?3Ak0s%)y4XuHe&6M zjrDs2f@IEUd?4hPFO8Amhs2~YrM{8ch)x7zUeR@_uKO?}<{H5tc}nA7kj0(%(DZ$n*dz@Sn$XfVFm+@d&X zqrOqEL`#YjiMxr5NyLlKX4k%Dued*%NGvQ%F4ZZrYOlX#&6220B-Z5AS%mY?4H8nLfB{dx~eN!!Bifeia z?$4W5bIUC$w#@BUkyBC2V^*8b@sx&qx}YZvH556_${9Q`=`pFU!me_wswG546GKb$ zm)Fk_jTO!MP8o`C|6n$%KSM4Hf4FBIX$^m^DG5i0UZ#Xf&(6m%_9uDYW)egbwfMef zq^65zPnDxt>Zogh%y7*xIvCFs-^^;9F9QESW4)9pLb~XFo&d z)Oq=NBX#q2{pM7fPA?cB0uW+|%7x5@6Ph(VkDsxh#=T@TS%RamQG!i&)u%P~Ti4~a zs0u{p_-nbVG@}C)jruIU>%9gtkSuXqVis4!%tPjr{5~_JR7r`u>YBw7&1-hM#1r`*5`~8K*fq z9HX~Ic)=_+`&K{?zRh#D0~2HL78%dotS3%Uu?w z@ZInudko`=N>InUO6Kcp>O5`joTBSS8;(C~ea@}3vTx}hdo5NTUq0ZB`QnNf&$Y_6 z@zDOT_jG3WJRsgrEo|JfG&j14`UBN=!qMP#Z}M(aUqes8O7zdl(cGSKaQv>;_GRn9 zjNDr7$nO1)xahvgHkTi}%gl%TYd8LzU8w`Sc;1KzNHO>95loT3BdOk-Lt#bfMlC?u zKygG%BXVbWhlzz(LAT635J4`@(eNM@ zxd&N7Sm{aNvPj8?Mj`_ecD9XG<2Uxo_9{n93HMnJ-^g~bzRB&_3G*OXC99>_acq=n zq-bXJNsCC5Pu`Z8q$c^8vP3sHj0ElM6v-AK}maTQBeLHidP(|(AmaY%Ild)_O$qCaDV*OG*JWqz= zmc?cJpQf?6iROZJH7T}}*17Cvd-*gH%yWq&>AnTNwUt|_0f4%>8N90Se zDV`GkD#LYkXTGt+ke(D)d=(B6E;F6ynam!i10^A4MbzfJt4G5M&lS(U6NwZx(`?z1 zU4Ge-cA}Do%VdXvufb|_`kqJoDSQuGoKumtR=EqMP0rhuYL(_fQ&qEKCT)my-!gls znTDCv_;t1AMSbN8(~8!Z(m4H?lgc}_XI5nsWwvb{-9_DnD%UD#L};8$vuB3U)+0H6 zp2?fFk1S}X4?GKbf@a18dZ`p-o{1bWAXh+pRD&{ zm*EoO$D{)M4`xh_<__0BXtKYuZC%kT*(*tDA+)JJ)V!)D8doZH zn#G)Rc4a&L*`tM6%QvHIA7Gt(RPuB7z|H@(z^f8coY7Ofxd-vS+{SoAHseX-e0CLi zHPo@Q*iqH7A}5^&=5z3zn!*O#?N7!S%?{)Y@E^rpl^kDw$LC=7ylC4EKWWZx-ifb> zhxcM~%egTf*JJXkJ{CKkcS}9JXiFZhy|6NyE1ysGOh3Es3eX^vAfxA-^fWyuJ~{6f zm}~WpVP3+xe(*}5uDT0mVL%6FFbeintH)!9AXBgB#(}1CJb|rVUkiAbKYrnlcRiCM zn?<>?EP*exLV{71PfvRwfP?1!9K%~S<@vDxsjc6oY|g_UbFVg!X8op~w#xPlva z4gHvz0`$iv_GY{k>N4^mAuC%05C_v!rl%Bq4?rN$3){Db+={}Yx5I&VycEXv_SW3Y z%uY^DOipY}R<=gWEL>b%%uiXFSy>r@D;VuuEbU)AGg{gK?k{wbyLp5S?DTC-tnE## zEJ4tDU%#<(u;-~FnAZE}% z_%h%h&7aS}YlICxMARHc7#IN<31LBHXV|S-#0eag`Hp@5;!OL;j6ts`XgY;xY0#pE z(T2r2u_$IkpZQkV*M6K}#2c0jdU-TacA}F zUVPIUrZx&3K878Brmp zCy#wQ{~tH`#eXmEV@4c2Y4f}t8t4KGr^s@H1L(!?P6mQQKo%DfpjHsM9f=$c1Dn71 z*-q_uX9Dis3oMB>OGM+2ELtd8qqEQOexIzDhyYo}9SRnAWHke1EfCou{R^_La6zDz zQqbz-Ke;9Vet;PfG*y!G`uF*$@I*j>E{Hq&)2##uyaMP%9E&a_|80o?VWI-t^F@9o z>6Q#kTsVa6%uFH^gx{1ckhee#3b3RD^iM9gbG_2>_KtDj=@^{ z04+i#L_1#L^>1&z&m$ZV2-h!FHu~Y8g58<`152wf8}ssCkd*?E^)-K>>29hq;Jx`Q zuQK{?3l|J5usb+%2zTpg^#iEFc)BI}`&6}xLa6`~C?nodu|+8$(5(v*w7A{9-eugvoYUHu%$ehu z`pNbHY!W~HvoBdIjyaiHBI=3Fq?Lo8cZ>>GztEpl`{dyR;&H4a4S&niUtSDsEu7bl zhf?$Pg%(78tSFIEDz4YEr+lQLH8wj0ya>}DX#I zH}Eb7M|l}h^lD+=v+ul>TWRVrZ?{`G89;L5O@%4H-?X6_c-H9R)h4sjEs&L-_?6y7 zz2(-1-3H}cw_5b!GC`eqj~`ROHmjd&)mnCy>r+3mS^VbulujX27`6!(Hs9OUmgQEC z1E~Py9Rep;-D&NwD(hGB1>%T{H9uZgf$s|8{MVnU>{c1$-Ur8VId+AtFRdizrUy-< zk3WFTx9-wvV1}hNvrKt^yG#}kU^?z`CsI-To~aO!M{GpQ28UYBH#4^RhetY%SqyJR zGU;ee*1N<)S!~gyd~UbO+*wz-N+}k$6-6auTD$UMTX~g?-$t@Tr-4eN@-3bNzf*9c zH#n4tb2(nGzdN>2jZ)tqHHuP-Djt;3uvy7m251G`X4mK)(gZ<~M!{$p=ht=r4~$0+ z_Ad2~DFE}|Ts=;0IEp*P@X8(^8evdG!XSz3QZnqq;J?BdkAp)bjbY7^67RtarG#R` zNQ9#Yv-VxpXy3x0)iGOK?!6cT9bM zm5JT*_KKNye_sl1R+YuHwyARIRz-_b`WM@^L8Pd~5}XRlZLKI)1IhTMu+W-q-B_z; zK8p7!{8Z6YVYXXgYht+4JdRh{bN(}Rw&Iz>AK<1);S^z4mfze#g$%GMnfaV1D8C66 z?}S>#1tdq!rcY*vYv(bWf{r(upES8SIOOm6IL*g1>9{p)+07YGdovb%eLaI*cXMqHYlaP|g@P^lomy}})nY8G3h5RrepM}AGQN5SI$jDF%h~DaJKn%$ z)KoX@OTbhcmE(uJ)U4Ubwkn)Vyob6*`##-WyX59Pw^y%BIF3mthb)+k+ZG|47@tnJ zG4Gj)w%)(GhxO^)nD?L9c6OMawB0d11NmWJ@bgR4NfK{if zxkw)SUEN(k47rzmeSP@}$E_ws*GHSnu5*fA>gd?(2BM~0q%H;ClB3_1i zA^y>2L8vUrwD_SR+A(B<-)w&J34jx0wdw8$EQ!2!nNowR;Cwd47Qvp+o0iiRnyiMs zALG-XxqM>Ku9DW9|Lj@!wkJ+R&*>^xW}4|>s3ZMD(x@b2Ace>IQ6+PZ0sVMl6S0M6 zqjek(opNjW`uJm5+Lp8D1b6mC1F$F0PMIujjry;Oj*mR@BQ){+dgaltlDTr}Lw@o| zCssp+PG^4MIXag&hK?IE`JvCuT&eSx^KR03k+K^&vZ5czZ_n3iU{OkDli8w+dmMcs zjkk_jn#t)=?VhkvI_^6^{M5A+&YM~4h9X{VJSoU`c}3qe^D~tG3E&`v^|bSr=vM0L zlCj9n@5~kZJu{bA)4MreT~aKR#rEL!+}q$}L{PncPk2#oSz0p=r*nLCEPg#;q$yDF zKENC7>oT%Z-tho2+XX<%ubm=yNJ$6{RrE`TseTtJbG(PVW0GhK*4PHtReI8Rlg-sn z=NY6-Ybe<_qcly&3#b6UW^g?9#%{UOiB{s`yyZeLpoD{0|Ya$(&Y_eN&;;3)9R@?EU{QITHZ$Q=i*N znu4#a9P_Bt-KBr_CuWYDZ=KOr*ymqjMl}>LYd%#Xby)!D8ftB(MYT}Twl zZDc1<1;pGzHKaMbx5nXDtvIGBY!+-juiJd=j%J`J<7k0u#Q43_(Q&cx#3xqFdRe8p zjjN8k<%w!kqHfxOB8?GDi+DwB}Y`O82NMqL1 zVb*JQ*C06lZg$~%b#rw%z<*$0zIr^{@55ZZRN8VKWxHb5S-m-)qdOa)?NnM~yV9*f zd#H`u`S_PiZPli?`R`c&F+GcdqU7O&}$wAMhcLdV<|fNo98YH}d0 z@!2>rZ*Y7^XVc)u=QP`>)W2oLO=~#bhFoGuj9cFHQH?fg2(5$5Wwg2U z=tbca+r&a*+08iC;*;3zGI1CWfD|z;Cq>ETtZ4Lk zNq;g&QACjjTf>Z&MV0xi1RvxGC0{)wg$8LN4F&4lxnI;LXUv)(8Aek7hA2$E}q0ySkXZ!P@6ka!3$E}&iWS(ad z+ST20jI{cqXb$&@^8MUSn(1fVj>73VjbAa-o=89JilVH%+V5cJa$YNA(5X=rHCr|8 zk6|fEu@jI){8(>qLcKxyq1w71>cz3>Q?frB`<=WY3_m zFE6&wAIso&Ch=LMEJbKHILhdnj59JJ<2tpPjM#J2KE56IaY#UwpmzzA{@r1I{u$aaKa0yXeu%Ekc0|0ex?mRvVH3O~)b- z(+8?r`ASC1}qPt!hF?xJ;? zb%-C2(`)e5kxhHScsiS`sGQd`o9U}a9hM%&f76(OSfwacqRmC=a`1L1h-%{Wd*^qLd6Aqi%7lBz|8SNN|8Jb7KwQ{WZ?G8ImJLzngWPuJmXz3TmCX&6yc=7_ z^~o%|=Y`lYZu3xi8na}`mAhHAg`{xp#;efdNe*XA=$vTHfp13Jn%;Z zes^!*!15qvgQeBs@1e&tYB_E2mf1jTyW$w7luy17Wp!k0gW_wIQ8@|@#`HJ%kbaKRRQC@VS%ytB4~W?Vn|vbhUB57w_nlh4DA@fxz%UcVMc>`GBgs zysa;^IwD@C{rmME7#|p*DDwbB6@RB9Yf#&-1CcQMO%(xX$y5TCWPd_md^Z;iU_Bnd zP{jXYb?raz2is+26WNW<7Mdox;y`Kxq-}pG&Lhn-wAi?4N&A-%>UK6>p!mZh5$^P zYzfaJzr}=i(6}aHp^ouiU<4J^3vbQ5s~&uV>gL3TmFAsp-jVyi>o;a1uv-+B^Lgxd zWC;LdAxG39{tL1^0sT3^j<^eNK(0_ZwVEGh{e6L0gUTtdhlJEE(?G2Nop7k#EB}It z$iBe#fIiwv+>#N94q(R6Pg4^JgNAfQuLJ}x)4#3~B|NkRJPE2bkBBVaJ{7aT(ptab)$ovf`+yd~S z``SYo?G6K_0WtlGFko@}{x_z=dEzcY^%ua}F^AORB$&AG=+WHyN;9SFKCRzrIU< z;l@j-{~gDC7VRYWk8bBe1@M&>S>&#S_}4vufvaC> z0J1{PW(aSm+5(o`JqHom{)Mss{StX#cbrLYQ40o+6kvpl_iN670S_F2gJqD_|B>#N zXRnxn6`yQinEq~E78vi%1B~5Kp7=|ajG<%_{zc}mi^VH1C>6VpO#^R5=rz<2q;j_W zee>$=eIJVa=@nugwzMUDxO1^3p{=hr3uZWFxIp%RX;Y=0}^fw};h zL({cYcUjl}&hvkkHIOU7e-SyoVXWJ^kjnr%Vf<{l{)P7VLJQQ$dD>XFVwwq@n zT}ufR-ZPqLo&KXP1d>Fcl1^a?j%VPUP1>5Q?DBW$kxuWpf44J#vzPy^k;#V!+A^|2 zLX6lq-Q?DCw8lOtFZDf~h#aW#K2=u5P2w_Z?oF#~m3LuZ5JS8AvwaUay!AawOBWKE zI|V-pM8orPn{(T%rKj!?f4O&EZ+Yr?(=`WtY5z%@;7K%|abE;^({;;NIXlejxn0dRrtA|uW-a zX_gz#mSxOWoTW}=Gl~);rKJH%@UMhXx47+os+kOL;nq3tJL$=sIBRQUOC*1%l!hjI zCE}SYZqqZbpi6ID!IHnYNKv~Y}=Jjnhd-CuvZIonECTM+FB2r8To?08V#gw z(_)5=3;&?VVbo{PVHJCT<2-n9o?@)$V|+91=dqGc@DGT%0BwwOA`82-T&U2-xgY)E z5ucLpix+D&3EQrAzy55%B>#_e7%t(fTIfEkVbd)8HefWT@0PZ3kFznz3sET zHo0tz1~m`d!^_VP)?%3TRDpCfr5wJ8qGGoAk5l$g?N?)4h^%HO?vF)uy5I4DaKl!M zT$}M+HtxGD?u+F(@8_=UH0;{4eE5cj8!}blkSm?c(P95+MBI(dsGBC0yC(JlZd&;H z$Cw9mcrNut4>_8o=lN!{Ww$r{dz3H%DGtQ-5r^!&3Ap}I*@@mHK(h4&vt(SjU9y_d z>r}VGxaRsXf}?FVlh4DzlNMeC(GP|H1mYP`O?0)}xdZBl6aaj@(HseRE#oE0PzS)JMNMK(Z7 z)GvQd>3fMvds1}U~QOk^aVF;oaK%OmmJf49j#)U*l%G)Q<;2Kgb? zh_qv{tDCyVq~mORxdk5VWZkt+-s$WuC}Fk(Xr1}l&V=w*qRyZWEI)rgIB1-IZv{g&If2bDG2?fX? zA;=dQgsBe~c`@6LKKsomlEl@AdqM)FUHW0t4zIIUQA+~Ze3Az9^UoHS zmh012;_92Kd9Pp-pL)J~+Zn-Ebc}r5ce=wvGh2Vk=B@>&=$}sm$Po(_RLi#pW!&*5 zGw}2LQW3r$Cq4JN_5~M>s+Z`rSOzVHKJ*jmd)AAU3O96J8?d&;uK5o+uyUw1!*fmK zxYcxTN&s`?CZ5y21Ngb~>JRbkucnaW%|N34%EcD0bB~tmBEx(#iuJAaymucJZoXg0 zHeQ4xBgP44!4oSXbq;9wm)og{Tt9;QhakGi^l`8`Y&?=krBhKacvCAV z`G8!lpP1EiW;2Rfj;Tck$|e$c#R4{EacF7dpz|@t5g!VGz9Uc^Hw2T^e;AYfzJry@(!2(w=ID`9(5fLiKEGF5~3ytvzNBz?V05rzSkN zdgcu6+KvtI;{ZG@hg@uAKp4x~H1`)p45nhrU*`vrAkoqy<1%S9I@c`s$F?!Yi<7)H zANP*QVuSXZo*%B-nfq@m^bwEBTpGAGywAcB@4CSq@%4NRUDGnVRa z1yo)BVO8EFy4akSGZAP+wOvm09?W9lq|w;aa#B!B2edd+mU2v+j?&>m?V#hEEG9v$ zKS1+CXwN_O2?;_$)BcU+9mqt|0aB{y#^ksm1AwL;R4QM`R|A#H&a7}eR2PRWZgC00 zxBW2+cuWbjiL3@?TUb!JB#od`+0aOQevQ@x!OFufKC$|^v%Ahs_kzP}N11nRHD9`F z+iI#Y^$+JES>(UgSL9hk@~7cu_+F)*++!43h@^$Qm$bn3iuw=JnOcQdZ^8g7e{zA@f#);m|tI|pfv}f9`4;v`?kckiV?DGfv|D|+5~rzq(}R#*B(Tl2%8 z78OqOg7F?{92Z9$bnC;p!8}EVub{1gHq#mEflQN zSr2TVc}EzpO~*`eZvvlgn#%>PQ$VKc{B7Yd$rR{fQ_{u}h4FcW6$svC`msX+IF_^U zGxL^kjZ|A>kHORc91CFlQ?ce&al>41FAx|6TztgT1foYc^6*pfjnyxKNXK$7WDie? ztr=P=i($}s@fH`@)8r|wiEks59bxG)vGn8J6}4(^bc+rxu1v%eE$wEB0%TR(;wC`* zk|NN?^g?wtoWG>&GJroyr{Sc#X1jqmj?EQhH8+!K_!HW;Go*B68_C`Id=Qq_$vD1L zJJLeSscVZ9Uo(kTLT4m>vT0_ttF!r!<|Ez21x?Y(c;JC>}NR(v=j90+@wl zJNz!7mYU+Uxp~`FMma$iL~?U|SvnWc0&!IE95-yP=QNO%C`Ggx$(5rAdQy>Hartf* z=}qP`{qip-$U!;2ex`eSR`|@48_M}l?0$wsaaa(J?6Q3#l>Om-AKDp!;;tJLJau@T z?sXjtbm)ltqpnjrY$!+3C}eui2B45*nnEq*;+8$+BHdQ@vR64hnQo#;H-3Hino~$0 zs86Q^-!tb`aJvQM2ueVaa;io1hB~JDw)X>x%bjyB;i?Y&V@d zNqjo4evoD~*c|<~D~jvYsQk@bi|tAQ>4n-U(4O((8vY3ZTXuBJl+#CXb+A+Id{uoq zkCSf6^?tyN({9tnrf}aI*WD&s_yaDy{L&BNYc1BJEW`86nGh?C>le%vO;$^_Ivyv5 zWP|6&TU>SS>p|JF(UO<$Js&;v}-#koqjJ-`L zl4y5DOH>5-sSX!wO9O8gT7TH862Ytuu$nB=(4M!v`NZ@Pur)#tBWwjod0nNGILxR` z1qvru&Q~S75{BA((+ukx36+&kSAJ%0m^&!=RT!GG@$&8o=oZFDKwE9o_ za}CqAW+vyZqH@aczN1@#rW}P?{AAI3Uc7~)Kk!RoIdL0%<9G{SsGQm4E7tkqFq}mU zO}j9j4)EWs`9+r+lV>%#VRQmsNMl^MSDE{H#qdYZ?Xw1K^Z9h%WYQB!>(*s-c1)La zj*%tO;u31vw5Tk8lEbr@zS^84P3iUQb1TD_7 z_U)JC|JjicSmWy z`ed6w+vDb3w%^pRTP2})AJQ`-v0>LJ;By7^$pkAlsz;Uu^u?UI?Rw<(J%^N}a64#X zM4VqZDFEG<)n2DK@wvf#l#>+(`(QA_<@QmMJj8x~F$clnsOGU$oV2FE(;t_<2Z|Yv zl~N!+?9mqf=0V+B!?+?%9#*q2NM86nje2L(_f`+H0KMwBIIgygZ&s;fogXwr7gehfQW8)I1;r3TP_BHcTNPc^1_nBBo>2kF;x zp4b`D06u!bZqrSM{n|vEeetM38qk}b$TzTYpT}uCTDJ*FgI9;_5j&9k0Tk~_2i%J& zG8lw$RQ?PjaYLI!yDts5^+!};fv!5G3-CL=Qzdt)V~_mA7yZ|GIN2);fmmn$t%thM}lN_DJb z**U1-U%Eu|+~B~Nc7Ka4Ohzi4PGIm8- zQfT8nr$xHbVpPHjDd;JeMkzhD%6t8(wVlJK2FDlXr;<`a{EPcQg01VTh?D^k*4*D% zFca%bQW5c3m1mPiv{SfoRAb98)oqeRX(z36r^lu7T+b;uIMWYTZ@x$UxADq9IQ%sh z`aMnZZ3i&MJkJWG*Rg=$GVr6?d+!*lH(qdx8yo3f`?1fghQ6GJF2*8%1C=<(AJr2S z2ug-YqLQ(Ul}3Ow-?ObmzQo{RT0LaJ@eA=#{rb{J9w}QHxA+|l_~QutWv#k##<`O6 zUYY7Pl|PbodyAi|Orb_6;1|xF1(??rds2FJY4sQY?dzUsz%PkBXOABHT;7I;QLk8e z7UNM;klTZ3(NeJ5zx@+wu8_bB$cs|as}-ZD&w^i#&rP-Ju#pq7yD5*FGAEY^0c}&u zE3WKl>A7F;KNgdDs;!Ibew##5TCFmf76BJ|i{c-XmRs#o3BkTRCY&#~D`n7Y)`Xrd z$f|v+B8wKbHJVkKPY|cn^{v@I8Fo2X@H=1J zHRSO{c)35@ZZ04SxfF)*w7V$4dyS%ITqE#?i3G z+0B~gWzmU_n+xu_G@wL2?>=({1a{|T!v;hX0(zGy;!%=6!08n1N?JV3S2^!u6uq+R zx>K^*$c(w$(?5P#RRY(nl-qa0GlP3`kt4}l=;7~Rlc>-Q6;dHMgS0}dmiv6QyolM# z))(&|d~d<$vdo4{nAK&!v9j$r->~_SX#nlGUe)0nSCQ91jO$}?^k{iHa!=0yirHyX zR;$hI*g!1QsJiO}xj@^$WV0N87pYkB)~5Ddw_Fo?u5{}5$>*WejYGTdA^V@M)7~CU z6{K7Uz9(RtJ97JJzv@=7E56Z`?OdMO-=Oig{V<>fz}111?FOlSTm^U~G7dR)h_Tf`ugVU*~MC=CN$Z8_5AX@XraCPnE z$YQsZ`c2K-fjD-{=@NQVm^C;Hm|^uN8bxEH=ULt@)d`X*=U6H>RznBf^AaC~cevu=9QtM#c=%O}shR*t&M zorW15)-Qa=!D}MvEOV9)J{1O1-Je<7u(f+!I-p@r;!E^(zpl|D@-Mo|PtAMtUPsX? z-4m)+^kXe-0S_J>=65Q|b#7@yqt3SJWGLnKi&qmK&ypN`W(KlB6^?ya;T>F<1aKK= zJ8w8ZBN-m_CX<=v=+1{fWjrDduq-&T1sH+Im&VRe4vXD+avDn_U4#zAYCUM}!STY% zqQX_W@5M~9nVS$S`0VuQ^vz>kvM0E@?tD9}!FVl<*wu-z` z`Nnr)PeeDA4s+;S#p!__Xwjf#x~%I|Hj@L?9<~C4x!>v==LIK&0}AwMq-8kzCx@_hg*fDm*QEk1XH$_yTD_Bqs#NT`i!J@@4=h za((3cdY|%q_$srr)F&CQ5uHlLZ97h((cP)ucE5Y!5q<&aTJ?rk(O|c=#rDeC{vd*u zq9F@*{W+q?9X+4=rbE--)N3b?^<_I0QUU$#SR<>paDQ7%5C!CqKp_+L_RDWTGgp4X zmg6*gl6Oow6ADc`N)brZxXZ67CgEaV03YH3v_Dp2kM{W@2wooU6O4G1_Ost055oL)6}qn zUvZnd9B8# zn(vjBC(@UlWq43lQKZ$NY`ff9l3w8JNK`e+@o6B1=z!oO3N8bPty)7^@sqSk4uR#s z`N{Ndj^0g1oEO5^ZIPxeVs_g==n28CizH-G0n=iRhKaHtSxtGL`KwIY9&{zKS6=$@ zo#+BVsbpNqa#qadYuwg}2BWMWAivppW^Jw2c~u~nMBXg>vZUM&D|GZkgncVA+XgW4-w(cx5)kf$?t z3ecfBkNqW4geGo;Ya;=ZnL0aJ6C^Y0$^s9mA%KET0l}4&nZBDU0U43N#-16pU zAJ1EFj^Mz__O&f@qk**OpxDiXrbgXn{eUrOL%qa9{>M5;290N0K)J7T``GQ& zL&kAyV(CPk#Xmwj>Sd zUws~5DQO7lyO+0PRdX{c9c}u(3o>QyrPFX+4Mt!u^So*912Pd53T%v{MWdZZTS0M_ zTR*a09rU8+n(AGjn_AY6Viwmm%+MAys4?h$4DkF@wj|#_0nfYjGL@ojJRU7jdFGa+ zJJ*d`BkJkgs*ot|++q!8#|(!elu?%WI)G5LqkOop2wBZz5X{EZjy;yPbL>PA!!>Jn zh^uyqEvBEv*H|QRIVG+i%b%7Pe004@XLYH1vHvaW(`L`#7s;K0-scsW0R6jszz8%S zP=9GL;tZ$wz1|n_$*8^H0nc+b3OH1C?2+Y6sk=R;OUPM(Z)LT|(Z(*`m9CKuR3I2f z3kfbMcL6Ly7u|jL802tqM3Ar9)V%()F~3hhX<`BO*(c`f8){-6Pen3uvz4pF5$etn zIs6)1)6V{L$f?wU`}qcWLwW;I{ufmuPKJ1JzET~J`82@;;@Ij7O{WyAW1F$w4~kWt z(YkW{9zXH(HMb2)4Q5D=aB-|tD$Pzk*C)+B>W_f+fojtb(j)uf)}fs7T`?t^pJfRo~EgW&J}JJUe8HPRBJS7rPo>>KL-M=f;DEcpVcl=`Ee7Jle|_jMonqdbYD;GX69$tK{}%)6WUi z$t8M=WGTP^pw17N-{3RrD*I!QfJUo!Kd8dn_Ord4ZT8Sgsy1fU^E?mz!Htx)FvF#^ z+(k`xxH(*?S{e_WHaXlRG?YtwW)l;0rN;t!*3eAl`!GTg&2m1Yq0#H?vC>=M*9&45 ztQrkBh{b-CPdTOMP_g4Bv-H|jlSfZKlr6iTcgi73t|0Y~Vj9`%YHCmy!1>S^Z;t}B zXK|zc83mEBK(~6W27z;7iMiumYVA}Wtc^@^?V;+oZI)xRd{cqiL`vefu`r1+vQ#l2g5Esz}p3dOhjKz@duqK&bNL$RSeeQs!VhT zncKH*#_cN&d*D|=7<*@*o9~)zjxog&b5%4r1h!gz^flF zwC}HGPE{*jqH?lb#@Ttau?Xn)UE~>h*#jQ63<5o<+7e<&z-k|pb?#3JIY0HD^eQcI z+n#LtqW^qG;fJ^@04ZF5LW%*7iHBK-w=yWrOtj>+171D~zGrkiQ56n*q0Z+&eqZ6- zE>|)LhxV%q;?Cc7PhlDWV3AoQWP*O{!XU^3HIfB}x+n*rRQi1jS^V7b!0BFopO#~% zw@;hLJdN)Cdc%U~Bx{Ff&=%SP$n1UuUhPIwULGv;_|lU=VVe&_jdwJ%=TH?TfKw#f6NkeWwQV zo7>~BIw$8-^6;NW1uU(4rhCRa*I1$(aqnNSWTn%Z8g_Q8Y;*&aNtsa-ftxf%zz_Y@ zpUCP^)2p`^mv0B0H2u!pm5fzs^#f{Lh$8Oi#3f0nG?mtCZCz*_?Y7)(u!(sb?HlbB zkVJWyijuQeMnqB)L-?(Bo+;ck+v*m1w%9fMw``{JRL2_15G6GCCbG3iHpM3gJj2X2 zY)C5Z(@q2mkDo85j@cg;d4qGL;#cZyM;HGe&b~4%3T};7Ktw<=2&GX}8kKHPl$08} zL`8Dw8X62hX_Sx_W{4R|ItLU4loS|dq)QqG>4v-K`}8~K+o1;}A3V7Ceq*im zu6M}E-E#7fVSOG>8cC1qFiJ8r@E^BD{RclgjRYC$-b6!i$JJIoF(tLnu6MF$pJBn~k2gWSe{O$BBb$e2rJiHY<`_R> z2d&e(u^f!7KUZ% zQvD1WmT=~Sv3KJ~NTYljoLHDgnWnp?|C75E^{0ydQzBcETE%3V9kX4=Hl8sx-wR@K zi4@p)uDz_>VaB2H+?{+#l?GL@WgxJ>ICATPW_pp0r^*;nTn>;PdxTTp@<;eYo=Ipp zU;XG?W8tV2jzG}*Kl;-C(23xbVcBe#Hc*JOxx=sZZN_uIYVa5SsgzDt7s1o>Ly|;T5@is);<48f&P=s(PEi&zK_d0NYTe5<(@mgC6EzCK< zSTkKvjh@eRv7WbJshA4S3&zRMLRqRM{!5GU|EO{=oCfduuKdi~9KcmEaFj8|2zqW! zBnS%5a|KY{V-VFQ*&c>1U$y%c9X|sSM3IuL5@4Q_x9k^w-RnQYkX!<%tw3g{c=ggx~OaQE(c^gxY_pC8olrp z_smPWiPFuV`yqT-4Q-QDNfC`~TVKR=3>*Z@g~ukLL>-52f@bix2NUe&-##s{ z&=I^&Pw&GdA=P9I@@F&@H?zC;E`j)4_hmL&`ygGE;h1;g@Rr-0fO4>E#R%(0rCTWd zqIz+#Yy_WO2r4Yzj)|MmAP3CbLyGZgF2e=)EC1ZB90P2j8IOePGfn3(7P;_-{e2VL zzRmd49wN1H`R8OnALZ(Lay0m$OBc5M!)0%+4W2c|)FogSl3C-Hvn$PyIa=laA~6-| zQfjy5li}e>`xS5-^p{j`0a-$#X|3ObsAKm|$m=l|nXT&IP0ofu@zT4SJ#oR|on4Ys zD=$#$CpP0m0}&6>6Bzp2I)pUk;wL_`nA4!%>Mso_9Z2UdO>fu(6m}h;v6l?JhM!n^ z&I1ixM^u_G+6+7Me*aN}8l@?xW_qV(r?*S^ zj{vm*2wO_CS|y&-gEkK3wUz~`v-0qscwWE%xZoY9CbW^>Uw4+@@aZD%D6(9S3d<_j zJ#y+92QJb)BuLjU_SLZd8ExcT>n4MfG2{0HyPj^PE0@(d>g;r$p=NX_1EsDSWQ4j88$sqhO@%dY9xFOZ`Jfb$W#Lkj{ z>Dy^Fe+w7nqG1NB>OFGiis^k#w&(52SifXEnLh8}*#NB8-m{7?`Zd#aT>eV3w=TZg zha+g7tf#2P(5O)-?D8IY`~Hx2rM-&xqRMI+B@#MPNG0lzRGc}TsPpnGuK^NjJDd6T zg!d-8oTwVuLZ~~NRVa`m_BXw|Fwmy2=V*C0vzl&M(QA@qvEZ~w$_L~f0_mNu3b%PE zMW%Cg7!-Ke@B0aFFrfBJwck~;Hv8Lsa+ztrWkb>&{+jV{)iEa*BsP^C^$Z6edTAwv zh*w{jEeVNwvEs26gZPKU;klAJs3F7d3iON+Z0AXaKPdn%Z^RHW&>0s%vR^zqIN8Bu zRD4``PO{2CeNekZ?j&?|aG^|jz>HJ`vxHrKE8{E5AY0HVef)#N5h$&wQt`-OTvs=w z+!}XArI1t^&HsEkaPA8hk?O>94jzQHg_~XiUvepWfm3;eRfsrrFDNYxOcf+xe zZzJdQ2Io-8i*u-vGTtv!fq3+25Bz`{{ci3 zKyGh=I1N`7J_oe|QK1OR;4SbFykS#L!O)dnrZwAT{&wR101LnlItCriAi<0{v~e@w z-T~cOcETAY$to|**Q*dPpg(7J*Zlkr`;B}OSN&qs>j11B?Qut_-F-h%%V0fQo-UVo4aqi*eqUu0}*b=#xBa{*34)mR|2nh{u}`+{gqfb=AA z8h95HM*MZkWW>$e<|Ew5#{A7|-9)zW(xaKXRXCkMw ztR|R&SxOIkKd@*+bmaT1cRf64ef@cTin7Ohi{k!?)c6PO=31Y#?V~#^mWZa2ow1a@ zJ=(;rq_SD)NztQ;QcY6i64qNKjNV&NK>jC$eJq5+xWaaL0CHRNh`qg>+hW?R_b7*v zx%kXC6SFXSem(%&{&ds7yLANHeO;1uuSq92;?A=P(_CltO=gde`$M+{U&hsj?lbFW zv{@y%SePf4S{_@^$MrPsswP*>Kh7(F$LzyGedIRF4MoUz4uW&H4F}l@fynP?L&g5i zmAPZK@5Q04aZz%<$;pF;N54V`*@V+KWT+| z-cH4yMm7)1YO!Rit8Wo1?`rGpM`Y=q7Q=&cqgiF>t9m-a zk_(-G#2gR7=AKz;lxMvUc!n zs*`zSwNR=j=s7|g-G*k_-EBt8o^+M?boqGuzbavqdL3B&fYQ31BbYU+QR?${(da=o zFf$_GW*WQ$oxLxkWsl&q{O3CV3oSnVzh)_=&YtFB*Q?VPS;#%QRvY_(D$W60k47fZ zNt-1G#c?J{9Tc9ct-K^!d{WU!zR4>=zLP-lcP}_u5j6*~YOb|>ME0wO z7As2dt)$~Q7JDppdjmHNuDi^;H!6#U>X{vS^w^|#z zz^K|}oyL7J#f@S%m?3S<_vx`wrW<;wgLCm7w36QI<*DM{s^@d_2NeqhhAUSS8jeEV z%la44-}lvEdhhhCM9|siySxZ5a>qO!MyUezf)+DS?y6=cqMfFi0`5*1-dC=s-L8-=L=P3(b3+Hb>}l!lPloj#yE zGBvJPu-_kAJiV2M?~>5DC)%u3{fLG-^)VTuz*O$H<(m>6T2>S&QW_4|nTjn_&+8Xx zX*^=X%lx>2T)o`AvI&zh2I}%}{!kJ2Y*~`lC{_U&bal9aUWBPUnXae(sk^@MRx|11 z>Suj=^GQVy^?V1dz-X<&#M`y!m^W+fhw4WONnLU5@+quCr`GhEWt3Hp78~|kEX%@s z=k5Z!BBih{c3qzsqFBU~CrB_ztaLQG!L5^X4s%21k@XD9V;y+Uutxhze$ThQto^b& zQUJJb$T}cu{Xr&9bn;%8tl_2*Pjd6h{)p_ z&0$ErvgFLgJfmIEVpHC?70%&GgjsWZ#!t4Zy%s3e`uQmaPF zLZE+LpY2*%lhr>Mclj(I3TMVqeSr@!i0j)~?bW|W)*2l>A@jh8%~0}ng=KeFl)jo4 znKaepCA#U+(93|(9V{B%ZwI1&3~JIcQq7#k) zpa}+LLK3ZJh9-9|HFyY|tW=Gw0X0w>jN#GF0Cn@_EcX8fPCNv&Ct=?lb9;Z^qR01v zy8Pme_R}Wp7l0=CQ^$hi5`;i>>19RIn;LUnm@5FmU z`NH_3fEPl5wM1=2`04H`lx|7E&|==)4dS9*e3lryke3!4>QUFWvdwI#&#`PjW4U{q zb!_@7CyP{h76G@S;TG7iW^J6yON&!99`HTNK3W~D=*3?Y$gW)WK1W4G*RV5^<9m1< zxZ1EgI@i}RyI5-!XuFpr|Uah^UxyD}M+tVZb zc;f8zC{Op!D()rd?Ci?Mo|A+k4cLa^cay|^ElrNn3R#`oc-mYl29y9AO+-QsyTdQ5 z%uQs7xC`NQYdh=dXfoS?GgOST8Y&jDVwUKKech(CLR%b1(*lS^Ev|1~mKw=HFdGB3Wlg&%m};YN1; zNL9v{O4lrdmzt$75v3n{9wT@0MMQRoZa*d+dr?zF#8F>DS~~UGwQHTK9Bo;RjdFW? zdo|0?%+^L9ILRw&%BwEj-ZWb zzRUG=Z zAmw_*Dcz>lv+}EBX!r}cIBiGeoYqA>O70G^BulJ#@mH2&^64^{2l9&p`8eJ3>dH@6 zAU)fFX`|LqyF0}qSGqZr)?jQV@4$uoA$hBC$#1QS%&D=`?l}E+W4AF0@a8;D^yrM4Lpbm=1j#TE-5jzB=h17= zFNzNPzb@IIt9P3o+$}?rjeoxWqzwFdIL>v}s(%72MbKAo!0;x#UWSHO=(u!^m#hGU zl1L1JE1t`upd0aJ85vbUy1c7=s-#q?SyYjKxgTe44H@aAON*d8e@}^2lS)w|k_mg81lom3oVxV-!HO=j*@BX?I8gi-&_U(x! zHGh8ESYx2(3@;Wde2*!5UIaROebCH?g>&??spuIxB1h`aOvZ4+Nq< zB!WJ~5EiK&h|JrxpNmvPNLCrL@0sRNs-er;(MS94aLIe}*dfHuzMC&F9)|C(O&dXn zGG}_yWiB`DFz3RXtUr~&ITzHGUn>lYIX*;kfc|GRn`dB=$tlZD)~N3~ZM_>>O(DVW zF@86|>fBUV5bY{3!0wf_`}xM=fcy4s)Y@cnC-lfnsf$Xpe($L1bXzy6`Qggv;o?GG zVhFQUp);hZHpw#nWnKZYLbH43{_(wbXfTe~4`NtBQB~#B>HV3w zs|u3P{KABdacMt}tA#psm0d{S@NF!n;QwDj6yf%~CO)e?`Q~2{tl%tgd>}p3B0%?j zDCq%yV$=O;-!RcvzmuZH<*=Z*n);-Mg4(M#E|lYBO8FF)P>l{sdrk;;?Q={r|W zV=Zq(-8|LTd2(ecX5qJ4lnvU1BMjiIX54+=xN{mQ_@H4jJ%_4CtZ4md09-~n-Wk(q$3){4fn zWIsrhsht*i;^*qqSj-)K5V7-lgV}iE*NX((&xZ_goG^_+b;ptF2O!kNnpqeAN_;8B zt&#czs~wRFAMx9IwNbe)(spwk*sU&l)rxC>U6LhHh0_}N=n;x7xlzdNEbGSL4`r&0TW zT1I$P)OXv-cJQqPs%oP=h&EcI42b=c_xk-3N$*FL05g1*@xYAcYGu!{0T$=tM|F2S z7DeC5&}(w((ik&{SiQaAHC(jd3?qkYf_Y5L$F4(CHCqsALoA^}DWXM)z~h>%+Kpeu zp)j4!_jk^9IsB-OY>m)kV?=4AFHtf682*UHCPAB6X04BfHJSNUmnyfhU`dKtSBj9H zju24QWugX{Y=*2l=dbD(*{g&*lm~>)d}j`wGZneY9vnm~huZ|6C$!3}RFICJAl@`R zXs?^uFp7ltaV7hZKzxcClc(bsnPog7hbhTQOfFkrKHs7aGD>*?TY=wwsbpm0P*HC_ zj@Qlg6vMF8!n!h`K%B zwR+iguCS`}a-i?8ZRk>15T-x3=XK+pU0V4WgqN-oYp>c8f;OgapCxUlc*{3p?M`Sg zI+r+DmEkfn8Ru3;rbh+8^iMhS@70bx;sRkr)zm8W`k(K8PG5-WgNHaU^M9B-sZNjz z0bFqE~xbG^4QHgTui6VJUAP`7o)-!P>IQgpE0E?jeu)B1@VQd?y3VeCWNC z{_?ckSL&DuzO02W%vytKX>psLsnh-XzG9C~BbEc-y4#Gu-NoKe!IV(=XAYS%Qq;l(r;Oq0olb*3t0AY9`or*#osar zQ;Zfw*ByZxKxx3vkgeuR{YqbUc>iumb7y#3ZA1_)G34-cKTXV93%J_&U5i*(4gape z{^}}HMEco4rStSnN?>rQWUhQ=NS~X%;@JYvo$?|NPMX%}50D$-cB7s6{n#7qEn5Bu zRN4Lq=aHfvZ)c#!XxHcVM~*W4Pzu{<<~uP!bjdNCpgX^^db!VAe5w~0;QK2W8XiO& z3$}lsM80hdnu(-}dvv~X%sQ@eX3NL%`WKD>r?GN~`bHkff2(vs8lfaQjo?$(`d1o} zC1+<3wtm5RR-A{uW$vw*ra~h=oeJNcs)7!my{nc*wh))hMPSGw(9n^JL-OEdyj$dEf<>#dZ z^!jKWO!pVra2hQ8<-%AYiHM`FfiXhVp{_4!{P;E>Z4+t}!bpux##w(rPj1{Isg6D-ZE7-$a1 z7x5EADnZy8SKB%^yNYhuJg)TL?z{z|@p|PiZ?WAO>dhEff-Q%a-Z$r(B3` zomP1#CB?nKpdGaGQ*r#G6(}%781~j*k<(mgJg-;$D5>EtSl-on_?LD+%KSQh*#%QQvZOEDC6k}Z|WmDRT1jdaGCKB4`$a0mMeYgyuV6}9pC zSR96--gqEP_?a_Xi9|)(oyaPrUM{y;V?h7VV0O^AN12Gxh1s#7Ss0p|n)03@QK+=t>-1+iJZA1NBBApiOUnJ*90%rL z%QzWPl0y_zpB5;(Pa}30UIbxsFGr?-$PYl8HGFr zLHbrlCO?rOqc`Bo;1tOBFS-H~)Mxs1hgt&dwf8nGG0$H7Og01mq@H7V^XwL5xh~xZ zWT=V*yF{hD){sm2+?zF6V+B)SOS0HkQsC;0=zcdKpNL9 zuKxIOr)lR0q{wH_b}V}BpmC(tN6K%h=GgUMPO##MIdoX6))YAV!w|hC79o&9!5hr} z)nO|Sa)M|rHdo{#wpXO`=CA2ZXiIpzYfRe$Tu0bqHh0yybp|^dO1+?_m#_P=a!3mg zf_4av%cqFqqwNVw96b^DFcCLvjhNH%RC2~qpK?R8*kS#1>^QTRkoDnq=FV;j%b%C} zA2nUj4v5b){Ia+D)(G*q`{RUXWu2$Wq1>Wpr=WgR9zp>Buri91JSWkWO zRB7%RgyUGPx`>@9L^=SbC%&$ByCsZH&1J`ToAm?em3Xm2Mb5LByLUc)O@r0Y6!$%o zP96Y+gY9};Mx5wH-0BGHeQwBgI8g6E-dU~aIyA?hp`KR}f$D0T1@`Bx2-`)hf2CBV zS|9DBJo+W)f~f;z4mz#pY;AX6uYGwU$^TA<2$>>wlFeAbm_No6`ccfum>Y{@y!3sY zA#XoOSNFa=1rc{Z@;v$1d4@Z}dZ@T^|NQ;$nNm&c-TQSqEgjk=VSNX$Kw*!IzEO7d2bv9CvWJO zwEbNZh6ly1fFiZ3BI>YX_?LBm(PRH{76k;0ph#-I8|{AF}8 zQ<1aXCigZj^HtQM3+aC2U4y8pQV;wh0#mlbPCzP)Hl(+gSi@LlYEcdxI`XOu68RLJ z3cP85byfWjaJ}&YW~r^wEIEW*lzK77xQ6F-&cpcg&)QjDdNY_G0N6tN_^^iCW$OOT ze6R<{$*vdmP9-Us0Cu3s$umJ^o-aX~h`Yn;uVOoQo>dyLPFLaao_g%!eK2D{qx2m@ zb1dQYSgIwQ$v`>|3{OGos7K?e)lB7Fnv{8rvd)r{QKo$k&09TJvXm2?UGKh`1+IGwxIsXMO5Dy< zF-R&7WUhO&?Rqmkh-B2$^H&p`Z~p*}(9s@dMxdbW@#O*5 zzh-wR2e^26_NScAj|DgY89TM|+I)-M@wQ+fd8&A2G%*QDS2$hb*4PDvW7;C7Dtfzz zyM+WSZ~cEkQ}$EOm9G*^SgJe4RsMXx2_T7VC8}X@<4+7kNYF=Igzbx@mwSG=Gls=V zg9^r7ws0dZh<2Q=?rFAUtt2dP$AR9s(mhK2((82wsj3huNp9N}cPGAI3YjNwu1ZV` znrsx!KeLCP_~2|;Y~aaubnW7o+q4G)rD4EW^C!5078oXei-Lg4KeI#u}+<2%Nv^E#hduE z%C;So@vWxV4`>@Zl{zgTmnI8U#Pf@*%ezxwQkEY}2puZ+iNBHrA2fw>$x z)?_0hM+wK;B<*f>1g=W>PJIF*q{e~K>h=8&mRDolvwn}jX1PqzBp9Ez$UYM1WzVi^ zuC04f7z^5LT;Rlq*mA;n1@gnLc)*A=j~VK{;i}lA)wx2Ek%;H!9fwH;p6lS4E9UZY z(r+DaPplA95BOjEIB8)Ebdh%HQ#U;T4#OGc429%9z1ySQJYt{N?SKSq8qanR&wu2e ze^r(rX#sxFclyRZU(16JC^S!NUT{_fZDE)59K#0EFW7iyy2y4-6b6K=s0W6@nGbeA28P1Jlc_gx^-PJ6c0}6{<0%7@PoNn$}9HFi3K`3fzkIZ4-C_plDRS)khN^U_%JFu zmeKv|nsBHcl_Owo zV&7gbIX)WmYa>8T0Rr262mki&7g%J{$IW;B6_*fm5-kT;L?G3B_xAdXVWIyYm{LTL zF4+9|Mv3=!hUDML>&~r8=%8NOY!j%$<*%-sw0ZvqI6c$gV?A*6xL^@a}bVJP`-ZOVUwpn}Rj#&?;!o*y1eTiH>rF1YSlJ{#* zk7mDTcR|4xeu6()EKAzkRl7|CO?y6HX(tMkmuVY=ItiNma-f?m{@VTLF9DQEBdJ-9 zt2(l_`jKNGRy5-E>kraE|%zZ7Xu%>nWUt}ioIu0y}@JwU@zloab867J5S z1KK(fJ!~6e{Dja^bna{QHTIdPo-8_ETrbA9Ohj(5{0y(~s(j0tziyD*LIyA9L*~pl zc6O^XvFh&xB6zFP&{JPN42IZ}`rz(dw9jNd#3n~TEb9Ydpk#5{xF?q&B6}`n<8ah@ zBPaeT3qL%0fuRDIGJebcnw{xFc%zKhLB#`kRSEVA+YM?)5udD9YpGcGesX@Mb!)?c zMXH7Fv^rx^nk9kvaxd3^b|bx|?Eiara*7C09uz$!f06|IW3a@FoA)-CCo)I~?A7|4 zGBHVQt6tOLwmG5?clD((9qQVCUyFHi{r(iIV)zp-wgYSkOdJzaW~>*(t;6KRnRhWD4i?keV%brp60`&NCMx6Z7wq?@_tbs(&?W5HBs_5NmPaF9I63U{fwr;x z0Y~(`Z*VeXnwM_gs@sefUEaGT_9~yQGs$4>B5HJH6#b?>EVmcS+ZbogQx=z<}+svHFri z?Af9=d@SK>(?A!9RF1vZ`amNM2ExVetC5Q_Q%6w$#>r1lN2*=&CeB+%oFAX{Gk~4 zD?b?@>+(G6zFHBi^v|LG``JJL$_*-mpY?cg=%4R(36%jcxn8Qk1UMU0P7B#su*E5y zgH(JSvAl4*Xh!@(3l<^}LeYR%(>xf4l%l$yVWa6%@JQ zyqF*5E;q6@6BRQP7VH2PEjkLfNVpL57B*7paG8qfjz&Osd*5n9*S-FnNceq5#cM*2 zL%l@Y{IG(=D2u(mnmB$FecRqyj_C3)90Im!zX5550V`wmYHjNJ<&ldB=>1|aF$h_E z8?VQb;++lFDr)U-+IEGHR+`q>`442U0SDeo3d$*>4hGU=&7m*2J(mq{ZXNG#FoSKC zy@<}Crq`1sHebKr!RnjDWr$a15R=3!2~Sf6N&!N_vA-pY%7oMy|EW7|Iz<#(ED@7( z%iJ+p47kWOYgI%kwf)oC)u_(uPD}V@1IG%TcF?NCXnZjvLNd0{R9L$RTER zB>OxI0LnM=Kd8>Q3ft$^?p!}+%8CE==u2RkZ5kL~RU57I_+HDfsPLaG#vkDN6R;$Y zdrmC$PxbLh7eo-9^L#Z*ggWzaCJAi)X9COP013ai+MO#C-)TVLN&>cJ2_D;{=J zr!1fBU0&>my-rXN#03Pr@H8KW&=OF3oxzW8tAPx+2yqMMBWEr#}jrW0&rwd zSuQ#z@dJtcsSDDj6ysEl_p*mJHy4EJqM}znnifA&M*-KRALJ$u7Kvc(qYo7(n!0&~ zCz8E|wh|8bt{C6JfsWH_h54uVRPCUcKVdt1v9S!JfTSX3(K*I7=q({9Snn z*b0H&cE8Pg5SShQ;70NYArQ4d_0JJ@NL z8MLe5_5$c~Q(>}{e#Vtlx}CH!G(V~QO!x~JdFJ5fp?hW6?r(>?WA)w&z)UFnC0X?Q zVu&f$hZV~>Qt2;7#UzUzvNM|@50>YQUYuE7Er$Bq|1=|LMF^b<0EY0pd_(2lWRYN- zF^=Y;+7BZ~G6L7WF?UNF?Ax8uALetL*%Fhxu>DkwU&nt^%4GFX`{$vQDo33i{Luoo zv8qD&7FLfYW`B1LE>4^^wwlQhtbKpBGx;GfQ`7hGt8Xl9>LSuFHqsQz)%?!f?$QRm zCwT`(irDqYv=q+s@qlV&-6yxjE(iBz4*UIN;x90i?XhBk@}hxab7*g#duISpXnRA~ z*s}%LJ?0bP7x}W#r&z2CK(FSarlp%k>udVC>~!}o`tHiu zPG-#r!^SQZhC=he9c+Ou8ZhPDS(bl4`Mr&C@<`J`>B+Q%X8L~Q5N!U5DZ(dxTqlHj zMJZdAUJp%WQ17#HrqHbJ?(&Z=2Oy4*nrf+Vy8c?m_q&xhrxgiQOTeKf=4N?*`;&P|i{i3LqXr0dx%wr&Gbb9GBbMa#-0gMOhzyy*jF% zQrWvc18q4a9LV-HamRO>Cv1a|E6IfXTTP8CO^4?nA0{!CJ0iP+V$fo@v}t0d+Q%p zo~h`Le}AcDGZ|~V`EYDMAal;N&|x|slu8?fQprp{OeyLX=g5!yPVxy(=O7J!-0K?~ zWnSLS!@LHhy%9fiEz%%2gMa$ zq)hE4BJ^{)$(%rvk{6vJuFfL01(796Vq_LhmUF7@WSqOau!8t`$Z=2_8q0A8(n4X` zKx^~9tcnI6VunN+S3V?Xl(NcuY6dnt(QK`V#R0dtXLWB`pnGV!+s5ppXg5vVyjp9W zK7}%Yy}{Xtjc8Zuw28)n8NFt__!W=+&ed}JK|4v8^@vQ~4Y@5`KV352B!?!-{l48N#}L>hPXMDn1avRe{b-qYa6s*yo9287_qdy~ zBEkI5!JyDk-l#0V0TMzwPWSy_^hPNeVr0$eOV2(2C+BLc`J;Js!FcMLn-+c}s>&M} zAi11%@2E_>CvI1#A|##FwQlKVDP@+TD{Sm+Egv?{v?l{u`ZC@H8kXx3*Hg!z3ClFt z5*$xgj_q}r2FJ7tzKHHg1A3sl_98_lV#PVawh$ze$E1yE{MIwg`_`GaPPQis3BA1s zKq=dojsk9?O?Pi{Ml#_pEBUB$MOYnbU3suy>O37rN|~_0xTj^87-&8 ziGg|kBggnILC}S--L0}6)~ggs>7v=McWwW9=Sz>6foA%c3ep|I(wtAXuB-P5w zm!~5M9J9cY^3}DA(dk-+#w2ykYEcr5!-ZU=h@0wrZ}f{FIn`Zcg4PImwCi$nJ-pHt zDW%y+PF|vIa>T<=goL+gK;{JeF=CF{H>0`eI6dvBK83rpGQUO|DabCDsXC|d?MghI zl5)ybj*YnVC0es5SfkzE8#E0qIWSVE7%FFOM;qNt*MWWaxo$J~r30%PxL@UJX}f5t z#T+Z6pnJr!Bs#c}bxi$^MXMHCV%$5eQEpJk^4q!yz<^R%Wxbvmgx;IBaDFDg*q7b$ z1>P+2R5hOGh69bFc3jSr zad3GaYJ391rQ?;)CdHuIUnGJ24VXfQ_jsI_XAez2=})e{`t(V@O*8`g?;O*=>icVH z;9|t$-%h{!Lq}=xg8Iyu8{?J}1Se5bPr>9CnH9ji$~Z4@%zdE!`gT}RlL6#OJ3kl> zki6Um!l+EK%@%cMvU!k|DOs4Z7%S!vjWqFyFSy6u%iOg5#=@f)e54qb#xk2i0M~TN z*02kPo!fb=nGpwufi8W?yYo&OFI)~5F#*KeJ!z48^>sTJ9Zy`2HpfpX$Oab#OfX;F zh)pwn^NmAf?X~*$!{0K1q5fCjD%%1^6Ws5Z2&pkS^c%ho0Z6-W!*b@Xktmf z-0(rJw7D?LYa>ggGl*(iYGoI#_A#VG$}FZ*UC1}(^Y)|b|2osx8OGuC#UG5yF4wLY zwZeHF3>zZ|at4-{QX~*u@i+pkx<*NA80hz1ikV^;3#CBZuHua@`-|lKSgTblWljSQ zd2Q>;ZvZhfF+_=KrdMGltscyck8OyIjp2X{q!sY~ar*%BPuy9Skv}3kCl+8_=aGIw zJ>A&$Ki~8t!kec1vi;ASE+D*VUt9Fz7x1PTg+E*#$kQG~5y9+$yIxc>sDBbRe_llF z&hJId`&qlo=oGW2<=rL0jm2Vhq_|y_da~85bn3lawzL>4dxyXM_#X7q8gNm3dAS~1 zZ>6NNgD_>*J;-MZDA$zQiic|QVdvd}?+a~tKzMU>l2ZIoBhIHzno&E=r|S*dsEET* zHH3LhVfA>2f@Ni2A`i|IT~O{bL!2MFzBaAYzSDR~9|tx2%o1GF{1I!rA5l|ozsFdZ zB2m!*K%ErP)eqWm&LRKKyU@h%2IiLKbu7XYGQ>WOb@#V}Xw9F%{OMhOl|iclzHn?; zLm5R8Y5?7^1UXBoN1J|%7>seI;{c6P1W(Bs zCcpTjL$FdGFb&^1B@3eZ{21AsF116u<3 zdSd1}@^QpVC+ze8mFlB_4yXNMm<`FFQXqo>v2xzZ$EC%AKhF<$tk2KXF&23M1i_4= zetav#fnT3!fXATY<0HSA>};?(B)J;K%4_v-sKJ51C7Ml>q3N!ObjiC*(aa)Xx@PL} zEA+cDjn-{EZfN_gveCO<^9C?$o-94Xc|;}dt(FZ0jtx$Ynbb#oE?>B9sg%&L$)EePzELR8&fJb|WI zibI9Nc-k&{{9^y6?KJXNG^wzd$JV09ogX>g3Y?A;^FbJZ#^x!PPpimhHaj`}G2;=>y6=TShLio_~uZX#5m@?p{b z05>7jv56UHqNVEtMlb2%hRlk|VPpt`ru2rYzRM3T^QdF#?VKG-CE3sZM^$8%>y?DZ zvmK;s_`}z3w32e|>^eS~fExo~$Q_3-G5>>7{Aa*pBb*4?Egp@3&ae&CXERrn%AaO{ zKnL*?{`U6fRo1JSPqRzE5G8FtlM+ZQ1JR#>+vQneMWmusj3&j&<+f1S?b$GV}ZdEm&X2WuX3oK^nzLHxtlSdL%f z6VB(((cFhCQM;alh$3 z48DC~3q*)1lG%$0uj|}so!qEGN`)5~1w>!ZQ z28W29p_0o9o?4l}+4Jb3mVPy6(`k+1d=RbFj{^pK`@tdyH@p7YB$wFHjj{EBCp&K#MJARSWt4Ll)d|(k3K4fhHz8#i zSESU=xba!EZk9hCln%SdQM(z}{J#|b%LLK2<2~U&Ni3+C0vu$%0k*EYgMoFW=g-&>~zW``xASt#l&$j0 z*^6#;Ou{kfwo`;d{Mqfz^df;!5Y7QTf^LNm$)E?G%UuzWVAQ21qJ>%IwmNk$NegFob(;@DkT&Y1cdgWr{H659~}wILeDIItagW`rf?DJN@ilG69hI2BFs z%F>i5nYmv**;$dA@6(yoMiBJ-b{Tjtb+3E!i$9U$dH$iSsE9ZoLO0l-o3K+nyuntW@aWPPsOAJ9-puhx9GYX6K%cPK$1)>P7n<8Nxz_t z)}8qJhD2`Uj$W7h-nvLhbA?cI=WN^O)|Y3tZyuk+NlBpKy9@8gXqEVUfBm``U#z9J zn#ow1Z%}n+YhiTYV}bQeaW62tA*kAuBU&}td)9kXlb?vs8i4fd&;eW2l1Ca)Q4rE% z%G{gy2JXM#o_YFt{4t*o*mVzJoP?-DpQ(9X86tTel*aC0<`5AD6>u6b1Zs%FUtY zb?fh2)+u@QUQ*QYm5EsL-aiOlD;bhn1@H0wfmbu3=M9t(E8YPplyE?k?og$F)!qzj zSMY?jqcM&2^l9;W2jR2-Lx*@8@r(HnzITdPA7C$~#4n#Ff_Ir6#RT49uBk*BP@$H^ z@fssP!y@PA;A6H}&3Ac^pZocU8KBeZB)MH;KG+XJHsKe}OxKM{LHj^KV$=pyO=Zv2 zhi6XBzIxNYa2TwX!<$~q%HgVXvD!Qyz`cv^3q!aku*_NNa*mMmAAmhjZ>2+> z&5zeEDAG+JuqEq0i)NOkeOO}A2JtuqGWg&nxvm>514EUz8tRNV_?0_Z`I4g0c;-8` zFB5wBmHf;ZZ=GIczOy)<)xv87@G7j0aN>EUp$ssH#OatjBb^4M60x7XUfnLFiB4{X z=ROJZBNQs2+21#H0T$Fix(Lvf*qh{H>dxC=#%~cITHrdbSHY`EuI$q0O8t#h+Ux#+ z3$LDPf-w32;q0s9vdo(IEkY0k=|)gWLRuO@nunGSk?!u4knZm8l9omgk?u}Gy1U_b zo?YE{_xIi1_uc)+58m+H&pC6?nS17%Yi1ycFT%@QVxqtxHlBR>jI=?r_eaOTQGJYT z0DME=?dcjL{jmW?Lw3h`)gmz8qO7a#=6 z@8EHMRdP57kF&GUJ<_^|jkxWTzMlA{)5Ld?3@E>?SD(;?@6W@=A9>*4UZB6r?L-HE zpyhfV#nB2c((1ehJd-^#jG_?m*A^XlT~eZ2*{GJ3pTLqQ30U#6 zl}(`7_)&_rgfLcKGTWpgjv&h(+>>Ot3NjaHH#6z&4O9hYnlz4}OydkWRjzDM?(fCUOD$NK_R5r7Q1z|PoVq{Bt!wvkoEX8U=6ev zXD6539KY#mmW76g!S{vtDnZv@Od6HONzIfK{J?zmhDU%*JNA7CPo`LDv%2tZ zWitff2G^_ioeH^Gb*(35Ppv3G6@d;qzQXsf5~9!$Y z2-*4RBR-hosePT{3-@#$v$}E_NM2mE-(ko9MdDlW&2c%7eIiS;Hltdz@ z)(NExAfHglWTIeD$EGy)oFkoGTujYmTil(_vpd;NX)_-KUEP8dCK6Z1-XNu+y>6&w zrV(U9Ky;FXL5M1=Y1r%hkq`;-ujF1ihEM{iuvR9~@=q2Z6DKW}|IaC_O=s|?cibIVfa-#<> za62o8+Ah_>$vd>ue#bHV9IxF7_k^8zFwd9<<1srvd7i_6>-Pt~7wEf*K=6J{e{%U^ z$IADm8U#-%Zxb~0y|nvu`|)MH#R1F02}}c86OUZJoI3s|I#9e)8*j06q~PT4cDct& zqfsholN&X)wBnK_s*@Lof*+2@RbXF}&M8&44 zXpxu2jb)@nApnp|O^f1}Efwm?fVstIX3z41MtoHA_2Ju1?PnJfA5h?9OkWUJZfpuk zf*y(AYtGNj9vT*MZcQ3Gq(t^gZrJHIWE33aiU1}y#g_bDn3;bwt582X%&*l}(i^(+ z8%_3SGM6gJLSgksJ0=B3g+=pow>E~0B`#Sd4p)zZWQudM$QyWv8&I-A3SXhCP%8 zAq{@sN>B@W)6oVKe_KaRG{pZH@!Tne^C+R8@{*Iwbw4th+d&Y@<~+T>FywQx({>|Z5^TBDDdEWB)I!R9P4qj>s82fs9zPs^UU|;*KkOeXY zCY^fhLR*m0q`MCd=tAU+NvmAEmJ5rt`ZO3b-GEdMh|CzlzEcmC=sV;a<5eMwmm5W3 znf=YR%L4Jq)|B06RG-W~d)KB#JmlRTI7{stiA&A}DceBo#u=sa*#@`@trgRSFuF6R zKDBR*@#NRDt(sAhc`{OyOnKfC2i|$S@0`)Gv0tn1bljNhIS}@Qdp3h;;0d?T!H)Id z3ZC0e_mzvpw+mhM&L`7vTH@w5IMQI&3Z~5w#*J(BR z&2h7v-Sx>F!Cu47`BB3AIeZ4a-Y3r4S?Qpv_`cgi@*LHcxnyV-4h@;8{b&5}n_m3l zqM~#FweZ)*!p}kc;q16O0{b_tl9qh{eq)(TqYTxg`Hef%ettVleM!<>%nw@IzY-{O z9i7%6v9)9yBP#UOd(1kz%r+urG3XQ+;nsMxqSo1&p>s$$E{`-z1JJhj(eCI8yPkva zmgrm$KG)lv;YJQ3m$T{VHC&wIpcBNohR%;wjZP+K-Mc6CW*Qoaf66D`l?N-UKkEGU z@h5m^UJI06g8U2e&lLUdg5X426!eO?FOb-51D;=Dy-9;f*r=Z${k_Tmqey-g)X&PI zpLg^{2NY8Q|E|qFayI*~`T(IHTMQYRynVbj6SY24q-nz3`MX=!^Ui~EWw~ls#2%qq zmknG1tY$55eYpLnH0l5Q^}GdOU&?J#L_YwQcfzDlx?;^Rp-Va{Dr$vun{qt2RcU&dDl z)9Q`RPi@tY4l^XfpP&ka5Y7xeL~Dopj|)!>{{s&qeJUR7&Y;uD``C5i z_I2Qw5Ip3+KvRCY0>8=4y$gCcaF}iH{X8|*P^!C$!h`mo=jc}{>tiTZIU_+*=oL0} zSvCNz?meJB7?dN+|AkNbe>_!uKu3urWgfu%DIxBs;DV%@MB;;=SAKcy#H9T{`Zsjr zx(GnIUJ?W<8&lXSe~~l!KVM`G4PVfZ6Mp{v`~^_>vuoxrIKN*iF$1FW~{oA$qO$)J>dl=`Wv4DQlZtDDyVPiifJUKCzVWS2P1^3{}>y27+i5!_%jbc(3$V zes8Vt3V+s-HJ3x3oWhNF89#OFR|UZ*HwUSGKyLKjhAr2-iT)CZE1u^y$4mcH+5diP zx9&~T%#XUueMIp(zql5wb=+ZVaJ-IJZ*b-(;z4NT345M6ywWiziG4;X$aGGnWl0rF zf4?mdm$-<*q5cLGy4GI%sy88Ltw^f(UjWM9GmXhTQXc7Y3W-BFZeN8@gx9M)?X&$QZP$zL!_t-wJ=rl*|tRjdM6$`t<3K%eRH!9Ix?t z|9~K)#=C_FGu0oXQ^xO+fH^-!kK_1XpOilw*Tkn_qr+u^P5W$k+hqxJcrQmw;*22?)>ZJ|t95Zy*T5qWzqaSAEd?nsZMkmieESNy$3{$XRk z8HQi4+IzT9fkCUujcICn#e;yBzh)wwyWlH|AHEO&-fat4y9GnOx1Pz<98Ofww}jEkVu4aEKe(IWh5 z%pO8pvUt+MBE$T=^2r?L^B9~m-Y3h^x zsxrh;B6^`eR?Iw7q#~>Q`8Uj(kPMh~A2k$w$$FJHz@`2nVg^XE4}QKcOXDF!uk|^zM`ctRWGVu-ek&n%zW_ds z%ELB0;+oApKw5q(-oTAie-6iIP=INh1h|G*D?rB6Mu;d!2(g;J^82NP)`WkYsKbK~9<1pa7WCXw-~CsVsAO1QPA=qyVc=1G_UF z7FGrfV?_c%lPah$>m35c#uUmGyywdaW~)9#2-u@yj9`p(CI{#s0=TjR2P=IPpsQ*m zJ-?%93^zer5S~ze_IlWf1DL;5Yj?u!-RLAF67eMaJ7u_RZ{h@{U?6t>r2X+`4R$fw zGsJf_pokW`%IW5stMq7V>gK+>B&9|At4Mk+*v`Y(WIgSU3NPl|%&x>95^-e%Wk-C; z&LjGyB8JR{s`D|v885H9W>f61rg{#VuwpHF){F4(o0=Wfc$ z+GB>rQ%Iyl7TPm)mktJpVjfrMx8wMuT(^J2ctkiN>B^T&<$)X3AwY@NF546*B@5CA zcKxX@trk)v_P~%2#mu;?-6^0}1lT{`;ZR~leihCr(`8teHr(RBb|D){| zY&@%3S{fL|#>u4FqVIUL&J2lXib5vhHdJh9#BZFeFepmfoE+I;GP@>WcR6R}k2@1z z?$(fwp(UVDsWR(Ph@&QYv5N?B)Z}77&y}I2+0|DD_2^mWNbCOXkE0TJ)IXa0vDvq^S;xOV47-%KtM6qVC)K_pr<=~smCitjRpx{6**u1i zBGx6Y%#-)s=5Y`SuuCN2d91%g_r9p2;_1E2mexGiyu?Ua0TKw-YcpQM`DRV)&GGvY zE=bt@38FEy1~SJN6FgvgB`W$p%jZm0(|4W%vf05{f^MD0rH*?GIu&o*aQPh^H9zMV z43Ww7C10&hR&KnvG{~0gSYR@j(y-VYBW>5;4MiQKYHMd`VFMs}*xNRL0~uB(rpRoG zI1#6lD`Oc`lXv{eq(aDPJB<+!Rg<_6@Q&8F`n3<*0#*j!BdJilu&)-WndVF3p2-7~ zT`>zzU(^H%1Vk$rmcQ;V(_u6m&P^_&Qv5ZfP^ShK4@-j+pZ(uiJl;%Hb1`1SowaAy z$NK~bH>Y9TLHD29^3rAi#?px^aIb|>G`jp4S+K%9YI!<`Sx`PzOizx@$I%Cw05bR409K!!&Nc2 zE1O=aY}@6WlajpH{BQB@o>2zecxM=D) zGt~1kR^m1eTy|?>jA!!WZ&O^(A7l>YxRq1%MN*PljBhOWs9p0J_# z)tB9YW_{~Qh=PCfBL3hU{nwWju_gRRPU54TEAk5cDU1D+hbY$vtX>&`t&o&0LHhvthT^5_Ex19^4;%)>fv!V#WVfC1v^Q z@`}P@Gk3VuROFz@gth>16La~o=&nfv(OTHb=TT{BLw(Glb^fH2 zHn2*Y+u~HOKUI^g4J1OdNkHp{NF_vJix>Vv&d_&OTJfHE5bhDRpv~k*3nT!BG=Gfm zIRl1n5D1E9y*rUUuI&D8vPDWxPF3uy{D)UZV{D6WEnLTnY0$YGt@&#z-Q{Y^vhsvN zdfzJcN-4$0(Yt_#uVp|ljCFW}e8++9~FsCR?A@ZfjO;6M4%S)+OBHYJ%_`2hvN#RhB}mIph&%%k^i zGBiCI5@#;(8Qe5f{WEZq1`_>`_aDNew8)oweR%!R-}bXt-Y#UFa#7(>9Mg$(O8o-L=YWfLZsFUAq;H(e}sYhI5UTY2C`ahAp25BI(Qs`MD0_zO@PsK@G3JSQIsF;PSBa zgN(SsP^EdTg4YG_1it1=Y~clbR%N5^ z%<^C{VIPQkrjRADSE|`sJDfifGasqSyM8fUqu=@6f^13z@#fOX8~7B|H2xu+9;4Zi z=nR!Oxks4x_dckwnhmsx#jIVP1+?hDiCNA5K+S6LJm{|B50|PMia8W*_?7<=bNHWi zcozb7sbVb$!;`ExuF?GQ=#sG=!^0u2DQFUjehVUi|blQbni7!iE4$k(M(k5*TQFewT#WP%#oB*a`IF!8@H`Gn?U^RYscM*^Q5p{MJNjfnu{0I> z1Ch9_Ra=Ht_c4FL&6*LRa`g!C;{S>6{C~Rn-|VyR;T=;%;iT&eDw>J?X8;fmN*7p> z&TQeyO7jwXi|-%4+~ync{80AzfnT0Yi`!ZtOHZLF=noJcmgbG zV3tWK*MQ09c0kW{y@FK{W$s z3QC2)ARv}xhwzR2Ye{Ql_xVwRQ_iCq33@lE%Le3ouU+ggxaOs6O3)IwWjq3n6np#q zo1W*(fcj0!#^-$U{*fJ&y4|twZ7dmk5~{NjSj{?$WYpvf6*Yl}l$zEExOS#bo;>jy zg)>Pf{?-&){rJ6YkKXBYjyR6w+E5OfLK3@8{P{6~v3ii}HJjpt9Hqij)ej&zSkPig z?&jA3ECm+;PlE_d5~O-#_O>C*TEIYP==2KrC39E9mp>-f;q(6G&BF~vq5D)t6XE}z z0No!Veq<&-y1s(Ly&ht(wK<`GDiSS+t@c#|d%>^L+wH}-L$DNUCGVADm zI&|&P4dnVm*F)&!ruhDZjPE}beb`&s;;E!$qV$U9n`_8es9}`uvE=z$m60t)ORPgE zAu|+gk>tvkx|)eX&5-=UUJeYhUL;94U@OzhXc1^kjA^({X`qK6If+s414c;+o@~!V z_(KSXG9@5g3e@Ob#cB-_Map*1mHH4jMrm@u`~hWfL^Fhfa;|uYBEb>eUlNr~Y2-rE z0irXRtq%fE$7DQ&DS}DlZN5?g`%+&@%wRTz6t+H^Mnyy{rZ_U+7ieLe`5S11M)y7_ z$j%t^i2NtW{@)Je|A(v0x85;D5-#0H04%J{oTI}98;Z6^f^LsIA$)9l3HA$8L(q;H z23a1FtQQI?2t}85#zVK+Esq)6{IC>^1v?c*Mrw-0DHo)31Tiv6)IxgUg(1ldp0>sh zS=f>%`JU(Sw^xzrC}ZMdgoYpE&<1uNf!R2NhX&=;f>I0YGPl+z%2(tv1i1_tTNg7$ zv?wK$caXZ48Hro^ty`(h0~ptp-hp+`WI~f%vt}# z^7!MxLX0N`JuIXR5-kT(dZ?t?U;P;64`u6RV$%V+s7XnhR?C&ubN`)eP@1(|9 zcLbbD!>OtyCYyEfh($jfQnDh&8efpKc_UM7`w7$&8#HNZ?&K--H6EVSAj*WDjw@iV z@!8(OUnzbJ)tmqnROET^ zcJBD_&_MSnJ}Z*M!O~!0*Y~%g*Pry67cc*JLK6sAol7udf%r$`0zwEs(XWO4m%KbAZMN zl;NL=tN8?GnlKarOVB1B$J+h6;lyjU-kHWL!M;0}E+W(*eU(0apt)*fDTAp{zIul9W2&eVJOk;m4A9xYnfQl|sCbbLU~QR`?WENWfLIMT z^ICBEqyrije}O_#RxG{Nz{fpOnkvvva={i3316^XGBF*UQbsCVlPB}TGKq<%DZhRnJIrt)kzrbet356s|IudR6s@evRTtc%egD+|2xl3hR- z$?Mz>tN7UqKshTtl2L%uAM=geH?h}kYL;)Jy<-};$bjM<}JZ%CY}&7ppp=>gZw`neIUU<-prlIgvPnQniP9ln#Slkz|_GqC|AH>&=nn zK$)HO+%7yFkJsDF$OM2Ig$^d!yPm$lcAL+9LnXoTr_IuZpYhq zd;O_Y4sX^?Q~e;lN*+%{NJwC%q)-{Pcm*EBF1}b#aoAN53{R z_J!V@9uOfW3zes{H(l-aCz2uvqm-{c_*{w#i<7vIvWY5z+`szo2ysoO7dzgb!n1Hd(#5sddd;v}_zrdD0?jRsQ9;n{xkR?aRc{JDuG#R1cdE8$ z&ueyD(qt+PNBR%vg{AKaR~irSsXJYH&u5#M&jipll4!L%%gx6YC=}4HBytEdq2$XK z#gnOws+DPX4ED2Z*Id}vSdX`*?F16LLnZqVD%p02?-c(+vempDFv2Fi(M?=8Y2P=4 z@r^U~BtGqCDIS^pIz!n#%U(Gm5VYaZj~1i`W5q?8IYQiee%quYOUwz8u)e@o zn)K>l>e?gV5C-+^`p)(7Xl6QN<9aGr zY@wHUNq!0KhvIft-?}Zcba82E{dZsZ=gV2*PknVa^Ll?%aiUUB2~}@YEpp%354Dl;=4VY&r~LeG zcNAiQr+;#k{+o)V9wJ660{0F{Wy$Sk(PYBxZj7sEhn-TYYeHmyESdX$b=uqz#9dBZ zJ$WXWr{n|D&04hPV1`vf3#KOF4^hiK2MS3wet*UMPp4DRs#@uIp5u-G?LUYfpe(W!` zu(ZlaKQceDU<*4Cn;DMQEJ}&2a{6TKSrwM&fmEw{CXFm`5P(p!2P)@{lzaL z!R*@1&d&zvp+7!nPJItPlpS+@^QYqcw@30j%lPgy_^fDmK-Lezrrv>W^PL2XkN;nM z{a>$DN*4w`l&U-@*yG2hcr6gY&8Az7iT=hE|HWe84TGDB&V^9KT>>qA4Hx#O%Ux#{M{(P34;6hu*XNG^VFnkrDvB5<=PC~z~ZI zT^H}IcNt(I(ilI*`HlkoSKyr{!#_MlP>E#;gC{9t%|L=4g*iN9X@&HhnD=4qZ#TBK70R7 zDaDUH%27jY>FC+A{=cxL+2CF}2ldj1Kh%lhHdJ7@;?(}yD*UB=3rz<%qr6kb{!{oG z;9i|OHA`QAli>fXw4EW~UP~*gmQQ{j%FjS8Fz6`~`2YC>NX3R8XjnNGzaMg5;s<)N zZ$Ze0@INo;=N8`W(|~7r#@&QZ@MD{q#NYygeWCI)4B(GYZ;YCJXqT>&h#v0kqzcnQ zm@+dd*ElF4L1JU8({2w=Iu1=>HX@0ldM$dk8=#dyiRF(({N~>A_RKStYSU^=kd+Mu z-p+yt@8BH=hqE%xMM0%<-K5Rg2BG!gJQ{*YA_eLb)TV6~(`hLNMn*I$yZo*F^CP?^ zKokg9VwS~aHH__V6l-nKDY}G1#t#J(fmLeHk2XYta7V=MlIj3$lpzT^S5$%=MS~6ZJay!%s=-0`7yYFFH9|C3S+x9Nps`8faBz>hs4Q$ezK!N z(!RcP()}){EAL8@HtZ@oI!Qd?y4;!j?3qep-S)=`+g-^@Q)tPR_#=07T#OXi^-=DMGU0 zjfa7q@DxkYQhWiOr2br%=C?BWYa~3DjsUtQ(>2;g^V~#pzL>#g?9vCot)SjNC$tc1 z*MIB4)2Q{i(^M8+xDq01adN+8`(_r`dh`}s)||+FGf#|gwEj2Shlih&VkG){XIP4ouPOT7PYXu=$LOR1Fj>iWiHbiVDDbUlb$yuKjv1a+6*u)f(HP zCu%U;rb~)#F^|+WR7B}v=w!KGiScxW62l-U{7I01?6c4LsE-~?UdVq?_e-D>!&*5V zU&7{iT0;j(r3!8BF6Y2}^h2I_{gq0?2|ht}4$cbIGE&!>r@T}aZDshxHXQ;hV%4Te zXgsSV%352KM`d%`6<|I_qqfcn)rXN9s}(E<7rGA-S}(pS63z{qxd9SJ^>_y8>sC;~ zinER_D#5$Mmg0J@!a+1ye!myYZH)$_oFgT)*I@=DIm4{);T(vbFX?+X1J83~N z9${KG&bX1sCq6_E?^l@FB|d1f1o#VS8{G%c$=G2(Z$)c6SJM;L@*50)OR~t@;>d+N zZF_}DZ^IDOW^R%chJoe1QuZ5xz1RhGnFtFai4QM}uC3kb=Q_wcl=PBK%S5v-B5;1Z z`~TvFFZ>c{Ir^?5#!o5#h<&1?G7j!$!X~=rXJ+M0r0-0{Jgc*`?Epua&nNtKL3ALl zP0?_!Iu{eo1ZvYF{%x5KDzZj(3+oTm!6t@Mfw0`G;IJ&L)YKQ2x;Rx)D}GeOjTI+r zw=*WPRooj!<@uUf2q$b%)g+Q4Q7m;^W)I&=)8Fz|nZ&>LwJ%Y2{-QNhb|+c`I+UNLjQ!qmKYsjf$;{{iSC*Q^#9rM?y2^ zCSiKxNG6=mW$^nZ->IRmDcsnpOT!6Aoftip$J>+`9?s+W8ksj&Q<9EjSJ^9Hpghv3 z`eq>gDa-h4tQSyR;q4A#nhws(zkTruLx8-8FBN~&>Qd+n^z?gl8b6Tt^_8R>#jfqz z-Ry|X(-5+b7J+~*X`~m6Ol(&bu9QS+a~8q3@*>$5XL)~>bg@#ogu2U>iIXbu5nUXg z4SW11TbGk7rnB{pJPaz{d?tc}o0}M)t)r3h_WNY!->2U;jb7G@fd@1VoLryJlJL_l^k~K`gc8T9C}|;SEeWJzzHX@?OL-{-gIbkfCk7h zDSEx7pE}tWD`YP^ns4WL)5xl7_%ABeeeBGiI&xdVuyL1jXp~W# z9XntKE5&m9?FiV+=Tn`J&tk$8>NMV~Ii{TjxqW|1p^3N$^B21MtD8?u3EY+Nw^~ns znB`n};Bmb6Dpx%Zp_g}$B>Z%Jb+NP)!m0T%+of(%`!uSRgfO;I(`BU>e+sn#;4kSn zgF{24v>SGb0T{$tNo}^9gU!j1k(nwE4 zP*AYD$Dq9&u&P9g_x!3=YkPZ>IF{x=@ynE`6XY4r?h_bEjcslwg_~Uu^=l9M>EccY z+xm9(&T58az4UanZCD#}i|!+H)4^)k2u{e7NSgdKf^e1G$pXsHjXA`(XUm14uac1h zg@D?Ur()3^bvq%I2aESZMVD1y&D6xD;S5`L*NmhAlVD*-HC&s&qQKap&oyY2`r#$V z)b?yc`?lBypPCiW`lb$5M88osF59gqiOFHKM;jwifS-h63*T<6&S~>RIn*!Sc(zP{ z>6yB`d{Jh|<1qH5Ly6$x(>8#i`4GAJbMR7y9lubXsAx1GpDvuNOxLBK&@FQ&V7C^= z9WKFAsEhMeN^i&CdNFP7q_*;XyyOVtfR;BUfI*C5=0sSuU8}XnBWnKxYE4j67UgYwI$fON>1i`n=`>3q53x07*>;nA3c_z|6q9t9VZ z#{y2aM{Z^qIv@mAedg4?gL0+zy5|x(Pj9k6HOP&2+P-Y7K}CaS>md9LqR_{wB6LB6 zCy7_6Akg`RY*d&Ri%S^VN%o*nGg4j8Dc{SndKaUi971w zIf15soq9J2!X>#iHY|*qCwupV^FlU^3qO%m&sw@*Pq+?Iu5XkyMWyI= zKS(G}LhF06D7Q9|p)ziLwE#{JCUF))bM_6BY$I!&>~U;wvS~kZ24K=hq1D=0L@Zn% zg0U_0j3#d)baj-PG_~~c;(a^N@fM7EQ1K2M7HgG0`o@9oh5_m+_b~> z5z=ml?dh}qEju3El9}`)Q8+GLR(+qjjgq=aSIDvIM-v#HO4)_NiKefc6ILee3alC@ zZqpVf+Pm}bP&uyFEFByi_P45eIxE%~`Ibi4YE9iD-pxn4x*%GwC+o5EN?slNd~jJ~ zpOIW8(@StMSg~V$Vny>rd4pFn+ zzk+%&mcXGA-_i6(hYdS&22HcAl$EG2r9WAU(2~+As0rCO>A_8d)=qE^_m$YOmcFqicFR zRb>*B#@`pVI9V0ep&=dy20nSOTa{-+jGbm1)l^jGm=%qpYvh^iN02n2L(L{K^K z$lpePr5DO4E#?I6`p=Ql$v&~vz3#%aoDD695WjS()_>QB1?l&%a-)4CgJpj0sCKwL z)7tB{zJ|3~d$*gb#B2@?(U^STc)Fx+fI&EGkQR&L%^Q!p6(Pia;Y`T+n{IohM&~Rf z;ku!F%Z9vCncphM=1OlnfaG%7Sf&bDm`jb#+o(C374DJ2q*fKvMs?dbC(s}cf>ep| zsasPlrs(-WQy3(n`XfYGcIdtJX1}@w^O#wtIDund{Q1^N)y%^pZNbBdZ%>AngIFZk zRh_>{v3>K}T}jv?L$S!N3F(bzCbx#Hmrc47xH-dtV25}3R-65{BnTEGN*B4S-Z5Vv zL>Fw-mT>HR?6}9G5K_=+MBI#(jigksomY&Dd+3^O;#?q4!#6bT>xOLFxW+!!yJ}Ns zeCGc_iYL4fDAMyRYd2V%iIteIc$*Gxr!qJ}h3RfR2(KK8xhB#BLQ>PI$x_ zU(zRRzIEJYx8t6zu<$gQ>91wi>9o^ad6m-Y%DPE{_u-qFN_&I@87|{8sZ*oJ)PB=N zpM|L-!_iaw%gHa9j&32WU^)fsY^4c*=G-es@s0|mTJ~#PjU1fvmSh#{ZY)U&d<>8Mvowgg9E5w{kB(Eo7oooGS#oL3oOkk_x znGvf@Oxd{q4xf292!+rLxH%$!LTJHxz=La#Oi3cb@FnLJQHsy4zxs6r55LrpYdcxS)z)PEJ`*2={dbuau&>M$Far+cj-=`;F9> zSnBqL>){YC73eiEzi};?eu*VZW8as4BZL~8$+fGwGk`{vB=gsa7Gh$VrG~pXnYg(Z^2wZ?g5s@2iJ@D0KG(;w z!Tx5ay>(jJYh_Eej+s2JGon>oqg%Cs*fX`Z(Ulu4=&QViUHSyLHj9;H=2rR8n1mby zn?xr`WSBZkD_BnGk`L4gsjE9|(YVRH+jA0cR<00#OKTwqByH$J>jx`jn?b z$rm=PgOxF&b@*$F9z$#NV8_um2c~wMQ5bneQ?g9!HNCdKAa{&n-JHUK08%{eJZVb% zU7!8V4sb9G1Z&z;oJY6$H`bKMzCL~NWJot_FuU#|m3w8FaRSvX@mMxj_?l6Hst>;Q z)2n2)s|^Zl;w2tN!w=n%jN-~qF=>2Q4B_3C=`Yh$5{L;-IP>}#;splX*xomZA@y5> zK(yjy+d)+0d)lY?sVcJ*Rw7)U#fodP-TJ1hM%yF@dB1X`QC?mbh?IJl8)M~sV=ZCMXjT|HFoKh+!?6&k5b)uE25O3&LxO=13)k^3_gnAZRkq)C`k z2s#0;;}=|`T@FNgTAh8pAqbz*h?y5xCek*ayOOJJ_4Ay2?vd4XFw|AjCIwd~y*S$1 zeWp-cWoBvj>D4FXWY_kZH*j(@;~_)3u@A^JrnahBaUvubllm)Vb*k3vIV=|R(h8BQ zXDmIL^ijAjd}iEF+UH6VoVVFLGm|<{tXKOr2ZGIMTf=*s7OSK+%|`7A4w$+mV`$r_ zkgel!zQ<^|M(uPbT-d^;cd(#^*%BtR9blp1m$furP3esE z2RCH!oIXE{cI$EOFW0lnljZ2L<-6Ez^4|5J+T0}ApRpU_JE(XL9(z_T#6_h}w|uY=75D1^z!3A^cGfw!i4M`93HvzEbTx6A zm-JAj;~-WnW;Q;awb8CWO;3{<)1DkmlgSE{%1+Vl^_~&-;=#o}8kVIS?;W1hlrDE4 zF_Y@6bupW+SS&-n3zw)nXTQyo#!VEQf{k!J!0uRfAJ*7tbWAHw>&{)>zBnUKQPo@T z-M9?eM}{D<^hIs}-DMyMQ0QDqaieS%7lcd7=4Tk=OSkFWdBQ!cJv>mG6cMxOsMIQewP9h}+5zwNrGS#`aiw^S z9oh(kFGP=+15RRXh{D!=r|dqoma0$1_oS&e)MgLu!`7MRNDx2-6NaX#V7M!-$|oDo z&!=pY)yr|eXLza)RYK}E=M+3n%C)Dgnzmu@X3Chvn0(W^q@-@97+^7&YgBdkUP``2 z$b4%jJfM&6gB{QAUhk9{0t^m$EnW4umd>5%J)SG8)$9#MNJuhy?5%J^F#pj6jaTV4 z=+*tKB#u~{phBxjS9M5S8^A8l`#6Unqg^Rm^>0EWbVIHBFdc*R-^^H;OpRZBo~~)? zJmHb3-dIbSuT_TAa9l#FBp7odox$F`3AdX=Z!6Mq~(ZdmrU z<_!zdbw4$css3a|Y0sY;dozqQ=5IQL zbPWc$x?B$ws*XV3j6R4sYvaPwC;p0|pt_D3aGHnHJZ%LU*b5-3UT)l6`KXQU4DZB{ z+B|+{=e%htVBuHVZM$cx;)<6xRADlxhu*VVHgS*3?Qnqn zu~Bs}>?0e_#)iBSIGq-+KEB&GxahejF04;3#50x54_%iYo zqpG?T)Y!UV7d99@kUOeQKCMuiv^vH)`CW6Xm%fb>KmE>F=TcOD-g1j3VDz>NtzQlj z;myF4Go5YDx*!#^#))a+93>*_r~k1F|F_MflB>Dnm`F3+#9k0>Jq?dwQahmTA_k zbo-*#7;&{==Pwd(KMF0cikLx6>dcliLu z)fT%tcy4WON1uHq#vv`0v?tbl5oyfJBWJocCC(kijvI>W*d$Eem+UGaaQ5`PfjxX9 zWN<@sRGmOvc`n59?0iaiS@m=SuANznPhLJ634U+Iz^9vByy{kw-6x3&SK@Hk!Wr`` zW|%URyqAK4?`JaA(QX{iz5ACv+lv@4mm(T49~D^@muo$@dYzkU5$McdP}Xyr7I6J( z_+VXVqsBJYb=zBE6SHF5=*^qwUHu@k)13QGvgGk(|kN3pI}cic8{E@)LmBr0Sr>;AaU7zsHjunl%)-CyC=?8h1Mz0 z^_g`p%~*3-04;l;?fS@)&2Bk245Ri_Bg~Cho2G$v->MEt&NEdCDgL|Q8@AI5ykL8% z!lI*fZ@kF_aV+98NoU{1!ciyh-~k%+|PRKwB1ZHeuRk z9!~UPqSRXD+A4DDm@gvCq=J6%F*b45kbJSS)}5dP@9n4T5I7$GNjA^Ea8Ub26sLJj z=IAzvam1bb36cHYlmW-UXk)5OR5rfzg-EWDThUrK z=zF>)U)JscWTJCSwz0C{tgp|M1K5>l1r3`xBysbDD_(-5!NLNT7pJRtHpHL5hb>=2 zaULBp=-@M-6~qw(fZ|Cad#dk>67!#{ENd9JuKoZlU1jk5G0+p`h_wv2#PL; z!spl&s6F8_oIJ!iJ{g;_n|bvANPF+Nrq(WdTvR|rupppxr3nZq9U=rp0qIhutMn3( z5_%N{Md`guCsgU3hz+TsLkJKMA+!*BfDrOK+^aM5&VA?pX72Yd@dHUt&UyBJ_Fil4 z_3WwdLrW04;Zsj+x!hyuZlIe@k^g zFFn@a=x?qv{sN6vT7h;f|LC&b>yrm$+OJqYcr*<4B?zifno73VPoFDuo*KOiN*|GE z=WcJuUGWA0s;^eI8=yXN(749_0w}KXCuKA2NyqbgB0`STWo_RddGI;Zbdjgya2wAo z*q{R10w0XS9c{jQbkvF?jRF|mnwx^yKe*sCzI|@FYaEuPj_KEo^q_5Be{pRv*H;oG zJCS8z=HE?0zjSDu@h#JTjh?VT+EObd(M5S21a6h#708SRroXR=G%nTwNr68SvfKt; z2a%b%4(tz>8Dls_d(6=y05ESmdxrDq;HdHS2V0XkllP(4=Nd6<#tJJfy9c1`bN2^& z6sXfKg7=)hH-_XR-pCS>qOmGvlm)Z{%>#axE4^d=Df3(yB z@j12y7&h3x=~d2w?gkTZbs0a1PiAbfZPp<^8}ppO58DY(gEPG7#B3J>*?hieP*LEx zf!L1N8?(qE(+)5$Cj-@|p34g8ZxPr{KIX5A7c7X@K+#eBK9|(`}bMgigK<_4miO zR!TJ6+_xo$ZTR-d6HrT)40pgzYW&kbi369U_Y;NEx7SJwBs9qpyk%+qvbpU^hzVS1gZrr zp)j-xykvbCneYl4yXbW@A9q3Pg%EWx0XY9m?5`@3e?jeyVUicV>m-q0l;n5KyYl1 z=Tr={jEAlBDGM>8Streg0^Gn7h_B$&Z?#iJ(}Q}9Q=%o8v##4DwXP~GaTQe&I0hNrou zqVppVXH=VP%^{q$bUr(Bt6azG=?+&-&ee?&H7EFvBFr3~32y%A#Xokh1*+k>Y_rpk z^CxU4vdL;*=&fSFy-%rQ3)ohEA6Fg-;KE2frNCR$W+l9MEGKkjSOSWt_Ekrd!kSCx zs^$gev7U-3E9aJgmkkbjyn7h|IyBLn)n&*GR^nXwj-^a0k9Of(QTZ-s$wP?&um)?? zDBH@Nv03pe7jn1U*G1V*BVP4oNje9=V+P+<;=a;v!OKRBd!4fK(tLb}zARD=IGR;1 z?J$38gwMk{`f5KLp4DocG5v0=_%uo?+`MCvPmz;43SM98Oh};^kSt6N7$Y3;*4UY;sW~U z!QIbR?Qbv7%++6vS#u5u{bHLL1>Ptv1GleCCTN1`ym`)=3!_iN+UR^@Ua+4l?9Mkb zf5r-5;~XF~vvcH+HPqo{irN8_GBpMy3s7EH?Cxt7u5@#Xx!JOAIp!)1ND z^NK!|F9L;C7Oq|!urUyf3zT&Tif_Ab(bEv+o@m?uj)I>&@DwuT(NUgjS;I$unt)@j z@zd@M55v-{DXyAoMQGwKVowh&y!n~mS~G1o4mKH9L`T?Al1d|iO$ zFv7ibK_T*p?TpYdNVUq<3ireg@=2xry=nt?41?%qv7HV34TESO*X}SxTSgE&2b2gk zQ1vlt`ZbmnVN>~_;L`#CP3doKU-i=tfi=>u zfr(bMH9mP7n5LQ}#5g96&fqoX*y<1t3-oge$iaLnV_b3rn8lyDQM|9L?KtAW4L8eV zu$j$H38ke**~kFqi+u83U|Q0+0`o1-SEjsgkf1*n}Yu*Tx(1EDtCA`Ok=UX@z)G2#KfT^Ry&(`VkuHQT2g9uyj~m?mmx6b4&) zrpo8@^UxWzDDaPklTf_LfSu1e#@qsEv3>4TW)>cvOX@tW+`7HVoXsK2_?o%Gq8NHD zbIvX0@|<8?_bmq8=FQ*n^KU!lc#32B=V!zB=YIWKi~m>xo=}+oiN5Q6^d@LzpY^e^ zm^05Otb_GyyYg#it8(@_#!&K|R$;rZu&T5ge2K3uvK)1!G& z(uv2~+PVv1#GPzbDoYKkg?{AJqdjsn{@6~V)*y3DgCTe{1fM$>5T;2i&p3*77#p>o z7*JDS*OVF@5C{6GZu`1mY4{=kklFWC0wG@j%_( z{hDN6uR4h*fbxt+inDiT(%WqwqT5=(Lbu;pE_xo&P|SbPQ3}sJ1+9WMNgd;6Q9Gv_ zn-~P+l=*@DkeYvqV`u+og~E=M(I{KR@;4TB9(jJ`t_k(r*p%%tQr*2V$qOY7oa8l; z9k6;fm32s{7OJ><+T0tfyk1M8#)htJbwpx~aOfv~+Ym*53aO^suS=$JyvXNHazn z%&8*+(SLLYPJqObj5WGp3P56Ku;p7B~(drj%ud|0XSbxZ#PW)TtvdXLD_VS0A)|p=_iyIMu{3zjDwf08W zWkGor?r-n*Z)|_VsS3%MH$hiePIdu0DU$c%MFQZaG;K9~n$2#!37U3iYcfV7_Q-hj zSM-UNI0=fO^*JGK{Mtxvrb>*=DWZvOCV6%sX`Wh~r>3;`{U+go}*HUzl8Zj?bu9#!eVsOgw;SKQg6$HPVRQ z-9;r^`=x?B+td!+U2T7id=`$IduADH1P`#dp%C9j54N$1^pn^x`4G7zw5L_=Wa`>U zz8T0a7rVa-uud@mZXLE;2mnwcgBLgwR5Eq}Y21?+UyIWmKCx?ea?Fo?wVbdJR4S#d z=%i52f0n2c6wfXXL&|O+#dSpk6K(v*WheT4^q_@uzG>tbhf&ZYen{--5O1bmpNBSX zx~#;+eZae*EX!`w?K~uZ+_rZghFPYjf?fiZTll2$u9P{;5 z8`D|tA+XcRWybKkW&o3GTn;EIVy|In&n678Z&cHpJwLhtC~DF&Sb>P+kypW49n&#u zbCD=TBw;N#ir6hDOr9m{ehtpjju|@UKwMse+t0Z%ntO3k$H0rG8RatfU>TSF>b8Ez z+V=$G?ovZrNykY`-YK80oxu-JPf3EdQ~r#!+#lzn2j3sOddq;CbAi*vLaMbPLqwZ= zWuWo(jqVY>mxCw||6#6lU>fpH%s8zsS4hG6mT#I6qn+}O(E!ALom80Rf6;Dh*NV{x zyonmIMEOqLE&U_J4kgL}}XpdPv`{A^F%~{v*o|_`TrvE`yE5~ zQhxpVz3v%6rNpnH@JjgpQ!?#T^@YyD%z8zp8P_nX7lX7;PV1m>#pU&i?){7gpy`*_ zke=siZbG{Vjgvt)8FlVCZ)@QXz|Dya?}Pya&$@aD%JdU68x-ed!J3UM=Yn_*gQAncSnK`k2L6l9+iEx ze*>(yDs?oc_PQ!?VF60t(v%|NzeO;=VpV;VMLpJTva;XGv-QZw$7qg;*1gCE^|64WW>zA!4<&FcKcni;L(v3oT#jFT0FHJjkq7zl~!!sho}&GAUg zaYqLTZjl_StYF`D`DP@$Iz?@#xgP`-+pQkm&g|UgQ0UOH1NgBtnW8k|L(TvopwBu$ za~Vpxth-#p{(%lg{N6hye4ZJ^>L(0pKimLWPzt)I=uTLeg7w3uErH$Y_FHI=(V8vP zvNP6dbB$mDy|^@09Y)oE2NlUn;7vKfHM(FAi>Q zgb~)n#z?+ZfRt%zw6?l8Z!t;uj{t)E2j0oFIR)3q@agJwT}UY;q`j6|i!@|>y{kqi zDBlIwR1_J6G!c?WEjdHPCPoDD;TW>UuKJF%NZI0rp2wEFso=H*(qWymdHJ2q@i>p$ zH0y1hlg&eepK$pyLxF4CD|LTVf4|pOBB#ApumKDizr6rtjRBgf zwNjqv7M$g1Bi~hMweMu0H6oMwYykhwqG|l*sS=_@PSL46lMI#Z6y@_S0Mc*SHH=#w zu$MQ7#}Q6P9)2JD6Lx96Yyjk#)g?+6Zj^QYL;(S){m8WKW4iu`m?khuW)N2%>CnGD z#N+Uu;)jENeVQ|qU1sCw>bFaqN72i*KWzAiB{(|nq%DgY4Dm$d*;Vr_RJ#O=aG|Ch z8ZBfs=@|eMXy#S0@bb|Q@8Dn|OejD~J8_O~>>Cz~`3^@&zI)!h@VA48HzbdDqBNb( z;9ua2{G;PBz~LN6;j3iuIjcMksJTC}%nwhD@)#28jzJ-SF@`Xa9$SgzZeciAsc5N3 zr0)VU$&pvev;#N3!$fPtmxG_TLcItA1k_wcv|# z;uiO;oT*c{q#0pNFnGaTev-X~QCPj^uy zeVDINm?GqMyo$t@gPam&p!Q*cbceBB62zMqlW3ZiWuVJf>h`_27```b(5==yVF)wF z2*)3*uePp}8*uvyeL6ei@8XQZpBkBT#4h52PS6tDwBr#Aq-x4^JN?Q(sD2YW-kD-O zQEct(7(^D6dhMnct=Q{ME~855H*>idhid~e^$_2>Veyoi7r{@B65&LmawsqORC@KB z?WL;xqA(8?J#a72$kHCIhYPqn-a2ML?c{(+Ism_f+`U`5{Ccgl2&BcRm$6%2U@Kjl zyWY&xJiaXMu;cLM1nf3pQV5-qC?S1Oej z1o}FDLkZNlvU8O$dAR&1@geX>$YXI_z=byqd;v* z0<>>hbMLFwwEWcoe02 z{(8c}rIkG>;VV7xKxGfe8jd!2H+vHQpi4Q4i8^m}GcO*_g=7ANMPCsEw#_rMR~|5F zx9SJ%J#F&a7?F%s{-6QCB#GeBEnOhNZ`8CmMe(8ic!8$#t`e4-(y4L(;I*NhLAep< zoI1njW93$=1jI2(Ghb+t;u&j}(AKoCh1JHOl?;D7+RNj5;J@AQehsV!iwJ2gpUkV7 zySvurUj>#laP2j|f1Xt&1EI#Nj1Bts8!ydQl{c5kw65|3&RsGrPW|{oE$%_SeXac~ zj~aecB`q&Ip!b^$Yvks{2UO%v&a%Z5+V;V17eV~#l3Co@Q zL)TW)VCe1x8fI+pY0$^{RLn*gyKJo8dji$e&lg(=s^&UkeS$-O7a&#xM!Faz`tO6L z?4Cz>+!~t6w#`uCq&i^Tbq}DV8yEk0`jVYQQ3Q|Mr=Bd#co0oSA!|xS+j5oG$G2IP z1)ddbt_OC7jdUY&6e^YNtWd$0v1EBX!TTq{G_dEZ^B_ABWQ9p_o3Zs1x7X$~EK*|U_N051`E*-4MKhZb^J{ucv8h< z?l|5+NQmF0Pe4$7W=Z{Y0QmBqe9Us5T&tQX#0jkdOap1BNC6^6HI@J+eShDhtA+R^ z35ZdOPomHusV%g^E zR>fj*4r^f;O>=)8Xl2wX8W)(%P^=&W)~4VC_ZK)M^+}wlDxeyiY_+NAls6a~9kWDP zOxgS{qXAmBcJkH8MbhC)6(+@@8IWY;0rL_CtP}R9BDPy5UALgOMR&NFIT9Rw%kg2J z)6BVFCGerw28l1r3y9#@2wouZINgc_3w?e`Q91eSq_p_=F`74Z51nN3OLf>bhcMq> z9(ukDQb_+s|NH!KR0U}X%zU4^Z(yD=MZ)w{#MFCC0R*Kl@!A+Qo!_nhT@&0J58xFy z3=L~1pKW%PIKL`)Y1KR@f)In++aNKaICvVvvU0h=9;7TQK@$_fa@z#(6ypbZE_{FS z>g{|Y%F;XVE6;2I^xR!MKbZB$K)~K z1WSKAqp_RpL8uIrv*INn0^8@eo7sz2DZTcv#=r;>_m)~7q80&PPlXAfuupBD_)W>{ zD`z}{X>@A<&>9_Wd|j=2f)4eLoIs*Z29k(!n9DZiy3 zx#Y_En`!D%-Dssdi1H)o<#UO{4od*?Ms3wi!?b)fsdafQJZnU)dJixpQR6o#|KV}5 zsXmm4h4o=wYt+siy~jbicOV}aG%M->&l80@$0>W&5<~)#kaZJ+s*xb28PX%4WxD8> zny{X2*)cy6V{iHz>Sy$RrnbzGvDqEV6~QENfktj9iQCqC#7wpsh5ok7vG;q%|KB54 z*Mha_IH2n@WJt*L%O;00p%>X*TI4xJfByApaxq)L8^uU_$X#X|RChW_fEo>Oq0~08 z!=&S>p0ho_^p($SJIrWQTtAb=Wz0oLqN&O6H7*@t>ld|8&_BYYtnvU&7i{ z-gw|D*?p%PgL@6fG z=F72*jn7w<4z{Z=K1ju78H43jl16$=0D4qoaZ#>Ur?VRoXrP>a+>euW+KpLtY2VpyHU>J8{H;r^;3b)W zps)bXknZGpK~bH^r;P=TA&@99Px&BKFe81yjuc%Q$t!3t$!=TY8 z`ypr?99Q1I1+Ci2FT&xBzOjNly5clt*>5_1!5x@*4!pu3U;$(_X0S18B46Y-}1BlcV%68sj0wr~HGK6qQTDvlt z_d4s~T*cMA!*{i?;a874FlV!cYrXapBtK7>N+SFZf{mS+{$9Llc;N;;nKlxM;6xiSs~V1bDS@FDClS0 z9J*VYNibTF1LD@Lu+)TMFYaO7JSL?YTlru`w~%#`DCzHD=&X)Hs{A*0#8LEZ;QRZKqswsa`Gg_(oVG*lm73KSb?bJ~+eFJ2Tws zJN7aYL#(es2cT}Gi29cMk><8P5bHqYWxdvOo?LF!rJ~53Y0-98p%1Es4uxNJG?yH@ zH~Z0S)o+_l@DXziS=xxZlq;*AeIwoz?VCT|hNkwYkfTJfhs>IzcWYhWIT>I2rq z(*0IRJIaxLYKG9V3V^uXe7I1y=KpyaW~QT4;O8I`+{>?34^X9!RZi#PnZ)g1)+PLr z2!d3(bl5kT z{l@zK3JDloTFoQv;VBOdJ?`kU8n$?)v`G4tn(lsEQF?9tl$!R*3u(5y=mWZj{s0L=QJz5QtXpE`$KOS#!EX@!JEtazJ}jdII(s z@&Q+rk{7T)Z?I*&TX+&7bwJP!#M7`mxBnu+gKWC6vUj+-{SxWE$b|%)WdBX$bHMt{oSk!z!Fr!=MBX0NPMSzHiNKVC*1(wDF z&tyhM%g|Qjg!#i$-1W}rry;TYDOh0omUk69eN@%?Ze@2zaE$4lVY5df(O}%8X3oN6 zmll;`kW`^pV*T`OSnp6@64P)DFkc(th|=>ad~9l1eRF98v#^jlS8g3JH2h{oQa!Ts zeFrg}x>ZjHfO= zucgbk7M1doLc|XpdWg-dTlPCVX>L?v6}#(V$TO5}?R{hQaV_0X51piK$&%KruMO6* zjSQDyZa#-b;GH`wZyzj=Rj@B)yngUd$oMfUU%4xnabazrJ%u!Mbmj(4XcY(OXIC1+u$i!jT)gJvF@%(4JQbhAVYW3a0M1<3)E(u(nT8YSv~s*hVwB zuX^H{-ppcbVBR&PhxB0Gp`Cp}u;O*QI$1SMO}q_i!*U|;B=Z}$dpU&g!b6uenIF&A zjCyWkZcJIq4PeM1cnebm)6GKYBTAaSEu7s&Pj~kmIbo|b4gnf%+sTHfJ(!Z2I$(dd z3ypCc(#TIC*?#m&;IGUwctMy1P+XKuhFP_B?{!}WsfYcnr20|Zsway zQ_kKc!f)yf`$`%n-0S^`YV&`nBm&_b&rD7Y?T}Ll8jIBn=lA=RA@;q!!>qFGqG}^o zx}{7!&=(ENuR$hi&I;I9Vg1JyFCE4zGndW}VaUrs1)X(p@b*>F%xeiv7ZAJ96o5e) z_YJ0SU&2|p7JfNEpZPH*(fzkxA|3Z;lF5~^l7>7I&J$eTVO#n3BWi@u4%>`}k>uwH z_y4pE2p&GYUG^odm)Wj1?x~Mrs+j%cxVvRV>V%@I7X;vYucS}?ys5;5q}zORsH6XkR<$>RFog&BY^@IO}r$R@YpZ#2w~&vna6QwLiNb^0iN zy((TO7`h_)y_{uh!Gn%#B+qV0B;jHB37D%9pY+YYj2TN=pQL6KN<41giReJIDbJSm zPsU{cZ~ViEz@zb(;wsWG1$p~#FH3%jQHqC58?e4lIz>2tru|Vq{Ds!NKR+-0-beC> zdO(Y>o+d63uAcZ^0sucQT6-KjQ~s;yj9&Q(V!Cy69GqFDG@0!Wy6Qf9RT~~43pm$Q z4;^EYz@+eL_cKXtV8yhEJtE<@9YEGqM{oZ(>x#!l> zW%%Xu6EVxH4^4IV8U%#%_EV+u9{PIlusj2|1&)ex@ zJ!+U)tI;bk*@-`Iiq@lJ?;>6Rg{Tcfl|B%KnKY`v!0y1RBJ$+~{nxkp19Z|-BnJeT z*46hR$R_dCGxoX_LcOWtfE#sW5-{9NmDa)dm#6R-R%|M2<2Ql|mWL3>=LY?aj( z1ME?5Q;j3?PGnByncp#`6fEvA+LLqp7ypmk9D-%y#fDVA>AMy(?=w?o{N8`Dy`*XH#TVD%ai}$y8gQ* zrIyFBKB*ZK8T;!Y{{Mfq|6G4s6USEqv3a@qE0>W0|1Unlo1*Y*50y*ht$z^RXD(j{F8-QSPx0fENNL}npZT_|EIi6rCp|x&R?L4m z-<6ObyPxEolefFpouCj#t1|!2y|Xl4h^D&uJ9(i;d|fZI{>>Ya48X&md49p4Hp|uS%-GrYS#h|91_XawsT#d zxhw4_rKzUPhpgr`NGvlEru4AtW)z+TOmy_VUOZkF9LGk9mp(pw^p{=bzb}k`W7~A& zc!c+!ce3H5({g)$p@&QCba@a|!C;-94sZmL>ZTYZ{wlv-vE@-g%Ic%A9&^@BE8Mhy zl@FE&u+Yeu3@|ir#7tCUS^iYRf8ho0ub;-vSO1r_|9h=e9xo2amrL^|Kv82Njq59% zf8tXx3`^lFGp>#Hp;=b-QM7ufS-}M)N}jZMWizw3pBQ$Dlsww+<*djm*AXLRr7wJS zvgR(295k)QcC;Z~)x`^VBJu;rhA{4lj#U4bDEd{fh6@2-=|Ke<{py~iGm6ilZZ%Oh zOl>``^R%Ae+l`P4}6^A=gJrthzQbB9mxYqeR`r9fqH(vsqCRiyW&S0v=zT zRrvyzvo9W0d777(8rK?8>^fc>GYu$TP2$&<#(uEWqqiHW$s8`#&woMX)^L2$en77J ziut1bzgtrOdc!Xm15b5a@#Z59pdf`D0Pnbv^#V&ct41)syzKhRxQkKUXINks!~` zZzB%#@n#*}AG(F&=~1rSg0kwVW2IdCVY}Vlt;?#`^Y0o?7piAn1JoTTE5feqqpB)< z`ljh`5owmwE#w|4fKj6gs;-RsCeB$@s#-C^ez4SA)Xw_kXZ{s3p4*O7P3p%A2BT_! z!dxEU4VaA^Py}AWJp_=asZ(8+*yZs`f51iV42Rayan2=Sk~#lLn#?;+lYDl&Zaw7s zkZHjZ4}*l|5rJ+Qy6?lcO2TVwqpu0K;xpb|CFKtnWpIxa>m?Y87pnsQ<~35WF+KNj zwnG-h{#@-zyy`aA9X^7r@>({O|52AP10P7{o{&7 zY`p8X?NWKlm~Fm7SI4HFr`@(+*WNPBPN^`fs4795sNo_#onkBf82b^2u37(PE3XR2 zt8#haj%KZTY*({*nECNBAz`joVN~xXlA1L=u;YBDGIa@*Yc1NTlBN6t60C`belZ^E zqLypn8G~YO@bvzI0F#b@n}C#s3<1a?4^bg_l`^AOHWjYj$IQb)-W!WNz;Vne=5<#4 zN5PYM*F|n$40b*CNNtpBW%(!7AU8ko;y13<)fRlW+?E(ROLfc#Mfp3w<&tSb9bT6r z6NTsG4(`bvts192;OFF2pjFj_#ckyu0?gw4ut1oq&uGf#K^nS~T1TQgds+Rb)|wfz z-Z{(iCu{7*&%&|QlYA zHck}m&Q=Js?C8s=a+%}V-yZ7RoU6H-Et2V~V(z^f6Lt?x&HUyv$4(*m=&n%X-eFM* z;Pas&)cnkH-0#m*4dh>40LB*XrCZZp`UhF?(na9K_mhzi?Jhk2z++6S+bKe(onW;U z`FXHGBXk(%^deO)Xn3>?HJ>mBj^-|rP>v|QiG+hSmmyy2M{s7%MA33DI?^t68XPK&X| zYX6P$&%$dbU`ggaMlznuS1NA;Ao>m9e94&p7SCxPL*V>A`U5rymv)dYe$8~g(S|Lm zF1+Pn!gzCAR5-MkSDyb}i0~QRGr(TS|HW>BegrW>~rOLAN+wh*`fpzQf#4(_i zQyyNMB*%#s!)+Z=*OBrJdVEvU6Q{KR!zxP{i;UmnRB;GDU|XkVO9V^;J13ybmVkxo z;QPD&)e$_$)`BnVT-Il<8CE{ax5SNK14z_(VDgu@Mvj_O>oTyR(Z9Nm`d?HffBlo= zipfXo>FKu2R(+;gM?PQtTdHW0cD@}kd45xU$2(khcGt0~y0P^gew>={-E4PTlycu9 z@^0~(A-{jBbMz`b`ukz)6j%NJ^AxdpUu3nU=8=2H^Cb%r<+AA(bEeU9t_U^7;)JyG zs%yc1cIeXBQdE<{rUy1!RO2*zVO$=!dyt~qfN#`c* zWCeOu-^-GWY*GLe*p_g{#Jehtds*;9Q@I1LSW{piXDt>GpbbYd?slAyK0cvivojBY z{o#p*zBO^%Ng&rZZSjJ=YdO*fO_|FX1q}lRk5-#ME37{8W_9(XxP7V{D|)1G>`B~6 ztV_F2!G}fFYxcp{&o=iJF)NL49`4P#d*g|f$0X9{I_Sm&h57Rf$)cj1lqwKZoheVM zNKq7p=e`1Aw*&d4in$!FKRBfRAY5c|--m$3TLDbRr^F(&sl40N=Kw^8i?lm~`&=Mo z+GJwpw2Qi7t&zt6 zXh{kN*ryV@cu#U4*V}JyRnz^0(v<%tQ0UYD5Oa&>U;kPP9c*~;meeur|554DLLdu? z=UT+eX!%PV5oUrnp53i{B5Xt6oWuJ>xDW#?v~iw6PfL9b-Emyx)4zX#`bGHVr3>~a zskoz~r;iY#QogFn64WnOdHfd4(o!4AIpiAve3oG+=j z)?G6~%fqRXEpXO!XEi-X;k>k3HT()J^XpvgQ`LMG{86C{p73^h57B&hL3nx0>%OnWT;%=`j9?0R5sDMh|$ay?? zPdQ5V$gy_>)e7I-gf9=O3Qst-nm!GA1Pox{jOs_~s;p-qpCselF{bB-M~ADO_C91Y z$*o>O_=<3w(z6?T1z#Ta|U|>E3TR*j3ieq%yLC6lpzMxmBxqMslP`r8YlLWlATf zrt%apDBPeHF(6At>;VzscXDrcU zQCL6i^$e$h#Xt zYv8P-C&16&1$(hQGKThvlzq0x&E$d9Lije%utslA9~%wP+*!S#bDM8ex8*<)fR%S0 z>#~KIxi&M|0)H5`+7xRb`8YY%DbwWj5o}X>j~^ESdi`SaK*H3}q4b=GJdLC=v`ZgT z71v5@oU!tnlUDEHLzvs7xem?Ej$k?llMs(%ovUH!-9M7A`E)a`oXYLz_JNq&mMmE;K=(D;(zF%sTEU#xnSJWk3+FuOK zS9wQmpn^I>SI0k^_(KJ%`SvzSMYkcKOhYbDCn9tBXcWW&9emTlce@gTr6HN5r8M^F zY{0C)Uh`DKJ?|;0c;|6aaZSRrO@h||Ke-icoyrlJ;i!d)i$d#pc~RFt%y7d6#nT+s zIAoO?JGFM(1tQIb2j@ADmXUaTN;Pz3L{bI^c@IM$`9nvwrLco7@S85|VBB!$=iK>~Xa(9$Mb(pvNeDY*4 z5a;w~pnboOFZjE5;lDK)@>`dI9B~IHV4(N*BQMyp*k9404B7BiH-KM~tndmiN9_%V zw^Zpbiw%rQ>7tEdsvlgOjX7&h%=f9A%1N|SD$e6DU}iWpa4=>ouA=SWzn-TnJ7i=R zN2Of$olgWf!81PbavAr+WAd-`%Q2%y4|x1^J_s2Xo?q2gb*?j?XN{f+D8Y#zoz4Ty z6){R((xtxpVofZb=^iB)#-3{DR!LE<*r8K>jPT| zxivfN6eY9S!ERdF7|!kqL!zK@_Lgu_w*PTZ*X@Yq?SRR*N!STu6I$0ABDPp;ru99*Y2Xm}r!C5Q`GwlMQDBCJ*? z(S4Vh&8tqo6(@TnAPlZB56H;Q0PwF$I2)Gb zz9r}>(~LZ;9woOK4w$_p*K{V+2)aJcSV(b6-nZj$r8(2k01Spfp z-Z_K>!-X?>C&*y(%Wh8ppcqO`0$#fS1vfIWpJey1f4qThvh(hvfb>lH5d62xj6kj^2Zd`M-UY?s-A7<`XPO(j;GrO#*8=F*iuNv{LcB2cs8*Nmp(QkGY zqV^T`Owcs0@aNtYs1u{}Cw=C4bKM-dtDXE6rCKi2_8GE8VNqs8=QvOKG)YnTY(SAGR^PP#n*liv0X?2KL}@hwH&p&GGCx2PrOzLix1KVJjquz0+c4a0^qpd>b8-tJ0slnr5^Bhv^6^<^S8K0opffqvdG@YSb_-qiYV!4k+m zFIGeQS^r7Qd;nsG$8T%#Ad<>Y$|d;vkLJ{%VWTpXNl~U9c36hTs9MAT_KHe*XC-yv zbl!puy{yehkuls)H>4@ zIk{Pr_XPu1Y${NrO{vVmbweXu5B1liVnQrplF$rP{;&E(hGwUkhXuD4Sb86enH zR8>b(LP!RcFl%}HjoADquf=%{c5KvJVHr&&R_T5!xmLoFQdPpo)%f)Eyz}Tlh1uEJ zK3_tvwR{&v-*TD|VdX*7;wLxR-ZRSHHy)o6Av>vk*F#~E_8&xt`ePtE=-Ry9{X0&7 z(=^N*Tz9uB?|rmZaB@e44E9oAP~4)EU*|5rF0sk#hK)|!(c?Oehb5T<1FT0{_C772 z=0E~s(AW3o4p#8&azn!-+58b`O-j77_}!9|f<_r7!(V`XMP;zm%5cI&x0KkF2LhS~ zfwARM4jN?$QoCrVPYR)vZTn|S6U%z9On}2mKq$k1O$+nb-v>Y3vQM2cAI9FK6cQ;j zX;?1I0qrjHv~AszE2U$;ru^)_+{o5Wndg z8+?j#@f&lwzP)|8n+Cq)S{U1$P%&^PoxHZnJa<;zgncH4O%_MX-e&qei@$`bLs}2* zGny4xb?Zy5ur=T`SbbL?p&aoT8Zy4Xcw0IEts;G?S9kQvE^N~TiDpfGW+KJP7Dm8)i?t1#V z#n45W3~%==BcEzw@wajgE8q3bLi5G!F!mkyZ~8GR5AAT?=IDE-WPHu&^^0SFB#^KV zBT;hfq~M&K>lx5C;BFe%EjG+);#Ubr%@qQ&Q@UHlx3OzM@%SAizwsQ{Tk~nb#N*yh z19^UsZ&_$W!#9|`mB15v1_tS1ft5;906{9dqPp*ndS&Hp@$)5V&ihG)pN1bWX68Cn zG(KyV*eT=S(961jI>jw7&#$$a{@YXS-vWMrg`tkqQ1AjT&691I2A^-s%ndJ! zG>Xm0UEN6=7RYVAH1}+0woB|=JN~neCWfCT(_%-aVNzD7w3i-IX^_KGcsLTwALpF%u`DWWG2y#SE;GN* z2HmzrjRc=j*3;MT%Jl4vRVx%dPUFCEcR2*KFqh#Ov>3Wleq0=3?T8zJ;Gyr2qI{>b z{y)y%0xs%wiyL1MMNmWpL_x}+6_IYFySowTu0a}9Kxve2X@>3?LPS7^&S4lBM7oiN zA^s1$df&VI?!Eu_uAk4k!s6`j`JLyS^F7~FPDhLlYoo~m8$K7a=erXNiL~@Ls;Wn_ z(3n2$K40_|zm`MD$-xkZ#mIU-&F&G!#^T`sD{rT;*O%Kz7zKn!I$EbDD45c>(ZZ(( zUlmN83^DXsduV~lemg@-Nm*P`@ub>vK=cD$!h+4waVTEH#%Z)7F-5=UZtQ^d)FUL~ z5z6i!O*JJR^Gk7H0HP)?Hw65;rvLBhQxW!aj^V2P-S}_m9}hO>nMxT|dJy>U)g0EVC*q+2`({R*jv9xHasG0owu}0CMb`J8rx`EDz5$0_UMt9cY z700Ga>#@1j9?8{?8WDUoHiadW?>J1CWbGK4G|ti}HB@y=sx&Y1t>vSA-5y`ZVnmgL z*xLYM=w6u@*(ZoexVBiEzR*;9(Fm*HJVXO4n6_4hjt=SN+p~QH&tG*#MFr=KYC@IZ^#Lp}>-Jt`zPX^akioE~^tvz$!;UpFS`> z%kSRhKU#aAVyzzdz>KfVf}S+-x>9_N1RYIvt&yPHD~_5UHo2 zrzJS@OXu;8fSC49 zaj6HCazCkoEe<L6`W?A9YEiK3vlhxz6c?ztU&6eFdgDiJswZ-&it`su6PilaeRm*gO@+zbqzp{s&*!L~Z&fcf=>Q>?V=6^EW;AV3U z!tjWj-Izep`Py8==1UhryS1158y~@}*&;AT#;!7z`!bF!(36Z=Uea0e+QtLGneTzt<&1j*Qb%pA971xK{SSS?g%mkKHtLA!CwjidX&qkV;4nxB6 zTwbz#7|~GGCoC>qF8%7#6{|BK6TF7%#L3>OVz5aqpo+~`%ngYV)GRN!<7V8~J8s*{ zo}u+RVs)xGg31oY7lp-0QE}V3S6M0e)MZQt$TFj*ATMi|!I>_5!E(O8<7 zcIciOT&5AThlEAak5vOFx;M)`wZ(d}**46F8OU>2(ny+02U|Dz?SMYmx)m^&9_giG zyd1_#H-yLq4qPeSS7|mzJolRjcY)qHRF>3mkIshn)ezVL@hOD3wbB6derp}u=kHDU ze`P5%JT704vZmUPF5DFR)_FVB?1`#wOOCRfe13rorUou8+aLCzP+iy11Y31!TwA|5 zkX>jXdfK7TV&RsAR3IbE6U}JK2u({^i{)B~yuzVLW}L*;VgF{I*lORbxDsTBeeO$j z-DfXB1YBA)95BgmRg8peJ;me8b|4e>qz6GG+IPjwZE51`g72w%R7L9@ivDsnpOIH- zUZ)aGKjvYIsJPtBf!C!%CAA?jCo@?Oe$4^^uzN~5)&+YTFaVsZ6``EZ4szD%`i@@_ z3Xf(*3eI>qkl9Wt3jDZb;MV>aO!L?BYw@s|D`I06DE8Syb`8~3RjEr|<3oBl>>utW zD+!_+$xajajoPI2SBl7a9Q~<$X0bMUfEnt~mrCo;n`xOa_pz46B{t_%SV&{nKJ%S6 zi<~4>YgP@t`B1>_3O+tZ_Z`1E_(+=D?n8ltj|P16i}z}+xH|P$Y)q$|nI!BgTN}#} z?(4i3%Z)C<2~brhlx_3Tcru4+2aAP)Ps;_=>K6(eNF{*rlUKW^{{ee3_6pd;wWr)( zN-e{Oa)`x~w}t9d-h>6(%3kwvquGR)+N`2Mtp}MEwki!%bu|KT~v|$Lz{hW%Hi5{Ksq6|Qo@0c&W8Lb-oXrNKufNv7`YV1 z>ez4|?--63>cP_#TjUT%R@fGaU2!%eP!@WQxajDT{{ATNB6?T#_4P9)LT@I+jSrWH zVDUGIQKMc7pFZ6+a2g>15vy8F!_bhDVCMC0VG4AGTD8aAe2JR*{1cxa?Koy;rB0*N z&W?V9ho2cN>gOYODY%%r)Q>A%D1u~_k5mOtTtj_ zt)o*v74^ZVs?yb>HQnmSB*yVN&+2)LKnC_7OuI|@+uU1YE0V!`Zi%kBN89`ZGe*O6js-e>GRQ8|^k2QW z?zc$@GMSDe~#Hi(I?SyCEe2>whZOi;JQNuyzwzXZCEKXABIpO3? zUw`c%MBdn}!1`|P`%{53CU(+a5{m5@KrB)Gh-g zp09y~goGl$o}l&4wi3gWigjzXvBC2;hK{G*YO*ev&Kt+_bw}SsRJ3t#ys*}DZ!xzGmkMgw)On?N^Jv=HM+2W#9R=A9|VikAUePSYMHkVC}N%sqDbDpU*)q51`2_6=#}%aHsZO zfY0(eF8%GdKcFo~GkEJA+zVm<1nFvl&+6IjbvF3pFXMm>l?61-ysrOsgwx-@!~i-v z5DmVaeB2QF`~47cIS*6rtlvfazm!z{$F!(_K0~BuUwtV8!;5|B}b##u75@2AIMT+z^8%LZ23>Jb{RP24{6L;A3L;Nnp+@aprut@8?U}H%;{qIQ#O~QL=+VxK(i4k8A2A% z4s|J?>@+>jKjuuc>K_zkf?QQNc@z^XtJ>bO?Sd|#-G(d7Tr*0Nw|{mQiQKzFLP}}_ zU}dk4+Mp^471i;`eLQK-Empj||2o$2@4QOBcMA1acQSSzyKT7ade_yZRO7cJ3a$~}qm!(CZjg9g) zh$0mYGfvt9!Grinx!-;17(32{qq?Q`n{oHWjLpqAbS~4t19lf@X8rF9?o}otf~`6V ztEv*O=jBzDW@2Je{rXkL8u`SNu$fe!?~m8iBbSk%uU%iiqOPjSL=hotk5G(`R#8~K z3xo&us#^T-4rWg|!2e=)QEl=4YTrrE*w{GUV>~>k5G85l=i);JEakzcXh-B9#~Ef+ z*44~a0E*w$}wY)|5!n7fJ@3(MBd<3~b@czAXK8_DC~ zPUS*67ytK&|6>}%brLMbD-MM+c3)YQpFRzM7Vdx6Yvuf-4W(ZIM^qG;dR<1Y5{>so z(Cm|G^Ol0Hi~D;3Y!%%fU)t1b}fV{Da=FPyhdaq9pwe!msk?VkO+ymREGKlg^N;EYzVwsqkQ{xRv zcmsoipdh*hnDo7Y`OkCw#efBL5Hmb{I09nrYV#g)3vUpM_=3Xzth;ybl9v*PyXGzr z-snpdy_+S_#qysHjt0_i4Q_oh{Evnyc{;jvT9OX~h zmb}%bM1D4cfI^EjRh-`5>`Bdn*y$~SfGTrFNkFH-$_i_lGVf=*>|f4|g@b($2>0=N z%CBYgN&Y|{bdK*AzhA=}3sDA5pdfo&4IU@&`}b!SeiRUVLPB?JWv-psOKG-&46q*l zTXSjs1PVwG35mc}Vw<(Dw6CjqdM3(->!f$M(FZVFbMc)V>yW<~?kHb#t&O0u!{2|J2+hHHcr_*X zLF$)T4s12gT?UOv$=JPfst};*uRs7|P^g|55xnv{x1>zA$m*E^N_H=C$sS9`TFa?H^>pmm7GQX1P_r+Dv61SiX#kO zt`rb!WJQh|Ej%d8Ht_ryjNFek*zeD2Mmb+lMNc!pR!_FBNg+WEnPmM_MvAzKjnI&g z6xL_iIXMNO$xKnC(4;ga>u=#;|1s!M?ir43VO6*Ch#jY^R+H_tA8&MOf)+A$^B8A$ zD{X(w*6%q!rEcJ2h7}BbZu(rFSYRll03{3 zPc|m`^7ZQgV5L&vDqLgFnZ?^XI`}}Y&ejNM3ZP*bz;BI(k{-BO6OJ)n#hhZQkS2o# z&36@DXmI8`8mg+_)nhAAw#x8cz(E1Urq2^z|B?9j?T{iT*jH{o8s!%5RkqRzyrCe? zRISEshkREG-BPV|sn&fh#l#_?Nx?;CHOeY%IloW66f=WsHTI^Egqqsu4A1gEWso>% zL2Re6Xg;9v^Ws|SgKvbd^dKPyAA=(1`_fB6xd{($<#b+M9S{9=vv|A50I@ZcV4Am0`m%hCsMJHmmWK@_Ge-DUyA|I0n|^mFlhy z*95080IIiO)~3Sw>O8qED=!~IAd5VG^0!yi-`@{g_)I{-NGau+sA{Y=f>wjlYez-K z>g`u44d{4Smxh)`GMfk^AD4>Rhw`J-LU+=4Euw@1$8U72t>>m>EdNulAWr>xP~P&? z^;b7BHb}cNIqsz({jey6bjyqbG>f#S)g3FQ^Tx)s>2XFX%;fT(#prRugK~7MET?fY zZQsBzW!e4)_d!;UlD>hKN&#g=jbfT8;+zX*&h=5QJ~n&eV~m~^)O`aT5*b;#5o7!J zOjCgHMc-sLwe*NH-ML(AFIClwC!RYHs4Vh_m`0)`t$B9|R5Y5cm}G=cf%uGHlTue> zWfZmX`4@@uR~@vxd2U?V?Pl-({4d7tfax2jgx;y7y=eQzvNwenI56jHHm{Vq?vN4@ z(Nch`DTUleYh+Cr$MDzkZ`eMDV>~C6V_>~L8<(3w-t13-T3SywR7VKz@l@NPMmM_7raKFc zj<@kZaDr5SC(BLCm=_X*CyV_0Ij37JjA^7np@OSOr(`757tylbfVj==O1}9Ye9d*s z%yZ!_hG(IEDtj5`+@kuL_LjL|b7_O@#Z~${W@3t5*XM#VS0{pURwrQ2=f@0D*)yZ( zzF+Mkxar5reaCFStZTi{TpC=`iFk?_V1x#RFsoUwU*nu9Nz36}w}fEx^42FEPeuxr z7Rtpd@|B&P3ZcsW1u=i}Q31we+O_tnLI)cb=hNu#-o00CjeOQF5xjI7Or;_#8!|O{ zcHD)RB$aK> zGRrSs`@x52grPU>PB#1uw46Z^5(8(Bm_c%_UW@mkEJ!*jfifk0LWd6#l4tQ+I(QZC6ZX>>ol@# zMc~a7aFqajse117S6WiLV%y&()HJHuRx!=sZeJ)xN=C-NVHagR<+JaGZM6O);hz7; zv7WozC8)YK z{&_hKSCWyFbEH%*!L*r1&~6kv&rTF|Kb9mue=w}wu9((LQ%j4Nw_x-~arHB_E2XQk zAEoEmv@d0{WB~EOw>G7nVYMs`ODd4h=UZ51cO42xT=&h#YxBa{yB^RLWRR6udAjLQ z{hrR(nu)mM(6+7PkGH%*W;haRYPjt|q$F?}6(R8bak|~AB&L3nSV^eVeF~F5S_5iR z=~4w}cXVsZ|J?HT*Fx0G)Qnrp@m&|<%DHig=JuO|a1kV4iINY~Rc>M1aBacO#OG}Y!EY0wS(E+L&x;0lU<-YKfB9zOa2!IjhpjKOakI!y}VOaeUaZt4myshCY)Cc zg!s?5rY#<=lx!k{!U6d&aFP7U^C5x2m-|riEi~tI5$oZ5N~54tmwHUw-H6$NNQcNuC?G7g~F(LddfINa53bfN$wnp97;z6H|O=4nV&V23T)U zHn@mESpeBm@=!$K1JDkXpZ~tuE0uFYmbLnRX|?f-06}#|hRELAdaxcOU|nbdcpO$N z3P=4WK0ONVd8?D4@Nr8+k7Tao7B5`}HZ~O*8N1k+F-=O+~vAswgCKQ5f-CY zn{C}E0vS**<%z9oPs&K~t^y0dbGQDdxV(s)`N?(|uc?zPbz7mBLI=JwKVgE~-nu?L zW6i;MV^g#kQ0?>lY~qqwRC&oMa(Az9#-XKWFlNIq#>cswK65S6Wp%&vGSUr-x84ACme1Kbc!h~{Fhey zhS}~JCct(E=qnf(D{wfZ`_n8^Nkt`r`AEbYvkB3$y^zh<= z$fd`t!7c00<0qql%hgahCIV*1Nkx5=m ze);ktpL0JW!8it#l+6gxnD=M2rCQSyxNQ~{*`|6P9r{v6_-$a?N%|#QpO|kCbfzzz z;!@$|2kw6*OIu7qf*h$2OB5h*!SlzCR9et>aT>wes{bqP|iKKFSNltSY<~MF#a0hlkO`Z6N~bt_FOH@GHW4eeeKV@IoMUY0;(Hf4zup7QO80cJ_WoI3p;B`^gY&5fJwB>M zIEOjCR|snShmUqCTxS$&ZPptvy6t&rabJb4J~QwcDJwP_hRzrRhSFR!GoHw`FnB(ZMbEH76!=Ar$ozGDW4GY4T2HCmA{H!z8jRz>) zj3$BkL{HSf9|ZERfXz}KC|P(xPyH_^1m<}}S*z9->bQ>zk?tXk{M=rO*F588^*&OX|y)*hu9uEZjTa@O(K(InT>E z$n-g$1uI$zM`4aD_fx&f#P|p6koXgxs7ayph}-g51>N0q4fEs4?hGV#mE7x9YD&W` z>j%Nx`_Ph-cD+)QAr zx^A)B6Ly1dBx{u?1wh!Z5#6!zBvwf3mi)#B(X7)AJZ#m7?)5{b^Xjyql~PwK?`n37 z1a5~9kH|gi`wSdFV^_nsV_Fc9v29`l!zl(TURtgYAYTCm<9OZz?e?oZwF2E z&8R6-WO=(!Fc;WhwUs?%17y^G$p?@$gA1+p2<`MBb~;vAElY)(yg5xM{!PsQdR+ui zlsq+s_*ec@jR@2isIRMUZ!7?ctw&A2GWAM$c(|LWsJ+NJk#(p%36Lf;!0+48+Y$J2 zfUtTDH;jVWUQj`^a^E*A1ol$3y}eyggh^P~D45DKpZJDNH^1=FRHXkj6)Nf zWRuta*!0QR2M_$={ihaXIN7!|OC1zv1)nlAC1FH+a-twqs@3h25`k*Qd1{UBoA7`% z1D@5t@*%$)3LN>5nreP0#qO~%bWr-;VV8}Q?lJ9V;wWcP)%jLwHoP?wbVay4zATXm zE&i2);ke`7@v28sBeA6TY%4wAP@}RciURP);v*vwcw8jhut7R>;UqUE?CRuM|5>8H zy7O1^8-ODLP7VTDr;M`x;uu3=4PB8Oa~VN&Od@J3sVm0fK@`>n64({2 z6j9DV%B(@Nb8`h!kyOirIiX%}Boottv#<93k*9)rw;!cp~PY*e?TB)ed!Jzju%**TZ2>w97`@(d-Z_#j>L> z<-*4cn5D!#Dz^0GK+K5HEppD;CYvw*F*cP*Z$+y!Sf#Z_9C3Dyi4kTh733*u@m&6F zc=FFv9wTcR>^~2GUH&{wT(V!S_tB}bP4qq4)mjL>ULzUD>;!+!NW z(9v^2fxv@)!lmH@13%y=l;NOJ7Z8z5=Ok+dZ2`}=$r&)Dpg#16M^Srd{PM)M`=i;I zq@+xxGfYI^Or=vF(IVnZI-QJS(O=!k>FuLB$GYu{6rfDMoGsT(Zg-`&ln>Ia9CYZ& z!N(^HV#!28d94+c%oKkYCj>daX5fy_WLGT3yzRlsu9@D#^i0_w<>ksB7WD1`7nyBx z;ZKGK#K2%?%;;FT1F)jR&Q)-TNx;##s>R8X$5MC00Skg)Ff@UtvKY%Vn%*>_1<^YCZURZ^DaOUP}ltdf>pRwzuG# zkz1gx34sLa7gh0r0SpG1>?@|4>s{mHPI(@0lRUAuF0|JKvqiyBHYh~88ybT$;|BCV z&O}s<&t2&GUC0NCy}Zofj{z4_jT3|y#z(kJ4ao}!&U|xv+pgYn%*a(d$?09&XxqS7Ij~#glkK4w{GL(K zsTV*f`SN}8*3G?(z7+cDy;7#-qM0jkd#94--piB5f0OJPRf)QWAyk6y*6yS`@kpM# z1EtgP*JYoNzu9TWXK6lUa_cTXyMj|6Y#ZU?hppO7<@>yoMXR)zi!eAb^T$lf_%v>B z3TW-9IODPy-^NKgO6a*r7T*8zVyHK|P9)OhXjPwmax98A+2Db0g==Cj@BSbqUw6jS zV4K&snQO2L8gd&O6>{&6ka;dI`8gmyM`*Mh9$it)w}yp3ZO)|K03xPoLH;Xu@H^rm zmjKK`)?=3&KegI21c*54yMc|NPYrd2-`UANs19)vRpPn@Fpljm112Oa%=M*cjb{wN zrrv!xfX&V7G--{;`Gk*Gn6=o`m1>l_|zlI`k(zPre!(qj+q5d&`5mkRqyJJ7xZ1vh$>7>p>(%&pli?^4RmSHzFGlOyw zK5(Kt&x2VL${^Y5tkn#eI5@;fbJ~-HXUG)J4!v^n+||$8BGkdg7g<>u?$Tb}=5Owl zby`85d&`x(V?758C6}_T2U)c1w6mmIvN!w=>gRL$rlx!l#e63mQ-s3>E*4xIIsv&t z=OBs4Zx^s}mCRa})V|!at8&NJ!Xe$#i2Ue%s1)HD^^;maJxfM->l639sN`?5gKq6E zBPV@O+4F!g0=caMJ*7WY3K??)3_Gq+cISL3=edtfIpegT!kbHLA85Bp(n%w)d zx#qxz2-vPNms*W>3wPf#zT&oqR{OE0rfz7}G1z^OND+o#E~j`49t9~gf#uB5 zfj&Vxy%Q@bdW^*qa-n0fy4+zUF^_t{B$%o(EBJEK-tV%#*khLiW;*^K2zgxu#4A{z z7lwbi+lg~`i#c7zYwGIhabuE3%BphV#70GBBqZF*b5`{MF7}h+Xg`0pdUC-&#J#@6 z6j6ho?4*LtLpJOnNZ*XPUOOJRW>f`YT{8-F+O7U60!fHS z^PLami(K;EgbaQai+K+#>4ku{ZLw57v*CIlEiUa^B#4_`=56PagvNYl=WW;IlDU4& zx3zQi)?@akraWF|5)5*7#KhNr-LWaD(6q1_S!OBLuhu6F5u3OCvn)+dNf8kNLxhzmwa@q6#}Odj;Va$ovP9N{#zKHQj}KZe66mWu!4 z^v=h0|KUv&F*o25YWP!%g)Gu1QWERskw}|vo8lUhn~5z-dWGSK{3ECJRn%$wivv9c zjkS|l#nFCH1D6jbxp_ofe2+qgV8s$UspesWh7%o>S5sf+?m<4ya^W~aU@>#!N^<0G7RX7`?~c3sRi9T4syrmMH;kscPfCH%b+S_% zzWYS8a6w_9fSxMr^XJc!u#OI)0l<*dBObfV-!b4i*4+oo5FUv2A;4~rrAmb|+X>5# z9y-7MEHGIGe0aRo``dnRLc$&sR>%$q;8JBgl_F3Fy@QZ^SSwFZotfMR^PbbT+buek zbe*@kt4xC-6^QtRvC-Row;jI(ERPm)5a1u)Ry1fKZauoKZ*XEJ`0qv{!D&F@Py*7;VM&J}485`4-l@uNsr>AcCo>cL z>`pg9(xXA5pT6-x?W6BU@*CvOUQ$fYi74P*elmlPQQ9?eloR&?!&y-rfIw9K!B+gAqMH+pYp+BZ$r6%^k{4?|;8 zYMKO_y{^^z^l+`S*?IvKWl)0|12qXUNY;Ql;7Nxk_SR7Rw2ba&lbod+ZRoF45$-}C zy%wiCcUptfhmqkcr7+Q&adk08uoSSrE>dWO%*gj1hW6Wff~uMtn4HGS$i>yM(S{-v zp7L=>Swg*N+2=h^uc7N-RkPDR!C;&={Y_T{5MaYu?vNtCRq|Tkyu0P`LFrEVnfIdB zNOnTpL$@Bzd#-y?L#e_Rs5?b*+^i8E3e1{CPmFFLS6_cxjh6uZy*5_$Crn0?P8_<6CPKV1+z~mYg)R(1rI^t{GkO)=j-TmTBB(nEmg4(YyNs#G6h+g zB43rs+S@3x8nReiDWzYo>}x66*g@IWVij!n^2&8mdBy0~grUv#>>O6rbXv|#pj+G9 zW-Sx3@wGZD2#3v;h82ttY^6lcEoBLp_}^s>g0gQHR}YZ;YPpLi?&uNg?LbW37*9@i zF0&A$PO<#l;Z34wJvEgYPxV-jgQ(l=$O@-dajb^<_1g=UK(5P!C9nAQ7K^osy+gP( zmfP-S^<0q8Oo>t#cMW@{N6#QspLIK!-PDQK&vJ7qkH_?7K+93kM(g@jX(ak||9zj^ zhELY=THVRBGlWsZc39SE^(#v1`X3#Bqt!?qzqEofpPrF6pPF)t()x9Ch14Ut;CDiLDa zUz<7eov&`+2YVr52bnHz>-83P-1cMoowmyq?6uEeGf$QGsCo{3YHn|1q>1U|w|q<+ zYp6i(hP}iq=-MK}J-P33V-m-g@6PO0$I-V-skztHS~})dW$dIE_1PcUw*?Z-Kb3_& z;XGGI%nvZQ&vkMab$X(LC~rRc-X=*{Ix-U9ai^=enD9KU^@L`3i`ikZ8i6h;?n-mJ zCxuqqNbr2@8#Gh@8}${Smno5tD*sf$a7uDH$zR>WBe7T@6aHcmg#Avvd(s3^0cBVPFSaFc7y9Boo)o!XWQ6quWr3=R z$yS29^)8s{52>pYC6%o?yDJEO(!}u^yA}1^3ToB5waNO*_lG7Rt?R62XqdJ=tBoQ= zp;K+W+^M~;q0m7|=XHn-#A#I;VXL0|#}HxIs^U5qDBLjOssf|S;6kl^X)O*@lbC>9 zpXFIAit*i((omBMt z3H$2pUbJ2o>{sO2q^EI16?4Nct*!0=IPJPGNQgO+z}a||>eZXrFh|Y(;#6oG4G{K8 z{|?x`=+>M)t@OAH!DeUKrCG2t%r>dT9_~3SLo9pz#UeR%QabkkPR1JVBc z(4Sqibk2ta4k*3gLedOrAdV+XaXG?$Pn{^e`?1n|Z1gS<-+E@} zT1ce0r2j*y&!iosdsmnEan(%n`U1;3m*2v90&zcu6J_ArPEcW5zpDT_-W4Eby1GO1 zrhH<&r2agibSayBS-bV%wxZpBs{vV2dTafO>94fI@5pfX3XX&b6RShk7WbT&Cxxl3 zT<7N+j#!4wZW{Z~5T)@S9M6BHYp@>~U$~l+W0t#R`WC;IB(bdh49fAjy)!X{+YTGu znYspo2asHE3A$cJ`(euO%pjDE6(V%KlJ*`2en@C2`*c__D2VL6lryocV&6{3T{H`v zosYAa^j_bZMY>Nes0-TDBr;;^+p1j3qWB0&XnO%LMuo%{(S>!q0j({xU zwXHWd&+m1CI+h=0eXKH^2v*dA^Iu4C|BsphJ-ME{&T4_aZ*Pm~n>J?~8ETd|ss-K` zxT~*``)+v9tZR2@IK8@Ey*A>>%6M{l`wo{(h+T62=IDH=hJx`@9D~ z#c6Y%&wMBWfEwP`!qTRkg)~1a3wxlwSxUw32*76X627lYzjx6Ze^%Sx4l!|_M-Wx0NhfeC zlDsi$@M{);@$uEX8jLaG;);c%+iv{szsFU|FcFapsx{WbM{X+`=FRGMd9PpdwA0^b za3+(JLiiRG{o3`^ah>5I^AMey2_370%>nPH*QvzsyMv1r%92Px@8v=G^c^?6F~|P% z5`O17S$Y~XF_k?rHb8Q1$IZ)agDMO$ReKS5F^vX$+4H_>z=$S)eNS&QoUa1nP$83= zvNGlKY9T7`&8OA&ljC@F_0u_*x6gXWShS6WW?_?fQb?Eayy5W><$F>Q>XB4}>MAPJ zI|G8HlfL_t?lxfJw_-w9+LU1=fe8VXz?h47thQt*CU<(U=f+jHq_>Bbq4^F$-`%ak0dm7FG#jNI-`kLq`1 z=VnV#NU_ynUobgj&lfE7Ug^a(<9FNSgH*)204^vUuCeP=XP%Be8fWg~HK_%&bsZ=z zCdx!mSlT<~Z%~N__(dd!E<;KOyx>gW`ffVM4x5O>mhZYd22A=g-+~Dm= zHo?Y(FZ2mGf~L~4p(6pyQ0+Tx1{z_h{o(Y_u1~DNZR?0kLW$v<$nlh9Ws5M35%8{ z?jv$x{2(^*jpp&W^Bmq>4J_58#}0OM8q#$JWq#CBh15K;FY;IS^$l2WSfJhBtVxIL z#(^QmL{fL4nDzQeZ}gxw%GusWibmzWi=w97fg-);XuQhU?g{oy)^yF0_Wa&}Ij zC?=1@os}g1=0Tqx-_o2jbNuRq_|5rk9SFqMqQ$HJHTSmH#;#s@!O#Vncdy?lq@^9P z&)<~y$9s%L_Yo&SB!y;k73nfTlzd>+_~PyBM|e^9In-H7=t78H;@>|Hh`HaoFh(PC z#WwgFC`S4rmP9f7 zvembb0Sw9+{&!O{Asm3JtpmYkU&%LjCD!^<`GXZx z`4}_llBlcw_Er<`T4s7F-+m~rQr{@t6Aqt!+Q&+$D^N}%9jzRa#%eT`W9_K$VX zMT}jDNcg;>)p8XwM0;^kW7e+MxVNCAEhzrHS?`c`DLKE_z0ZmzeMP;(<66myRZuu zqvo3b^`d_xHpV{xB4Gd3Z^+U4?$qE1S8jgaojRjY<}sCJwcShe4R9pAmdNlf=I*;2 zT;7DVG0ywh(yCaO@ntTx+_!Svo28xYP0@e5NM-bY;Mcr6R+cM zSF%Olizluy6A_fEyf0O#UeZ+xTHUR*P`LR?oO^|7az!PJ{@GH+NW)s@xWD-OuDG73 zA+ukv5}M?G>DW|wNFG7SeKGWAX&FO#s-U+ctU~fRY_vS5*SM>C_qBO5f2Ftrhf-yS za|h?0mHlr;Hjbm)vo`0K?$^6Gzky*yZ%6}3BqAEzwsY@BZ0&BBuL$pUP=)HT{eC~s zf3#Qh+fjJ(i{r(&ci+h-vFN0P+;7Kbv^XSbO5e^1OxkEUL+6UXwO)9{Vl%E>R#hFl z-!3(}lkYp2Eu-Dy+jL{{EspH{h`T0W=KrfGZU?kuEAj3F+Cg|MGn?D8dX8YY+|rMk zPRq##PV3=(BC4k$a2Xo6rNIXhG!oGT(pM$kiYtJ$4xZ=!dhrX<9E@s(z|fsVUHmLd zL)ZTAU%TR2W7#a_$NQhY{j5)d)kS309#Vaa&AD;8Kky3a$Jalm#PfxHkMAdOIZ%A) zK6<3me%dH_e2At#-|?7KiKOdo0RW^l7==yrYDQ z%~ZnXO~q&=2lmVsoW7SSnR7)3+okk!P%P|g0pp*(jjb<8pQ2!xF>ib-=vEo$;h8i)DtbrM!X z9#t}na$5LXLDGoEWrRaOmsSbi+@ zs^saI^3IS)?0PwNaPqA1ZgKw9$A8_j4!qAHBFppP6fbtSVjtI3@W2lWzsLR3-|vsG z7~eVXJ4#i|ex2!lRVKpJ+X#KZt8xR6Qpj7Ih$5QdD+)!C_G}1SA}X^@mqtRA?uPJJ zG5o~r-(*BHk7ha}neP^~oE|X?G^m{(9}WNx?=n$zJ|SLOi2*E#=$_=8_Tnoc-@r{R zP|K5?YW9iNKbC)ACWDUb@Y`J`rBg}^16wEquOvt_oOg|oJk(Vu5>(-rb@p|Q8^~H@ zkx;F26b%led2ms6IDLMVA%FxgReCc!4vg@4ox}+h&!=P4YQ}zWFGj~`=Q@#<_1iBT zd-t){5k$@kiX7UKdlm2B@2lCR}XO^{^iRQX459tMzj2-0nIdcJKg zwH~&$v@|8Kv%QRmr`Zf!M>hof795c)982T!^~{nos!3**=57t*A~1h zg?#d$`_8-0XTKZSe+l(>i>pny+4M$NSpH4+`V91uem$-fbwVN!$5<3pmYS@fPR+wv(PTw&1lHS{o zRIurCIAo90(Op!9zVFW;Yjz{JL4Pfx|5Kvv8xFRLr~8YX*ZCoYgb|6TEcVJ=G- z3A>U_B~TYBkgIptTeBd8%KE01(4{$E871TE`c7Ex!rlT)Nc$*1|YIJ>`YM z56HXwH;$I_(jAX>tjyR2BtkO3sBA^OWW1pe%@guk^uBp!__nJw*1Mb+Z;c1;J{}nw z!@YJ#`R-HDdYkDy2bZSHY#jI=bw{iw&&i7F|D)?G!=l`}zZC-zlo}cYhVCv&fq|hV zm6q-lML<9l6owp9T88fK5D+A!yOEMkDam(p&U4QBpXYksFMgTJx!`8+wSKkMogiS3 zj}>&>Cgo-l#jZy*QEINys<>}B9QG}1!_(w_8@M+RU*f=EmNPr2X6$$@^WW@O61kH z*4EixJC*TKw|wqlC5uIGe3AAe^3V2zh31F!6R zY_A$h6~-3w2DXzSUnX#JIHrMQEl}u+W%U;*&JnaT;1<$Yc|;_pk$H&paAFgf$|vL4mGvL^PX zB#+C!mu|JKm8YpGP~7jfRG{b9x^m=0B5D?us*DW@3GwC#P6zKWM44t8BI5@c0wyc+ zQv0*+w7OaRgrihW^YiD5Nln|81Bb`7eG9A%4|uI9nM1|f<-AWiM{(!P?WfA0{3JAd zPAhF93KCi5{Q9l`?&^QEc;~n#HDhuLaWf_dgD(#_jop+eDc;?{gLU=v83m-jiGr)E zGc9kg-MadWdz(=nv+r%XkdFzKg7+!+V0uV{)sRJTn7r@s0qHL;uW$N2){(pnE?wy{ ziH|{Xg}5+83crwT^TnC4w%%Gf+7huE|LowGBa(0*$xvnYn=F`upQc5$f+JTe&+qQx z1~nU-xt|56QDZCjyuR)DZppk#PRFQ_$I|n*N)Gb&rwJjnchnDbpmH*@6fcmxsg*4b zO7F;PWFK!@C;8ePEc$zGR^lqH(U;B@=n5N+F%vfQ$%cF(LdzfWkY+hFssNL$Qe1M2 zdJLs`VJSk&V;mf3N+ehJJ9J_Vc{I$@+&DK*fh?2e(Q|jS4O!@BXOy*u&907)PS~2s zJ~m1l_j|h=pMOBKhwMA)m=oLF`?7*&o!*dNe_y))+)|kk$>4kGmzQUR4j7K}Q5^vw zE=5|Q^MdnlR7Ks3_xfm4xKO_)vs=WyYsut&aov!_Ev)p!OTxA@%w$n_{Ps{vjpU)3 z8OqDcOMyG;<$Ea)bZ?i7+TDymW44jQs-csCA_)yN4DZF}pGCkC>nRdGg)&7EV=-Io zzxW=~+o;-GU#j0Q7psK826TSru+N0Gs`05ZhXYpdrd2j%y5x|42wPGm!nZ8`G2 z0P9ls@6;a8LCXTiFR9}do?m)AzPFew{-iejb`S&0b(eFXc+!xg(l%BgvVpLARDN~u zeq(+bZ-i;y79m`Xj}P%CoydCQXV27U%P()1bGKW=F3MG_?(=`%Da@1-VxpBA`1X-O zc|A?`kY&MhO3n=%u>`|wIEWa?)+S(7Zm<8X$oFw^^-28e>WKevtA9!>?hdAY^=mvY z&*L_KUdwR!_;$;?_js$s7{@o9F)Vp@ZmS3WU_r^ddz>|Ka*;I4fAxd22=|_sQnl?{ zJkIRaSzych1c8I$bRt^W&@nR@o*h#}h%Un37cxNyZ-s@{2h&-W(KLD-3M$0p+QDB- zze-x03zgHxi-xlzDUlR<$jEpdUIwz#n{DXesBa}|Wc7T@pTH_FW_@Y)Jz3m7k>Lft zPKC|LK=|hzouRuYr+Jz2$6{}mm~^YwP!k_m)_(C?9+@dynwcrmmCU;A&3uj(Kj&io z*_V_#hJB5|94$tEw06$*SIgVx4R*vzP|f7k7~AC4*MT{5|2}(Z{50RmCfjQ*gD5~a9;k`HJFt^t8$zp!h7_P z8Hs_`W|CLNNCX!?BS&2Qs&kr0JKC8mva8QxQuFrqMo1!h4NZ{BvPkG2-bf^Fr#gT} ziu+2fR`YTU%qA$F?=Ii#?G!C-R84a*ld?nVi9;T{ubYaASEQM_Q;`jw7So4&>@NjE zxE8xSizS4EEW4M&bqx$di>Q! z(MKj40YY?*RLCdyX$$p<{sk4Y0cANmdxx3IDX)~=pCVYQ#EH^6JLu1KxD z@D%*o-mX^^r>?q?oxETfEc$h@ue!qRS;J$vzC2X1Kqh6Lbm&lwvi)O^fG$OK=)eC+ ze~1R%G)`;#=GdzeH@;k*l8gG?%7i?A3@7_UQrMyvSr>~dkNGicTMBoPfr5>GA0L|Y zwiQD@jwtY4%6WclM@?5I;+~@Yl!a+8Hl?6~(!KOb$GMP)4R>wCu`$K3N%}+SolB+Us z2fe|JHMjmH6)#L+)O5pOXU+$M^3mb2oouih0zKIM2WL?odL0v{wi87VqvInRiFd?Ayv~Z*(0;Qfo zM9#@$*~|8kf9w25vk{X{E#l{E4Ti99jCb~PD|6KHXgKt%({+ugeI6<$@yJ%pmM(11 zcrxiV)?Op^Iq{iY_YnlL%bt);zno(6n`(ttM%4Y)If7``XpI6v9x8l)yXj)=n>R0W z7oR;{b-zphm<)5sjy^5OHbaq6Lq?B|i7!I8YU|zCk6|_f6(1YY1RhwaJjp86jLWSh zf54zh0WjRJwcw?3n=~|LPn3a}&n239mv`X?|LdMye|RKAcL~nP&T1@&5v*eUkbegz zVT69+cr$52fYuZ%V3jzGM)@q@(mlBU{^=8!M3Fr)#L{;bsGY6cB)2(=0S7-4Er-fS^n5GpGPq!nc+?LlyC@IgfblYyMPO4tToBzQ>I@ z1)n#TT2eCA+B!L}e|*dM{om&^@sngQW_q%i7ox5up|+}uSUp2wz7_u3=CAVl=`Ag$ zeuD?4YKpitJOcMMrte4I>jL|0%3b28tjTBa62%1|lpt;d-u~loMuml`-u8_$hcGio zK^5pUOGVk*lx-j*liAUFp|t*L`*wMuT~=(u$r)SIuWuK%HxS`oH zVNhh$L}!Q8?EL#F@Ct`i4_mPkoR`xTrVRo4G5hB089@ z&>t};oY1I;_n6JfmhF3~`S-EsOH5mEF?KyZ+3!RlY;`$5DJz^M4?o;xaam^0owC_$ ziR*}7UJeD3$O&G~@2bEc zdHjJV#S^j{zyEjLq<6Y%zICuMM%C_pf>+q`Tb;)TU;a*$0fj?L=nl>3$&4s|vjIhN zqxUY3LNFck;Juq#_=#f9H`*kAW03N+7G5+NLVd34$Hk@5xA{> zBvV-RMhJiaGCZFC^G;`oaJY*Z0!M&cZbDeZZR@?5=L4&SEVykDac{s!uo}XO)a|}%}`lU9KrjGrZz7qR3AF<;LS;(%Z zl}aik-5+bkzKM^ib~}e$(18idn=Z5z0@sm6f`4o7sTT1dAU&G0;iVvedq}5VZKj$; zp_g26re3pL9plc7fIg1u9x59`VZyFY9s~|lCt^64cJcGaetv$><`^c&wnSXf-E%b+ zb%WAN`}P(Y2g=>`z~Rd$&XIq`1I5-UeNDt;*+pGvmFy$_3av-4T*F?Y{dlY|=r~--E))uAHWh1>N;x&1ZEM$ev3Z|EbCy z**?QtBe~K0xmqkJqdA9od$SBs8awv3#D@CxX3yze>tM@X>kn)!Z_!GiSJ--mZqGa# z=ie`$H!Zd~yxSu#efRLO|GfMx0K)%^tlfka;tjCh9dW9}Km(#iO7|AMvAw zTMy1F{Ere@40{WezfZx1D?uraCTMJ=#E^JKhXtgmbQuWqe1500Oo?08d+4NR5M2E+ z4LqJj4F5M{8!4WA?m?DtxPbj5J!uhh9Y9J{Qf^LsIEX02Os}-l4ealy^B4-%+_4jo zp?$~h5wIh`Cbz#NPb(G{cZV&!RQXq15HZuU?EEc-puw9L9-m#7_lfAgl_peeN~Scf zzj74rO5LfVL7pU+shI6B)_u{={Szz|jI>(J^dwZNm8i3W9IF4E3gyQ)zlSM>RtuYj2jr5nTK-)l z7x=Dw_zWcBkx|1N|Kj2(6OAm(#1G#hE&tY)P%1wF&Z>3+vvquHp=O zB|N(q>&j^P*%u<)S!PaLON@zHin~p|Pwfk#qzgs0sWFG6cmp#*qd++oQ*hS0yR1IR zZ?%m7dayP_I3hmgApMKO5QU$N2zY8E&kBt&r=p!z&QhifI{)pW_e!H@XR?9Hpzcts zph&v_|MXxTrRzwy(C_P&;QyI3b)Ce9&>s<{k%#8qAzJJc)Z-z?UU+e`$X$L~o7Usm zB}MyAcw%(owmM%?3#`l5TO~(XN%jfqY(hbnh&c@!9xrbCFthKI?MBSJxxu3a`^hfe<;fyNZ?Z_(kjabTorP+r`3Qrrh5Fo7YeN|Wn*>w>Q&!^CPkH_>3;*1G zX$;Ym4bEUv5u&;(J1wZZKLBB=m%dVT%jklfjx?bjjN#A{O(-YTZ3Y!6R6fI3)rq7x zQ8eJ4@E|RC)BS_Ego*dx(j~j#2U%cc!RB z_t6OeFTctKgVCG~du9H5HoejAXVuR=PAbxP_L?mgF+^4>Yz3dx*ccdWl8>^(x}uJX z*k>EP(3e?9hbet7Ch6hPEU@%f8C{xM6cn)BVkQ#4Vq)>UY|j1CRs7@v%?u8(AP&#l zo;7X7wFhW)1!uO=1v+PxAFwGiMs5qg00GL0NwrKM==*nlDqW8MLj=Hc0FKE|ehN#y z_d)()!gK#cCR-KJxEaE0J(hW9>%D_OWnoU>U!+&|X8QYXOUB*^r0Pv>czf;Tv9MJ* zoQ>Nxq2-LH_Z`RF<}hl^mq9`Y?gu7=!??h7eY!ByMMXs+zL3B)v)j!0vYqe0(IO)t za-C)MVFhIkCd_(JrVXC&+@OiehLl3&2Hpi$NUj4{9Pu3C_Z)1@(@Gv1txwanXboD* z3}Fpt)5+duuRW%CODS?>RXJ66CPE*dtW8Us%&S*v6&%A(W__Q)P|#!3j7_JM=X~guP#b(XfYSqYidaTnVkjM?BD zt^|bKntw0HZQ7c!BS?D}<(gxnR{vgjhJrp^x5R{9HkhJFxRWDk^0T4XMGt|^`~Kre z?sGbt3>^i>XLAWM~sf)JUm zPf$zkE=e5`4)2PUQ8!$`?Tzmsii68U?C-%OpP2Y%DxvTE&=fk`!0CwOr+4|;y;hAG z&*uUtPOuEi7{O|5xuk+J6YAp}seJ%wH0{1SVOA;)WyLpxoOs3*r8~<#Mo_7^f}jxc7#i5VS!J&4_h6aYE5O*T zM$3KIp8+4;J<@wuXLT^-iQI5KelLukKH?-3|5Al3V;9(oX4ge-U43iX!XC7w$0 zX_WZ)?;oIg+eC@_a<993o2BzXuy0Q}(*wQO< zM;5$_IUF?|s4y2htg6qf*F9j;WkaH~t8`$f11Q>-CKm>F@=ytxMZ#7Jy^1aSZt8{Q zMr9D->1hXcu*(j*B~+#wT^YwKf5hH?e{WgQwlY2;|C&gA4 zo9qu+oZb4xC&k#p&$=O;_M>ewH17TMsNBwD$VSDXig5+_v&z0{_vCV?=3bn5u!2tJtJB>Su|C{Xn z1AI(En+%MDHs`)bgkJ5^PWv@*n@i%pgHzLkH}29z;J<R?rk0_-GkcffBBXX@g47Zv;L*)dFvk(V>*43vp@K==1q+{iD