From f4db121e0eada8e7598db4bbda8f90ce765ea99d Mon Sep 17 00:00:00 2001 From: semen603089 Date: Thu, 8 Dec 2022 06:14:56 +0300 Subject: [PATCH 01/12] add email change route, refactoring, fix #21 --- .github/workflows/tests.yml | 2 +- Dockerfile | 14 +-- auth_backend/auth_plugins/auth_method.py | 8 +- auth_backend/auth_plugins/email.py | 101 +++++++++++++++--- auth_backend/base.py | 6 +- auth_backend/models/db.py | 28 ++++- auth_backend/routes/user_session.py | 16 +-- .../templates/mail_change_confirmation.html | 17 +++ auth_backend/utils/smtp.py | 29 ++++- tests/test_routes/test_change_email.py | 3 + tests/test_routes/test_change_password.py | 8 +- 11 files changed, 186 insertions(+), 46 deletions(-) create mode 100644 auth_backend/templates/mail_change_confirmation.html diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f5f6b87d..6504fcfa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,7 @@ jobs: docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-test postgres:15-alpine - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Install dependencies run: | python -m ensurepip diff --git a/Dockerfile b/Dockerfile index 27737afb..4571e09b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM python:3.10 -WORKDIR /app +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11 +ENV APP_NAME=auth_backend +ENV APP_MODULE=${APP_NAME}.routes.base:app COPY ./requirements.txt /app/ -RUN pip install --no-cache-dir -r /app/requirements.txt +RUN pip install -U -r /app/requirements.txt -ADD gunicorn_conf.py alembic.ini /app/ -ADD migrations /app/migrations -ADD auth_backend /app/auth_backend +COPY ./alembic.ini /alembic.ini +COPY ./migrations /migrations/ -CMD [ "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "/app/gunicorn_conf.py", "auth_backend.routes.base:app" ] +COPY ./${APP_NAME} /app/${APP_NAME} \ No newline at end of file diff --git a/auth_backend/auth_plugins/auth_method.py b/auth_backend/auth_plugins/auth_method.py index 9da4209d..53c71df6 100644 --- a/auth_backend/auth_plugins/auth_method.py +++ b/auth_backend/auth_plugins/auth_method.py @@ -2,14 +2,16 @@ import re from abc import abstractmethod, ABCMeta +from datetime import datetime from fastapi import APIRouter +from pydantic import constr -from datetime import datetime -from auth_backend.base import Base, Token +from auth_backend.base import Base -class Session(Token): +class Session(Base): + token: constr(min_length=1) expires: datetime id: int user_id: int diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index 76b95da5..e2587a7a 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -2,7 +2,7 @@ import random import string -from fastapi import Request +from fastapi import HTTPException, Header from fastapi_sqlalchemy import db from pydantic import validator, constr from sqlalchemy import func @@ -19,24 +19,32 @@ settings = get_settings() +def check_email(v): + restricted: set[str] = {'"', '#', '&', "'", '(', ')', '*', ',', '/', ';', '<', '>', '?', + '[', '\\', ']', '^', '`', '{', '|', '}', '~', '\n', '\r'} + if "@" not in v: + raise ValueError() + if set(v) & restricted: + raise ValueError() + return v + + class EmailLogin(Base): - email: str + email: constr(min_length=1) password: constr(min_length=1) - @validator('email') - def check_email(cls, v): - restricted: set[str] = {'"', '#', '&', "'", '(', ')', '*', ',', '/', ';', '<', '>', '?', - '[', '\\', ']', '^', '`', '{', '|', '}', '~', '\n', '\r'} - if "@" not in v: - raise ValueError() - if set(v) & restricted: - raise ValueError() - return v + email_validator = validator("email", allow_reuse=True)(check_email) class EmailRegister(EmailLogin): user_id: int | None - token: str | None + + +class EmailChange(Base): + token: constr(min_length=1) + email: constr(min_length=1) + + email_validator = validator("email", allow_reuse=True)(check_email) def random_string(length: int = 12) -> str: @@ -49,6 +57,8 @@ class Email(AuthMethodMeta): def __init__(self): super().__init__() self.router.add_api_route("/approve", self.approve_email, methods=["GET"]) + self.router.add_api_route("/reset/email/request", self.request_reset_email, methods=["POST"]) + self.router.add_api_route("/reset/email/{user_id}", self.reset_email, methods=["GET"]) self.router.prefix = self.prefix self.tags = ["Email"] @@ -125,7 +135,7 @@ async def _get_user_by_token_and_id(id: int, token: str) -> User | None: return user @staticmethod - async def register(user_inp: EmailRegister) -> ResponseModel | JSONResponse: + async def register(user_inp: EmailRegister, token: str = Header(default=None)) -> ResponseModel | JSONResponse: confirmation_token: str = random_string() auth_method: AuthMethod = ( db.session.query(AuthMethod) @@ -194,5 +204,66 @@ async def approve_email(token: str) -> object: db.session.flush() return ResponseModel(status="Success", message="Email approved") - async def change_params(self): - raise NotImplementedError() + @staticmethod + async def request_reset_email(scheme: EmailChange): + session: UserSession = db.session.query(UserSession).filter(UserSession.token == scheme.token).one_or_none() + if not session: + raise HTTPException(status_code=404, detail=ResponseModel(status="Error", message="Session not found").dict()) + try: + if session.user.methods.email.confirmed == "false": + raise AuthFailed( + error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" + ) + if session.user.methods.email.email == scheme.email: + raise HTTPException(status_code=401, detail=ResponseModel(status="Error", message="Email incorrect").dict()) + except AttributeError: + raise HTTPException(status_code=401, detail=ResponseModel(status="Error", + message="Auth method restricted for this user")) + if hasattr(session.user.methods.email, "tmp_email_confirmation_token"): + db.session.query(AuthMethod).filter(AuthMethod.user_id == session.user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "tmp_email_confirmation_token").delete() + if hasattr(session.user.methods.email, "tmp_email"): + db.session.query(AuthMethod).filter(AuthMethod.user_id == session.user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "tmp_email").delete() + tmp_email = AuthMethod(user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email", + value=scheme.email) + token = random_string() + tmp_email_confirmation_token = AuthMethod(user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email_confirmation_token", value=token) + db.session.add_all([tmp_email, tmp_email_confirmation_token]) + db.session.flush() + send_confirmation_email( + to_addr=scheme.email, + link=f"{settings.APPLICATION_HOST}/email/reset/email/{session.user_id}?token={token}&email={scheme.email}", + ) + return ResponseModel(status="Success", message="Email confirmation link sent") + + @staticmethod + async def reset_email(user_id: int, token: str, email: str): + user: User = db.session.query(User).get(user_id) + if user.methods.email.confirmed == "false": + raise AuthFailed( + error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" + ) + if email != user.methods.email.tmp_email: + raise HTTPException(status_code=403, + detail=ResponseModel(status="Error", message="Incorrect new email").dict()) + if token != user.methods.email.tmp_email_confirmation_token: + raise HTTPException(status_code=403, detail=ResponseModel(status="Error", message="Incorrect confirmation token").dict()) + db.session.query(AuthMethod).filter(AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "email").update(values={"value": user.methods.email.tmp_email}) + db.session.query(AuthMethod).filter(AuthMethod.user_id == user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "tmp_email_confirmation_token").delete() + db.session.query(AuthMethod).filter(AuthMethod.user_id == user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "tmp_email").delete() + return ResponseModel(status="Success", message="Email successfully changed") + + @staticmethod + async def request_reset_password(): + pass + + @staticmethod + async def reset_password(): + pass diff --git a/auth_backend/base.py b/auth_backend/base.py index a212192c..a0494e0b 100644 --- a/auth_backend/base.py +++ b/auth_backend/base.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, constr +from pydantic import BaseModel class Base(BaseModel): @@ -15,7 +15,3 @@ class Config: class ResponseModel(Base): status: str message: str - - -class Token(Base): - token: constr(min_length=1) diff --git a/auth_backend/models/db.py b/auth_backend/models/db.py index 12876649..c59fb5f5 100644 --- a/auth_backend/models/db.py +++ b/auth_backend/models/db.py @@ -3,14 +3,40 @@ import datetime from typing import Iterator +import sqlalchemy.orm from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property from auth_backend.models.base import Base -import sqlalchemy.orm + + +class AuthMethods: + class Method: + pass + + methods: dict[str, AuthMethods.Method] + + def __init__(self, user: User): + self.methods = {} + for method in user.auth_methods: + if method.auth_method in self.methods.keys(): + setattr(self.methods[method.auth_method], method.param, method.value) + else: + self.methods[method.auth_method] = AuthMethods.Method() + + def __getattribute__(self, item) -> AuthMethods.Method: + if item in self.methods.keys(): + return self.methods[item] + raise AttributeError() class User(Base): + + def __init__(self): + super().__init__() + self.methods = AuthMethods(self) + id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) + methods: AuthMethods auth_methods: list[AuthMethod] = sqlalchemy.orm.relationship("AuthMethod", foreign_keys="AuthMethod.user_id") sessions: list[UserSession] = sqlalchemy.orm.relationship("UserSession", foreign_keys="UserSession.user_id") diff --git a/auth_backend/routes/user_session.py b/auth_backend/routes/user_session.py index dd55ce40..73d22669 100644 --- a/auth_backend/routes/user_session.py +++ b/auth_backend/routes/user_session.py @@ -1,19 +1,19 @@ from datetime import datetime -from fastapi import APIRouter +from fastapi import APIRouter, Header from fastapi_sqlalchemy import db from starlette.responses import JSONResponse -from auth_backend.base import ResponseModel, Token +from auth_backend.base import ResponseModel from auth_backend.exceptions import AuthFailed -from auth_backend.exceptions import SessionExpired, ObjectNotFound +from auth_backend.exceptions import SessionExpired from auth_backend.models.db import UserSession logout_router = APIRouter(prefix="", tags=["Logout"]) @logout_router.post("/logout", response_model=str) -async def logout(token: str) -> JSONResponse: +async def logout(token: str = Header()) -> JSONResponse: session = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: raise AuthFailed(error="Session not found") @@ -24,11 +24,11 @@ async def logout(token: str) -> JSONResponse: return JSONResponse(status_code=200, content=ResponseModel(status="Success", message="Logout successful").dict()) -@logout_router.post("/{user_id}/token", response_model=ResponseModel) -async def check_token(user_id: int, token: Token) -> JSONResponse: - session = db.session.query(UserSession).filter(UserSession.user_id == user_id, UserSession.token == token.token).one_or_none() +@logout_router.post("/me", response_model=ResponseModel) +async def me(token: str = Header()) -> JSONResponse: + session = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: return JSONResponse(status_code=404, content=ResponseModel(status="Error", message="Session not found").json()) if session.expired: - raise SessionExpired(token.token) + raise SessionExpired(token) return JSONResponse(status_code=200, content=ResponseModel(status="Success", message="Session found and exists").json()) diff --git a/auth_backend/templates/mail_change_confirmation.html b/auth_backend/templates/mail_change_confirmation.html new file mode 100644 index 00000000..1ec15d68 --- /dev/null +++ b/auth_backend/templates/mail_change_confirmation.html @@ -0,0 +1,17 @@ + + + + + + + + +Авторизация Твой ФФ! +
+

Вы запросили изменение почты

+

Привет! Это команда программистов ФФ МГУ!

+

Для завершения изменения пройди по ссылке: {{url}}

+
+ + diff --git a/auth_backend/utils/smtp.py b/auth_backend/utils/smtp.py index d0145322..4590bd08 100644 --- a/auth_backend/utils/smtp.py +++ b/auth_backend/utils/smtp.py @@ -15,7 +15,32 @@ def send_confirmation_email(to_addr, link): ( f"From: {from_addr}", f"To: {to_addr}", - f"Subject: Подтверждение регистрации Твой ФФ!", + "Subject: Подтверждение регистрации Твой ФФ!", + "Content-Type: text/html; charset=utf-8;", + "", + tmp, + ) + ) + + smtpObj = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) + smtpObj.starttls() + smtpObj.login(from_addr, settings.EMAIL_PASS) + smtpObj.sendmail(from_addr, [to_addr], BODY.encode('utf-8')) + smtpObj.quit() + + +def send_reset_email(to_addr, link): + from_addr = settings.EMAIL + + with open("auth_backend/templates/main_change_confirmation.html") as f: + tmp = f.read() + tmp = tmp.replace("{{url}}", link) + + BODY = "\r\n".join( + ( + f"From: {from_addr}", + f"To: {to_addr}", + "Subject: Подтверждение смены почты Твой ФФ!", "Content-Type: text/html; charset=utf-8;", "", tmp, @@ -40,7 +65,7 @@ def send_change_password_confirmation(to_addr, link): ( f"From: {from_addr}", f"To: {to_addr}", - f"Subject: Изменение парроля Твой ФФ!", + "Subject: Изменение парроля Твой ФФ!", "Content-Type: text/html; charset=utf-8;", "", tmp, diff --git a/tests/test_routes/test_change_email.py b/tests/test_routes/test_change_email.py index b1275a58..6f91b228 100644 --- a/tests/test_routes/test_change_email.py +++ b/tests/test_routes/test_change_email.py @@ -30,6 +30,9 @@ def test_main_scenario(client: TestClient, dbsession: Session, user): response = client.get(f"{url}{user_id}?token={conf_token_1}&email=changed@mail.com") assert response.status_code == status.HTTP_403_FORBIDDEN + response = client.get(f"{url}{user_id}?token={conf_token_2}&email=wrong@mail.com") + assert response.status_code == status.HTTP_403_FORBIDDEN + response = client.get(f"{url}{user_id}?token={conf_token_2}&email=changed@mail.com") assert response.status_code == status.HTTP_200_OK diff --git a/tests/test_routes/test_change_password.py b/tests/test_routes/test_change_password.py index d1f74457..239aed3e 100644 --- a/tests/test_routes/test_change_password.py +++ b/tests/test_routes/test_change_password.py @@ -66,10 +66,10 @@ def test_no_token(client: TestClient, dbsession: Session, user_id: str): reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", AuthMethod.user_id == user_id).one() assert reset_token - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "new_password": "changedstring"}) + response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring"}) assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "new_password": "changedstring2"}) + response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring2"}) assert response.status_code == status.HTTP_403_FORBIDDEN @@ -87,10 +87,10 @@ def test_with_token(client: TestClient, dbsession: Session, user): AuthMethod.param == "reset_token", AuthMethod.user_id == user_id).one() assert reset_token - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "new_password": "changedstring"}) + response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring"}) assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "new_password": "changedstring2"}) + response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring2"}) assert response.status_code == status.HTTP_403_FORBIDDEN From 8c52005dcf9b773b07fdd1f703a510880532bc49 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Thu, 8 Dec 2022 06:33:03 +0300 Subject: [PATCH 02/12] tests fix --- auth_backend/routes/user_session.py | 12 ++++++++---- tests/test_routes/test_login.py | 16 ++++++++-------- tests/test_routes/test_logout.py | 8 ++++---- tests/test_routes/test_registration.py | 2 +- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/auth_backend/routes/user_session.py b/auth_backend/routes/user_session.py index 73d22669..45b4b272 100644 --- a/auth_backend/routes/user_session.py +++ b/auth_backend/routes/user_session.py @@ -1,6 +1,6 @@ from datetime import datetime -from fastapi import APIRouter, Header +from fastapi import APIRouter, Header, HTTPException from fastapi_sqlalchemy import db from starlette.responses import JSONResponse @@ -13,7 +13,9 @@ @logout_router.post("/logout", response_model=str) -async def logout(token: str = Header()) -> JSONResponse: +async def logout(token: str = Header(default=None)) -> JSONResponse: + if not token: + raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) session = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: raise AuthFailed(error="Session not found") @@ -21,11 +23,13 @@ async def logout(token: str = Header()) -> JSONResponse: raise SessionExpired(session.token) session.expires = datetime.utcnow() db.session.flush() - return JSONResponse(status_code=200, content=ResponseModel(status="Success", message="Logout successful").dict()) + return JSONResponse(status_code=200, content=ResponseModel(status="Success", message="Logout successful").json()) @logout_router.post("/me", response_model=ResponseModel) -async def me(token: str = Header()) -> JSONResponse: +async def me(token: str = Header(default=None)) -> JSONResponse: + if not token: + raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) session = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: return JSONResponse(status_code=404, content=ResponseModel(status="Error", message="Session not found").json()) diff --git a/tests/test_routes/test_login.py b/tests/test_routes/test_login.py index 31cd850b..0492a43a 100644 --- a/tests/test_routes/test_login.py +++ b/tests/test_routes/test_login.py @@ -83,24 +83,24 @@ def test_incorrect_data(client: TestClient, dbsession: Session): def test_check_token(client: TestClient, user, dbsession: Session): user_id, body, login = user["user_id"], user["body"], user["login_json"] - response = client.post(f"/{user_id}/token", json={"token": login["token"] + "2"}) + response = client.post(f"/me", headers={"token": login["token"] + "2"}) assert response.status_code == status.HTTP_404_NOT_FOUND - response = client.post(f"/{user_id}/token", json={"token": login["token"]}) + response = client.post(f"/me", headers={"token": login["token"]}) assert response.status_code == status.HTTP_200_OK - response = client.post(f"/logout?token={login['token']}") + response = client.post(f"/logout", headers={"token": login["token"]}) assert response.status_code == status.HTTP_200_OK - response = client.post(f"/{user_id}/token", json={"token": login["token"]}) + response = client.post(f"/me", headers={"token": login["token"]}) assert response.status_code == status.HTTP_403_FORBIDDEN def test_invalid_check_tokens(client: TestClient, user): user_id, body, login = user["user_id"], user["body"], user["login_json"] - response = client.post(f"/{user_id}/token", json={"token": ""}) - assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + response = client.post(f"/me", headers={"token": ""}) + assert response.status_code == status.HTTP_400_BAD_REQUEST - response = client.post(f"/{user_id}/token") - assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + response = client.post(f"/me") + assert response.status_code == status.HTTP_400_BAD_REQUEST diff --git a/tests/test_routes/test_logout.py b/tests/test_routes/test_logout.py index d8200a0c..93d07239 100644 --- a/tests/test_routes/test_logout.py +++ b/tests/test_routes/test_logout.py @@ -27,11 +27,11 @@ def test_main_scenario(client: TestClient, dbsession: Session): response = client.post("/email/login", json=body) assert response.status_code == status.HTTP_200_OK token = response.json()['token'] - response = client.post(f"{url}?token={token}") + response = client.post(url, headers={"token": token}) assert response.status_code == status.HTTP_200_OK expire_date = dbsession.query(UserSession).filter(UserSession.token == token).one() assert expire_date.expired - response = client.post(f"{url}?token={token}") + response = client.post(url, headers={"token": token}) assert response.status_code == status.HTTP_403_FORBIDDEN for row in dbsession.query(AuthMethod).filter(AuthMethod.user_id == id).all(): dbsession.delete(row) @@ -41,5 +41,5 @@ def test_main_scenario(client: TestClient, dbsession: Session): def test_without_token(client: TestClient, dbsession: Session): - response = client.post(f"{url}?token=") - assert response.status_code == status.HTTP_401_UNAUTHORIZED + response = client.post(url) + assert response.status_code == status.HTTP_400_BAD_REQUEST diff --git a/tests/test_routes/test_registration.py b/tests/test_routes/test_registration.py index 75c24c76..92dc639a 100644 --- a/tests/test_routes/test_registration.py +++ b/tests/test_routes/test_registration.py @@ -68,7 +68,7 @@ def test_main_scenario(client: TestClient, dbsession: Session): token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == db_user.user_id, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == "email").one() - response = client.get(f"/email/approve?token={token.value}") + client.get(f"/email/approve?token={token.value}") response = client.post(url, json=body2) assert response.status_code == status.HTTP_409_CONFLICT for row in dbsession.query(AuthMethod).filter(AuthMethod.user_id == db_user.user_id).all(): From 611307c131b2410f90d00610af1e7c0eca5df0d2 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 9 Dec 2022 02:14:40 +0300 Subject: [PATCH 03/12] email changer fixes, tests email fixes --- auth_backend/auth_plugins/email.py | 15 ++++++---- auth_backend/models/db.py | 24 ++++++---------- tests/test_routes/test_change_email.py | 40 ++++++++++++-------------- 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index e2587a7a..77286bc2 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -41,7 +41,6 @@ class EmailRegister(EmailLogin): class EmailChange(Base): - token: constr(min_length=1) email: constr(min_length=1) email_validator = validator("email", allow_reuse=True)(check_email) @@ -205,20 +204,24 @@ async def approve_email(token: str) -> object: return ResponseModel(status="Success", message="Email approved") @staticmethod - async def request_reset_email(scheme: EmailChange): - session: UserSession = db.session.query(UserSession).filter(UserSession.token == scheme.token).one_or_none() + async def request_reset_email(scheme: EmailChange, token: str = Header(default=None)): + if not token: + raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) + session: UserSession = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: - raise HTTPException(status_code=404, detail=ResponseModel(status="Error", message="Session not found").dict()) + raise HTTPException(status_code=404, detail=ResponseModel(status="Error", message="Session not found").json()) + if session.expired: + raise SessionExpired(token) try: if session.user.methods.email.confirmed == "false": raise AuthFailed( error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) if session.user.methods.email.email == scheme.email: - raise HTTPException(status_code=401, detail=ResponseModel(status="Error", message="Email incorrect").dict()) + raise HTTPException(status_code=401, detail=ResponseModel(status="Error", message="Email incorrect").json()) except AttributeError: raise HTTPException(status_code=401, detail=ResponseModel(status="Error", - message="Auth method restricted for this user")) + message="Auth method restricted for this user").json()) if hasattr(session.user.methods.email, "tmp_email_confirmation_token"): db.session.query(AuthMethod).filter(AuthMethod.user_id == session.user_id, AuthMethod.auth_method == Email.get_name(), diff --git a/auth_backend/models/db.py b/auth_backend/models/db.py index c59fb5f5..888787fc 100644 --- a/auth_backend/models/db.py +++ b/auth_backend/models/db.py @@ -13,30 +13,18 @@ class AuthMethods: class Method: pass - methods: dict[str, AuthMethods.Method] - def __init__(self, user: User): - self.methods = {} for method in user.auth_methods: - if method.auth_method in self.methods.keys(): - setattr(self.methods[method.auth_method], method.param, method.value) + if hasattr(self, method.auth_method): + setattr(getattr(self, method.auth_method), method.param, method.value) else: - self.methods[method.auth_method] = AuthMethods.Method() - - def __getattribute__(self, item) -> AuthMethods.Method: - if item in self.methods.keys(): - return self.methods[item] - raise AttributeError() + setattr(self, method.auth_method, AuthMethods.Method()) + setattr(getattr(self, method.auth_method), method.param, method.value) class User(Base): - def __init__(self): - super().__init__() - self.methods = AuthMethods(self) - id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) - methods: AuthMethods auth_methods: list[AuthMethod] = sqlalchemy.orm.relationship("AuthMethod", foreign_keys="AuthMethod.user_id") sessions: list[UserSession] = sqlalchemy.orm.relationship("UserSession", foreign_keys="UserSession.user_id") @@ -47,6 +35,10 @@ def get_method_secrets(self, method_name: str) -> Iterator[AuthMethod]: if row.auth_method == method_name: yield row + @hybrid_property + def methods(self) -> AuthMethods: + return AuthMethods(self) + class AuthMethod(Base): id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) diff --git a/tests/test_routes/test_change_email.py b/tests/test_routes/test_change_email.py index 6f91b228..f67fca17 100644 --- a/tests/test_routes/test_change_email.py +++ b/tests/test_routes/test_change_email.py @@ -1,23 +1,25 @@ -import pytest -from starlette import status from fastapi.testclient import TestClient from sqlalchemy.orm import Session -from auth_backend.models.db import AuthMethod, UserSession +from starlette import status +from auth_backend.models.db import AuthMethod, UserSession url = "/email/reset/email/" -@pytest.mark.skip() def test_main_scenario(client: TestClient, dbsession: Session, user): user_id, body, login = user["user_id"], user["body"], user["login_json"] conf_token_1 = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token").one().value - response = client.post(f"{url}{user_id}/request", json={"token": login["token"], "email": "changed@mail.com"}) + response = client.post(f"{url}request", json={"email": "changed@mail.com"}, headers={"token": login["token"]}) assert response.status_code == status.HTTP_200_OK - conf_token_2 = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token").one().value - assert conf_token_2 != conf_token_1 + conf_token_2 = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, + AuthMethod.param == "confirmation_token").one().value + assert conf_token_2 == conf_token_1 + + tmp_token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, + AuthMethod.param == "tmp_email_confirmation_token").one().value assert not dbsession.query(UserSession).filter(UserSession.token == login["token"]).one().expired @@ -30,10 +32,10 @@ def test_main_scenario(client: TestClient, dbsession: Session, user): response = client.get(f"{url}{user_id}?token={conf_token_1}&email=changed@mail.com") assert response.status_code == status.HTTP_403_FORBIDDEN - response = client.get(f"{url}{user_id}?token={conf_token_2}&email=wrong@mail.com") + response = client.get(f"{url}{user_id}?token={tmp_token}&email=wrong@mail.com") assert response.status_code == status.HTTP_403_FORBIDDEN - response = client.get(f"{url}{user_id}?token={conf_token_2}&email=changed@mail.com") + response = client.get(f"{url}{user_id}?token={tmp_token}&email=changed@mail.com") assert response.status_code == status.HTTP_200_OK response = client.post(f"/email/login", json=body) @@ -43,29 +45,23 @@ def test_main_scenario(client: TestClient, dbsession: Session, user): assert response.status_code == status.HTTP_200_OK -@pytest.mark.skip() def test_invalid_jsons(client: TestClient, dbsession: Session, user): user_id, body, login = user["user_id"], user["body"], user["login_json"] - response = client.post(f"{url}{user_id}/request", json={"token": "", "email": "changed@mail.com"}) - assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + response = client.post(f"{url}request", json={"email": "changed@mail.com"}, headers={"token": ""}) + assert response.status_code == status.HTTP_400_BAD_REQUEST - response = client.post(f"{url}{user_id}/request", json={"token": login["token"], "email": ""}) + response = client.post(f"{url}request", json={"email": ""}, headers={"token": login["token"]}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", json={"token": "", "email": ""}) + response = client.post(f"{url}request", json={"email": ""}, headers={"token": ""}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY -@pytest.mark.skip() def test_expired_token(client: TestClient, dbsession: Session, user): user_id, body, login = user["user_id"], user["body"], user["login_json"] - response = client.post("/logout", json={"token": login["token"]}) + response = client.post("/logout", headers={"token": login["token"]}) assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}/request", json={"token": login["token"], "email": "changed@mail.com"}) - assert response.status_code == status.HTTP_401_UNAUTHORIZED - - - - + response = client.post(f"{url}request", json={"email": "changed@mail.com"}, headers={"token": login["token"]}) + assert response.status_code == status.HTTP_403_FORBIDDEN From c73cd0f0aa311829581d389284731980a1063cf2 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 9 Dec 2022 03:23:20 +0300 Subject: [PATCH 04/12] password changer init --- auth_backend/auth_plugins/email.py | 11 +++- tests/test_routes/test_change_password.py | 65 ++++++++++++++--------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index 77286bc2..e5b989b2 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -46,6 +46,11 @@ class EmailChange(Base): email_validator = validator("email", allow_reuse=True)(check_email) +class ResetPassword(Base): + password: constr(min_length=1) | None + new_password: constr(min_length=1) + + def random_string(length: int = 12) -> str: return "".join([random.choice(string.ascii_letters) for _ in range(length)]) @@ -58,6 +63,8 @@ def __init__(self): self.router.add_api_route("/approve", self.approve_email, methods=["GET"]) self.router.add_api_route("/reset/email/request", self.request_reset_email, methods=["POST"]) self.router.add_api_route("/reset/email/{user_id}", self.reset_email, methods=["GET"]) + self.router.add_api_route("/reset/password/{user_id}/request", self.request_reset_password, methods=["POST"]) + self.router.add_api_route("/reset/password", self.reset_password, methods=["GET"]) self.router.prefix = self.prefix self.tags = ["Email"] @@ -264,9 +271,9 @@ async def reset_email(user_id: int, token: str, email: str): return ResponseModel(status="Success", message="Email successfully changed") @staticmethod - async def request_reset_password(): + async def request_reset_password(user_id: int, schema: ResetPassword, token: str = Header(default=None)): pass @staticmethod - async def reset_password(): + async def reset_password(token: constr(min_length=1), password: constr(min_length=1)): pass diff --git a/tests/test_routes/test_change_password.py b/tests/test_routes/test_change_password.py index 239aed3e..a64c63f5 100644 --- a/tests/test_routes/test_change_password.py +++ b/tests/test_routes/test_change_password.py @@ -4,7 +4,6 @@ from sqlalchemy.orm import Session from auth_backend.models.db import AuthMethod - url = "/email/reset/password/" @@ -16,10 +15,11 @@ def test_unprocessable_jsons_no_token(client: TestClient, dbsession: Session, us response = client.get(f"/email/approve?token={token.value}") assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}/request") + response = client.post(f"{url}{user_id}/request", json={"new_password": "changed"}) assert response.status_code == status.HTTP_200_OK reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", - AuthMethod.param == "reset_token", AuthMethod.user_id == user_id).one() + AuthMethod.param == "reset_token", + AuthMethod.user_id == user_id).one() assert reset_token response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "new_password": ""}) @@ -28,7 +28,7 @@ def test_unprocessable_jsons_no_token(client: TestClient, dbsession: Session, us response = client.post(f"{url}{user_id}", json={"reset_token": "", "new_password": ""}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}", json={"reset_token": "", "new_password": "changedstring3"}) + response = client.post(f"{url}{user_id}", json={"reset_token": "", "new_password": "changed"}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY @@ -37,16 +37,24 @@ def test_unprocessable_jsons_with_token(client: TestClient, dbsession: Session, user_id, body, response = user["user_id"], user["body"], user["login_json"] auth_token = response["token"] - response = client.post(f"{url}{user_id}/request", json={"token": auth_token, "password": ""}) + response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, + json={"password": "", "new_password": "changed"}) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + + response = client.post(f"{url}{user_id}/request", headers={"token": ""}, + json={"password": "", "new_password": "changed"}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", json={"token": "", "password": ""}) + response = client.post(f"{url}{user_id}/request", headers={"token": ""}, + json={"password": body["password"], "new_password": "changed"}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", json={"token": "", "password": "string"}) + response = client.post(f"{url}{user_id}/request", headers={"token": ""}, + json={"password": body["password"], "new_password": ""}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", json={"token": auth_token, "password": "string"}) + response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, + json={"password": body["password"], "new_password": "changed"}) assert response.status_code == status.HTTP_200_OK @@ -55,21 +63,26 @@ def test_no_token(client: TestClient, dbsession: Session, user_id: str): token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == "email").one() - response = client.post(f"{url}{user_id}/request") + response = client.post(f"{url}{user_id}/request", json={"new_password": "changed"}) assert response.status_code == status.HTTP_403_FORBIDDEN response = client.get(f"/email/approve?token={token.value}") assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}/request") + response = client.post(f"{url}{user_id}/request", json={"new_password": "changedstring"}) assert response.status_code == status.HTTP_200_OK - reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", AuthMethod.user_id == user_id).one() + reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", + AuthMethod.param == "reset_token", + AuthMethod.user_id == user_id).one() assert reset_token - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring"}) + response = client.get(f"{url}{user_id}?token={reset_token}&password=changedstring2") + assert response.status_code == status.HTTP_403_FORBIDDEN + + response = client.get(f"{url}{user_id}?token={reset_token}&password=changedstring") assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring2"}) + response = client.get(f"{url}{user_id}?token={reset_token}&password=changedstring2") assert response.status_code == status.HTTP_403_FORBIDDEN @@ -78,23 +91,27 @@ def test_with_token(client: TestClient, dbsession: Session, user): user_id, body, response = user["user_id"], user["body"], user["login_json"] auth_token = response["token"] - response = client.post(f"{url}{user_id}/request", json={"token": auth_token, "password": "wrong"}) + response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, + json={"password": "wrong", "new_password": "changed"}) + assert response.status_code == status.HTTP_403_FORBIDDEN + + response = client.post(f"{url}{user_id}/request", headers={"token": auth_token + "wrong"}, + json={"password": body["password"], "new_password": "changed"}) assert response.status_code == status.HTTP_403_FORBIDDEN - response = client.post(f"{url}{user_id}/request", json={"token": auth_token, "password": "string"}) + response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, + json={"password": body["password"], "new_password": "changed"}) assert response.status_code == status.HTTP_200_OK reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", - AuthMethod.param == "reset_token", AuthMethod.user_id == user_id).one() + AuthMethod.param == "reset_token", + AuthMethod.user_id == user_id).one() assert reset_token - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring"}) - assert response.status_code == status.HTTP_200_OK - - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "password": "changedstring2"}) + response = client.get(f"{url}{user_id}?token={reset_token}&password=wrong") assert response.status_code == status.HTTP_403_FORBIDDEN + response = client.get(f"{url}{user_id}?token={reset_token}&password=changed") + assert response.status_code == status.HTTP_200_OK - - - - + response = client.get(f"{url}{user_id}?token={reset_token}&password=changed") + assert response.status_code == status.HTTP_403_FORBIDDEN From a71b0f3593893eaf200979fef486eb429a96931e Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 9 Dec 2022 22:48:08 +0300 Subject: [PATCH 05/12] fix tests, relations add --- auth_backend/auth_plugins/auth_method.py | 28 +++ auth_backend/auth_plugins/email.py | 193 +++++++++++++----- auth_backend/models/db.py | 20 +- auth_backend/routes/base.py | 5 +- auth_backend/routes/user_session.py | 4 +- ...html => password_change_confirmation.html} | 0 .../password_change_notification.html | 18 ++ auth_backend/utils/smtp.py | 30 ++- 8 files changed, 225 insertions(+), 73 deletions(-) rename auth_backend/templates/{password_reset.html => password_change_confirmation.html} (100%) create mode 100644 auth_backend/templates/password_change_notification.html diff --git a/auth_backend/auth_plugins/auth_method.py b/auth_backend/auth_plugins/auth_method.py index 53c71df6..cbca2300 100644 --- a/auth_backend/auth_plugins/auth_method.py +++ b/auth_backend/auth_plugins/auth_method.py @@ -6,8 +6,11 @@ from fastapi import APIRouter from pydantic import constr +from sqlalchemy.orm import relationship +from sqlalchemy.orm.collections import attribute_mapped_collection from auth_backend.base import Base +from auth_backend.models.db import User class Session(Base): @@ -24,6 +27,7 @@ class AuthMethodMeta(metaclass=ABCMeta): router: APIRouter prefix: str tags: list[str] = [] + fields: list[str] = [] @classmethod def get_name(cls) -> str: @@ -36,6 +40,30 @@ def __init__(self): def __init_subclass__(cls, **kwargs): AUTH_METHODS[cls.__name__] = cls + setattr( + User, + f"{cls.__name__}__{cls.get_name()}", + relationship( + "AuthMethod", + foreign_keys="AuthMethod.user_id", + back_populates="user", + primaryjoin=f"and_(User.id==AuthMethod.user_id, AuthMethod.auth_method=='{cls.get_name()}')", + ), + ) + for row in cls.fields: + setattr( + User, + row, + relationship( + "AuthMethod", + foreign_keys="AuthMethod.user_id", + back_populates="user", + uselist=False, + # collection_class=attribute_mapped_collection("keyword"), + primaryjoin=f"and_(User.id==AuthMethod.user_id," + f" AuthMethod.auth_method=='{cls.get_name()}',AuthMethod.param=='{row}')", + ), + ) @staticmethod @abstractmethod diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index e5b989b2..6c611521 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -13,15 +13,43 @@ from auth_backend.models.db import AuthMethod from auth_backend.models.db import UserSession, User from auth_backend.settings import get_settings -from auth_backend.utils.smtp import send_confirmation_email +from auth_backend.utils.smtp import ( + send_confirmation_email, + send_change_password_confirmation, + send_changes_password_notification, +) from .auth_method import AuthMethodMeta, Session settings = get_settings() def check_email(v): - restricted: set[str] = {'"', '#', '&', "'", '(', ')', '*', ',', '/', ';', '<', '>', '?', - '[', '\\', ']', '^', '`', '{', '|', '}', '~', '\n', '\r'} + restricted: set[str] = { + '"', + '#', + '&', + "'", + '(', + ')', + '*', + ',', + '/', + ';', + '<', + '>', + '?', + '[', + '\\', + ']', + '^', + '`', + '{', + '|', + '}', + '~', + '\n', + '\r', + } if "@" not in v: raise ValueError() if set(v) & restricted: @@ -57,6 +85,8 @@ def random_string(length: int = 12) -> str: class Email(AuthMethodMeta): prefix = "/email" + fields = ["email", "hashed_password", "salt", "confirmed", "confirmation_token", + "tmp_email_confirmation_token","tmp_email", "reset_token"] def __init__(self): super().__init__() @@ -72,12 +102,12 @@ def __init__(self): async def login(user_inp: EmailLogin) -> Session: query = ( db.session.query(AuthMethod) - .filter( + .filter( func.lower(AuthMethod.value) == user_inp.email.lower(), AuthMethod.param == "email", AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if not query: raise AuthFailed(error="Incorrect login or password") @@ -87,7 +117,7 @@ async def login(user_inp: EmailLogin) -> Session: error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) if secrets.get("email").lower() != user_inp.email.lower() or not Email.validate_password( - user_inp.password, secrets.get("hashed_password"), secrets.get("salt") + user_inp.password, secrets.get("hashed_password"), secrets.get("salt") ): raise AuthFailed(error="Incorrect login or password") db.session.add(user_session := UserSession(user_id=query.user.id, token=random_string())) @@ -145,12 +175,12 @@ async def register(user_inp: EmailRegister, token: str = Header(default=None)) - confirmation_token: str = random_string() auth_method: AuthMethod = ( db.session.query(AuthMethod) - .filter( + .filter( AuthMethod.param == "email", func.lower(AuthMethod.value) == user_inp.email.lower(), AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if auth_method: await Email._change_confirmation_link(auth_method.user, confirmation_token) @@ -188,23 +218,23 @@ def validate_password(password: str, hashed_password: str, salt: str): async def approve_email(token: str) -> object: auth_method = ( db.session.query(AuthMethod) - .filter( + .filter( AuthMethod.value == token, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if not auth_method: return JSONResponse(status_code=403, content=ResponseModel(status="Error", message="Incorrect link").json()) confirmed = ( db.session.query(AuthMethod) - .filter( + .filter( AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "confirmed", AuthMethod.user_id == auth_method.user_id, ) - .one() + .one() ) confirmed.value = True db.session.flush() @@ -216,31 +246,41 @@ async def request_reset_email(scheme: EmailChange, token: str = Header(default=N raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) session: UserSession = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: - raise HTTPException(status_code=404, detail=ResponseModel(status="Error", message="Session not found").json()) + raise HTTPException( + status_code=404, detail=ResponseModel(status="Error", message="Session not found").json() + ) if session.expired: raise SessionExpired(token) - try: - if session.user.methods.email.confirmed == "false": - raise AuthFailed( - error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" - ) - if session.user.methods.email.email == scheme.email: - raise HTTPException(status_code=401, detail=ResponseModel(status="Error", message="Email incorrect").json()) - except AttributeError: - raise HTTPException(status_code=401, detail=ResponseModel(status="Error", - message="Auth method restricted for this user").json()) - if hasattr(session.user.methods.email, "tmp_email_confirmation_token"): - db.session.query(AuthMethod).filter(AuthMethod.user_id == session.user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "tmp_email_confirmation_token").delete() - if hasattr(session.user.methods.email, "tmp_email"): - db.session.query(AuthMethod).filter(AuthMethod.user_id == session.user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "tmp_email").delete() - tmp_email = AuthMethod(user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email", - value=scheme.email) + if not hasattr(session.user, "email"): + raise HTTPException( + status_code=401, + detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), + ) + if session.user.confirmed.value == "false": + raise AuthFailed( + error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" + ) + if session.user.email.value == scheme.email: + raise HTTPException(status_code=401, detail=ResponseModel(status="Error", message="Email incorrect").json()) + if hasattr(session.user, "tmp_email_confirmation_token"): + db.session.query(AuthMethod).filter( + AuthMethod.user_id == session.user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "tmp_email_confirmation_token", + ).delete() + if hasattr(session.user, "tmp_email"): + db.session.query(AuthMethod).filter( + AuthMethod.user_id == session.user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "tmp_email", + ).delete() + tmp_email = AuthMethod( + user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email", value=scheme.email + ) token = random_string() - tmp_email_confirmation_token = AuthMethod(user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email_confirmation_token", value=token) + tmp_email_confirmation_token = AuthMethod( + user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email_confirmation_token", value=token + ) db.session.add_all([tmp_email, tmp_email_confirmation_token]) db.session.flush() send_confirmation_email( @@ -252,27 +292,84 @@ async def request_reset_email(scheme: EmailChange, token: str = Header(default=N @staticmethod async def reset_email(user_id: int, token: str, email: str): user: User = db.session.query(User).get(user_id) - if user.methods.email.confirmed == "false": + if user.confirmed.value == "false": raise AuthFailed( error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) - if email != user.methods.email.tmp_email: - raise HTTPException(status_code=403, - detail=ResponseModel(status="Error", message="Incorrect new email").dict()) - if token != user.methods.email.tmp_email_confirmation_token: - raise HTTPException(status_code=403, detail=ResponseModel(status="Error", message="Incorrect confirmation token").dict()) - db.session.query(AuthMethod).filter(AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "email").update(values={"value": user.methods.email.tmp_email}) - db.session.query(AuthMethod).filter(AuthMethod.user_id == user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "tmp_email_confirmation_token").delete() - db.session.query(AuthMethod).filter(AuthMethod.user_id == user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "tmp_email").delete() + if email != user.tmp_email.value: + raise HTTPException( + status_code=403, detail=ResponseModel(status="Error", message="Incorrect new email").dict() + ) + if token != user.tmp_email_confirmation_token.value: + raise HTTPException( + status_code=403, detail=ResponseModel(status="Error", message="Incorrect confirmation token").dict() + ) + db.session.query(AuthMethod).filter( + AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "email" + ).update(values={"value": user.tmp_email.value}) + db.session.query(AuthMethod).filter( + AuthMethod.user_id == user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "tmp_email_confirmation_token", + ).delete() + db.session.query(AuthMethod).filter( + AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "tmp_email" + ).delete() return ResponseModel(status="Success", message="Email successfully changed") @staticmethod async def request_reset_password(user_id: int, schema: ResetPassword, token: str = Header(default=None)): - pass + salt = random_string() + if token: + session: UserSession = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() + if not session: + raise HTTPException( + status_code=404, detail=ResponseModel(status="Error", message="Session not found").json() + ) + if session.expired: + raise SessionExpired(token) + if not hasattr(session.user.methods, "email"): + raise HTTPException( + status_code=401, + detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), + ) + if not Email.validate_password( + schema.password, session.user.methods.email.hashed_password, session.user.methods.email.salt + ): + raise AuthFailed(error="Incorrect password") + if user_id != session.user_id: + raise HTTPException( + status_code=403, detail=ResponseModel(status="Error", message="Incorrect pair user_id+token").json() + ) + db.session.query(AuthMethod).filter( + AuthMethod.user_id == user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "hashed_password", + ).update(values={"value": Email.hash_password(schema.new_password, salt)}) + db.session.query(AuthMethod).filter( + AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "salt" + ).update(values={"value": salt}) + send_changes_password_notification(session.user.methods.email.email) + return ResponseModel(status="Success", message="Password has been successfully changed") + else: + user: User = db.session.query(User).get(user_id) + if not user: + raise ObjectNotFound(User, user_id) + if not hasattr(user.methods, "email"): + raise HTTPException( + status_code=401, + detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), + ) + reset_token = random_string() + db.session.query(AuthMethod).filter( + AuthMethod.user_id == user_id, + AuthMethod.auth_method == Email.get_name(), + AuthMethod.param == "reset_token", + ).update(values={"value": reset_token}) + send_change_password_confirmation( + user.methods.email.email, f"{settings.FRONTEND_HOST}/email/reset/password?token={reset_token}" + ) + return ResponseModel(status="Success", message="Reset link has been successfully mailed") @staticmethod async def reset_password(token: constr(min_length=1), password: constr(min_length=1)): diff --git a/auth_backend/models/db.py b/auth_backend/models/db.py index 888787fc..1bc354e0 100644 --- a/auth_backend/models/db.py +++ b/auth_backend/models/db.py @@ -9,19 +9,6 @@ from auth_backend.models.base import Base -class AuthMethods: - class Method: - pass - - def __init__(self, user: User): - for method in user.auth_methods: - if hasattr(self, method.auth_method): - setattr(getattr(self, method.auth_method), method.param, method.value) - else: - setattr(self, method.auth_method, AuthMethods.Method()) - setattr(getattr(self, method.auth_method), method.param, method.value) - - class User(Base): id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) @@ -29,16 +16,15 @@ class User(Base): auth_methods: list[AuthMethod] = sqlalchemy.orm.relationship("AuthMethod", foreign_keys="AuthMethod.user_id") sessions: list[UserSession] = sqlalchemy.orm.relationship("UserSession", foreign_keys="UserSession.user_id") + def __init__(self): + super(User, self).__init__() + @hybrid_method def get_method_secrets(self, method_name: str) -> Iterator[AuthMethod]: for row in self.auth_methods: if row.auth_method == method_name: yield row - @hybrid_property - def methods(self) -> AuthMethods: - return AuthMethods(self) - class AuthMethod(Base): id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) diff --git a/auth_backend/routes/base.py b/auth_backend/routes/base.py index 6174bc39..b05f8b5e 100644 --- a/auth_backend/routes/base.py +++ b/auth_backend/routes/base.py @@ -12,10 +12,7 @@ app.add_middleware( - DBSessionMiddleware, - db_url=settings.DB_DSN, - session_args={"autocommit": True}, - engine_args={"pool_pre_ping": True} + DBSessionMiddleware, db_url=settings.DB_DSN, session_args={"autocommit": True}, engine_args={"pool_pre_ping": True} ) app.add_middleware( diff --git a/auth_backend/routes/user_session.py b/auth_backend/routes/user_session.py index 45b4b272..43d67164 100644 --- a/auth_backend/routes/user_session.py +++ b/auth_backend/routes/user_session.py @@ -35,4 +35,6 @@ async def me(token: str = Header(default=None)) -> JSONResponse: return JSONResponse(status_code=404, content=ResponseModel(status="Error", message="Session not found").json()) if session.expired: raise SessionExpired(token) - return JSONResponse(status_code=200, content=ResponseModel(status="Success", message="Session found and exists").json()) + return JSONResponse( + status_code=200, content=ResponseModel(status="Success", message="Session found and exists").json() + ) diff --git a/auth_backend/templates/password_reset.html b/auth_backend/templates/password_change_confirmation.html similarity index 100% rename from auth_backend/templates/password_reset.html rename to auth_backend/templates/password_change_confirmation.html diff --git a/auth_backend/templates/password_change_notification.html b/auth_backend/templates/password_change_notification.html new file mode 100644 index 00000000..eafec0ee --- /dev/null +++ b/auth_backend/templates/password_change_notification.html @@ -0,0 +1,18 @@ + + + + + + + + +Авторизация Твой ФФ! +
+

Вы изменили пароль

+

Привет! Это команда программистов ФФ МГУ!

+

Сообщаем вам о том, что вы изменили пароль в приложении Твой ФФ!. + Если это были не вы, воостановите доступ как можно скорее

+
+ + diff --git a/auth_backend/utils/smtp.py b/auth_backend/utils/smtp.py index 4590bd08..069927ce 100644 --- a/auth_backend/utils/smtp.py +++ b/auth_backend/utils/smtp.py @@ -32,7 +32,7 @@ def send_confirmation_email(to_addr, link): def send_reset_email(to_addr, link): from_addr = settings.EMAIL - with open("auth_backend/templates/main_change_confirmation.html") as f: + with open("auth_backend/templates/mail_change_confirmation.html") as f: tmp = f.read() tmp = tmp.replace("{{url}}", link) @@ -57,7 +57,7 @@ def send_reset_email(to_addr, link): def send_change_password_confirmation(to_addr, link): from_addr = settings.EMAIL - with open("templates/reset_password.html") as f: + with open("auth_backend/templates/password_change_confirmation.html") as f: tmp = f.read() tmp = tmp.replace("{{url}}", link) @@ -65,7 +65,31 @@ def send_change_password_confirmation(to_addr, link): ( f"From: {from_addr}", f"To: {to_addr}", - "Subject: Изменение парроля Твой ФФ!", + "Subject: Изменение пароля Твой ФФ!", + "Content-Type: text/html; charset=utf-8;", + "", + tmp, + ) + ) + + smtpObj = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) + smtpObj.starttls() + smtpObj.login(from_addr, settings.EMAIL_PASS) + smtpObj.sendmail(from_addr, [to_addr], BODY.encode('utf-8')) + smtpObj.quit() + + +def send_changes_password_notification(to_addr): + from_addr = settings.EMAIL + + with open("auth_backend/templates/password_change_notification.html") as f: + tmp = f.read() + + BODY = "\r\n".join( + ( + f"From: {from_addr}", + f"To: {to_addr}", + "Subject: Изменение пароля Твой ФФ!", "Content-Type: text/html; charset=utf-8;", "", tmp, From de23ddd75fb38a01eef1fa93575dea36d6cc58db Mon Sep 17 00:00:00 2001 From: semen603089 Date: Fri, 9 Dec 2022 23:59:00 +0300 Subject: [PATCH 06/12] refactoring --- auth_backend/auth_plugins/auth_method.py | 1 - auth_backend/auth_plugins/email.py | 76 ++++++++---------------- auth_backend/models/db.py | 11 ++++ auth_backend/settings.py | 3 +- 4 files changed, 37 insertions(+), 54 deletions(-) diff --git a/auth_backend/auth_plugins/auth_method.py b/auth_backend/auth_plugins/auth_method.py index cbca2300..5694490a 100644 --- a/auth_backend/auth_plugins/auth_method.py +++ b/auth_backend/auth_plugins/auth_method.py @@ -59,7 +59,6 @@ def __init_subclass__(cls, **kwargs): foreign_keys="AuthMethod.user_id", back_populates="user", uselist=False, - # collection_class=attribute_mapped_collection("keyword"), primaryjoin=f"and_(User.id==AuthMethod.user_id," f" AuthMethod.auth_method=='{cls.get_name()}',AuthMethod.param=='{row}')", ), diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index 6c611521..3d7d6fa3 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -86,7 +86,7 @@ def random_string(length: int = 12) -> str: class Email(AuthMethodMeta): prefix = "/email" fields = ["email", "hashed_password", "salt", "confirmed", "confirmation_token", - "tmp_email_confirmation_token","tmp_email", "reset_token"] + "tmp_email_confirmation_token", "tmp_email", "reset_token"] def __init__(self): super().__init__() @@ -102,22 +102,21 @@ def __init__(self): async def login(user_inp: EmailLogin) -> Session: query = ( db.session.query(AuthMethod) - .filter( + .filter( func.lower(AuthMethod.value) == user_inp.email.lower(), AuthMethod.param == "email", AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if not query: raise AuthFailed(error="Incorrect login or password") - secrets = {row.param: row.value for row in query.user.get_method_secrets(Email.get_name())} - if secrets.get("confirmed").lower() == "false": + if query.user.confirmed.value.lower() == "false": raise AuthFailed( error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) - if secrets.get("email").lower() != user_inp.email.lower() or not Email.validate_password( - user_inp.password, secrets.get("hashed_password"), secrets.get("salt") + if query.user.email.value.lower() != user_inp.email.lower() or not Email.validate_password( + user_inp.password, query.user.hashed_password.value, query.user.salt.value ): raise AuthFailed(error="Incorrect login or password") db.session.add(user_session := UserSession(user_id=query.user.id, token=random_string())) @@ -147,8 +146,7 @@ async def _add_to_db(user_inp: EmailRegister, confirmation_token: str, user: Use @staticmethod async def _change_confirmation_link(user: User, confirmation_token: str): - secrets = {row.param: row.value for row in user.get_method_secrets(Email.get_name())} - if secrets.get("confirmed") == "true": + if user.confirmed.value == "true": raise AlreadyExists(User, user.id) else: db.session.query(AuthMethod).filter( @@ -175,12 +173,12 @@ async def register(user_inp: EmailRegister, token: str = Header(default=None)) - confirmation_token: str = random_string() auth_method: AuthMethod = ( db.session.query(AuthMethod) - .filter( + .filter( AuthMethod.param == "email", func.lower(AuthMethod.value) == user_inp.email.lower(), AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if auth_method: await Email._change_confirmation_link(auth_method.user, confirmation_token) @@ -218,25 +216,16 @@ def validate_password(password: str, hashed_password: str, salt: str): async def approve_email(token: str) -> object: auth_method = ( db.session.query(AuthMethod) - .filter( + .filter( AuthMethod.value == token, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if not auth_method: return JSONResponse(status_code=403, content=ResponseModel(status="Error", message="Incorrect link").json()) - confirmed = ( - db.session.query(AuthMethod) - .filter( - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "confirmed", - AuthMethod.user_id == auth_method.user_id, - ) - .one() - ) - confirmed.value = True + auth_method.user.confirmed.value = True db.session.flush() return ResponseModel(status="Success", message="Email approved") @@ -304,17 +293,10 @@ async def reset_email(user_id: int, token: str, email: str): raise HTTPException( status_code=403, detail=ResponseModel(status="Error", message="Incorrect confirmation token").dict() ) - db.session.query(AuthMethod).filter( - AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "email" - ).update(values={"value": user.tmp_email.value}) - db.session.query(AuthMethod).filter( - AuthMethod.user_id == user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "tmp_email_confirmation_token", - ).delete() - db.session.query(AuthMethod).filter( - AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "tmp_email" - ).delete() + user.email.value = user.tmp_email.value + db.session.delete(user.tmp_email_confirmation_token) + db.session.delete(user.tmp_email) + db.session.flush() return ResponseModel(status="Success", message="Email successfully changed") @staticmethod @@ -334,21 +316,16 @@ async def request_reset_password(user_id: int, schema: ResetPassword, token: str detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), ) if not Email.validate_password( - schema.password, session.user.methods.email.hashed_password, session.user.methods.email.salt + schema.password, session.user.methods.email.hashed_password, session.user.methods.email.salt ): raise AuthFailed(error="Incorrect password") if user_id != session.user_id: raise HTTPException( status_code=403, detail=ResponseModel(status="Error", message="Incorrect pair user_id+token").json() ) - db.session.query(AuthMethod).filter( - AuthMethod.user_id == user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "hashed_password", - ).update(values={"value": Email.hash_password(schema.new_password, salt)}) - db.session.query(AuthMethod).filter( - AuthMethod.user_id == user_id, AuthMethod.auth_method == Email.get_name(), AuthMethod.param == "salt" - ).update(values={"value": salt}) + session.user.hashed_password.value = Email.hash_password(schema.new_password, salt) + session.user.salt.value = salt + db.session.flush() send_changes_password_notification(session.user.methods.email.email) return ResponseModel(status="Success", message="Password has been successfully changed") else: @@ -360,15 +337,10 @@ async def request_reset_password(user_id: int, schema: ResetPassword, token: str status_code=401, detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), ) - reset_token = random_string() - db.session.query(AuthMethod).filter( - AuthMethod.user_id == user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "reset_token", - ).update(values={"value": reset_token}) - send_change_password_confirmation( - user.methods.email.email, f"{settings.FRONTEND_HOST}/email/reset/password?token={reset_token}" - ) + user.reset_token.value = random_string() + db.session.flush() + send_change_password_confirmation(user.email.value, + f"{settings.FRONTEND_HOST}/email/reset?token={user.reset_token.value}") return ResponseModel(status="Success", message="Reset link has been successfully mailed") @staticmethod diff --git a/auth_backend/models/db.py b/auth_backend/models/db.py index 1bc354e0..0ac769d6 100644 --- a/auth_backend/models/db.py +++ b/auth_backend/models/db.py @@ -16,6 +16,17 @@ class User(Base): auth_methods: list[AuthMethod] = sqlalchemy.orm.relationship("AuthMethod", foreign_keys="AuthMethod.user_id") sessions: list[UserSession] = sqlalchemy.orm.relationship("UserSession", foreign_keys="UserSession.user_id") + # Type hints + email: AuthMethod + hashed_password: AuthMethod + salt: AuthMethod + tmp_email: AuthMethod + confirmation_token: AuthMethod + tmp_email_confirmation_token: AuthMethod + reset_token: AuthMethod + confirmed: AuthMethod + + def __init__(self): super(User, self).__init__() diff --git a/auth_backend/settings.py b/auth_backend/settings.py index d871aff3..8fa15514 100644 --- a/auth_backend/settings.py +++ b/auth_backend/settings.py @@ -1,6 +1,6 @@ from functools import lru_cache -from pydantic import BaseSettings, PostgresDsn +from pydantic import BaseSettings, PostgresDsn, HttpUrl class Settings(BaseSettings): @@ -12,6 +12,7 @@ class Settings(BaseSettings): SMTP_HOST: str = 'smtp.gmail.com' SMTP_PORT: int = 587 ENABLED_AUTH_METHODS: list[str] | None + FRONTEND_HOST: HttpUrl = "https://localhost.com" CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True From b34c66b66a2b94b05cad9116bb215c3b3a60e334 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 10 Dec 2022 00:00:30 +0300 Subject: [PATCH 07/12] refactoring --- auth_backend/auth_plugins/email.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index 3d7d6fa3..d8a58e0c 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -149,9 +149,7 @@ async def _change_confirmation_link(user: User, confirmation_token: str): if user.confirmed.value == "true": raise AlreadyExists(User, user.id) else: - db.session.query(AuthMethod).filter( - AuthMethod.param == "confirmation_token", AuthMethod.user_id == user.id - ).one().value = confirmation_token + user.confirmation_token.value = confirmation_token db.session.flush() @staticmethod From f83feee9f309ad3a36606d5a36caa3a06dc44d04 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 10 Dec 2022 06:10:00 +0300 Subject: [PATCH 08/12] All tests passed --- auth_backend/auth_plugins/auth_method.py | 2 +- auth_backend/auth_plugins/email.py | 123 +++++++++++++--------- auth_backend/models/db.py | 1 - tests/conftest.py | 2 + tests/test_routes/test_change_password.py | 42 +++----- 5 files changed, 89 insertions(+), 81 deletions(-) diff --git a/auth_backend/auth_plugins/auth_method.py b/auth_backend/auth_plugins/auth_method.py index 5694490a..3df4c6b2 100644 --- a/auth_backend/auth_plugins/auth_method.py +++ b/auth_backend/auth_plugins/auth_method.py @@ -60,7 +60,7 @@ def __init_subclass__(cls, **kwargs): back_populates="user", uselist=False, primaryjoin=f"and_(User.id==AuthMethod.user_id," - f" AuthMethod.auth_method=='{cls.get_name()}',AuthMethod.param=='{row}')", + f" AuthMethod.auth_method=='{cls.get_name()}',AuthMethod.param=='{row}')", ), ) diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index d8a58e0c..0c6ac129 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -6,7 +6,6 @@ from fastapi_sqlalchemy import db from pydantic import validator, constr from sqlalchemy import func -from starlette.responses import JSONResponse from auth_backend.base import Base, ResponseModel from auth_backend.exceptions import AlreadyExists, AuthFailed, ObjectNotFound, SessionExpired @@ -74,27 +73,41 @@ class EmailChange(Base): email_validator = validator("email", allow_reuse=True)(check_email) +class RequestResetPassword(Base): + password: str | None + new_password: str | None + + class ResetPassword(Base): - password: constr(min_length=1) | None new_password: constr(min_length=1) + def random_string(length: int = 12) -> str: return "".join([random.choice(string.ascii_letters) for _ in range(length)]) class Email(AuthMethodMeta): prefix = "/email" - fields = ["email", "hashed_password", "salt", "confirmed", "confirmation_token", - "tmp_email_confirmation_token", "tmp_email", "reset_token"] + fields = [ + "email", + "hashed_password", + "salt", + "confirmed", + "confirmation_token", + "tmp_email_confirmation_token", + "tmp_email", + "reset_token", + ] def __init__(self): super().__init__() + self.router.add_api_route("/approve", self.approve_email, methods=["GET"]) self.router.add_api_route("/reset/email/request", self.request_reset_email, methods=["POST"]) self.router.add_api_route("/reset/email/{user_id}", self.reset_email, methods=["GET"]) self.router.add_api_route("/reset/password/{user_id}/request", self.request_reset_password, methods=["POST"]) - self.router.add_api_route("/reset/password", self.reset_password, methods=["GET"]) + self.router.add_api_route("/reset/password/{user_id}", self.reset_password, methods=["POST"]) self.router.prefix = self.prefix self.tags = ["Email"] @@ -102,12 +115,12 @@ def __init__(self): async def login(user_inp: EmailLogin) -> Session: query = ( db.session.query(AuthMethod) - .filter( + .filter( func.lower(AuthMethod.value) == user_inp.email.lower(), AuthMethod.param == "email", AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if not query: raise AuthFailed(error="Incorrect login or password") @@ -116,7 +129,7 @@ async def login(user_inp: EmailLogin) -> Session: error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) if query.user.email.value.lower() != user_inp.email.lower() or not Email.validate_password( - user_inp.password, query.user.hashed_password.value, query.user.salt.value + user_inp.password, query.user.hashed_password.value, query.user.salt.value ): raise AuthFailed(error="Incorrect login or password") db.session.add(user_session := UserSession(user_id=query.user.id, token=random_string())) @@ -140,12 +153,10 @@ async def _add_to_db(user_inp: EmailRegister, confirmation_token: str, user: Use user_id=user.id, auth_method=Email.get_name(), param="confirmation_token", value=confirmation_token ) ) - db.session.add(AuthMethod(user_id=user.id, auth_method=Email.get_name(), param="reset_token", value=None)) db.session.flush() - return None @staticmethod - async def _change_confirmation_link(user: User, confirmation_token: str): + async def _change_confirmation_link(user: User, confirmation_token: str) -> None: if user.confirmed.value == "true": raise AlreadyExists(User, user.id) else: @@ -153,7 +164,7 @@ async def _change_confirmation_link(user: User, confirmation_token: str): db.session.flush() @staticmethod - async def _get_user_by_token_and_id(id: int, token: str) -> User | None: + async def _get_user_by_token_and_id(id: int, token: str) -> User: user: User = db.session.query(User).get(id) user_session: UserSession = ( db.session.query(UserSession).filter(UserSession.token == token, UserSession.user_id == id).one_or_none() @@ -167,16 +178,16 @@ async def _get_user_by_token_and_id(id: int, token: str) -> User | None: return user @staticmethod - async def register(user_inp: EmailRegister, token: str = Header(default=None)) -> ResponseModel | JSONResponse: + async def register(user_inp: EmailRegister, token: str = Header(default=None)) -> ResponseModel: confirmation_token: str = random_string() auth_method: AuthMethod = ( db.session.query(AuthMethod) - .filter( + .filter( AuthMethod.param == "email", func.lower(AuthMethod.value) == user_inp.email.lower(), AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if auth_method: await Email._change_confirmation_link(auth_method.user, confirmation_token) @@ -185,8 +196,8 @@ async def register(user_inp: EmailRegister, token: str = Header(default=None)) - link=f"{settings.APPLICATION_HOST}/email/approve?token={confirmation_token}", ) return ResponseModel(status="Success", message="Email confirmation link sent") - if user_inp.user_id and user_inp.token: - user = await Email._get_user_by_token_and_id(user_inp.user_id, user_inp.token) + if user_inp.user_id and token: + user = await Email._get_user_by_token_and_id(user_inp.user_id, token) else: user = User() db.session.add(user) @@ -196,33 +207,33 @@ async def register(user_inp: EmailRegister, token: str = Header(default=None)) - to_addr=user_inp.email, link=f"{settings.APPLICATION_HOST}/email/approve?token={confirmation_token}", ) - return JSONResponse( - status_code=201, content=ResponseModel(status="Success", message="Email confirmation link sent").json() + raise HTTPException( + status_code=201, detail=ResponseModel(status="Success", message="Email confirmation link sent").json() ) @staticmethod - def hash_password(password: str, salt: str): + def hash_password(password: str, salt: str) -> str: enc = hashlib.pbkdf2_hmac("sha256", password.encode(), salt.encode(), 100_000) return enc.hex() @staticmethod - def validate_password(password: str, hashed_password: str, salt: str): + def validate_password(password: str, hashed_password: str, salt: str) -> bool: """Проверяет, что хеш пароля совпадает с хешем из БД""" return Email.hash_password(password, salt) == hashed_password @staticmethod - async def approve_email(token: str) -> object: + async def approve_email(token: str) -> ResponseModel: auth_method = ( db.session.query(AuthMethod) - .filter( + .filter( AuthMethod.value == token, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == Email.get_name(), ) - .one_or_none() + .one_or_none() ) if not auth_method: - return JSONResponse(status_code=403, content=ResponseModel(status="Error", message="Incorrect link").json()) + raise HTTPException(status_code=403, detail=ResponseModel(status="Error", message="Incorrect link").json()) auth_method.user.confirmed.value = True db.session.flush() return ResponseModel(status="Success", message="Email approved") @@ -249,18 +260,6 @@ async def request_reset_email(scheme: EmailChange, token: str = Header(default=N ) if session.user.email.value == scheme.email: raise HTTPException(status_code=401, detail=ResponseModel(status="Error", message="Email incorrect").json()) - if hasattr(session.user, "tmp_email_confirmation_token"): - db.session.query(AuthMethod).filter( - AuthMethod.user_id == session.user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "tmp_email_confirmation_token", - ).delete() - if hasattr(session.user, "tmp_email"): - db.session.query(AuthMethod).filter( - AuthMethod.user_id == session.user_id, - AuthMethod.auth_method == Email.get_name(), - AuthMethod.param == "tmp_email", - ).delete() tmp_email = AuthMethod( user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email", value=scheme.email ) @@ -298,23 +297,23 @@ async def reset_email(user_id: int, token: str, email: str): return ResponseModel(status="Success", message="Email successfully changed") @staticmethod - async def request_reset_password(user_id: int, schema: ResetPassword, token: str = Header(default=None)): + async def request_reset_password(user_id: int, schema: RequestResetPassword, token: str = Header(default=None)): salt = random_string() - if token: + if token and schema.new_password and schema.password: session: UserSession = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: raise HTTPException( - status_code=404, detail=ResponseModel(status="Error", message="Session not found").json() + status_code=403, detail=ResponseModel(status="Error", message="Session not found").json() ) if session.expired: raise SessionExpired(token) - if not hasattr(session.user.methods, "email"): + if not session.user.email: raise HTTPException( status_code=401, detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), ) if not Email.validate_password( - schema.password, session.user.methods.email.hashed_password, session.user.methods.email.salt + schema.password, session.user.hashed_password.value, session.user.salt.value ): raise AuthFailed(error="Incorrect password") if user_id != session.user_id: @@ -324,23 +323,47 @@ async def request_reset_password(user_id: int, schema: ResetPassword, token: str session.user.hashed_password.value = Email.hash_password(schema.new_password, salt) session.user.salt.value = salt db.session.flush() - send_changes_password_notification(session.user.methods.email.email) + send_changes_password_notification(session.user.email.value) return ResponseModel(status="Success", message="Password has been successfully changed") - else: + elif not token and not schema.password and not schema.new_password: user: User = db.session.query(User).get(user_id) if not user: raise ObjectNotFound(User, user_id) - if not hasattr(user.methods, "email"): + if not user.email: raise HTTPException( status_code=401, detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), ) - user.reset_token.value = random_string() + if user.confirmed.value.lower() == "false": + raise AuthFailed( + error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" + ) + db.session.add(AuthMethod(user_id=user_id, auth_method=Email.get_name(), + param="reset_token", value=random_string())) db.session.flush() - send_change_password_confirmation(user.email.value, - f"{settings.FRONTEND_HOST}/email/reset?token={user.reset_token.value}") + send_change_password_confirmation( + user.email.value, f"{settings.FRONTEND_HOST}/email/reset?token={user.reset_token.value}" + ) return ResponseModel(status="Success", message="Reset link has been successfully mailed") + raise HTTPException(status_code=422, detail=ResponseModel(status="Error", message="Unprocessable entity").json()) @staticmethod - async def reset_password(token: constr(min_length=1), password: constr(min_length=1)): - pass + async def reset_password( + user_id: int, schema: ResetPassword, reset_token: str = Header(default=None) + ) -> ResponseModel: + if not reset_token: + raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) + user: User = db.session.query(User).get(user_id) + if not user: + raise ObjectNotFound(User, user_id) + if not user.reset_token or user.reset_token.value != reset_token: + raise HTTPException( + status_code=403, + detail=ResponseModel(status="Error", message="Incorrect reset_token").json(), + ) + salt = random_string() + user.hashed_password.value = Email.hash_password(schema.new_password, salt) + user.salt.value = salt + db.session.delete(user.reset_token) + db.session.flush() + return ResponseModel(status="Success", message="Password has been successfully changed") diff --git a/auth_backend/models/db.py b/auth_backend/models/db.py index 0ac769d6..825b5340 100644 --- a/auth_backend/models/db.py +++ b/auth_backend/models/db.py @@ -26,7 +26,6 @@ class User(Base): reset_token: AuthMethod confirmed: AuthMethod - def __init__(self): super(User, self).__init__() diff --git a/tests/conftest.py b/tests/conftest.py index d07e15d0..838255e1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,6 +16,8 @@ @pytest.fixture(scope='session') def client(): auth_backend.auth_plugins.email.send_confirmation_email = Mock(return_value=None) + auth_backend.auth_plugins.email.send_change_password_confirmation = Mock(return_value=None) + auth_backend.auth_plugins.email.send_changes_password_notification = Mock(return_value=None) client = TestClient(app) yield client diff --git a/tests/test_routes/test_change_password.py b/tests/test_routes/test_change_password.py index a64c63f5..72099edb 100644 --- a/tests/test_routes/test_change_password.py +++ b/tests/test_routes/test_change_password.py @@ -7,7 +7,6 @@ url = "/email/reset/password/" -@pytest.mark.skip() def test_unprocessable_jsons_no_token(client: TestClient, dbsession: Session, user_id: int): token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token", @@ -15,24 +14,23 @@ def test_unprocessable_jsons_no_token(client: TestClient, dbsession: Session, us response = client.get(f"/email/approve?token={token.value}") assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}/request", json={"new_password": "changed"}) + response = client.post(f"{url}{user_id}/request", json={}) assert response.status_code == status.HTTP_200_OK reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", AuthMethod.user_id == user_id).one() assert reset_token - response = client.post(f"{url}{user_id}", json={"reset_token": reset_token, "new_password": ""}) + response = client.post(f"{url}{user_id}", headers={"reset-token": reset_token.value}, json={"new_password": ""}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}", json={"reset_token": "", "new_password": ""}) + response = client.post(f"{url}{user_id}", headers={"reset-token": ""}, json={"new_password": ""}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}", json={"reset_token": "", "new_password": "changed"}) - assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + response = client.post(f"{url}{user_id}", headers={"reset-token": ""}, json={"new_password": "changed"}) + assert response.status_code == status.HTTP_400_BAD_REQUEST -@pytest.mark.skip() def test_unprocessable_jsons_with_token(client: TestClient, dbsession: Session, user): user_id, body, response = user["user_id"], user["body"], user["login_json"] auth_token = response["token"] @@ -58,42 +56,37 @@ def test_unprocessable_jsons_with_token(client: TestClient, dbsession: Session, assert response.status_code == status.HTTP_200_OK -@pytest.mark.skip() def test_no_token(client: TestClient, dbsession: Session, user_id: str): token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == "email").one() - response = client.post(f"{url}{user_id}/request", json={"new_password": "changed"}) - assert response.status_code == status.HTTP_403_FORBIDDEN + response = client.post(f"{url}{user_id}/request", json={}) + assert response.status_code == status.HTTP_401_UNAUTHORIZED response = client.get(f"/email/approve?token={token.value}") assert response.status_code == status.HTTP_200_OK - response = client.post(f"{url}{user_id}/request", json={"new_password": "changedstring"}) + response = client.post(f"{url}{user_id}/request", json={}) assert response.status_code == status.HTTP_200_OK reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", AuthMethod.user_id == user_id).one() assert reset_token - response = client.get(f"{url}{user_id}?token={reset_token}&password=changedstring2") + response = client.post(f"{url}{user_id}", headers={"reset-token": reset_token.value+"x"}, json={"new_password": "changedstring2"}) assert response.status_code == status.HTTP_403_FORBIDDEN - response = client.get(f"{url}{user_id}?token={reset_token}&password=changedstring") + response = client.post(f"{url}{user_id}", headers={"reset-token": reset_token.value}, json={"new_password": "changedstring2"}) assert response.status_code == status.HTTP_200_OK - response = client.get(f"{url}{user_id}?token={reset_token}&password=changedstring2") - assert response.status_code == status.HTTP_403_FORBIDDEN - -@pytest.mark.skip() def test_with_token(client: TestClient, dbsession: Session, user): user_id, body, response = user["user_id"], user["body"], user["login_json"] auth_token = response["token"] response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, json={"password": "wrong", "new_password": "changed"}) - assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.status_code == status.HTTP_401_UNAUTHORIZED response = client.post(f"{url}{user_id}/request", headers={"token": auth_token + "wrong"}, json={"password": body["password"], "new_password": "changed"}) @@ -104,14 +97,5 @@ def test_with_token(client: TestClient, dbsession: Session, user): assert response.status_code == status.HTTP_200_OK reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", - AuthMethod.user_id == user_id).one() - assert reset_token - - response = client.get(f"{url}{user_id}?token={reset_token}&password=wrong") - assert response.status_code == status.HTTP_403_FORBIDDEN - - response = client.get(f"{url}{user_id}?token={reset_token}&password=changed") - assert response.status_code == status.HTTP_200_OK - - response = client.get(f"{url}{user_id}?token={reset_token}&password=changed") - assert response.status_code == status.HTTP_403_FORBIDDEN + AuthMethod.user_id == user_id).one_or_none() + assert not reset_token From bcd811c982da6657b879e8f1e18aa005eff4a739 Mon Sep 17 00:00:00 2001 From: semen603089 Date: Sat, 31 Dec 2022 00:16:00 +0300 Subject: [PATCH 09/12] review --- auth_backend/__main__.py | 10 + auth_backend/auth_plugins/auth_method.py | 34 +--- auth_backend/auth_plugins/email.py | 131 ++++++------- auth_backend/exceptions.py | 4 +- auth_backend/models/db.py | 44 ++--- auth_backend/routes/exc_handlers.py | 6 +- auth_backend/routes/models/__init__.py | 0 auth_backend/routes/models/models.py | 6 + auth_backend/routes/user_session.py | 17 +- auth_backend/settings.py | 8 +- auth_backend/templates/image.png | Bin 0 -> 270288 bytes .../templates/mail_change_confirmation.html | 2 +- auth_backend/templates/main_confirmation.html | 2 +- .../password_change_confirmation.html | 2 +- .../password_change_notification.html | 2 +- auth_backend/utils/smtp.py | 172 +++++++++++------- tests/conftest.py | 32 ++-- tests/test_routes/test_change_email.py | 26 ++- tests/test_routes/test_change_password.py | 100 ++++++---- tests/test_routes/test_login.py | 61 +++---- tests/test_routes/test_logout.py | 30 +-- tests/test_routes/test_registration.py | 85 +++++---- 22 files changed, 430 insertions(+), 344 deletions(-) create mode 100644 auth_backend/routes/models/__init__.py create mode 100644 auth_backend/routes/models/models.py create mode 100644 auth_backend/templates/image.png diff --git a/auth_backend/__main__.py b/auth_backend/__main__.py index 3efa57f5..8c65be85 100644 --- a/auth_backend/__main__.py +++ b/auth_backend/__main__.py @@ -1,6 +1,16 @@ +import logging + import uvicorn from auth_backend.routes import app if __name__ == '__main__': + + logging.basicConfig( + filename=f'logger_{__name__}.log', + level=logging.INFO, + format='%(asctime)s.%(msecs)03d %(levelname)s %(module)s - %(funcName)s: %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + ) + uvicorn.run(app) diff --git a/auth_backend/auth_plugins/auth_method.py b/auth_backend/auth_plugins/auth_method.py index 3df4c6b2..01306a45 100644 --- a/auth_backend/auth_plugins/auth_method.py +++ b/auth_backend/auth_plugins/auth_method.py @@ -6,11 +6,8 @@ from fastapi import APIRouter from pydantic import constr -from sqlalchemy.orm import relationship -from sqlalchemy.orm.collections import attribute_mapped_collection from auth_backend.base import Base -from auth_backend.models.db import User class Session(Base): @@ -35,41 +32,18 @@ def get_name(cls) -> str: def __init__(self): self.router = APIRouter() - self.router.add_api_route("/registration", self.register, methods=["POST"]) - self.router.add_api_route("/login", self.login, methods=["POST"], response_model=Session) + self.router.add_api_route("/registration", self._register, methods=["POST"]) + self.router.add_api_route("/login", self._login, methods=["POST"], response_model=Session) def __init_subclass__(cls, **kwargs): AUTH_METHODS[cls.__name__] = cls - setattr( - User, - f"{cls.__name__}__{cls.get_name()}", - relationship( - "AuthMethod", - foreign_keys="AuthMethod.user_id", - back_populates="user", - primaryjoin=f"and_(User.id==AuthMethod.user_id, AuthMethod.auth_method=='{cls.get_name()}')", - ), - ) - for row in cls.fields: - setattr( - User, - row, - relationship( - "AuthMethod", - foreign_keys="AuthMethod.user_id", - back_populates="user", - uselist=False, - primaryjoin=f"and_(User.id==AuthMethod.user_id," - f" AuthMethod.auth_method=='{cls.get_name()}',AuthMethod.param=='{row}')", - ), - ) @staticmethod @abstractmethod - async def register(**kwargs) -> object: + async def _register(**kwargs) -> object: raise NotImplementedError() @staticmethod @abstractmethod - async def login(**kwargs) -> Session: + async def _login(**kwargs) -> Session: raise NotImplementedError() diff --git a/auth_backend/auth_plugins/email.py b/auth_backend/auth_plugins/email.py index 0c6ac129..6ca80035 100644 --- a/auth_backend/auth_plugins/email.py +++ b/auth_backend/auth_plugins/email.py @@ -8,7 +8,7 @@ from sqlalchemy import func from auth_backend.base import Base, ResponseModel -from auth_backend.exceptions import AlreadyExists, AuthFailed, ObjectNotFound, SessionExpired +from auth_backend.exceptions import AlreadyExists, AuthFailed, ObjectNotFound, SessionExpired, IncorrectUserAuthType from auth_backend.models.db import AuthMethod from auth_backend.models.db import UserSession, User from auth_backend.settings import get_settings @@ -18,6 +18,7 @@ send_changes_password_notification, ) from .auth_method import AuthMethodMeta, Session +from fastapi.background import BackgroundTasks settings = get_settings() @@ -82,7 +83,6 @@ class ResetPassword(Base): new_password: constr(min_length=1) - def random_string(length: int = 12) -> str: return "".join([random.choice(string.ascii_letters) for _ in range(length)]) @@ -103,16 +103,16 @@ class Email(AuthMethodMeta): def __init__(self): super().__init__() - self.router.add_api_route("/approve", self.approve_email, methods=["GET"]) - self.router.add_api_route("/reset/email/request", self.request_reset_email, methods=["POST"]) - self.router.add_api_route("/reset/email/{user_id}", self.reset_email, methods=["GET"]) - self.router.add_api_route("/reset/password/{user_id}/request", self.request_reset_password, methods=["POST"]) - self.router.add_api_route("/reset/password/{user_id}", self.reset_password, methods=["POST"]) + self.router.add_api_route("/approve", self._approve_email, methods=["GET"]) + self.router.add_api_route("/reset/email/request", self._request_reset_email, methods=["POST"]) + self.router.add_api_route("/reset/email/{user_id}", self._reset_email, methods=["GET"]) + self.router.add_api_route("/reset/password/{user_id}/request", self._request_reset_password, methods=["POST"]) + self.router.add_api_route("/reset/password/{user_id}", self._reset_password, methods=["POST"]) self.router.prefix = self.prefix self.tags = ["Email"] @staticmethod - async def login(user_inp: EmailLogin) -> Session: + async def _login(user_inp: EmailLogin) -> Session: query = ( db.session.query(AuthMethod) .filter( @@ -124,12 +124,12 @@ async def login(user_inp: EmailLogin) -> Session: ) if not query: raise AuthFailed(error="Incorrect login or password") - if query.user.confirmed.value.lower() == "false": + if query.user.auth_methods.confirmed.value.lower() == "false": raise AuthFailed( error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) - if query.user.email.value.lower() != user_inp.email.lower() or not Email.validate_password( - user_inp.password, query.user.hashed_password.value, query.user.salt.value + if query.user.auth_methods.email.value.lower() != user_inp.email.lower() or not Email._validate_password( + user_inp.password, query.user.auth_methods.hashed_password.value, query.user.auth_methods.salt.value ): raise AuthFailed(error="Incorrect login or password") db.session.add(user_session := UserSession(user_id=query.user.id, token=random_string())) @@ -141,7 +141,7 @@ async def login(user_inp: EmailLogin) -> Session: @staticmethod async def _add_to_db(user_inp: EmailRegister, confirmation_token: str, user: User) -> None: salt = random_string() - hashed_password = Email.hash_password(user_inp.password, salt) + hashed_password = Email._hash_password(user_inp.password, salt) db.session.add(AuthMethod(user_id=user.id, auth_method=Email.get_name(), param="email", value=user_inp.email)) db.session.add( AuthMethod(user_id=user.id, auth_method=Email.get_name(), param="hashed_password", value=hashed_password) @@ -157,10 +157,10 @@ async def _add_to_db(user_inp: EmailRegister, confirmation_token: str, user: Use @staticmethod async def _change_confirmation_link(user: User, confirmation_token: str) -> None: - if user.confirmed.value == "true": + if user.auth_methods.confirmed.value == "true": raise AlreadyExists(User, user.id) else: - user.confirmation_token.value = confirmation_token + user.auth_methods.confirmation_token.value = confirmation_token db.session.flush() @staticmethod @@ -178,7 +178,9 @@ async def _get_user_by_token_and_id(id: int, token: str) -> User: return user @staticmethod - async def register(user_inp: EmailRegister, token: str = Header(default=None)) -> ResponseModel: + async def _register( + user_inp: EmailRegister, background_tasks: BackgroundTasks, token: str = Header(default=None) + ) -> ResponseModel: confirmation_token: str = random_string() auth_method: AuthMethod = ( db.session.query(AuthMethod) @@ -191,7 +193,8 @@ async def register(user_inp: EmailRegister, token: str = Header(default=None)) - ) if auth_method: await Email._change_confirmation_link(auth_method.user, confirmation_token) - send_confirmation_email( + background_tasks.add_task( + send_confirmation_email, to_addr=user_inp.email, link=f"{settings.APPLICATION_HOST}/email/approve?token={confirmation_token}", ) @@ -203,7 +206,8 @@ async def register(user_inp: EmailRegister, token: str = Header(default=None)) - db.session.add(user) db.session.flush() await Email._add_to_db(user_inp, confirmation_token, user) - send_confirmation_email( + background_tasks.add_task( + send_confirmation_email, to_addr=user_inp.email, link=f"{settings.APPLICATION_HOST}/email/approve?token={confirmation_token}", ) @@ -212,17 +216,17 @@ async def register(user_inp: EmailRegister, token: str = Header(default=None)) - ) @staticmethod - def hash_password(password: str, salt: str) -> str: + def _hash_password(password: str, salt: str) -> str: enc = hashlib.pbkdf2_hmac("sha256", password.encode(), salt.encode(), 100_000) return enc.hex() @staticmethod - def validate_password(password: str, hashed_password: str, salt: str) -> bool: + def _validate_password(password: str, hashed_password: str, salt: str) -> bool: """Проверяет, что хеш пароля совпадает с хешем из БД""" - return Email.hash_password(password, salt) == hashed_password + return Email._hash_password(password, salt) == hashed_password @staticmethod - async def approve_email(token: str) -> ResponseModel: + async def _approve_email(token: str) -> ResponseModel: auth_method = ( db.session.query(AuthMethod) .filter( @@ -234,14 +238,14 @@ async def approve_email(token: str) -> ResponseModel: ) if not auth_method: raise HTTPException(status_code=403, detail=ResponseModel(status="Error", message="Incorrect link").json()) - auth_method.user.confirmed.value = True + auth_method.user.auth_methods.confirmed.value = True db.session.flush() return ResponseModel(status="Success", message="Email approved") @staticmethod - async def request_reset_email(scheme: EmailChange, token: str = Header(default=None)): - if not token: - raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) + async def _request_reset_email( + scheme: EmailChange, background_tasks: BackgroundTasks, token: str = Header(min_length=1) + ): session: UserSession = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: raise HTTPException( @@ -249,16 +253,13 @@ async def request_reset_email(scheme: EmailChange, token: str = Header(default=N ) if session.expired: raise SessionExpired(token) - if not hasattr(session.user, "email"): - raise HTTPException( - status_code=401, - detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), - ) - if session.user.confirmed.value == "false": + if not hasattr(session.user.auth_methods, "email"): + raise IncorrectUserAuthType() + if session.user.auth_methods.confirmed.value == "false": raise AuthFailed( error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) - if session.user.email.value == scheme.email: + if session.user.auth_methods.email.value == scheme.email: raise HTTPException(status_code=401, detail=ResponseModel(status="Error", message="Email incorrect").json()) tmp_email = AuthMethod( user_id=session.user_id, auth_method=Email.get_name(), param="tmp_email", value=scheme.email @@ -269,35 +270,38 @@ async def request_reset_email(scheme: EmailChange, token: str = Header(default=N ) db.session.add_all([tmp_email, tmp_email_confirmation_token]) db.session.flush() - send_confirmation_email( + background_tasks.add_task( + send_confirmation_email, to_addr=scheme.email, link=f"{settings.APPLICATION_HOST}/email/reset/email/{session.user_id}?token={token}&email={scheme.email}", ) return ResponseModel(status="Success", message="Email confirmation link sent") @staticmethod - async def reset_email(user_id: int, token: str, email: str): + async def _reset_email(user_id: int, token: str, email: str): user: User = db.session.query(User).get(user_id) - if user.confirmed.value == "false": + if user.auth_methods.confirmed.value == "false": raise AuthFailed( error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) - if email != user.tmp_email.value: + if email != user.auth_methods.tmp_email.value: raise HTTPException( status_code=403, detail=ResponseModel(status="Error", message="Incorrect new email").dict() ) - if token != user.tmp_email_confirmation_token.value: + if token != user.auth_methods.tmp_email_confirmation_token.value: raise HTTPException( status_code=403, detail=ResponseModel(status="Error", message="Incorrect confirmation token").dict() ) - user.email.value = user.tmp_email.value - db.session.delete(user.tmp_email_confirmation_token) - db.session.delete(user.tmp_email) + user.auth_methods.email.value = user.auth_methods.tmp_email.value + db.session.delete(user.auth_methods.tmp_email_confirmation_token) + db.session.delete(user.auth_methods.tmp_email) db.session.flush() return ResponseModel(status="Success", message="Email successfully changed") @staticmethod - async def request_reset_password(user_id: int, schema: RequestResetPassword, token: str = Header(default=None)): + async def _request_reset_password( + user_id: int, background_tasks: BackgroundTasks, schema: RequestResetPassword, token: str = Header(default=None) + ): salt = random_string() if token and schema.new_password and schema.password: session: UserSession = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() @@ -307,63 +311,66 @@ async def request_reset_password(user_id: int, schema: RequestResetPassword, tok ) if session.expired: raise SessionExpired(token) - if not session.user.email: + if not session.user.auth_methods.email: raise HTTPException( status_code=401, detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), ) - if not Email.validate_password( - schema.password, session.user.hashed_password.value, session.user.salt.value + if not Email._validate_password( + schema.password, session.user.auth_methods.hashed_password.value, session.user.auth_methods.salt.value ): raise AuthFailed(error="Incorrect password") if user_id != session.user_id: raise HTTPException( status_code=403, detail=ResponseModel(status="Error", message="Incorrect pair user_id+token").json() ) - session.user.hashed_password.value = Email.hash_password(schema.new_password, salt) - session.user.salt.value = salt + session.user.auth_methods.hashed_password.value = Email._hash_password(schema.new_password, salt) + session.user.auth_methods.salt.value = salt db.session.flush() - send_changes_password_notification(session.user.email.value) + background_tasks.add_task(send_changes_password_notification, session.user.auth_methods.email.value) return ResponseModel(status="Success", message="Password has been successfully changed") elif not token and not schema.password and not schema.new_password: user: User = db.session.query(User).get(user_id) if not user: raise ObjectNotFound(User, user_id) - if not user.email: + if not user.auth_methods.email: raise HTTPException( status_code=401, detail=ResponseModel(status="Error", message="Auth method restricted for this user").json(), ) - if user.confirmed.value.lower() == "false": + if user.auth_methods.confirmed.value.lower() == "false": raise AuthFailed( error="Registration wasn't completed. Try to registrate again and do not forget to approve your email" ) - db.session.add(AuthMethod(user_id=user_id, auth_method=Email.get_name(), - param="reset_token", value=random_string())) + db.session.add( + AuthMethod(user_id=user_id, auth_method=Email.get_name(), param="reset_token", value=random_string()) + ) db.session.flush() - send_change_password_confirmation( - user.email.value, f"{settings.FRONTEND_HOST}/email/reset?token={user.reset_token.value}" + background_tasks.add_task( + send_change_password_confirmation, + user.auth_methods.email.value, + f"{settings.APPLICATION_HOST}/email/reset?token={user.auth_methods.reset_token.value}", ) return ResponseModel(status="Success", message="Reset link has been successfully mailed") - raise HTTPException(status_code=422, detail=ResponseModel(status="Error", message="Unprocessable entity").json()) + raise HTTPException( + status_code=422, detail=ResponseModel(status="Error", message="Unprocessable entity").json() + ) @staticmethod - async def reset_password( - user_id: int, schema: ResetPassword, reset_token: str = Header(default=None) + async def _reset_password( + user_id: int, schema: ResetPassword, reset_token: str = Header(min_length=1) ) -> ResponseModel: - if not reset_token: - raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) user: User = db.session.query(User).get(user_id) if not user: raise ObjectNotFound(User, user_id) - if not user.reset_token or user.reset_token.value != reset_token: + if not user.auth_methods.reset_token or user.auth_methods.reset_token.value != reset_token: raise HTTPException( status_code=403, detail=ResponseModel(status="Error", message="Incorrect reset_token").json(), ) salt = random_string() - user.hashed_password.value = Email.hash_password(schema.new_password, salt) - user.salt.value = salt - db.session.delete(user.reset_token) + user.auth_methods.hashed_password.value = Email._hash_password(schema.new_password, salt) + user.auth_methods.salt.value = salt + db.session.delete(user.auth_methods.reset_token) db.session.flush() return ResponseModel(status="Success", message="Password has been successfully changed") diff --git a/auth_backend/exceptions.py b/auth_backend/exceptions.py index 09a94860..838fc3a7 100644 --- a/auth_backend/exceptions.py +++ b/auth_backend/exceptions.py @@ -8,9 +8,9 @@ def __init__(self, obj: type, obj_id: int): super().__init__(f"Object {obj.__name__}, {obj_id=} already exists") -class IncorrectAuthType(Exception): +class IncorrectUserAuthType(Exception): def __init__(self): - super().__init__(f"Incorrect Authentication Type") + super().__init__(f"Incorrect Authentication Type for this user") class SessionExpired(Exception): diff --git a/auth_backend/models/db.py b/auth_backend/models/db.py index 825b5340..8f2381ad 100644 --- a/auth_backend/models/db.py +++ b/auth_backend/models/db.py @@ -1,39 +1,39 @@ from __future__ import annotations import datetime -from typing import Iterator import sqlalchemy.orm -from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property +from sqlalchemy.ext.hybrid import hybrid_property from auth_backend.models.base import Base +class ParamDict: + def __new__(cls, methods: list[AuthMethod], *args, **kwargs): + obj = super(ParamDict, cls).__new__(cls) + for row in methods: + if attr := getattr(obj, row.param, None): + if not isinstance(attr, AuthMethod): + raise AttributeError + setattr(obj, row.param, row) + return obj + + class User(Base): id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) - auth_methods: list[AuthMethod] = sqlalchemy.orm.relationship("AuthMethod", foreign_keys="AuthMethod.user_id") + _auth_methods: list[AuthMethod] = sqlalchemy.orm.relationship("AuthMethod", foreign_keys="AuthMethod.user_id") sessions: list[UserSession] = sqlalchemy.orm.relationship("UserSession", foreign_keys="UserSession.user_id") - # Type hints - email: AuthMethod - hashed_password: AuthMethod - salt: AuthMethod - tmp_email: AuthMethod - confirmation_token: AuthMethod - tmp_email_confirmation_token: AuthMethod - reset_token: AuthMethod - confirmed: AuthMethod - - def __init__(self): - super(User, self).__init__() - - @hybrid_method - def get_method_secrets(self, method_name: str) -> Iterator[AuthMethod]: - for row in self.auth_methods: - if row.auth_method == method_name: - yield row + @hybrid_property + def auth_methods(self) -> ParamDict: + """ + Эта функция возвращает экземпляр класса ParamDict, который создает внутри себя поля, соотвествуюшие: + user.auth_methods. = Соответствущему объекту AuthMethod + :return: ParamDict + """ + return ParamDict.__new__(ParamDict, self._auth_methods) class AuthMethod(Base): @@ -43,7 +43,7 @@ class AuthMethod(Base): param = sqlalchemy.Column(sqlalchemy.String) value = sqlalchemy.Column(sqlalchemy.String) - user: User = sqlalchemy.orm.relationship("User", foreign_keys=[user_id], back_populates="auth_methods") + user: User = sqlalchemy.orm.relationship("User", foreign_keys=[user_id], back_populates="_auth_methods") class UserSession(Base): diff --git a/auth_backend/routes/exc_handlers.py b/auth_backend/routes/exc_handlers.py index 8267f96d..ad90f6e0 100644 --- a/auth_backend/routes/exc_handlers.py +++ b/auth_backend/routes/exc_handlers.py @@ -1,7 +1,7 @@ import starlette.requests from starlette.responses import JSONResponse -from auth_backend.exceptions import ObjectNotFound, IncorrectAuthType, AlreadyExists, AuthFailed, SessionExpired +from auth_backend.exceptions import ObjectNotFound, IncorrectUserAuthType, AlreadyExists, AuthFailed, SessionExpired from .base import app from auth_backend.base import ResponseModel @@ -11,8 +11,8 @@ async def not_found_handler(req: starlette.requests.Request, exc: ObjectNotFound return JSONResponse(content=ResponseModel(status="Error", message=f"{exc}").json(), status_code=404) -@app.exception_handler(IncorrectAuthType) -async def incorrect_auth_type_handler(req: starlette.requests.Request, exc: IncorrectAuthType): +@app.exception_handler(IncorrectUserAuthType) +async def incorrect_auth_type_handler(req: starlette.requests.Request, exc: IncorrectUserAuthType): return JSONResponse(content=ResponseModel(status="Error", message=f"{exc}").json(), status_code=403) diff --git a/auth_backend/routes/models/__init__.py b/auth_backend/routes/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/auth_backend/routes/models/models.py b/auth_backend/routes/models/models.py new file mode 100644 index 00000000..b1eaa04a --- /dev/null +++ b/auth_backend/routes/models/models.py @@ -0,0 +1,6 @@ +from auth_backend.base import Base + + +class UserInfo(Base): + id: int + email: str | None diff --git a/auth_backend/routes/user_session.py b/auth_backend/routes/user_session.py index 43d67164..424ff11e 100644 --- a/auth_backend/routes/user_session.py +++ b/auth_backend/routes/user_session.py @@ -8,14 +8,13 @@ from auth_backend.exceptions import AuthFailed from auth_backend.exceptions import SessionExpired from auth_backend.models.db import UserSession +from .models.models import UserInfo logout_router = APIRouter(prefix="", tags=["Logout"]) @logout_router.post("/logout", response_model=str) -async def logout(token: str = Header(default=None)) -> JSONResponse: - if not token: - raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) +async def logout(token: str = Header(min_length=1)) -> JSONResponse: session = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: raise AuthFailed(error="Session not found") @@ -26,15 +25,13 @@ async def logout(token: str = Header(default=None)) -> JSONResponse: return JSONResponse(status_code=200, content=ResponseModel(status="Success", message="Logout successful").json()) -@logout_router.post("/me", response_model=ResponseModel) -async def me(token: str = Header(default=None)) -> JSONResponse: +@logout_router.post("/me", response_model=UserInfo) +async def me(token: str = Header(min_length=1)) -> UserInfo: if not token: raise HTTPException(status_code=400, detail=ResponseModel(status="Error", message="Header missing").json()) - session = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() + session: UserSession = db.session.query(UserSession).filter(UserSession.token == token).one_or_none() if not session: - return JSONResponse(status_code=404, content=ResponseModel(status="Error", message="Session not found").json()) + raise HTTPException(status_code=404, detail=ResponseModel(status="Error", message="Session not found").json()) if session.expired: raise SessionExpired(token) - return JSONResponse( - status_code=200, content=ResponseModel(status="Success", message="Session found and exists").json() - ) + return UserInfo(id=session.user_id, email=session.user.auth_methods.email.value) diff --git a/auth_backend/settings.py b/auth_backend/settings.py index 8fa15514..5e050b0d 100644 --- a/auth_backend/settings.py +++ b/auth_backend/settings.py @@ -1,6 +1,6 @@ from functools import lru_cache -from pydantic import BaseSettings, PostgresDsn, HttpUrl +from pydantic import BaseSettings, PostgresDsn class Settings(BaseSettings): @@ -12,7 +12,11 @@ class Settings(BaseSettings): SMTP_HOST: str = 'smtp.gmail.com' SMTP_PORT: int = 587 ENABLED_AUTH_METHODS: list[str] | None - FRONTEND_HOST: HttpUrl = "https://localhost.com" + + MAX_RETRIES: int = 10 + STOP_MAX_DELAY: int = 10000 + WAIT_MIN: int = 1000 + WAIT_MAX: int = 2000 CORS_ALLOW_ORIGINS: list[str] = ['*'] CORS_ALLOW_CREDENTIALS: bool = True diff --git a/auth_backend/templates/image.png b/auth_backend/templates/image.png new file mode 100644 index 0000000000000000000000000000000000000000..bb50cefb9bcc76d9c18806ad5231ef173a87767d GIT binary patch literal 270288 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

RFPWH{3P2TRk zW9Ksc#aqvy2S4n-JTzbBaW@ap;anIm@YgM<3}(==;!mFI=351QdloS$$;5>#D!#X4 z+EJdG@NSv)HG7m9K7e@XC0Ej)zV0pb=}&!;b{u#Jjm%MlI7*Jw7|a=Uj2!rX^JW(x zYi8#_Fdn#Q#(elu95CQs<<+LMMe6}Ohz46W#(hSp*o!Yt)0@@%Oimcx{|uc;W>d)P zEEu}!tkBxdFfYDYsn1$Ev~AMEbXPZ&sV&#;%2-}9-Ou>?sPY_UpMu7)N^NJ!@#4d! zF5G;)iiU}dGA=7hr_p@yAgD`pP(d?#i}o5l3+61qz}JQ&3?m!+XO1A_HI3EP8|X17 zJ&fM;=cm)on{DCvHFKmG-xx;-oFPj~%j0|qEq85fY?vK4pn|!Pl+l(gTj-*TE}~~X z^ORy2AMaN6cxb{XXxOc>|XZ&*k|-I4;4tt=fJD=TATdIdbRN?x&iT_eZEH4~RT3NNe*>_T5vt6g*|tIbZE zqn35JpDhFihspTshBJdnL0brDS?K7`Vq)!H&qJ6Lt`Fl^ProZ zGUy|*gJXl=irTVek>2peSI}?$?n8L2ghiiK^LA5{4jFCdZU&K!`UaJ1dbao za!s5umb{@0KC*+x#$(b12V8Mu>xx4LYU)euL~KN!b(*=k=VG2LW{(|tiMs27anVt1 zB7=D3M7DS^TV-*(bV$|y7}6n$F>2p%B_m+!t+vPHpo zKE|7OS&Y~j_OmOmqsKk=h4k%jU&$l>6$KcN5hin64%RE}~J$1-vz>68XrqH?jEQ?oZLi72k zd9EafOsFB>9B;LihAa=iA#KYDbh6smK{w_dqtP0TxJNo*`xZL$%;(d|k3W%z)O>sv zJ7ln4iVh0Lo7Hu}=J5yHmOt>4x}e`|(lT_jF32Jj2E$mwMcV-E*eSx_buw?0VdHOfxfU^pOt z@Pi-Fo8I&$`iFlwXM9ANEni8ee}#6ajhFa7{R$Q$bM(B)J7u=esi&SwuYUEb>F~o3 zpWr5GtN0asH7(w^#Ju>AUoZUJdEcZbKJkULZx4Srl_#-Ck9l#QFRR0}2D9Q|nbxej z5VSI(b%w}xU4F0`_p@6+5u_9p3;=!!I1fO3}oXE$t*3c(<{$-0X^Z#zfB8E8~o0beY-}E!Lqw8 z(ul_6fwg{nNuH$)NuA^2f6CrQ?NjZ1)aLphl3 z=t~&L+HLyA$-^U?P2;HnAN;{76JddzSuQk-QLZdGZ2D}}v(b}Ox-3!tA;+Ag@AI$N zx7Hc@xX)Hc6jA}i%`Do3QlQRwQg%K_94T3L-q^`vgrgtp&a2!D0aNWeWShOv&I%c_ zIk8Etir5-(k$c3O-T?KnS9qWfMfFJQS?9frU7fsK+Pci#xZydDfG(%HaGM8*3{b5L zzj%qw{?inWQ_WYu7mACQpasI#SvX@b$G}_*4jJy@_{e0;L2%Ud#%Lei>z=ovxBlg8 z=uUUO72!1HMLz1o4kv-%6sR0#dN}^geo6a*Uo-bn^E^Cb{`i%ze1)F(yyww(zVjVE znrnTiCkkE+C4k+wZ3ocFC!a)r_ji9AJHAZU1;JWnFL%ql4Sk-kU}wx9pZQnxmcRH1 zF0%tU9BT~rB||%R7w4bPZiU^l@Q@B2bq#}dJY&$O zFR6$4mGnBk*u6|R#uE}gddyxN&Tk4$Uzpt3f#@P*jvX_LJo#B(+Co3S@>=@yH~lUB z%ZEQn`&Ty*%H@IS5buIv#|^$MgFMDj0n)6ab-bY9w9$!7)7;&V&>JgmI{j=|>wt>x zrF?{3wdEvKyZP(t9+R@@u1>ZO?`3Du-l)8Rdht25K%JLnp7h|Km9K9ykcfL8L58%c z2*j`s*=8!0{w0X%?TC-I)HvRIg-5(!byS+U$E5O`Ib|zUs7~Bw!68#8J{1;Ykd(+@ z-B>!vs&s_NY6y9UMf26BMSuzzc20!3A*M|hFT2wP&x?zD=@BQ~kKX?FGic|*+wBW- zJePz+#!ejZtr*bKa}QvupChhGcjAOW8rT-*j+pNu953g6-~%6^7ru~pUhLav$Y58_ za773JGyI_pv0LQ{Pk17i+uQkg@)d)jV2SxL@WgYKmz`c%N8MFFy`D~f%uDH#?_I$* zw1xSt@~=H!jjhVuXXP#JOspK1J_&&wJ?n^UtTW;Oje{fP6~d~c|{h4;U1U1HvyIY z1HB{NqsfR&N~Bg}z&u{c-dwhWIgWzs1;?Cy?SgO6*&p~Aec^Nem#({hA8%;dHkKP{ ztP95Oh>2z*_J}f2fC$2`I^{%ip{TW#up+-wE=BV8E{_?FMz+{H`Nsl}E!Qi4R_BN} zbIpOi$-rFMU#jou(=b<(sN@ zmldDxu)a)x{my@;vtIi*w6+GP+S<58P(Rrb1p5&)amq^CmZGvrzFFx*9C&*9I3wM8 zFR`C@q1$)J1RA>zrRWc|fl_!(#{7cA(QO9Jk5oaR}eLR$(H0!Cz83@P=XTx~- z%0)VLLEo1Kac(+E-^gzQt|55<`alK0^U4L^kUBF0Xp!a%X>Ds+VcMAVZ&`D+nNDhu$lyLp_-ygR63b2VV8u=l`se6xy0sP zy46t!(V4G)0iF2B`_tBKOT0_p&Qr|X26GU8#+)l|AJyRx+IT7Ni(mXAz5L~`qzf*aha3jEtbL3)PUh*;ob!(+{e8$Zz5efNNI0 zeEP(~co)nj50Y+u^kMYjkGz8pI&>#3tzZz%Z>*qR^We$65ryHB8OQ|$iV^ic$RJRD zaU9u`p7bR8H>gwubKnh7^;HUZcH7FV?4lHRKFepU@z#ro|9*NjNBk+~<9Lwc{ zt^XLHnvaX}v1c2+m3)!Q2l+qx&tIa~yylJkeVL!r!8;D)H;s@RZ$NPw^0$Am%gJ10 zhYWC&E6J{-R7OI{QroYVt2QQe6AtXUoC)0+JKr=2lc+9U?%;k_%Aku_L#9N0x!;(# zD-n+X7}7fgqB|BG^-*0A40tE?XJuq8QkG@-&0^l77|H@&Uogq5r=+jTcI(7VWY;{p zsz)?-L0>d~{gE!C?DtWRB)eiZHgJUXh;DzoThKfI>QCst_q!vluI=MF6wgQag)uva zLtAX;RhY`*9{KI1`;GDZC%ZtVjmJC&hxzZy$`XC-V;?gwF=BoUY$d*`I=#5KY>p*c;uGvP*741l6`sc~(udCZ zEFY8pM|9u;hw(fY^ZsQnLpCS;Q_%%os?wt(kp?mj0`8p6i5?yY1ZqQ|e-4?zVpDAh zb(}C7c<&Vm_NL7fsK=0bdFZ@(c=kB0DWAMJHK4Cs6saP*88XR;OfF?1q{I217wiu? zY&-qw>t99>d-!oYrsg+{uv-{T0ethsIAqvS!v8JscS|<$VG-UvW8Z?xB#cRf^1#3k zzc`!r>tFvmop#!3=9@86mt-)F3~)XmTn14(jsITjWhVm|e6GPm!PkKY9!L*+*u&^| ze&=`0?wMQP`qunmLwnqp4E%(b$xz8Zz#M-vu#{gA$HYX+1kaEk?+vj-itqK7f1KUC zz`9sGIF(TqgJ~`i9^_v3LV-B2{YNO7Yeq4D_YoqwdRnoM%jEaVQ^nb8Pq6umq}NO zT^>8`GAJpsmBmc=4k@fU5Um&1+7(A|*ZsF{4U4u?LbqM*`pdlv{JQFpsY18Dl`M4D zbqw-r9Nf6p_4AGa&>F;B1jw!RXkG5k2!NNsurnWv=O}CbUd!?Z-T(e~rPsXn1$6g& z+?Lk(yB~OI3>RjISaiow6mu%zihMS|%y;mM)>*gE$ery%IpOcxYpLx3 z{4g)w6FKwMxXzWZ}Cf%2lDPK7@NDZDxx#o%_agU_Td^3Xaj+T zV{d+fzeh4g>5mEun%9yG8GfNmL6N-?In{Q?RbG;117icVyz*sb-T<^&MQJQuMl8+Z zu~X1`0d6v`V+>`1(8On?-b&Q$!vdP zNx5uI8V*ExaGv$mP5SzOT}=OY_P^05KK=!sq>NbCZG29zni!b^)r6o6lL85IBQkK< z#4zJx6nPR{i{$NMo@kQiNGHYX1P_n-5ztne1=d)f!0fW*$uCuRbD%v6U8dT{B3-m% zk)#S4!p#NLI%-E{Rl3+737FPwo+ zlC(lg(htAc5pc&rX;=pP&LGRAk_%?4B_d6?T9~#>RO_6juC*g>WFxv=4$Bgy52u;c z80jm3!59Y~AeiH+d0vPxdt|bG0Spra{I|3;;$z81{1Ny^@zGXKpj#YyGj?%cZUK6j zkMJMgE-qMyj9F|qyJ9pyU@F*4IKuoFY){*qEa-pZ8yC`RU;A3KqXRjy69#m3TNK@A z=jWGVwn8IZ7%?6*3dR^d;u-VecfIRfblck=Yv%6I9%*DAUk(jj7FeIa8iQRU_|oSu zrpG`2Mg09(gs!kt#;#;!;U`p)Hr=YSP#n_u5D=W}!Z`0Twok?(Y(St00t?3+dcr0e zK<_nvr|3si6B`l2fd3y4|?Z4fC^%WINJ4?+vcl?RlXbjTw@D`_*7cdQ&*5wZrR zIm_GZRLLa<7~qcw%NQv01se7UGCbf|Fh25%H^1!Nb0a`!{suGfZ>o&lwieEt9Xk%BV~#n7PCfNh zo@hLQjymcnv!e!%BfQCjJotm`c*X^6a6xY1CHk7c*gx#$FpWnBLyzUl_kcl+!P-oI z7HM_gh|d4wH|TX|y^SvZ=J$DL&>?nL6Rd${$6IVhCzGBB!@QGbgFmMD|JeHuc)5z| z|Fdsj$$KQBgeFmn_*W2-jub_yq5dc;L?9AsNJ1~c2qBPwv=AV)KqyioNJkM+5CIhg zMXI2P(o{O6C$H?j|M||`vpakD-rak5@9y5aJM%vK?!8maoH=u5=A1cGRNWg{gM*@Q z^{U?}a19DW?Gg(b*~Y7g0^mRV(0a&>r!3&OY6Q=%SY4DyJF6Iz>NF5b&I=?nZ~3fyvVYmm|EabNb#xBU zl+!2j(NcR+S2v6`1FT1|j)YSah>tN#@lVaDMmJuV{XBFs=#P&)@>@Fj)zzi^aP&8hF*9{wi65>gro1BW8Rj)!49PoX zjyz&I-GATXoM$()65|tahCa{5<#mN{_}bV)fqKw5V08;h_Gu{)83mL<#ur)~4KlvS zHZB0GI-+!*!klPxQD$Vqina(Q%Jjpyg0pwIJiN)%Q!x{Kj>jgmoBB-Z@--{pZ_r}T)0r_EG7h7_a-$e zdl%ikr>C2D$;?)|upIdYWVuUG_i~xtvuAqm0^%PB`dQ^2ILq>7r>)*vI4wNEb!gzs;>pm_V0G022u^%mviY7H`hDmY|S zcvp7`U$367MD(T!V!C?KUj}=W(;Y6~W}L*)LrbxN(GI?GSA!HIj2Lp{TZMAo@E_!R zV%Aw_6`FbJS@iWyzQm_qVY&`GXY$6BzzF(8lsbYyhckXJyX-Q$;DQU3FozW}5Vqi8 z45+u$xD_^R_<_G*j2Sg*6z#d^p0w?@+tNxat*BOuAxz3tZ+!jp<1(=Egi%w*m`n!B z3zvg0i&4rH)AohGzD~E^d>{SlSC7-8#RJsY!T08He4Oj79bclTju}8bpp4G31!`hf zI;TQf2|m&u^(sb$9Q_?cgeyzoCCO#4 zC+^^kg=A>Hm@9YO90uR<1QrH#)U7)Rxw%%$3nlg0)ZMxZ)aF!GzFv;a)fQ%Xw zHLn0}bWV5>I_aBlzNzktx$e5__}FWGv^4zTgfHY79R{rh$!za;a?q{HDBTM4)B+D@B{Cp>BA4^(Zr*t({CSpfzoZAtfL}EdA>4Z2vnrBE;0snZ_6B}M|R#|p%8J%<9 z3H1HZyYn_=j+54Om!r9Y)Kw|`QHLp`Lk%6B}6C+dG-& zsCgVEfG7KNl`mSjjQ;wU*XfGOZ=z@Z#KLa6i@*w`cGU3NB)&WdWoyIE7{?%!;T<(O zVc2KKrb)BP@o#L(Tp5avQegtYa#iH-!k2j}=b+Rk!0Fz>z&pk`1J_bi)uo7{ETwV@ zN1&)6<#;np_9j1uNoV7hqyWw|heMq-yNpsu2tDyt=vSXFlmElg+)P^^Y+EB2Gy^fJ z#H}sZISD$<9L6Gqkbd^Pt+imwvy6djH_-OB;O6HL`cvePl@Cf)!FZ$C|#?j!& zlkb=18Tq+4|C)|F{wLJYHk9*#ZeG4F%q}26^zs00j3Nq{G&TdHq_;K5n3Qeodu8pr zu{c+gdr5A zDHSJJm@!}lN)VoB%(9wU1vCW7oU6i)fNjaW9Rbl z9ahDVkc(+wLQz0QJ#9DF6~viBK*W@d^Ii@7aFNc3QkFWie4^d zz;|b7JDq#(xin$I1cyI#{esrAkWu0Xdm=sa3>N(<1D=M5x=gq8Leb0sefn{RzO&u_ z^xyw3;RS+S%$sJNdv^||Z$5S~jBKo66fkLQrYJ>kYhW=c)wp-F-)tNf@y^*AWZd=R z*W%sr?04)a;M?F`y*1*AQtelWQgI}oaxeh96db?2F(nwvada+Gs3f(I4192872gd~ zK!5(GKH7M*b?K)!UrNJP?9hUd!arXIp&Mu{^!CHqFAmZzSg?R*&6=eII=uo$BZD-O zt{i;7e8c)nenHYnTzSgqwAA&1b8JMKtpuDPb7kI)OS*C!quhE=qZl~Wr@n_yW(?{*>rxY}&eJcE!v$LGO68;w2 zMN>8(CbPg0=PysP>qR>QVxQMoZf}aUB3>Bj*SD0LZ zZNASmaWD1mE^8q#mP(lqQ*8^MUcBbTlli2Pyuly?N&$~(re7jKD-;mmuMA5bsl56w z)gOA$*3aJqY)riLI1v+MszRfZwi6`7nUfr>+$rM2=_-0SfKV z=b5gaZ9s59{wiNs1w36Lj%F0W--Km`+jmfa$8C(=Ebq#3(P|t=<7R!Ky&a3wme6Xe z4Wna^KbXcHeE_Ez;B?r1kHu?%fjZz9l@P`lOs?|--%1+ruOq|=;!u~LefF6;QQ^W1 zFQWPL=X3sg-aw2lQr`m4*1J%mx~H42&?T`L@uxq%iBF4IPsx19K^8K)W z74i!hNb9UPs_fz!x6;{DujL(5!<3;7`6(V?W>c)@%E;t8uW(m%`&l6cOgamQQuMA= zwHDa+=}iIK-+9xe8sCxznQ8#XCaqWNjSX@ZkxD5Lv@?*2vY_!R#V5*iyhbP-xLEQy zkm4U!G(n1nz|G&-2?JsKirHt-Om0TFBNjTIRr5X$9STC&DAydUbbu* zU3%%IG-JjL^*%5F;XRhb@~m$umjvbo&nzvs+;U1t#W7~1M~|j8)>uRBszJGB)sUoj z(+2)RH-R1P6Lrl$Wrz`aYn}!!DvY;*qirr;)JKm!`bWC%+Mm<2e|nXAx`(k5uY*@9 zWw;!A+j@q1MweZ~l5zoO^Bv#ycDN@el*3UBZm0O(?aAL)2_d*(r&L+AD-yzAzR`>TWps##+4VrrPakRsZTd;1(>LVXztsRal!#hTQ=?CyF z@BCWuj_V;<>ObtQEdK3|aR?=uB&#M_vupl);5RG`yVXg068iT zk(dL)FPs1n#F<=Z`b)TdHiiNX78x2-=o;i|`@9+u)dezn1{t&0a}~yZwhjgSI){?R z46jkri=VvWIi%G?aES;JR6#xNcZmSaG9Vz}@R3_;#YvV0C>FMPg;9I^0G)iwVRY2Q z19)G0Kg&VwEZp$DA-F>(3qi@NLcFsA4~P-Q6wYvjS7&D@U4Q-cbo}urC__bm|9}$2 z{rJW^hj{vGLxtwS_V#w(CG!P3`Q(%6u)_{h=N_x$!~%Cabr0#LzCPv)7K~x^(5vZitbt!X z0=M?h0v_@+rbK~;Q&&tRe`pfNj8Q7S884!DnFQxriU?xuz!V7Q4xLI+w*V3E(s}z9 zBy7+}xpGOpH3+??%NF!5M{~bI7xXE9krM0g?)&3gkmjz3_|o0PtgsFmZ-f!t3h@$O z`%E$geCyO2WFo6plg5w{@tx+KFazp{VwOT!C~N1VqWbz5(Iy+OO}E^7Ijy!v56k>m z%f}Pp7*yL>SHz3DQxd4L9-tOC@z|m!%S}1VUq^tx1ib=J=tOX`fDOhN_&@*r^E776 z7<%K4e{raTFP}_+a8u7ziS56dQ$Tdq;lqd1lTSWQ8*H$FGRjE3Rfd2VSil2{!4zL= zo+AZ+NLLS?c>FAybM2kX7d?tkk(cJ@VwuL|z)I!7%F1F>m7+j_&Q~g;4a-r$?6Y&8 z_NC?&um+i$lh|fZ2?Z+a5MVAbheeN^-6N$}baxyB406x|xZxS`YqJ<+^hCVAppj1m zR%F{^tM%#1tIwttS01X3Fpz*)u*T$k8yjbIn(zb_eu40Y)lc`|e?J|1=%Lix+sEJ9 za0CBlaU!J36AH@LzrGQjaKiDl(@s0lh!G=HQ4x-x-fk`TvS=#EDzRv2;-;+#ynfJe zCNz{WZcm(aFq@`!R(hWLQA5?y@}RXixoalCzt64%)SWweS&eTnYw z0fXf3?qB3v@n;YKv5$lH5zjW_MLMCsSD5vy4+Zq_rF&+*Bc`$Z1D>ddtWh88+FSy) zachtX#3(XOdC}C*4{5U5ezhY85*T(Gw0C4_k3GIg7hZTOtvG@wzxh~e-1iBUP3Iwc zw@ie~H_$}?0DtKx5LP@I3qGO?VM1SwGSDgD|G)ze&}pZgMsL0KmdaODMhTaG0N>Wt zj{>M0xb3(94&8h2J!;{hx!^E7CNT6dzoZ;PA8CCR>J;W86weKI(vwfTL_ZvJ3ZLYF z9C-04*E_;+P_Fy9S>Zm!)T0;PtZOs{tR57+2BeP+p2EhAk^+r2$e2A?l%=w;5>0`8 zrx3uvlIW5)Gx9J5I6Gw67?Vl$@iAK+bo1QHY17TW#NslGEUNuOu!Z=7kIFj)@QeuB z4?Xk{O`JH9KKS4xHDRCUC(cQ0`-vsIR!$1hH~oeT8A4lc{SErw_r6E_?YAGTxZ;XA z8h}1hemu=|g%GR#a_Uly6>24$tV9Cr;(FG2K{@N7k3X7658VHIy6MKd=->bPFV}0Q zBjIYez#H-LGs{#h))3GW&K^~hASeJR(ojqA6c=ZMj0okL&jjOe&p4JZPyKvZ%sR>Y z+PAP2sO6MPHGCMh9F2)D6K9Rdy;QDVw3o5aE36bO8x|b}g!XYT!bqtEC-=ATi}>4T zoGHL^BW7A1=dM%nx6fb{FnQliOZT@MqL$tDB0U&QvaB1nw+--dRD55|`0vq(RhH)@ z&=q8YT=YX;tcRU2N|%72j4zOY5Fg*BU$JC_yv48rJn_@`=(|Q|&z?N_q+S$j3bp{AiRS6iQrJzSonv!1wW-|pg%>Q zd_0eiIP6UN)1Tg8f6bGcA0$1}YM1X}9CcL!>@zY7h#nUiKno~G0qN&*9`>c~6iBT- zVpQ?SC207Cm_3igt=H2Jr>OF8WZ9xB1K@&E?d`nY zDYJmi`N^sD!^8IFdt>;V)T~geNXe#I7BzILh#oSHiJUrR$&LBP`oNZ~p5Yntj!6bovClPUgqa08xrsChlJG zgaf_8%K2$7W{S9g_$deyO}M`t{IKCUzE>IKb16rIu=%udeCfraq(QFUq68W%Sm4Dx zKNh2gaYA*p#X)uOX@2_Gfrt@h;a3-NFv$4hrh9ggKO!Q91yfTBaDU_@r}@=$P0?vm z;6Z2!@mKF`;EC{5FD*a3o$vUZLc8w1C3ST}57Y~{^by6FfalRd$;=oxWL&|>EE1OJ z9S9SDiN{_pCpb#`#W7?be)u6xo;;a%Ufip6B*ek)9El6vQjUBE4qAk-g>>LT)_xXH zKo;NaxZ@6V%Plvn#fDOk1rf`9@T>t>BhNdiiWEKqpQV^LQP+H02kWLd(Qc6S%^4To zN;59LjpO4^Q^bd6Ymjl*M6>jx`EkZw zLhtW7ms^A#Z)C(bm_Y_xp2fMhk&3^4Mn?fQYbaoR5->kOdmrHxJjlXqmX^>CJ8eO; zub-|2>^459x2=s=M-5`TFoYXS%=4Rjwv$6kIS6C2;t2vX&IaCh-+k%v$DdICYKIQT z0j~w)w^F&qCyd2LMuv&&(90~F2&mZM4VvIC9UNhH=9y>G*S_{Or3|4Y5N@M&COqK> zFaY?&9R`C;8{>c@3G^0lX}4@k;xJw}*Pr2)bDbw|43&~y@=lX-)Gd(-o<>Q zl_I4yK|szo|MEpD*tG-vke?)DKw%|5qwf(YC7=1sN=9o`pgEzBmYF1H;%?%t+DF)W zG6C>AD3S8u4G%AsQdp`M6g1nrU;Zxi3qDb=ezYoxn6Qti%O$;7P%@bHVy2Nk*&U8= z2{Yp-``zs8y-I>`v<_(@UU6#Y=dOd=#DCek*MKHiCBU2~xp7 z3Ai_cUgh#EMK{paKb%Kt)?3=r8CvrT%hQaRQ)q`BzfMb+E#-xKdTh`~iQx#byip{N z4veE{bxMOeeizx*#z-cEFt@3T3Nu_|U(-gMzwn6ZP(9*#o2n1qGs*5n9 zg&Q^H$Np_R!@lxr-E`R%Q)!E>H(+UocS0zG0t8w~qrmaM@Y9zf{Lh;=k4`-CM7r&^ zpDSR+gAMRPe(pR~^rrYHw2;E!TMMgM8a8YgjUGK({kGU*3v~uBXe6tSBn|v@8u4k& z6ABX)q-=(Ta9*|a?mM5*b=Uli?!WI5`k1$occyz-hDM!&|9ERY%VUVI~AaVux__{6dHyi3)*b>LmzIQU}m1zg-|A9wts zs7ooMSN{|R{CdC;>BMiW6!AsMs%1J!RSyiCKaU)3Wrj4T|Djg$w1!?NarLg3L6+&4 z(ze@fKvSlUr_HzgDxa9pr|yr;8)Btq<(Y@)O<9CXTpa^>3XkI{I3N_dMSp)k{rcCx zrm0h>(%=975BAk@%y{l}8nXT0C<>VMdB6b&&^6aw&3bGX>#%u@%zDrJ`n?tMDq@-G zwGNu5kKSKO`|fijz53b*eD7(G(lcR{!O>>eWrKIkf6ys)USiPGU-9^FwxYt;;*Fmf z-#bQm-*Wj#_fZ~_jZrBD%s#DB7&cvfC}0gT^`Udq=VCT`HA&y}C2q9>iqyl#7f1Nj zt3YJf#?Y$;lN7I1Qo<}xhWGa`rU}Q5p%YFSOC4PtuE5}*IW32@l0GI`n=$dR<$ZoAQh2@`1j_19Ol6U76x#0n(P z6MiB9*P9l|7s4={yrC#KCp+EicCEh{9Mj7H=H2jxQMnR;|J8py* z;X^zjl&^Wb0XCk@6Us;fesuVQY0#_=llnn%f&*Dk^K{lrcft)z&-W$4hMbo%M1)5M7rS;!os zj(bCxmrx4qMNkdW{;MsmtV}{)f{)IZ_g1xaP@09|%PyHqmtK4=W%@gL7giVZ9ACo7 zg1J8OOw#d@2bc_k@T_DnCD})0gT|J@kLHcVs&#Xh`j8zm zaifIb7dO0WiI2!}$iuJve*R|KTH;tWY(0b;<%5UgSvBz11mMRLni`P2@PqQ_$S*9r zw|8K1S0C#hgVfoProMqCe5d6YnsnL^l^&SllNQ?2Ea|YwolbXheF2)t-|`GUw&18{ zEIJxQ{`3Afnee}^ZYu2T? zdw;|LBXLUeY;i%|=`{cF{084A^vu(Lr(JeAoR0usffxVb=&~#ypUwKRGRov(DHhIu z#_G+B!yApSHONHAB4PPGx|}Vfs3>3!GEq^o1>gj2@YW#H%*=q@2AI;tf4&0-tDn>k z1YF1nAzF6Fuu$BQrE9PM34Ld$&3VTI3%$;rAzE+}fg7}fSb$$};TyaNf$88cPCof$+Gd+=XvBySid^s)`obe< zyN{CAB7Emyq72MmYUd2IN;=g^e|qXAy6ocX=(*?qLCconXtOTd)yViV?=dU!)r|bX z2|)ljXnboDg?n2i%;L^%C z!0;01x`;Dw_2L`pdrHy5FHF+b3)isuc#WqT2FBvb#`Lf_BqcuT(yyLT1__fom+zwT z$&9MV)hdD>KeD_qk5vtZ)k`a^)Wyb_v2^57-=~gFZHQ4(IX$Z)Y#8C=uOXi+9a3~d zz(Ku*8^z7C;T^gTU_)1&Jb5x7RrZL|ec%Uu0&$dHp%AkF;z|M0O9j7`S6-Q(efC*e zV~y377wS{^RpM0p6qvnBQ?&iJ52e@s@gFuO^5Rmy_ZEgkch7>Ovo$ip!_{KopFXM{ zEnMsJ$0ulrbQwY8+j!+E06w)Y`4q4QndH}D)g{o4Sx>P87~X<8GhTjpz%-k6%}D`$ z|BL1k7-UjBc|S0KRV!GLgxv=aa671<&j}uV@LqJ`#V4tE?9fQ@CGF}`yfz3c!aMQ= zy@K-%cy_^p1@yxo{*Zq2o8Pd&t(6G5C?3$PV~QFR!_KSn)8n@mV7t4!Y3H4Hrb7-n zghq`T#Y$AVKtT~0^`a;8GX)I&4e1xWqI)L@!&tfW>BkG{7xz6zKfV4A`u7{}QCG(> zR=_lWseVWlMUeu92qM5JmILzy=e>H-!7m@Li3dN!h98VL_+5VcXrsC^+f21Y=PnGP!sHs<9UKxA6bS@1hP9ECbszp*jQzi(-#p| z-k}VD4`U4Q$9*x=rcL98doQWHq^t-Nql~-U<#Vwy>PZ3NL(?#G!3F2@-LWUAdPeTM^*Eqr$uUT<+ejr^jf0u&R{9zDO%~fEa4+LB| z2)KC1F=jjMv=g0w{&}?a+H0x1WF(%%Z3LwR2S~q|3`()_PuDN=4$3juL2tbNE?scm zY`X8>M`_q{E3v;5M=5g-*0@~QDKpY(MGyQ3!J2ll?{KZx z&>-V3UnQkNokjttlDw-)gSr&C0Ww6mp1qo!(L?B;K#{t}ci28G-|uK>@@>L2^QjiT zz^d;v{{0^4;YIWM(#htg>j8C9&lX)B-7Vi#N5A-_&i7Nkb?UGs`eENV@hMQOFeyhb zf0~N?Z1f4dFfZMnqGikG)0SJVPctu_OzUs3j*_?2kOx@qg80oIiU!D=RS@>W(P83v z_qvkFIZg`x!6ifuPa9)8J3Hz6>#wKNPCJbjE?lIHF~~vUSYwRPJ2_A2t8l10obd4A zgAdTKVZ)RjTaJ&zlv49N-wVSAnU3~udh|E1(D(Kq&-wOHHiJ12)|4Jzen*UB-n zYGNBwa~n@z!x)39LJaW`u=&Zt^dREkl@E(L?u4;4@x+7p9N_^r*znf%bUUB*s*lUk zf;WUJhywV@(NX@wj3@+RRJixvd+FF?kEhQ*`%DSfetgZqtqVW_5sV=0uC&rhtiYT~ z2OMw!t-9)}ioO70u1pF5GV&-`cqalMct-Od1allw#vGYR)6F;DO|!4OmEL=QA+utq zQvPss0w#vxlpTgS82rNP_c)wcR;_e$RyS!)F>>6i0T^^rzP?l6;YuWB5FB;O$N$DT z!KE4uyh6;@dk|@JC^?Pm%mCd^R;)nTm<_PhytK5 zex02iwDs0ot0Up|*kgBEVFkX-5QjapL%6B@1OgmEq!j8{@f&zW|1`(IK*)}F#lT|o z>MQ@IIoHmm2kv{E`ubULW@Qlzmw>Z2=wW)8Qz&H+kXQ-iG7Yj4R}bf&lvcP@kTKv? znzn-MN+$6tliN=NP&LJ`-k~Kki8wF{+PQn3#+NE$B2@?ubdj2_5Jcc)W*wOCegN!P zAvGgUKT6fs-_}EI>01su=#R17&v}Vd+Rs0YAFO)$w=z#j*-O=Fz3L&Fe1y;BsiCSd zRyz8^plGsf0w~}C;RmxgUVdhHkav^6pcABVVWZj^fIJ6i*zgYe;URnR2?mGK@a4OB zw=7RwE7=?U4M$aV=Tw}M!6A3U6MpjJSLG)ajqgGSJRr{tK2kP(zxn2ybndz5(mnUw zLraz{;W&`}{b3bCwqJ3ifLA?tbac>=EK7&JC;fLzWlA?kBLF()4qa%-) zMh`vwJhNsOgXO!BU`WgvWH?X$YZikHn^mhPT?7!M-Xj2&C=k){Z=#Cb(#WRPTxnO| zR)-^rTG=JBBalXHcf7PR=SE+4ZIA(6p5V?M@5EgPA5(_;Y~uT2Ab9uo&Zo<-IE(f> zU1+&$lV3Zz(Xyc=tC*BRgXtfE{(zQFQCAx3YjV5TVc&J1giC51us`1DXe)Z}d3|;)g?ilbSdt$-}M#2U2 z7ts^HeV)!g?@D^>?f0pxeHb6X#yhlf$DJ9Nb9t5Xnt?K+dA-(h0zL|ZffvrT@GD0L z)KuJDF#6^($6Nj@Zl!50e5CHcckX&I`?6A0k$C9qyqtt1%rA~ty7>F)NsC(JY0|IO zd0F_CQNX{BFqV3^i^_7UZ7TnA)HdHJp_^qe`x`(1DD#ddEZtA3@+XHC8%j#>jma;9 zjF+E;Rrh2=2P=4OX-q_CsCzl0(|Xta#te6IB;Z&3CZrh37ZUKletOdC6@IvPUi8(*_yjpm`Zwn-qggxjrF| z!++#;i>ix}D_18mLe-*D0}7Nh$kZUeM1qv#Zsc5acOlxGmDXT%9SQw~e~GI_ZQHj;E7O zI*FEBZaEbnap1hthr^)K*kC4oX(x7IDc)ip$z)&%`Xz^U7E?C`vOI1k z)ioRcndLG2BtQS8)>{y~QD)o~2xpc*NLm{wPXRC5MJca~4v@+A(sDfmH2a!!X^%a( z)-p8@RnXfYi(@x|+D*fE%P8kea_bN1f^Y~E;|ruOUdg8nGB`n@*hx81Mk(jR4?oQM z;i0r>(PEWA^u~b!7=o;81`0^M;|cy-V~y46{`>Ez_10TY%@Ig=RAt4&gf^bv$gnZ- zfnWTdCQLk^mM(@I#=MFhTE;wv))}J8xilQN4put04wZHiByQZ}pq`t-HB+55Lwz)| z4AvkM0rjOruC{{+GLQsqyw)HSLB(1SISOQz(UAv0B}ij(5`svE=eKzpl!cWn-yyU7 z3hi{=jpx%ATdl{F%$O+0HgUaU0V|{0Qk^U~>p^r-oeK;>+#FQR_gvOFVf&R=UZFkr z+><{1@I&P%u<|kOTQw9g`yU9p=xgAIAMR;^{Ikg>o6y9G6KRh<_MqXzhpRBj6Y*;6 z0N)gOH6LPUjNp!=^Y91U_1!XEw0L13-Ff>%^wS&eq_^JupW;KTy5lI0%cmC~dCG(@ zVAjeZk6Oa77H^?}2`5K7!&CUh@T%);lr+qjFR$#Y22j@$wN7SvdIu}B89%5@6by3Q zW5vTD(^@o-J5N-GAL>xcP_-M|D#NUNswD8A;21 zD5gR~HxeQzyZy_3d!^Ij{s0@!2594r{vS=7emrftHS2!R_1Flb`5Afy?v&BTmf?O; z<??+aKjCB;e{8{d+&XqV!CPI{`LT8y%InH z;YH0~#K&v5)0J0VNrxSF7@yF=3qhTQg3%QP{K&_a@j|~>UVe*yIA$`v|Nc@M=)*lW z{8PKWz^lyb>bMZ(Y)Yv3j@BvY9>ccf@nSj)pA?5Q-pAHgX+LLe=xX<(FTcw%vAHI_|jR_&Bp~srr!kc;Z)W z|K(LI)u$=LAmgVbQp7TO2;bl*hmYErPq2cC`F4R)Hb7%lQ z4*X)I@`=^D-qN1@Xp}MO91}80A0~ZUJkatUBcsGk${i(43#+jd@T=3L8D#v*(OBvh z=I+KZUj0WQu5tNt(@cwBweRcVQvRs$5IUMNxbUBZmCg(DPO+R*3=cX&mgVRLwEOPg zq$yL6p)Y;;bFBAqYBs(|p2#b=FpsMfh8Zohcksih-`wIn+Q>MxQcSotNkea4*vn#Ts!9Y*`_zrQ-heA%*PYL`s3xPup= z(+<$bA1|eY4m^qPuYHq!dK3?*sE@;$cd^J1`lFI%4ZR^+WE3<&_mib-kX_S&H2bAS z7RvTTjii2j^R@<=_|{GHVP=j|#WG*z<*gN5FO1B*+~HA{$ql>2cc~-wB+Q+h4;fQl z_z$!(K*+laz{EQIop1=xh=Vyv?0!k}3Me_k54M+K!@KCJYtQ1{F<&Lzp{@mZFM>e0 z22}jKoleX6sQlRE)80J1Em#?+l_#k~U?T4#gy6fjPT=Fn1Lh;>b*$*6QfXe*G)NEJ|2sPMlylg~H%QA3 z8K(JI84a;Dy+fT9j1&qA3^mMW9D;(tn^_kg#=G`S?0;ovEr&f9xtLY@V#6;%juPW()~)+UD&copW#lYnzY+9k>6e%UY~;a(Y8LhCpQ0TqV@|I#_~Y@C}56LUh-L`nA8fcA3kP&5+};?2+SHjGX}nXgGIrB z#k&X8H-vFco59Wlp5Vs(dvEVT+JE$JbpC~tsC@_~!67U|RuO?aSU~_ExZr2LfAYyE zwD;b7(+e-Wz{0qO<%e^yoNTO0DS&#Bx{>GRn{Q52rkqY&ZM9YI7&ECupApHn)1m2M_1@xSqND(Y2m9*u0{r047tLscxI8EYa6?@*sNFXGlFEd8Mu% zV?Q~}lb#i4Ck7cDCFV$8j&P;(!D1{d#9PX8ekYAP`T&|dd2H^!7-S2vSnq-nBrsA( zGtRlo`qE1;@h<%n=-FqVE$H*y@dOk5Y(5I0UC08##~yo(w%KMYrDuv?6eU&$85U|Y zyb}g`;+$*lq7zPqw5@t4yx5ycUFM?xnl$okJUK zx-L&~@)^N=RFqaADicb81qL&~zz-M6Uv<@0j?mp)6AmRP9?KxN5F>Te-Q7j|?YAEt zal{d{<(6CKRz1PXy-tDnH;g@6&~J-#w4eb=TZMZ@%?0b)<(V9>cCO z)y(+W#*+mZb$C!63rO6X2MU=}} z+GJNb5&Y^U2t;d;2|}gfIMvX+7+P!k$QT$0TDozN9WqI-8F!hITo#LaAq70^)*56A ziDZ1CukoUpm!FZW@>ebRa(YL;QU)39I?=~aXEJ>>V#IR10BZv6_r32>Pfw?cDt)5P zxOSs67Ik4K{#|$7MdzP?A-(a&8!AiLHG_|u(MA`}{*M_2u-FfEK6&zFI_s=6`N%pP zryg8CC|ev>6mTL)UuG#khtSi1e3f<@br^MbuHq~Zb@WU0W4R;w!sl=Kwrou_BtlSL z@~S`a3kstl1+#(<>Q~)aG05s+@WlydZ7&+zsKnEyOY&^ zFH8z(rRUyr6(+L3!n<&Y+d)2lo8o}{6{w!){*yZsd-2;!^q;qrdgs<1O}mKNlu!pB z3aFoV7BLyo#MpJ57BF3yJ^Z*|#IRp#j0oUcQxGK~X^w3x5oG?Ui63^Ur%N2@YF3(3 zBSKewjHs8C$o-e)f%FUOubj~vQa_Ai3RVS8aIl7HQuK$dy(JS4w)>P?hYi}2H=@Wg z>O(lOJ|YVk%yH>P9844wGCb_SPqs;>{X)YMd|L9BE?fw)^we5pa)~0I__ghf%I+BV zhY98L*or$`p9q)tysHyMAW_b`TMNpfzd|q)y5?n!$59B_SHEm5Tc0sN6@A8`VXrFO zyd2~WGbNa5TX#h@>h~1!-zC46Ai2}V0Hbk?Aq!Qz4Y7Yy;cA@6kUKC*??vRE z<~4Xg?YO=sw%I8zOEn8D%u@2I&{T0uP^l}r3WN&A?t?Jly$=#mynqNB>El>GWWQNq z?6Sz{2s~OX6+_RH$5K&kwNgZKk1d8&`DC`%bo#N}*Z#&y^1WR}4rQ~gZXKbPdsHi@ zH`UX*Dik~J7(V(X>V}z!xY{^SjDt3}DIXrm7^P=H1AKlQL2&SuF=!lqw-;79{#kYH zc0{ke^)+(c{*x8Wurge&jmcq&jaIJ;OIE#i3?s>5~oYO*-zgCO&2O1hP_26x*#29VJeo9qYx>N31nZjb5dfXQc4@mq7{bHk6`FSo z1{)PJvGk_B-HslvGz=k>bCHkt#go0F#O|TU-$L_iGn5=ceSROPvb_~Ehn=GdAY%}A zNqTQ{+F_U-du!nbacnC(y@8C0=SpoX@}$*6eusy^!w)F`(o1ytlo6egF}2WPKv=k^ zgl{vi0!9&G#{wGhqtp~64IM5^jdk|Jg3lwUi%jEoqAk~ZEATk)=JLP!=S=gNllZ%A zaO-p|3R)%gwxIRP^px}|nm`>e)preys^&@cLYC@r67)R(&`Jr-A@5KhyH~t!{lkF@ z-2St&BKu?OjdRuU$A+DTg;CUmVsUqO%CyLhZ#Mg`uiXX71VWbQD8-;25>=RUy0#?d zq;xAz6f``_LEy{f4*Y2vpM6qgn4Mxeg4ah2rc52sP$oj{`a+o8hK5`5IVN@T7L&8* zFMktfHy^@Eb{paX+@25xjwiYSFwEpz&o)z`WBAb$GvUV?L2s`?u?X8KK~yz|I9&wde8sYI#BjHV0KcuEl^F z@9N{Zz$GNYSRk+Xzs|M)%3+#a4iH<`T_+hri3heda`wIad8|qAm zJB%#j*fQB5PvfCSCfNu6O<`X<@5BKRua@hA(+GKav&0Na)N#x-3I&1A6rb0~!6-ED zfKc&n;C-GBAHp+({{wSoz&lOw>&sDe`%ZLQLGtv*rSfL=Eoc0&knP=;j@>Y?7ZOL9|mfgkVt0*QU975zCAAEk+Ci{z{l4xx7@+)lVg^6nBnH#v*b?IBIp>;tQ125r>(XqVxC&wOhQzIZ8 zCj7K_@f46mzTh_VDmAD289qhHs^gkqnc&xF=tbdM4k4GwsZ!aL)CA;SdeTs9Qf^6G zo-v{8%SjP1eab9zi_flUA%}>BL2^t;Nvlacx zyC2%3IS+47U5XHOo9hx5A&U!@FSIKJg%}kOk^~p)|??s5JtcT zr&MW*iV`i%xyC(9#J>Y#xeR4t_`AV8X+%DnNiqrmXR4B^tN<5?{At|oRKyDzJ#ZjQ|Nfp800y%64B@NSjl zQx5(F`CWhtOwk4{5JG+mNMqREpub%3`s63>zkYNa736Anx@pTCcoSswg$Fylm`9ws z?Wz?IXyM@34E!xB4Qhy{iZ57%B&H%M!hp=o0gU|bbVf+Q*r#hWtq;8)O{<}XF@2C# zyT4hbh=fD^#E27{VGLXGGydwc!dxWk_)90P7q&-1Yf{j{Jsi6#r@Em#&+;n4C|lr8 zhln@8I6d}Rw`m_$tGyQ|5`nBVJKtUr_OwRaV4A zco)Kdt&KPtp#%k@^KCz$eCf>3&mVjI{P-bt@U{Ky%7e86ZC`gBSuwTiBcuwI`XbeN;&UXRDXXW zRO>B<{aytv3(VTOCRGU5zzMq;4#^q3Bc~Zy0}+EdUUxdGS!3IEze+sIF4#$mZKyodC~Z>vgD??eGgZmeWkEs7ufC$;6Q9Z z1_|{dE^F$oPnFn=<`acHVV7HDPpvdGuo)NcLI9&$R&TAg0s?I}o@s1%7@>r{t ziz;rdoSXQbwPIwxcBXC>+R1z`u_L%vy=?K*7ejwGWtbI1G3mN-s=kwEc0nNAB9-V# zwI^w8ZTK6U+0ofIxHT%TI3}e5>KzyE6(7I}@|$Hkb{qgA)OIHnRFEr~*8MTcY0Ey5 zB$z`8Gg9nvq-`c@2o;=*!sj19z&5fMW~=@WPV>0UV6{U`y!kaTpZxWRR-LQLD&%|A z(U$ErTKV+Zok(&eAlccwnjDm-|C=@kMlWe;)W3*kV`Ia?c`ZKyXlq&xcs|qIDRh?l zEiM0D-upv5-o>>f6}>+sL@gYnD-#R^9Kl;6*K{3bzvFKwCY_v#k9IWHQ46sumqK8$ zk(ZHeUU46LSF{n7QZu7ih@Q3$qbU)Ru$SSVRe~n}a{N2JPx<#DdpYNV0%k(LpzT85 zwIIkuC`XxuCqTrK*6FKAC0vB!f(}Zipozh+p2n!~sSSrg* z3nZB6_i!Z4Hq+%aP`6VF;vjZxQT>Xq!=+=UO>jWf}3cr>Axg{Rq6>Z;9R{f!b93>*IMXf02? zzxPXWAl`R4VOU7akI-x&j>jFrOzZ%k$DPQh508y4-dAde=b0}Pl_p6JHSH>5s{|)_ z15JyKc=_sF?nkcfLs&9^-F8c@7x1*d#BR0BfL#kq9zu6z9+3K}+-|M%L^tnWU-fe3 zLb{+58uW5R{;z9x>vsKv1*2@w-?;9FGvwRmlM$EF#6}Ebf|jMv->-kqwC@#?-9%P- z9iJMun@N2%pp78Nn>~v2zgD?2yH{S7n3}C36tD>&ck3Um_sytXdIWFY)_Me}1wzQ0 z^$xX&AJrmO#eS&ROF#>+;{~V7UKB%NV2ts%oeLvj_c5`!FI;;|p9uxmMF{*tzn}M9 zbCkfJ5lZ`Q^x)Z1{-86+8u6As@+v#Slu$4}jRx zMxO2_tO-ATVrc=Xobkw^F;Mu0k#Y=1Vw3@NTLNI>VhnR4Rd3q9MUpKakl5#ay(o8^ z&X)xCmx*V7dF(f1cB>s{J#~GmKajQ9%Fct?eipjODdTSOSXk>3#ED819}zN|x@fNl zxFtT%usP6&xohX6hye*4r8i`5OH-@A8%9h_seNdu*rBacw@3Rz{80rqGML6h(3nK*EdB514tO*b4s?R)1 z{NL3JlmN94!qDfmH2umiyDPrymdPB$I}CT^)+DEjU>H3ldt6Ga#GX2&+K)H%6|o5E zio3yvr^~ufb6!E7D?auDfg>)rKe@I3;=ZpxRG>~2MIMXDT6o5{ZE}ag%lDJ+IR=-- z`$}L46?{AAFMHn^vuiOYux(kuZ<9CBU8mjZzxlzQlMJ(syK!N@V;nQhe@G&*pQ?_6 z_gho_>t!zio4SMZ_%#DcJMTf8etOHe) z++%x;k)t-}YHrI_OHDufa)0<<8Ov5LRKKIslVgTMooQpii;2(*1P7siy6U3~qu3uN zFUuj5@f2*9AZb6R$Mr{Dr#ICK`(plKe@Lt3rA%p9{hIqvd7Uhc8uz3!Vo)(%?_ro@ zE<30QaNQ9=RV5SBXKlD>pnImkC0|Vgq?G|&m}VhG3y;N643W<=q#iW>*W+O3IL?6S zp*GyE$pyA)b_!;BQoJ6{Lq5}j=k-HClMVIpS}bZg?=aTrEQ#@&3<7g8t4_#e=(9J; zg}SfqFnk&TSv$#Gj?AI8_u*XK%84N=-^(?c(oL>S8hM{~H=K}o_hs;97J ziPmDvicsq@`0{Fe&8?@qxl~@y{D-1);>zOZC54X$IUTx?ia&!Ls<;i!S$W$}WcQTj zZlB@$rxRJF;N5`ihAE--tCROn?sb9F{jRByM8W)iIc%Erai+~H)LYxFqaJ8EX;=$h z#qi%&b?1CmI!wJX^7zNtQv#s{W35z@*Vq}911=i3Qc2c3&RndwKik?scOb`-K@7p5 z1VK{}nm*ZeE6<{dlJsu89}Vj`vC6h}z@W0=WyCiy-?p8PK(9I3Wh4D5-~eq)=`Q@o zlX_i;y&?MD_)xR-l$4d3IQ*AJ7{_K~Ap>HH?)z!lj3UAL1n~=9=ww=cGpN$KqX2=$wh*DTmrXVMI=(7X zcNE{Q)M&rCMnL}VB2E!Cy}7iegU{iAjBJ%6_4t_X*{_e0_f(E*!|6Cki5aov((z@=K)Ex=FW?;bL-kOR zdUxb(ZzU3wAmeBx8M?Wo zbMb7BeH(%8KHER`nN#%vV!`uFI4rC^E%`$`ikWB>w6zm_Z6hmeO5f>IPZN5{157U zR_fF|FEMcTo|#-8ZN-{XE$G{$6|ern_MSfhIV!FizBoI1O@AKMhBQ8MxBmyhXtZ+t0@{8wq>|K42FuKWsjG#B5qVn!nQ@LL4Oh8rEWs`7v zhE&3Ea@WctOve=j_4xgMC1GEWgnX*A)5X{f4xNMFU2yDnOxs=HahEgz;F451-hdmw z?D9MIgq`#t(uAe>PzFr)55(h}f_EWdU`PsJ0Gal^d-Q6`&W=V>%}7}#$^F}d(E0b= zeb1i)0oE*LjZ~qCZ4~gRTXB9I?^~H{vy6{0+zRb3RO!a4IG(m{WwG*TB_>`<6X*q{^X>Mx+6v}_rD;1{VGO-rrnD#*ZhU#iz=K0Fy zSWF)CXuQT>jMpyZCv-{@tR@(c>C5c+U&3@?4z*pUV~B}Ob`ksiG7LS7df)Eg)8t;H zcJN3v3Sa+l;Uqz%f)xV#3WX40%^F-%<)-k}iUv8*GM>?@M8bAu|r4 z9h*}U1PwHtuM*jiN%5``jR8 z#0`x4g0_OyftR457JF!t6l}gT&!0>I?%n@u#$P3}FE(oV)Eau>LZTx_?D#_4W4~(w z8R0oqkFZ=sw{{`Hla$(bC!W0&Q}&y@IQy}s1q@L5KWr?nO~yWJ=xFfk^VNQV{$);R zg!4x>@Ntm*kt{6Q>{n#V0ZbuB>pvyZ5A6*G-EON=kD=fku#@Q!C^!8#c zQq@0D-ilK}3qNr8x=v*mtsp<})i+duyESIFW06FUkiyEEHN+r`h8yF4P!^3jSMdB_ z^$*mhn-Zej+aMjZu+`F9kDp9~L?KrCXB=|~U(U-$nI&Qg6 zj<&yXQ506>A*+C(5En1?7$0hk94|j zLZm-UQ$ZO+XVB%Q8^#O;d7GzcY83&UN3s7^6zbX6wx+Vvpg`+iqg>-yesD?VeZ*5H zAVGY}L?WbngVtrNmqbQF#v>_=m8QU=kQ zwzk}&A0cD-Y`c5k#=lYG$mnT)<;jz03iW$>Mr{t=+6sdBTY6bz*harRqI_DTaPq-& zCg|^>nFWPO48n@iE|-~XA!G4DFrqD}>1uiKpB>zadZx^XT5ou5CUpNxZvEaP=*r32 zcc2}VW^;+p9nRwF>m2(9puWsNVpjLeEQov-RSyGZP2>~20?OVKBElFB&?4Jvr&QES z{;+OmPLaRF-@@I(p6d%WXq7#k%Mn}G0U_hDLEr6^dNTTmE}f%XL7+nF<8VTtDmD_r zCPc1eluTl#aWqt7hQ>2r1km6}sXgf@7+V*F=VIa3Gfu zra=NzPm}7GYa8!u)0V+C1Ug-S-4BJEk8{=>w~>hXLXZa{2`oPAi1Qk*QiSrrz(Waj z*vwH83Hx#h15X&C@meqPKI7FKA;Jm|#m~?S6@TlTHX@s5Cxu{2&<3>i9l1OH#xU6c z%#zAml=(mftu36W7a8C8qEKy;Vl-2#gngGy!Ugen{|EJKz8!bplSEc^?ZfNE{I7>> zB=FEXNN0n#UQN`Bt)gMkVl>6*fBVUMAp)sCOWhcF`3^AAfulcnje^=+{?nM3uHKiS zFrj|UU#C@$tRFqrb1`%6evNSU%Wn#>r+ACTjc54gF5Ou|=xPyyJ9X0|ww*w8K6+Z4 zfn22^9(7v#5YR`_{nLd4u@9nO3I z5`?Dxts`x4If5<=#wIX3!((k2W(II&@GlEU3h;Y!ZJds_=Qb(G&CX$Du3DRS4n28T zjD~%;oLh+_VQZWJlj|+w*x*uNYSztV0yG$+tVB=bf)9AcOy(=rht!~45xKRZRPdlI zHj3NfNvELkxTLJj(CoX*kpH0l#GCnYQcmd?D!aWd#}qK*f2yfz!?Q{u=@-25Vb%SH zb-w$-Smrn(fHtSpIfBqzn$SWe`7wfNMv9&l&b>X7JRtMGhakQ{z*N9Qtmy%+Ls2q% z<9kjt%)mp1Ie~T<9SMswC}7>V(i}&P%VAkX>mZ@@YIn5SEd-1F7z#}0N9D)h(|VLfI`uT5X^1NY-gKPxL&qpiBN-f4LVC!3vnCi@%b=y%-5bUxEp-jQ_poc?Q zwo!IHBWsdqz|!qAA+Hkh%8f+(6pFvXyxHzHArHgOQ622Nmmo@}W~AI7;ii*_F6J&8 z(dvZqNEE=xGT<~=?d7PzRj@V9enRfbBO@C8~e6cMi_qUj;g0 z9!fZ;`LdOT`c$p_QtME5=afDj{*h@rHC*3ezmYSY#}PCISY$HDRid)B`P4If%Afg! zUynwYH3els`0!J0bYLb2fKRte1|LOvqZIh`)1O5?A(4EGcq2Iji87NZ&Pig3$eLs1 zO&Ixk9f|e+zRDKkWmLo<&IX}~iCldb-`4);^o4Vr-U!<)OV6#$aPW_$(ccN?4eey$uD^5$@OSzu7xR7Xb~J_=7P=}WDcVNAS@lrD9Fv9(?Vn#fW+8rck@pwgd)Amxh6@KLDl^zjxwvtWzJJPx}d?mw7)8+3pF^-f1V5f zF)Muu-gu;gJIgH8U9aCe(slI0PWjOGf#_$4yhf4bUc~w_Tb|x>Xt+zJ^KUOp8%fke zQ!Jxwm+bzx+;{LLC5k zyp$0Bbb~J@#G>P;8TeCJXT_n$LPhKmQQ47s=lQElTp>`Wk*1|%3O{UbgFTS4BM(m& zsRc#g$4vNxoIvpE^>6HZsqjPg7kT`CvCy`?mdVSkWt?;vk3y)&g&djw?%&+$FqJcj zmpo$?b~n|OaTRE;D@hsCKhZEuXQdgW&XH55N&h%e%))-zu}BiEefo5zDBA1QE3|Q? zI#Z}B4C1)kCc3B_F+70;M1Jymo#+ceP^P#!aQ$_}Y-7{2bM~(b)=JD#_ZaN)+b}%f zs=C_t=#q?Qw&iAn9dq?=gD{(})w#GbhV4ZIqqtk>&D`v@^^Z}Bp(ml#Men;f?!b3M z=hLNBoIWK&kT4&L>Eq?jZTE$L#b|2BZ#I`~H-ffN2So*YC1YjRZ8S+_{2+D1=QTsQ`%1uiaq+rv#Q1RqA+ z&pbY!E=>2v^}x#cWm(LMvX-kjG!Y`4;koSMmY(A?3M$ae@1<_>O%Hmb;~G`}*mIR2 zVE|q(iAU$17dscwZ^tE7wANV*m;`^F3esW387PEGcg@d(*I;k<|LbsE1t8Tl z@p86=1i}~5AA}JZYN%|Pqdozl`lNJDyBkIYbJK!k(id;mSkLbCZ-`rej4{xRh=`lH z=}pkFtA>#E#TJQLHFV^RLqWw)M4Y+ey_g#QJ=xWtv}nZBs7a`_FL52%uCiFUF= z4HK>r2y03QN+As3T)Ac|!+fRyg8W~!O{||Jp^s1d==828f%=Lz=$l#QRfpzZ71h=9 z$Xjgp{oL6Fw=MxHI=W%j-{#`~Y{@sXJTd$-o}GMj6Eq71m1SP4qc$wI$M6v3p?-$_ zdRTkZaxy||-4S|HC^1hD-Bq#=*m%^3qrLO^IK_Hd;v=ky63h2Yj3_~yn|Jt{=u7>o z8_so_Mej;_^@Wp8PaOgx^a_ST0!OMPZ0WBt2{7|AaQnN3SH*)3&`DE0U(O~Xg5kZ{ z0wwUxfojyyL?+^YWq_uh$4c^!?ASh+y|^_hbA0U|nIwDnm8icvv!0e_44W&GIYHLu z$h{p~`eTVGrGgwEh6p2D?Qpnuc4~8v`CX$Ps z?m;Yh9tdZAcEotz&d}5s<*HXEslkIZ1OY#f6$3I@;0FSVu=|f|e{2>3#kvXqLF2gn zZ4VLxEA?h5QQNig?2tzeq!nuEnlFHIhqFU9noKRjxMPqyU`DU{9CV>rrnQNTaQ`0|jEwsY@%(V=lgQ8XS zSRxCt85MUFb!gm?wCs8|A5_`}M{1kkU9B1MtG25XNoe<8S?(@x)v@bNd`-q1*iTEz z+;uMV^X(kI2#U1V`GNtH^BaQ%u&ZJL8n5`hKgyn0_?n_TSAFdNGe}pr?d`8VbKTSv zv9w-EZ#4(Qd*q5K&f}ii1`!Vxv3w?z@f+K{eBLF0AIr#7E(hM6;kj|}fo}O=%TsnF zQ^(vs9L)Uru8Ndkj2S?K&E~TG{#UfOhvv)2Rf@r1QX4npLQ`}^AhYwZQCMab;k~5h z$J@{?RqaESrQhp*WHcDWqiPg#C~O%V?Gw*;^e&AJA?g&Z(#W9-B-PE|$1At@Yy$A^ zON>cQ1r)klT@BQi*}@GLdZR?9?HCoHBO1yI3qud(_u^jfsl2Kvz3Hyzyz1&YY%8=o?D|cY+!Vp_w+NF?dS4;AEqimniZE=3R7|~QmETONh6-_-(sHQeuyvds+;Re zAVRUxh>19VSuET~5|=Xlit(KH1qJReqGcR3SWu#yW6VT=kV(|pl=NfrLQ9R4NS^cG ze)mw{LV%fpqA*&T zpa!_IegXO5LYyRa7>RbI_3i(z$WSG|9k`Q!|1=7>wFME$5bK+NO5KeX)7#VHyy+;G zT@GfB%yGVUbw>UGSf6IZ<00&FwQn7fl%O5H4n+yeCAO=YA)1o!@b8FS+3WBp_~v4v z^k^Ibmg%0@p?}k*`s+cfPrl`@AgO%>&eq zkqm(aCVDrPYx`;~Y;Nw0UK`6`^2!K{IP@|0t@_p$Ou!&AN$<-(>wk)oS8D#HJ+iZu zxKAi3eesH*&(R_&(Q&4!k#pm>?8DY8$?Nv^?g~Y7$ox(cuXE$HTA}KHk$!2_o(?~A zHw#^l;fBf^oS@I)#-HdYZ+_CgDk4nNAVr*HHbx=U!J#VPNUB6;7R7OO#e9}MWgfX1 zmY9Sb&gDHA%IiCJ25A0_>4PKM^;&cMF&9mjuMmsRsuj}lsGSzAxZZ@5#KY_t(P`ni z;i285aI3LuQxcyFjU{9?VqZE7GSDeLJUc(T{fqiu>IODc3Jkc{6d9<=Yl2lV@M)#~ zA&aYQmj>_mg8MH6nq)zjwy#wBygMj?e+$y}+18{IW2AL_*bs3Z@8;+JKCaigAIY;7 z!?qN3vd^yuAY7$DhDLv!cI&G;GJ;l_M3A1Y&zWD?PWe!|^-h!<8#PX4(jjSFX zKN>;EnO|5NVs8HIT+Uej!ooLb;qWNG`au5kUbY&4r~(B<%>cKkk-$JF}RWIhEd05;Zk3YTJ#ZnzX~HE185$!}t#}&5x!9 zFIevLYp%oWH6Va~zNZ;(ShM{XiSsL}5;+(GR}v=`L5MA$7ks?XbRp$$3rjy| zC!BqIxBtb^fLCjWUWSv{C+?)Wn`Y1m-n1Q`rGK& zQr3^1wbfxT73yBXInHw7Syy_8q`46RV?F{AgtX)x@eIs#5^x$^8ch@+n#c39nM9J2 zPP315({9Qz48tYaTlL_gmMX(r7~=TusB5i~7>3p6MY>N% zi7e#irXw_!pi{Qiz34R1TLTyrUrsaZZw_c)lRCpI`QR0L?&je`B<+4@ z&Q_XeVwE8GP^O%yqh3p!lM(YPMc~(XwA|z|Gb6VB_@VmVXQmg}J+sj2H+rbu+lC&A zQL*%tgw-7CpO8u4P+d~gLYSOlX*~t_zY5RiovgsQmwb;lXwtR&Seg@VNNi_LjKj)#+()q<5(Xr!L++%Q5>VK)rr0d%CAG5T2%>)laAn^L{&N!Gm;3jbz10y|p5Oo*(T4u^^#7-g0 zV+3=)v3pUYu{X{ecTprq1>dq}6VW)^*by!{B(oI97?fq%9}vFZdHh3ps!a1YK{*aI zJ+f(TDq+fuGoLv@C$<$}RDlabXz2)o-=dhV2KP*N^-zLf<|D#06spA7B172^{#(%| z^?XG4@baqW+UDO;7P=b-n?l6I(0ADuV|I`pSjQQqNv_0BK<60+fdB~mN~PueKD@TI zu+G9qv~|4ryIlLTRnwQy-D#cUecrx>MvOq+Vwq>e~i8}XfmyDjHC*c zh5U&pmacVfb=D7tX-BLlG`PH90F~7$6N;%P6QUg1$FnDEia{Gvhprj~b?q(rA;+_) z3bRvYSQ#RxbUWG#WdJ;nUzU}DD~G>hC@a&eY=4TZc{;YbPQ~7rXrK19Ug(~&F>jH( zMRhLEe>%Pw-47byCV*DEzuSmHfJkQp2j3Ssdl-R!riyxXzZDoboRan+n?(CLAG0T- zE9Ti#*J;-${(a4!O8CB0dzo(m7-c6?Jyy&IbB}TPzzYt`*ESB+5j@3`2(+qAZ~Ux$ z{KSK$5hLP(I$2xivTs8qZycp2(9| zrppQrFX>>YIF@bl9m*XBpAa!UF>*OXQg;f{Z~xc8+7iXqRgEf0<4k4L=1-FT z;dGw7=+n6J5%9Rua&Bdwlra{3q^{eKu9E!o=NPAg|2Y|N(j!RQOhSeW}Y{1tG zXKFK9Hl%eWC7}s|_d=WTHw_$Pdv_gj*G;y1!7%+aikG>3H-<0&npfzzfzQ0Okils9 zbbl&X7r~+4`*(d3jKmK$uA9!Ijd~Uu#Nba`J6>aZYO5K(>J)S>f`o@3EhVXnK^!*C zNB{0D&Q011Rt2Ar)^C(bLPrydd)~@w;7qs!z3B!7)y(E!fQ37X8m-{J6cVvR{moHy zgVwg{Efnx_cZn2{mw5Skd3i0HzQ#p2Gop#WS{|K8qUz7vLMwX96nm!g@@hkTmuM(wEym&g)5Cnyk zKG{u=ee67ssrT6L*(89b@+GJtFHOBFmg!EdNE%BFTPaOa4q2`$uA+) ztYkwO-ypSPqinV}3W1$H*h=f4ESsMOcWa5Lw@ez0dsf|Soi5|;3_tGcjSuX2W|<~9a^XrdFDfMhyZhwXXzZQ7j=KJ?Bs8WN4( zn3uKTxqcWmlH}IwxmTL1v`N+rONa))H_y5n1wH(_P`J}eVXIlj#A2m-THofvLdgrQ zL0tG4;)M-$GQVX~zXRrK?i7k?26`L^^Zm`lh(r3#F2&yS>eibS9hqpJQNXd{+xfx$ zxE$->OL<}kK@dC-sR(zLY2VnOTu4wn=okXydI7`n98J$Y*VR|b1DR1!%8KOxKlXip zTaHgI=+*A(B5%hKJ^d~dJkY_pCbrx}@QY4GZ3!MR##Z{nO~w5JLgwDJ5%(=mrP)?D zLlT%Zv5bXByte@3LCMKK%YpXD;us$~a;zrTEjxUSnva9%Bz<#hK!WTPlKJojj6Y|a zl28aN7U+=b^|fJGIm#rsE77Tr5ODMMaaexsH}p23e3+PaD;;_GJ1l<`$5}r6Z>4$Z zYT$??1M4p!=mwBzqNRc zX-Mz>mZLT7W%T%d;HqDpZKtQlO2`PHvfag{?%%DhGNVR^FU-P$??0A{rtcaX3Ir}#t?W2X6tEp$zCcX(xF7dqecF*ha z6Y}zO^P~^HJxL1bYjI(Q4^in8E}}qToiATlp3g7e@LW@LwB0g`{p3nPn#80{vw$LryQjcWhye`wxu`SYC%+*~3NNBcXJ=tyGJa zv82i<%O-sxEO+SP=*Y;xw-T9iXmSRm4C$QZ1SNjS9%#Z>I>L`wsee=zvnn%SVM)WH8skG@^>@yAH%_Q(y=Ue0dD_{!#L{vjQU6w~d!=v7%Rfous%<)SmW%_=fBC zn&5~T|BEZ(-$lOv+3+%&S6a>vVtKuNN~X5C-W#-epZmrpx*I@nyT$x+C6S*C0!(7o z{k}znER)RC7;icdz$t~~Fc18S+&&v9#r$c@E<-j@Wy?TUGD0|4Ig4 zQBVdbO(;|$RzvG=tgR=XT4qCbXKC|H3`3!|Zsl=?89~BqBHEQMPe-Y@hyTX~dGOnj zO1ASlhR(K=diCYqD{d6-X3;|McEC7o#w;*Q^B6_H8BuUc`?51`q7G~;e!%Z zB8sDS`duRUB;nT1k0FI#pkpn?Plhv{t4HZ?T=1-(BW)CCG4jACnFY8V5+@Cx$mr~4 zkA^8i%T-;3@cz*K>3-UlO$hZ0fvb?eb>y$s`iOY?lYRF<^E=Rcw z2E0am^aG{0x07!`N`G{|OZjQquvt+%qeyY16lYtv@#81m8~o~Y*CgPY5q)3&z{3u2 z;@ls@C<=sq!*Z@ninRa2*Eh)B5Px)8TlKl~{}xf#`vkM<*;(&~0<`$bF*_7Rx|DxF zEDE^#JN)0K0OYQod=5p5HpqcUJ~)a#iPMwsvOyi;rTe1id3HZzrWjY-AQ|%foW>DD zKc`ffWd-GxBawN(aEA7YYtT{p8RJ^jidEztL*V3c;pps#XbGic`eDf=f<}(r;FG08 zdvGImy^GuYL*{F8v^kDMf~+of+g9fuq2nuLACE4OcguD?jLcr zR;~UBuB|cK*zFYq!i~+vd(M#W%IG>zD`j&G$Z;M{RcvDemy=LXd@uG>702%-Y;oWu zp=)(CN{0bncTQ&5*ac++G~d|A#2}+Z7NN{j7#11x9n6ngJ{PSm*LIUGn3999>?eFQ zNK`wda1X#{Krp(kNLtk`Zu_i8nT?VkS&=^<8{$j6I?qaPDGGoJ##kgp!i zJ7*Z^1`{QEi-A-`2w7o2R{FG1Wp}Pn^S1paNh_Z!jOqdWvIveYy|<;*O-8ziblAq{ zGT{yREy%$L*UfiMn>~Cj3sqn;ebOY@fOrcl>Kj^PtTYW-%2UZPJ|Y6bQP1;v3Hkfu z=q>O^jj;s7o!`q?B}SPM9I9x`pdt+4@C{xjt)HTK#VV(A_l}WxH1~HcD8I& z2%a8pTnPsU5ty(Y+X_5(zPj#A54jVIsvA6n`lZenm)#siQ9qQpv1Sf&L~i1iEYs6H z9;zYw_Ma0NlzIVWgO}+KDN7Sr?m;f6aIm-|(sqm}-o$!rfYxp{gd}iMQDXD*c-3-J zRn16kW*Oqg@V%%OuT9(Eop4@kF5UT6Lw|Q^U%sQ8MK7tSVAC5BP?^yok13WleO_i( zDum{!p3OzZ+VKR=#D^zy2bR9YE@g4n(;v^c1*13!mHj?x_0uO+Yfs^~cYY^9I+k%I zLkon)i12zZ=)QnTE#aH5d$@-ehNlVn@(~gCW2VUzA?PDPLj#6+i0nud$!ARQ4+yDP z6v?4tM}2Uz7MLb{4%=dF{IDYC=P#j9p^i<^DXIBJ%Nq}W{s^}&a0)heHVfu-micoo zG(vLDPpaz9$pUjcKFStbl6Ry}mf5G-IT@H7KUL%%vwd+)np+fov+*7+41he%1Plo*Ah(QU zOQybL^$qYDB`t)&OQ1JLic80OQauI9P|bk2r#X#L+`VUng@wD+0TWO78;?H_0!ipO zU{Uza6I{K)pia@iqc@BdG#y6$aNhl3B=WjVcd`g;+c_8I0>K7xOb~=pBe`!$5x$Na z>9x^F(dBzH2tWUuBmpBN(no2T5(~n>WRsuXV^U_t>%qXpGbRq%Cow>q7?B6+~%G)a;LKR;G9@#t;7(r&oDbKkzcdYP_>j=w=dch2Q%2IRxH zL(L0T`L;?D1hTdDg|l^w<}0RJoZTI!^rWFAgh0`q7d@REAAtc{Uq21FdX+t%PZ1<7 zravG4z+e5f-#sdG-p0<>rkBd~n!SA}J1)b=TgYU`uez9U_pBL^$)v=qFWjSE#$nyr zaLi6*Q&h-MqhBnAKAiLjV#FZBJP1R~@?&8BniSZ`$H%CmlCF6TE49OymP3y%Q?G`c z9mWzMKW{LhnDncc5oz)7$)wd+x1)@C-tN~X#$P4UsNC_k96kmll{>phy3#H_ypmNE zE1y@LI2U`j1rR_F0sp!sk?_M4?+zBXkju8pvA#Z-a6L>;qLB^fs{pn&`NofSD*;DNVG)T>@Dp1Y=SG# zq{W|-C}C=j!=O{bXy zA-fq|Jx*70z~dmS#q_O95O@eMctpc^X7tqm7;-bT73hVW$9Zq&yA!U> zZ->tWd-O+zz&o5EP(^xf93i`L+Pqy}BZZp&7=geQ=zo^N461e#;f z@xn}zxy=7)ItT7LyLJo5W@D>CW7}-ZhK+68*-_)9v2EM7)#QzBt8sFkeq)@Uu=jY@ zS~uprCdoFgY-1hFZrUNsO;u;gBT6^rBZ>bTJiylp1eEn;A&qUX61!@*SpU`2Lllwb zx;B8@E$kg9$TBJ;ir4+e=%oWNK2VLLHuHuL<+`PE!VDX7I@}55J3Tn3(q5TgktL4~ zD_kGKg&7#V%AsNmS#30BCC_FK`sbmGer-~NoXY%#fOq%PU6YN2IU1frEHO6D37^MD z!j#x~XSZY|>I`+l1a|;g9Fb*o+oOJSGs$JN?{y(_jj{McttNqE-8rI9`>sx(N!g&5 zd34?Dtjgzp;lyfQiKqN_D4nKl8(IqRt)zL z{A1DLjBZkqv!N;eh~~AFuF&%lj>{gh`&`g-tkTRlimBYtS7E?+uC|q}{GRe!hikmJ zitXOqf*xJ?aB%;=+j%Wzd)zs$`-$9YxDrpdDZtVAta+MhvyH|n@#P{eR&9!_PHxQ$v+$gADXncocH8snSHmgF%xk;u0! z{Lt8xW&StDjf!B_+<6jJGP`ZQ(B@0Q-hv5-g{C41gNjEnq2Nro`MhFe% z@)*dGgZdu%YSNa6`k5_+eM}y0=^>~l|2Ge# z>CCbiR#1OAwBMyP{ex`AFxJe0qo1-=3Pe$z{-eX_Y6d-l_!%Qh7W1~RL+pQg*IxKwsQM0%P^xknN()J@$SQ2~Db zm)@veBUH~#kKMYf^1NrC-Dus|8Vpk`U#t18g-8k*2wKVoo(YoBvvq4S16SrUdZ{Y~ ze$>lz;ZOFb9i;0D*@f|oR1i{GYAuZ)pD18pz2*5EI?MTAIRRYChvRkvss%3h|AJRn zb!Y6FnU({V`U0!9;mXZU?H z-8ZZp&9{fxw_A}Kg4n@6UDVsp=bj{NAdGpTmmp^Lo9r*4PYK1H z7?_7IWO0;Eys_2F#3z~8+>G+lBh)2UlH&*d{%)V+r28KY$hr3iiHjNZbuPQlbRpE9 zGA6jDYUOCNf2KPQk~rn6io@7a%Pkyx{|($n+ue8puKzD(51q0iko&-o`bibb80xOR zvq^YsB*#$Ir_>$E)Gh|3z}#la3ltux5S(OZ=Rwim_Z$-e5zZ7pVDK39fw0OjXch-x zp$n7R5ImOsEqS8-WR1^W5|!V94w)binT&Z9sa7G>IBxanmBli7!y;F=XzqE4LRzFd zgym49=LKsbqGSR13F9w#*iXkqySb{^RKJiaX$?O$p-!Q||17u4+O?lh-2=|np~1aY ze?WWTT8q6@;DJFf6Yqd?K*S1Uqa)?yJMdJUTT*AU)>R|HshMT6?kBJ==FK%s3Mp{% zLJTc4m53BS*<{dB{^!hSuCPu~z15mPXWPv@S@r8Kh2rC{wjh+;WKp8eg~;xY7pr+% z7;Gd5=HBzSi>Z^pd`v}T105b;a1!ebPz))+CBWw;3_+N|r2D_lfIvu5pbe%%q~~t= zv8)c|`)=~74P^xj-YD-lA9F*0fk(@ck3`I$8pC@^!wW>+Sr$Zdj^pA_-6tsWYs`k^ zFncSHH}$CJ?~KsV{&B=1x<5So+`bz%U&uGtKQzh8WbGr^;qJDK0(X?mx=EeEqC-O* zdcL&}c3_eWxtWqIZK)FTgW|V^+|ZKWK2X${U@@_&F~vvBo4T_@kT%#$3=bF}@YK9D zU>YEF8i_lIQqJb4OkS58lV6u>D^6n$*IwS;#~PY#>)S2&-;O@DU_E~KiG{X{TUcH7 zKG_CX43;UGVih%Y5v%eb`{$ihRzl0r4YqM%QKD)QDLjve0=ZKrTf>w2A{StpF~2|d z8~*WnIW%QyGUzLPIY?+Vienru=&!aSoWXH#{K7v*vA)I>eIs2r%5l%r&VM4@*%vz> z*)mPD_WrM|ME?9Q7>_vCadX$=Z**_p_|vvYhzCM}OD@W52`e^0puW=MJVGIDhLl21 z8Z$mOC^2^syovXjM7-glf)Tjj&(?s zN8K$%kv^Xx;yMO@x2>lqCr03}rgp0X*=sAPwDIDU+)%a!E|>z1(=w2;;E8doceM3x zdMh81F;(~<{qI(agn0qsGqmTt6`qNMKKM0@wNMxx$Mok5M)mGJ6c`mm?J4nZu+Nu! zy7s%jQYXn7SH+ihvr&T454!6VZ77!aI)Vd)R9b~-9|oHvfAm9~@|u^b%>)j*Oc#!K zr)HmjCWm2M7T-|T#v;cAtQD)p?ybJ8!V&kWSxuU@T;NkMOFFH)V;UDLsE_hxrVQ%q zH8~@d+t=oZSt2*TTN&dv`7|;$AZI)(-&qYmQTg>}VTF>}s$M@lT8}Eh>vbl(#n1*N zLPN^(R=E#tYigM~;2rNN8vrx{z6gJ**;DK4@mdO$*|17jT2h9xzQ7tyB5LE{rv{C1 zNM9;j59|#&i_i0hnPZ^dD*yuNfAxGG|r!2OH z|Iw=NmFeYqU?ulZTfZu)Lm~J&n^NYBu(rUY(RPlCBD03XQR&0de65Z)Mvs%$fWzR< zX)OS?3^fEfJXbop49>mYz*4^AeucxYghRTHZCSF0Ci5T-b^!fRZH>4_%{v10^aWz5 z0*QJ6TY_4sYjwOEJW`l~G~jQ{mQ_VGK;0jXA*y%*kKtp(G8hbGDXyxj`d)zaLiX5& zP2ZOALrZCXXYf1iA{{oCF@y#M}>+*k1XRQCTF$pAcF>EBpE<#w^HV3crAWX|5} zJso@h?_oqvKz@ZG7YW}n(R!hOlAayI5gOx>cDKsJRTcZ{dAKsZpP7|)9Lrp3ymuNd z+t>}4mvuPHoaLK$BfMN9Ok3_>qv78!A08H}aq^+D7A0$!nIe2X)@()L$3IBWw3UdB zo^?pPc~C^Vzik_Ahs=I1sw&O_PrjVK&kZyp-S-+~&V*?_28f5K0Gh7+;}~LrbfKsh zL~TG3_M-_c#*ge>ta=Vw^)ieb^b^lS^K_&RYdk-;`NfP9S{xg$(-m(`*$_!^F zr}{9Cz@rfRPG{QvCak{06R9f_jN4pk-7D6{tTUKf5Ym3$; zLaXhdE!P2K1K~55ldAicGEZEEUbYGoUP`%?wC3367=8~ue)9Osk(BVJ??)=9BpO7~ z1Y0H~(@#)micHXB!|>2!Z8S(`*5sSS;dYmiH#*BISJ*o? zD1WIkrQqD)^`dZQCXKilhMOltS8U8^$?Sa*-zsYbsM$k_*^j?1?#1vJ!wbOVtHNy% z3>|)eBl{P|y#%h=Q##A!_t<2=h%vA(3S{$S>ff4eX}LW;4~prIWAgX+3rtSfZ@PF` zx^z1U^|LLw9DaGvOEUadGJmx5#^;q~;Y=RQPr{t4D6J4niZ(~AoNp;!iLhco(OUQr zc;{>w$`2b%GGYZ!i{xS^*X;EOw`@Tdw!}~lHgt%xo<>+-CP!lGHiPXS${vR*ZXc9q zI)xcF#B}WHIOVh4P%hdYW9SJ^uNqsg6t1d8co|yY1)O-nQ&4O8CGqyDZJ{;zBjZiy z@+H_Ecg0;@Fj(*IJO)!*Ll&2Y>);k4A$~z&4;39p&{gL)%n0)NfskC7AEs#X4<>%Q zZ1=?GrF_2WC5sdj*COPzXrhi6y~AH2j8DD%frMz=v=f-spJCSq$w6;syR&vZ<0dca zyIHA$-L^B5Pmm5YIfguXln;~hAYQ?rp6Doq5w=t7a(N>(x!&KZF|AS~vBt4EKzmP5 z<%Hh_}^>bsk-R@5oJG~8!0M79b;9MzSY{9=sqnXsLoR4 zx^Xw<<%6l9m~iNXL8{B;;PuTd`5-#IHBS}84Y_RrSODAB7zng&LA3tE(+@$z!G2%}2H&qx&w)_GFlVdM-4ha!3q{1b)hK_34dOgZc}RtO?prGSH}IzuNAx zMN#ZQvb-?laUbeqoHU8&J~}196q%Fqk;C&J2yGZ~Y;s`kA7!*(GsFD)R}qqo6ei7i znkNisqteo1!%HHL&(@_pO9);R{Od#fypErNsHo4_9-W1}m*lbEd*9eTe)hlm+Ua2k zQF4x0QIJw7HJw;2-Rdmi)%iUD z=?%&@tp9w7(1zV7NY-EM?7wBPeB264+L@uF&)&!R%lMT)#+pvuwN}JzEWv*cOCz=b zLqRb0z*ilcmCxi!9X+TeRKN8!=n<(;zxmP+0IRTDEz$rzeM@4F!VFDq5ZIAD&iKLy z@I8`2SS&n^or>ZLIhfsuz!Hfs>v=O&wH>D84Yjtqp_*p$kwf`E_g1J(J}VQM8~&Bu z(9ro$|54&2j{Z@W1?EI>T&rmHc!}q6o-$><_XfsMkf>ZSNN`c2+VzPyf}H2IOH-nd zhLnI?*^L0cyzTvZV7qEke2)nYh2c!eP|KKfKGFhVm?(Y6Qa5}1a_sHx_uDSh7rmlgPD0>rI68d&<7paNo+B3z4Y_xMW&iSs{gNL*>;#%yK)WPw&~b zJhdSyd`Y-Io}Y-MfCavZ@qK;AeVe;df2jLXwaeNh!lE%!!DRNs$R}hLxPP zg$cPyzWTtRWh&2q&6Wm%7!{laic6_>&wZd;6N( zCC9I!UD{CisNfAG!Y83Y9e2}w3qv^i;;-ioipBlm)Q&7vqq2JclQgD_xVGM?7v_qN`^)8t^lx3q)g-m{3*M2{ zr~O1p`awjeKP#qnaFnz<8kcB8qZKq9NFG`+KAqq55;ky7D^U8b%6MJD7@8&@*=fx( zre;Qd;9on%q{qA8QC1jk>SraRS1smpGF;Ho&}&~#q4QP#3Vk+zh>#Nx-WF-`;}_9@ z+OpZ(i%&T5yS(%9!Mh6Pxu1PM-jyzyUp)iF?+9CI!mQ^-WA}tf_ z14LExOpl+2jFl{SIQ0a`1CniZUD0VQ!;qlR&u%FUBJ{(FmaZSb`gh_kPgg^lqPD%D zU$GH|9Ww4;+9_|9@HLgJQ@^C`Z9Hu`QyEj&XnWNtfhEv))--EVzh9z0&XrqVW9xi8 zeYGz(uYYiJdr+1xMU?~WF05F$o4I9dMQ&foTjtP1 zME?SNfn0q94!tY@O2E%@UDA?K#wHG^-F@!5rca6WRK|p)?H{ToXF(h8SgAcuuzcDQ z>QPXo-V*+Nf2&}(m}Nc$Prp>Fk4-`-yD|phoxmKAOgwv23yGJtY^H_k#R^VFsM7i4a3Qat-hFM&_pe78e7_mjdc1xck*}1k%6FkhF(^3_ zc{6SsLvWg^Ip%-!C-QpRY|AE)xSseSMNo`3OA!iPu!&}&)iFphVb#@dXoNdhPHN6N zmU}JS6CPDP$0v*1Os)xIG@9KM5xJsbl{CyOH}ICH>R$6xHq;@v*rGv7Qg&7A5T%}6 z!uxVEKQX+(GD6%7gMV%%U8ehOK6YT-xopbU{1D_l8e2@_HvDgzcK>o#<1G1td!jNi zDIbsA1h+^9ad-krVAm=Ln5*vCo%TmZ2isxrE?*hm*Q-)VOpsVpAGe7drZ`@#J zxxJ|2FAjMTE9L~xgs@z(XsZ18Cc|Ba4wxS4uR|aW#GFV23@6%O#7Ij-sFr0pJf!89 zd1fUvqX+N?P3htdlu?0WHE zfFyFk(c`YhM(xr7+dQaPK0^|ScKgjbnma>^#Hd%K^^&y~rVR7aJbK75&De^G>4hRo zrOIx6D(~_bMD6i|k|63!)_88YXEo=z#U_{+iwK$Lo$VBN85bPu2}tO4n|d+o3bkQo zDoEI^WAw+}X>*!IWB!c!GNl#q3Xj?db+}Z7eqFtxt5rge+G53v>n$g7A8)PlK}|yR z4k_ss2dHH4%D*{aC!DKX?Vn0mBr0Dg>QP{q3&r0 zc#Isg@@JI-QB?9gAQS-`p{2AJn!XP6|ELDaYT;lG#O!D{^XQ+45ijTTNQI30P%l4^ z{6E3~2f36#5C`AN|1`0iwmZ@I4QTu#0S$8Qec$)4(Ygiyj*^^&Q_PrwCarAZZe|#thgBa+7i6$Q=8N3UpeAcficpQbwBjqJ z3dqp-%Q>_e2xnZR=>4c=%886{Lc*%+C zpd_2jnOjdu`_m^fmWeI8gdFN$dPLK6ecv$IEnU}?oFIwbItH7rnk8glBH+UTPUz3$ zMBKw`H-qy(NCo?J?_iSn$&vrT_3Q5$d!lTu54u0AG!CF-%r~AGTeMu+&5-$sIOQJYt5Vv~&GY}O4SvwY8h z29K(G0^lxiFH`n@VmkI+OA_$v-rl z+A}0C#7Y%g5}shZXyTeh*N{h@3UGW%V7WBgVPj@Kaw_V36DA-ySdZjanI<@$7>QhM zxbLs9M|0!%V90o^;&>*-ia82E+LkIEVV>H^GE?KuU=3LKZ46eDL#|YmL^xswJrWyJ z*ELm^&#lMH?wuazxQioU*B8vpfrIK0XTl$v=l(%F?moD~3bj+rR8Y*AN%Jr3oIyV! zoKzwhU-uv0DW+9|AKAbr}4)nCgs&9$dt|-wuaRYHhhC2u!X3mr6>UA%o^_9=8_jI5lB!q|Q??ZrW zp*a9zDEv>3N?}gzOag#E1VeJN2^ux{4p*BAxSdY5scExQqx*W2PZWqCH{g+#YW;2bk(`7klQy&4k@z!mQW7d<&;+YpsD11n8%d>eL*N)})kPdJnhhFeE z-82G=4cFx{T#mgmo>sz_HV2q2y5j)lVcC?!@KhhAk)L0m`IIWpKWAj^7h3O1=68p# zSnQ9DKvmecU6y-!&8uk%N>A5Q7@*OP_K z?r@ee^t;tAO0U>>BI5`V6o!BCqXG<)Ej#+=5dPZk{Q6lnFv{K6UZkDb50e6QKS{ir zycTjp=c2m=Vt1}4ndC^ zvwzpFq`G`TD>Q3)udv-6 z!ipgA-)*>?raewM%fLeC!+Y>HDa@=>{4AiW!1G@4 zhe^{o_O7EY|67aCx_Aum1XTM52V!z zhb9qBBWj)mi`|%qzgn)h8lMFJMg_E?Yuv<47JGkg+Qor&PD+g&EgH65-e~v-XTkA~ z^p^V;K(heM*dO1YuM)EoprYa2Pb)Yn)TmD6a2GTcn`+ zVCkJT7C^yCM)_NMQ;@`NPy`>){!GPyMKR@@?8)64$M8t{`j-_HP&B9x_wcg%O-`Pm zq{+<|aToPbT`q0woS5~lfV8QP-(iOW7OibGIWep{F(k?E ztpf!yEBA(<{;O&1&mQaOF{@4@i$mP?u?@ zLhbv#TE(yd+sR&q@#g-=DJ(@CuFmZ6wKp(JI}XNB$i>n3iJv&M_j7XlvXv3(TMwrR zRk+pILJi2+$6P+1kEAY{SW6PuNC)ua=TlZKdfsF5fa^BpdB~vZ+2o=<=BVrGwTq~E zuaCou@emG;qbk}Sof*AU6%?fvvc5s)UYZu$g(})Rb1&eiDIB%i$U@35(YW6EJnGFz ziOME5Hgmu5xI?z$c=wzrX*)dog|h*t8y3K9Dp|>`1LlEq zjg|`*Kn$MJ$6*W}n{MZ0|AGUwZ@T1cL;4n~$RLf*rikW^-fR~queNZ&BZD&Wd zVij@74jcyDhjhHk$t2H5VtSjrJ{#Qki3y3g6>OLYB`059CJkj;dK*n-SzHePeT=54 z+Is7`Eg$`+@A_;DiJn~Lb6z8bX2V6P?Z%mt+mp~!~|nG5h)EP7tV)hKRwk&bN-AhmJ^6ZdAlL@nPlr8f`vpTu+hBiGpRiD80y5& z35GyXl@gJS-exkiqisX9Rs`}Qz9`bf7NqF=X*F>**3`BpqyR&-bUg5T)VD9cus8p3 zD&U#s?~?Hkcl6>&E+-~zX=|bO|CCF|DoRdvdl^$yC@|0VivDsTrY2--sz>U}VYOm- zV`>9`L~i&kV`giAa!SKnXU|JC*Yu*DzkV|KKQ}InXqw0V#e%|{!5`z9Bh>RCT#Lcl zTo{}^S0hqnXGwPb#A`Ht@dp}qsE|G;z80ID+Q#Sit_Nqw>z9n@?}Q7A($K`He%FR! zUgQkb43&g7-9uh3YZnWaUD4`A$x!N@19ieXaxRu2q+paqt`-p(KBMlpTiJg>fByon zT-9CkP<9@+g@F?)9~N-U-qy}%GCSg-sI5a_+x>j#GADsE2XOX_-Dbb9_xC0EQFqEz zCxB|~>x=VRA?<-`h-jz%s=x*};@_HZ229{Nr>Eo>j=n_BdqoPeWMw$_Z-|5jZ-w?V z)nnbUJ>@*;6%US(0I#cI56UVW@z56dPuhKma;RwS#$eHpJ)ncTT{>X|oJ+T#@0@yT3 zzQ<}MB{KM(4wl)Zg!c)HunR5#r#_Q+b>7Zl=?XuR#)DBsxSUlfktRwqOtZchMtB4I zT_=0I(0z;Jij{_XOZJbVD5YAWPy+DP6i+nZMid?5EA|a*u)!n29{EdhdOf^WElcJq zwHh*^9pL8`+(8^%G-%<1v-% zOf5wclHHKY8<^}DRbOnatrOffZ28jfNER&q3wNW`L8@iWQ{TJuEOGEaUD0uA7=xgoc_Bv%!vhYWh1n)2kvrUK$E64eo>0eDo{e#VjwaD$Kku)443+sh-@; zbhJSgCh`#(SGC3e3B=~7iJWn<#v?_JW)OpdynDNllzIa9HW5?rJ|Xp8+f<~ z4I06?io$h6J>t?fnM+JgOt^4K>$zGuOi;i*Ut99sVnQZNno+p@FgvM%ds~DT^>pF-Bgu1zI(gNR?7%nY5TCqI(>ajm0!O z)?rk{;bxLa%wH{8%!C46mG}o|;=4>rYJk{!q}aSrHtaNbT7Z3ffa=h4OK&4(F68_9 z%C~TyVKz%U(05d;`kyPSG9n4+Czt3Z->B3yBSB6~2sHD?1|2{VX>)l#NV)TE;pp5S zT9B-TGoi>GxnU>zN5uv(^s~7lphUYQBDP5>ZY&eEp{&gCxEWnt=&dyyYuVYxGOoFU zhReg(y!4}hIuF8Bl+Q)N=-)Zb|9Kcale`%#^mWeUC=&_o$!jk2!+pm0rW=m+JW+e3J-?X*ueI?30we?o1}Nh^8pnxbP!d$ux&Qq}fgp<3n2? zi-lE4%U-s)Q7p{k!nwyM`l1-rS>H=?c+#OUu)o20rF{!z*?}W36gvyd4W!HFQ5oGQ z#l=>?+X8q$EZh#IaOll$5dt;hP_yp`da%CCYg1eum?91UH zze@SMnP5@A%^n5aifCq{@CTL9ANzm4QR#a0x!#Y{`R_TcJyZKYO8ExZTf8X2?!fA) zhj8ysqRT$T1amBKApL&n7v6N=QF~2VTL0E{LF4|DK${<8Zp-+_^pA{8nKPw_<(uoF zpGhrRne5vEyL$}w^6qAjeFAGt>eiw?$G zIoU!x3Rho~c)kSRxEH1pHMX?OH7&18;OOyl)r&|vxw6juwCR}<-r0GtuK8D#778Vj z=g1QGRwerE4sQUm31Rl)l|XI+m{7R0bSQBDSJmHnk{ekLmBXS0XNnAYD_r{GLV_Mb zLD4W3tB(D+R9a25flD9~C=_(jARSDqU_L%R%lSfSmy@EDy6pk!|I&~YU;pa?UL>Gp zIZiodYaLf4kp*Y*4Or6iwrzhot?B0WE<|3IMza4jKH(ABy{U{_+UEWssd-ms69pUErJ9txlmMMp6 zoQNOuac`{(E(f79PyCtgT->ujh4u=wqgI0S3)(Ej!n&20PFKE?4o!e-&^P z{!2Fo@~fYZMfA?qTQNevI+M-M%stL=IkJMe%{HmP5|~_e?m6;4+)t#L8-yXzebtZEX(a@0FD&A zyMqTQD*KH?xQ52|K$k_jxyk2xbHwHJ8_u0VTFWy58)&5!5xP{q>Ky9g$+8{ch^|P* zVj%@Kuzxir`y3%ZAVZXX8h6uZtY(Gn+j$o!ij0{r$iuGdkJ{-%|1D7_|FVuixrqJj z>*L;$e>F_jgB}%ov_lDOs5&wwMV91U z80R#XNG$!E0MSDOqUW>(i58V{y$qI-$Jnm*Xf2AauZnJ%Fc;0((72We>rcV(!pho4 zI8?-m6!%1jJV?t*5h?^H$03HGl|w7sL&T{#+~r9rDlu=>S2zPr_fYYY=)4W#zCHvL zcmiH}c|`$loT`=&e5iLKy8r6Rb=gR1uvsh9HF`$3^TADlD%dS|C~|954hpJSn(J3r z1JLF{prI8Yyi}cbOo!>#MPP*D*t7`k1n&V!fpv|C=@dy{m}ELGdv@O0b6fMfT(o$f zh1)|my&u`qSxR~p*iPJaq-;)T_AyBWy6mPa6|-ob&Z5JXs~C|9Uu+H5I)JEFfNOHv z6^qC<`6_Ay6)6k`@x0petz^tsnA7zjgm*9O1u#Z%=w(TPZodSiB-AK-sRr|ZBV+wK z>4g&l=8l8iW9>28cgiqZ(mc@a;-nF_nwJ=5QXUJWZxVUk3)$xx4)AHA_ae{_hV<^> z@QQB>x0Z!p7wpxRpZ!)8%mctT*@Ca<&d0nX;dHH#macv36r)@>#TZ22m=!-IaRr1zs(lJP(MPWS#1`{ntpHEej^+=h;?Av8Wu@+fsdfqbo`V9q z>P**nakGWCaF%73B#eBWh7E?3nSfU2QD>{olJU*1NxUys&1|5PfQ%VN?HWKCgd1Hsy?T3r*Z9NF#%o&3Zr z3Fre)pANL<86b$E5=BCN6GdPkATd#VW&)8}D}#kmwd#!EFSx95+K)c867grZ+~USm z7&M=#6<7v$%@HWh_u$Ypn?CyynPvg|YxXZlXt8NiGsS;BNMI^jz;+E*H2BLfSF;Mj zaPyj-tbl+|C(NUS@^umSz3Pf9g0)H+Hz`u#5588IjpZWY$DL)JjU$^`g)%w(t$cCp$bnLUFJA?NieYdAT(>HRJ!R$W_+ zH6JaF3y_$CU1QLHc)lXBvRS@_xQ4L7-C2WkrouXf~Mp9uMVnt20J^k&|B z&E3(F!pS0`VK#av(0E`-s1@Sv9VyqaM=`m0Y>MWlPzYdd)7utgc$*S)U~<=>?ZAp} zQMIucB;$zc?nLhEj9LOzCsPAe9s+_unJIJw zQqZmJ????^C5Z#bp}ci$0^x^-l?_}P+B&)hncDJ5G)XQ zoEkME10+cGpiB;y%PXp7jp3HDjLTC1oaM@~1Mxd`rHrMbgf~)ju+(GW}J7b2j zZjn7|LPY?54oUK@u)+P#)Kl_A>H6Pv;{MxHUmq^TH#F??VB+AQY7^Enw;`W@ zx=7nq%6~@N(v7Jvs2Xp8R9X{CMof5^W5 zxkd!W^cOS8+otE=qTrs*|l-kju>EBtRL50Y77@bbVP~?x-(3#%to;<>CFd|iW zslYWG?0KSlX}Y)FIC9-G*Efg8RoFN_!^@pu>WV&`Y>QpR06k@}dhJ5+ncO>?=Ayri zPok=kW$#CtpE>O)EBY*5Q|CxM5D-n?_o&OSYNl$}V0+WYaWQi9ocqrVyjYB=F zeOB3*4|`9*IbW(vx1D5TC2fEspYYz6M`s+bFWJZD=O9e;Q?D-wH<76xXTQEkHf5eH zj8GI9&q}$K|($`_&`dZN(HPDJSY1;CD@c^ZYnl zAYQe;`hDE>D7dG|jgKys!lO^p4B-IhwA8VQ21igaK38*!oMCj*1Wa1u8&2pUID)`% z3seMHVetG^5TNx}NDGh|J0U|~N(%&}6$S3Ve0|UUcpmV&4P9U25q>LFZ;f;^q$RAZ zE&XG3300$YvAL<&w7U-w9ALfAq!*bbZHpTo|21ijaH$ZemwD#{%n^R>$9ZA%Kthkm z|1g&0-}MLo$Z7l>OF&qM=^RQ6$L&SIog&fJUXX)T4=nQJpm9$6P)PTf`@!zNS@T4o z4#Mn_rh0B=rPng{BipK(|BavE{{$5_Xwh78PFw!yrQ_S!X|MFs(Svyt8w>j+TRSsE z(d+b^b^MrcUl;lic1g!p0~!>i#$)x0MSnp1{GYWUoWWPdFt#lzPb5}tYEf@X`9cqs z#vFBBTN+)5zHdNGGOQB8naHf?muW{)uNFomw&RExn*s$ww1G&9I~wNyP;X;SVOMht zNb31Q7cpG630P{nU&?tmpQpG!%T35e?ZQD0Z-Wl1D3i9?*nwuEVDIq0h>}XjU%92X z0kYV(u49L(K^!+;>=%2b&H5tWPO(GGml3|4(l#ug0i00XlOsxO!{0hkR)7Qdh%`*l zRM+vJf7Y>eMeT=ZuL;B-Kz&I=Lq4B^$ZC+USzs9#7zmA|$|A-5U9TufTiQB?P_D=K zLm9s=RIpf1v5=hDskfY&TZvvRvVVtTG*_l{-jj`WaLcA8fGz+|5b+vVql$$ zPdPLGoka`#bwsM&s@T($ocGb)(?A1jHN6 z)}>oMZuL&D4vx1~H9gCthD3iID)Vj~a*PJ|P;7(reLK+by1VIqKrV*05Xckuhv=(C ztXm~&m9;0S#2o3CG3ttM!Ms)_v;7xW!YDDTddaCp>novFmY0B8a1{(5;7+C;LahK` zj@r^&=H41NjZH#X6D|7U4K$_i#Ls8*;9nj|u0*J_TyU9)*P;%-2qFi#HwjBBXXvET zj?&7|EBhqeDM1Bjf0Mr_2r*wXI~RsbAbEE*Q$q5#4ZGgs?3h|xFK1`r#M-vZcj2X8{InRy zcJgQFirXvx5brqKf_xpn7p}PO0QDV8!xNbz`yza*S-)eEF$q!v4bxzMHIAOo}cRmkt zb(|tkhRcQnft?PXdD%qV6Gm>jF#4T=d6WZf2SH9FEi~bUQnNd_a?Dlkj~fvIh1$7h zPf?Z3)sS8ORO7EPY=69Jox?n7RnY`O7$euX&)7z+qBSy^V2}OS`0w8~Nh0 ztb2F78%aG6AVbdw=e*K-nDT0#-(?EsiksossUcYH^8Wru2D-A|bQ~da0g$|G_Q$F( z(7UNHb1Lx=SEpz44Rc5IFIzotpjcXCh0%`d*^;F0qF;uG&}OFB!zzf)CkzmjTS3IWHjEEcuSrJadC%3w+Ym1Xsez=RANt%>(uuUe5+y?VC!Fj5UP4Up z`yU@O2%~82fDqPmWKu9~Zz7mvx0IQNl8A^%M(dkK&Yx$a1I>h3rZYf?S6!fUDEM8h zUDBcL#e?m-YH7)BNcH9QKcz_wDpB)1*J`&;hqrvEPpjpx9X@g{S{g)9{cAs5kL!xs z;V{kO9AAZGCGB=auOhyySS<1E9i-CL+pc)GLbE%?7&yy(jd4G@WyF-Obm<>stIsL9_iD`^y_bLzeQiI}W#5KE+uW5w_?xsuMe z6Tm{J9BBw8c4;!ZYEfQ_o_Vnl1-!r1jjWPNB&n>B9Ajua21;uR6fh7X_y9Jf*$^DP zQWKWoZt2${CA7f@IPkX5HGS?V6OG>U^4QbGcL; zn7#3HkMhtf+0hf+fJm@($R2TZ=n`J6Pnh~3@*x+!x zn41&oXMJr3GiC4qJhLrwk4EDje(y4TJ3~Ge_v5Oi%*!Q0E@|@Ln_}Qd@@=*}Kf33< zhnce^L6^d#tNy2BP@OsA-O;~-s_Mi-g=_orIaP-t}SdNEWYgQxdOyL=xlDv~msb2^){>Z4?k_b@c@zdE4v9PJ<`bFWxF)_%Q#{WXIM_F#Z9mMkJ?*|s2k3e%kg5(@ z*E#;D&~gs-U5*sM_mj0KASTW!VrwY@WC_CyS(G(oc~?#ZDo(M{9{)-8-66J0;@+J| zA+DiFA}=WUY)`(6Si>O138m<*FTcB5dj)*~g|ldssWtM#vEHBmm)7_LL7og(`PW=B zv{=wD-+b80OXlU<%(( zhGsQMn4O`)T@LT3gHUU_2pzYsoe#pU`t)PbM0+X+lWv1U$*L5il8PQayftLVv_Ht4 zZ}03O;gV-GN#LwO4lcE3-azr0C_UhyU0l{bB}|4wIL42Ym{7}hwG!qrBB8<9`43<$^&j_BHO0z z^m?EnKBc7Rz*dUU>dni{IW5g$G;e6LwXI9vRtjmOK_qVmM%9M<$}@2v)kb*%5TqkN zOvN$6AgC&h>8d>@MtjRTb@9_NiDMVaqot+9XReQHK zL#QyFU-Ob#eP}$Q5cjWLM#ylfx>)$3WZyfPm2MO9f>Qo zDD=O5gonLO(QvBUUZX7oM5#lDe?nc%$z%=c_k6|c^sJH6rBy(|%K@%LCOmj?Zcs}{Y6jpg^!zE7CP zBV?d6FWRnCf7!QG@rp*@B>J^vRX7L>riM8aopds{%g1qIvxF4msBw3Jp;Jy1+!v2r z%U*Icy>AN>o87WSo|Oq5vy2RZ;89N~<`|;P`DloZg58eS$l1EwrAk|MQi~R=ZaOYjN<)cAtak;!KZESpp4_H}bZwAj}B!CQk=b4ks>7EvhmaTJy zK8tvFR?(UDZlj-$=C_e4$?~N z5uP1{0tAEoazC{`(wiK=Es9Y-HgCy!DM;Qh(moA(P}zL}RY=Ucj$7=MZT&cb~9vn+iF+*@9KHJ4jrkNfL@iU^6;l(xLfY z@0<00{9`9_(zp3Gqn?7gx`YpmmBqnW8bColr89+gq$IW&;oiWKY`he)t>0H^k`wBP zjAB!tXo)&l^JYG!djz8nyQvJG9jUPdeflIz)hD|G5hCB%?ww4=l*>bOq?RQ0H@%7Y?&f-rw|bFt^Lo(< z2R382rN_QxgqQA|v~$}Mk|5z0(?4zF0U6QfPhkj#abYnm?l@b4d&pO_E~ie}-q+Hu z4F7ieU(et;hsp!9TAClAF)a%|>T)V_gOSfm%3)tEKc>>86R6R!6Lxs*Qe*-qDS=j6 zO`+HM8&X}iXbT)`2XN5VVpAA9J!1pLxb(P4Z3eK!b4{aQxQBwtED4-&+4SC7iud62 zHy)d_cSt}^Nov)9fQK`a*2LyL?)4|Ig#_+ z;_$+J8g_UGCL_zMo-~jKVzQ;z;WSrEKaN-kYS$I_As=Xr6AK$85;{N8d{7qqM zF;8QQr;Y_glBKf^+WMxXbxS!0s5WX0iqrLU+<^EdsVR*=W_s;NL6|)t!}fzstzvg< zPSUU7YU`2@PQE;~_(}BwCM_2S3ltq>q}@pi2lsadrJykCGm{0a`jg#8_iGpXvW{3t zux~ee!gr1z$2Z`Vm{v&$`>q$zn3;hkCY}|agFwv?t`2{a-c64#TY6^{IK=C1q{2}!R zg@MWkbqP(EWN>s{;-1fKn-STLE+EI8CxlM%)4AV2Rm1)O)fsodQg9p?8|bKQ4q;M6 zMH{{^ut3=Hmm88ZTDr!FjGk2d({4#r>1Fwejv3NtH1lP+6W_60NiL-#8q{RQ$IhTm zY?f|xL8kI@+iz4?Uf~itf);1jW-Kb-Y44%WkUnKq;Wk~+*%HmQ>@665Xp5o@FP8oI z_IBY{0-#CbKR_J-yb~ff${MW{0^-^h&(>RdR!!UX>xY#10T`JZF#l`r9#Sy0)hr;e zvFG49W3n7i%y+RidOR)ZI~YET%9P@PND!r+1P)_x97l>HFXp#BWh7Sel&OeW>IIhH z`Os41r1?3vA||O}i{9k?FHu|uA~`*oH#T!9UU0I*>D+XGUT~YuOgt)!WPdzcxrouf zNYPo*o4ZD$y4A-FZtNmi?;*0(E*tqR0%t*&A+ndtxRHeUD`QK&`stB(_ottHc5-L& z%B==Uym*ZPxH7$ym)5M<8Aw1+%o6!FoRG497q5PopUe4REDxCPEC3+ErjcHv2l14NQ2n2+*<{(2aVLZ*~zu^inhGM;U$WfA*iQ~Ck(qvC>T3!yLUz@Y12eiI|V zs~-Ij$rprJhbxo_nr*=9S&9uI4={S+w|eG!XTF?m`NEJrNsLR*gy;s1eELj5Yp2nX zbj=O@qB0u!nYlAinO;})ID%O`>M@hb>T@&CLmRzu`<;3LEJ;Ur$KP&zs|G2h<&ej` zDg#qoL{>=>pL({jB_g&EQuAF6sB*wz=zF{Q_iu2z(voS4eZ}r5=xh@tFGzvh!>WmX zkxKe1Rh=pVZZGEX2n?Z!o{lHx{sxI%zi2V53p|-hfP1; z6CpD^C)rOdFgblWuh4*tegd~{^@#5midF^}|FM1lAHo^FE`CE5m%g}{nLcS_i^p~MBY6v?#`sja!$`w;+)Z1q1Jr|~x-B=0LO&<&am-+AN zSLdnvGb2fY<%pN)!N2|t?=yy^m>Pd9S`mf6FI>q>;d#nomK*VSI%h>C3|>ALNMSt6 z<+KWgE+}%rQ3P(*7>Vs9tM94=cZ1%RJo?&}7(1W|4S0iqefe4n4;S8FkuH~~L`{8F zE^W#~0j#WnW_&;g+i4_hlZs2sZ7J4n{9QoOn7NL&D|IH=*ffA&K|J_EOf zgh~7GE&GCQaz6L>0j49cS?WJ<0A4a7$9M*eU-`Eltpq?lOpd#r}EJ17uBkg{8g`J{&sA<2)mrrHse)6Tx zh{#M~bVOA|bxUa}}Oxf~ukTGf%YiVg|PISQ4avth2 z=GwwBsq`A)0r03XZ|I$i(LnIo)>&V3vp*I`z+RD$KR>CpH|4Wm9kNIH>Qr~puYG*{ zpP=GLeFFbHGiuZ8$j^y5nbzs~Kn7&IU8ggB6<2LUNW#qD>jL=?vX0A|pi>pXA1?&-_8I+pp9CR6*tXX`AKtdf#GEQ#i}T#Q$8!nHZfR z)W)Czv&}ZaP_7tQ#@r}9X^u;>P1dp7{LV_DqM`4GXc7p1+|)@jGYi@^ih#f(i$B-z zI9p{CYq{T#=aWfBtu*bwAI1iy%s?F^oGluRO72MX6e#YJTBu44{VwVk;2r$g1ZAzU zv2pez1(OdL6+DjO-WUoFAoiw!nD{3ga^D>Is}}9!ITncKun#2!5%+Qd1W<}`ue+!& zBe7vDlRCfM-87ALhpU-+gqxO!kh?1_*j!c|I2d{>Z74eD8dzJR0eRa%$74V;BrpxL zmnYm%tUnP$4Z6Vt6!1l5Elu$&Va_qhiMD*y8{{`WaVP=@S(= z$-#hR{fp(IZeziFlPwz(0jreHZ+?bOZxAgC{K#&MumV8VCI4syj7}X%6bb5U{@1bk z8Sd{u%RDIFnFOK}fSgQa@3yZS=PCdMr({4mWaiwE_Wp(7o4-q;PglUQ;?Va?rT~$x zDQq?YcZ9T{jmPgkq~m&G^*x_`KrRpPtVakxem-QOKyt@33aDwuAXPpVRVd=`?kbDn z-7aTPD(BLPoLgj2o(azp$Z*^zR{xYzp+MuU?o{?rQ9tQfBl$MVY>UEq^|$$CPJT9l z*C;|7V$$dy>qsjxkC&fz;yxr_KCxDo-G}F|p#YaS`MPWX*&Wx-h=Hl+%1b^PEtr$y zhVrf#ddgXMo(whAe)&kkfGOsL1dMAXF@fYaS~s5gLpGuqpJ_DSZ--;oP+{G$XDBdu zuR(MJcTuGYdYn(O)==V zlStEoDzgPXSPfniy{^(B^N+&#i^G8`Gsl^yUFS%iv z9T>JR#Pn(a%D-z0QXdQ-rM>{b5YjBhZ(>(;YOm2HP!oYPwdK=O7kC7t&-Nsxl(}*p z5m7VDUtYDMX~h6BFAB(bsu|zzyXxg~Ml)!HN7iE@tfe^D(nI4$hz-G`80XTrzeq&BWG6t)9c561JUvsh1MDzzxLRUY6jG0>h zu|032_?1R8^p>-|vOJN>Kpv1@`lLAV3W6u*K5RS%PY|<5fG&Hib-uyQg5zIy>(OeQ zCCUV%SO&3M?7)Il6~e-uLBz%gBgT5=)Y`l{76!y+)Y)_NdS5eRScH9ta1~DcAIOd! zzVZN#TN(+v!b(Z=!ALD{Yu{y&#&##=^hP9yocQ@eG4Tx!^gog6w_lF)^7^G3k)vX1 zM3zLeEeyLG{8e#n@AmXJh?PVsYX_L-xbWjyryr$Z61H@E&1^N2pqap#1E4!JCXo<+Z}aqP2~W*R38@ zkEcxP-WExY{Kgi*(m`Q{-uI}5NxRwtEtha;#xI5|5#$+JIcZh z@dO%+!JwS)pr~xfz>;{gkdQz?!ckM9+ayXmc3wDiFk*Brk#krGLQ9b5cedX#WjW1o z02;+)S!d_sv@wsr`pjSI6_0pxk+XY9+Z81hrf+HbJt7eO@B_4_xM+xS9TC^v+E+on zl(A2h0+1k5W?Vzcp&S@+w&{J81dcoB2IH@9Z52)`S`PfB%@jlMB2=>}2tNt1zb^em z(_=A{!1vO|he&@(PL^C&d5OGDb7JG?g2YF2fs!8F#gAmY9_JB6?oI5G{N<#w{3*U` z2bOq(g{c>Jc4Pr-2zCl#i)E(U(JoVsVxSngW4CF|Z##ySAx%r7B*zm~K8-J#uQgXS zH0H9iIcVJ_+qCb%@M2x;Cgnjrj!`vM4r z8qXGdRZnxDXfj2d4<{IFL_={R)7WVN z4jE|KecqnK{6x!LfV7DXKHnI)4_8++=hO=L=+bRb~H@8catlO&gb9GZ7wPJP;C_g-;=r8D_XsjY94ONpT zK_slIU(m8H+Z{#b*#hIF3t)j`OO2d%rb%_6E3sC7rS1;R6@I&t=NggA_X9cO-LTtY z!n}hsjx3Bp(WTFwY+lb49;68j;MNIrL$!T>{QNp*&fam6?QsW2o!#A(l<@hnV($>j zG+IQ2@E46?gBK_C?n+T@2OT?Wwdp=2?P+m#&70HeqFhObo?M3&mI^_^YQ{xFY$2C| zH{6^+O41)iMtlP+$8nw;q21&5VO#-Cd!--&0y-W=T-i%q90qUPt&7%%zn9!=Ea&Ba z$9*#qepHK@9m(E137hst;P>A0R3Q>6NKYv|C#=_xHgXrdoOh^yy_ShHIF+NGI zBm_0~S6_;sYd45qy5N=7Pf19HV&4XdEpDq^&4eSxi5m|Qd10O#LdhnVd>92I=~;>d z|ELJy!K4<;k%-%#X~q3iQDK2{pW5pDHKv5*M};suFLpL0c*4PN!>mkYmYcYGvDmWSO8%(6b* zPkK1tkg3hO{Lo-l_O-cJ6iCG4h( zt+NL31L&!N0a>+Snzd^JtQVesvM|9VE-lZS3R?wDo42tm@qyxhFh_qT4aT-{8nK*T8ZlNb_EgUX1PFCZBmCJr`Xfs82xH2xXjQ#;S-(P+U~veIK81j8^Sc{2 z6kw_@RShsKh{iHI>V1&w)_n)5>id)RcS(I-X$P(=9bhjZsNo`W*x8K3N0mj4Vc3c6 z{$*Wz5d3?+`W43GE7pm_^O8H|;;6sm4PpZvEcn`XelOY#d_EM^vz%MhQt%8PZpC(B z2tAij2>Q!(0j+KuYseX>a%{(USKoWw^NZ#tyfLvL5-SXv+UAd#4={S^U45MtUY0aK z8d1<&{4tcdBidzeF%2;c7Dd<$qYE?%P9E7<`vz2vvUTRlS><94XynpYG>4yn>;l=6 z9IjtNZ}pN-?7Q2a-`#1U+t0bxulLTNtSO`o9oIEj%nEeQ>d^+6jr}`~&=EL>O;o$w zFW^GrQ-EF&jf5idu|O=gX{ra0>m+40NWMJ^oZJOr-3jacghERn6V8`u-l9e!gEOO= zpxe3R<@ai%KtKZs6?VDn=M7rW_bT;fy`u_21w0xjg#yuJtFb-v!xMbNv5Aste+5#X zgmLC@QYy&HX`#1aVy^02WiQz20fHL&Rl670=YeGHDFAyT3;0XukA0HRh zbZE(3wA~1`-`#H>&oAngbavc<1E6ML(vY^H(XI9mOhluSuW%Kw6Wlu6gVD;H*XoT@ z$=mcok@wdxA@BZ&Kb4_hE85$$eD5msr{~IMj{se9k76X=DgeF*b#bzwPdMY@dlUIz zo)66(0sK~-XWU*ZiTI`q5+!kww3pwcx+UIiNUk&OOhh+)mTgxlK6VEqobsSKR8)eX zhVx0R&bQX@<0(%HH6uWsQOib&kE>Qsd?rs8A>G#Tvm9vjsjts$<2cm_X`d>78#tuq z7y^?|`$tPr%UgG^xV0ClQxFhUbudSJIoxye_^xa z_1Xj*9}yZR(q^s6@s%izm!3HLZA8WbhO6sCHo*fXcSph%Jwj~)hZ7BZL(A+(BJnw8 z@QmIDGOoWt4bsQgX%F&tRGOj+y1)BPKYmhIQshQZp`%v9lVr&bde|=cQWhv>l>9w0 zsJo97lq)yeja$GT$~q8tkqMVhvnxtxMy%B!w}Wr2grb`gsSKM&mme2{8e_ffdN5?_ z*dk;)%y%zmS}8U#vmpPxt!`h9eYE%{y+Vb`@hkxzJJMRp{ST|%tRbS*-_s99Vn*T= zKY`qVRua7}9bI3McPN}!u{w|ayz~mJl{fN!4;uy&ZYuT)#)&_s&gLf>$o1-hihI?@ z=NZ(|I$KKOo%46ujsjoc%Y$*`peJ0W`U-Sj_(5U1CY4-Gj4w`Oz0)5;8EuSiyD>nVy|fuAFP9PnF9#{R9EQ;nG+fuW^p z!6jjl8r3J0y<#~P*C3&vq}-DB?!4Jcq&JZEQ6tJS@`uOV6JQ{jj=*%7l99eVOD^LZ z7aF!MmTO*G$@E3itY2x%$?AYcO)PmBMKWHodg*9WxfXp8+$|~{K*0ZlL2+(l06r;2 zf~8jMC|a#0$`ty>H}Q{dR2 zI^-B1Q)w&o;U$B~m?nL$pkldfD3DJOG9BAY_+L(?*>HbwFn{&d&DO?Am~8DxYO63M zMp@ee)I*t$3a8}#W48?^jR~WQoJRG3zvN=9GRA||O@d_RM3<8)q#L z7w^FhAR=ioB$tZ`fSWOff4SsF!o_M353#u1LSEUjw!TI)BA9Lb0fKq|n`irXJw6n1 zz6Gj-CZXrS*nLZEBkh`vO@LXP)?vt;PK==2(RMXMqY4%A0@jCO#Vt^bV8a1k8`=9Td zPf}!b(tzuEsJ01-EqyZ(#kvDFtpf=r?ej&+u&SL&v_P<|1Wz=Bb82=v*Z_Mf;>&h3 zOh>`@!UrtKip*c3lHaKeQ))1J1hsY)$)1F5!>r_&R@fz5?M-OR78I2X zA&l2vP>fBj6wB{Grlu_uKI?z1bMYF(pwuCTHbP}Hhkh&8Egm3jtmU-YqjJf;0Vnv= zhR)BA-77*wPTHn2$_nXiGW`4zb`4-k225b08G|9n-vlSJ*jGq1)_75F9&GK5Vwj4l@~E{E!Gj&SJicfC4nnRxm=DDmw>Gp1AcKJ?QJcBH35#;v z8~tslTYnXL3IxyE$`RPs6IZn_NS&M*L84O+@ceLdWA=Hy>O5M107=_-fVe#mNl2z< zuwIq-0u=D)mis*dORPbaNF3BPTo(n=j74NdPc1mY7%0cg8@N$Vh1!4W1?vR4%zPlN zspcP`>3xqLf3>eo2=cwEKz;F2cUzC_o95mUn&H9Tj zPJ3nr^qk4{{HMBi*{0)}Q)Wl~o)ONPcK_C|;0Jvd#4NKy!`p*RRL*WJd?qud5zu16 zOoP52k!5c0YkjpZ)_;FqtT!1(|IW&2o)|`r!RuuYoZv(tF%x{A6PP7R(Bv7ogYF!? z{MMm9d9Wt$!TP5e@El@+<5h%o$Z?~g9O85Fc_FcpXE>0<5rHE=nIwUOi|{t{#Z2^T z!_*>AtY(z|rU3$y5NQZ1=;2DwRTkpk5J~+EkG!v3yI%LP&>?vOs1Z*B@u+3)tZdtB zy`bf@xIz^UE{1MP)Rgj*P<}Gi5-V8%k<=fskDaql6kd!XrW>aaQl_2{A6u>+8Y%?> zwcDlmX11`O5RuE2s8zmrFxn*)j|Wpj&$2)t=kq;~z`|`>toVYG?$f(&gNxrIVdT?? zH-e?*zrcH}ehO!%5L8U0TINnGZrpgrc{}-2atwOGzV>(}VevaJoKO%tH^_|| z#)jlpdp!9>eG!e9@-Uz*=|?<0DCLJdewB$(Qu;>DP*eKU_p5E0ra_c?V(pZr=C|D; z21@USDbYfcofI}5Po#kd&HO(_!}?#HIT8XvdU={*r{-&?4uS`0e!YlJe)=TLs7Dt? z0Mlj}u8!{Q_P28VJIHHlz{9|0wNMI`D3c(Y` z1e{gJ-C`%?lhWsSf#Qb0^`FzVw3nNL>R)4u;5g5z+~xXq;429iJ;)!96j(%MrodZF zVLJx~yMubKBKu8%h}&C$Aawb_Y(5Q4gnKwbbx4=&!wgR*l6}j4GX{3j91+CUr*xEW z&S8<9Lvy;lf1uO}!SQN}3!+C5`e476 z#}Cd;E_$f?VVvIZdW{e=jG9jC>KN`#t#L;7zm7R`5s?1=bwLS8tl}_@Gx*ks1@<=W zXlH*mdO4(^>i0!`eoE|WRBHk zhc4_o&h(7|>?momzs&&GICl?V1P)QeT(_(Jpk!8uLnryKi)n=ej-8lz1CKy1kou;% z_lxoGBcHC$#P4X#Hhl z?fQCi{>&Zs&q#T88dnlYidl5rI`!0rztop00>Up_dxz}HOPoA#oGOBNRN+*T8yW9D z*ctk)$N5uWe5}|s%Ug;zg(O=P18SNfZrwHwbZ7Om3$Lr;e|sF$2<*EgO|Q!i1(=GS zEDUk=S(pqFyW6Xo2jm`akns@P;KqZ?BtZ%5573|c?)ZZZGY8!4lrs6!BY|1O2souc zA>?tqQ3{28Qv2oZ{L#28r56fB(BrnoJfO0A0@v~p02C_$#sZVEnSFyiy}YoH<55nk zW-lQ=9kGYY%*pI-*W~#81u9D`67X%5*x~Mn@*5;P<1oZq>`N6OiR*2^m@X1JAX2Wo znXUj)f0}ulQZ75Gk9WoW6~p3F~C7zSub1pkPx81=G-AG3vYCDYO+pu{BhOe&&rQb-txeP}TxwbpIe zpfTWwf!1`ikH~S{oVx43d&3MZ_7m1N2~*zD1zR_`{@YlEuE*yqtBNWK!1$001i7>K z66~BlZ0SA*#EAL_0j)6Wv<2)9&V1^;)XCq^HH6OzG5h8uQ1|>bi}e&8-K`PxXFF1<|r2is#B} z27J3x2BluWKZuzp3Y23nV#eN}#+A{qxMBtfNMFMg=xz~hGtzMeVf|~mp3rtrqcFLQ zcfvGVz8ejvxysnXAJ8r&;@}rbvVkFHhSt!h&QsIJ!e|>CLhK^I{Mo&*4%Mzj^Tc z8Cbvj2nWml$tGA0guQJyPdME3qk?YeRA^!dZ{jE8h-z3rirG8@UkA8%AWQeCtRy=h za5O98nS6kIu$Yq|E`D5^LQtaUDC^U}HS-8}Xqf-s^~1DTUMw2w3c<=Zx|Fz#)+-<} z4kvjS;QGME&<((SBI` zhZ>jbZ=zI7$qE*3GK@zhlT3n1St=E(kA^1Q=}(#~g*_}9NQ;NbgWSZ=WYe_%c3R=d zd1LnDhntz({r!)NyY+uwHR7%xe{SEyhNL91u2xFS>4Ff!eA<-ZJcsa20?}l!T4JG= z7YzzU<}M8S+Mjs_`^eVIDCASn?fpHz7Jheb`sdZf{jK9TZCWKwl-i+K@Q*l5 z&=}CC0-}|!{kQ_87bSgBG0SSrB?2NL#6uvSgc`(?HO(y7`yHe7R$ic6=Y;nB2d|uu z)@$&C2==sx_)_d0Uxx{5+L>R@-^8B|g*T@I6bk6|envP_B4Sr-^h_G&{xs}ofhR-~ z$1_gH?GdL7aVq{g`n}}3PA_VO@Y7A1oQ`*7rpLjb+B0N=42CaH`?YE$ypOEma-$|a ztH@AGbKJh|^@w(i5F~@51m!7+wW- zJUzSF3$HNA^e5!>Dxgm!Ng9ZlQAs8DcDs|~;0YA`+X!WBZ$K$gWy*uTq!~#Kro6Mz z??2`!zp-WNrSysC3bRbt3sCGkUS}UCX}5)KmtW+M46`d|Iwc=0r30}*eocQs_mz!g zJj>*?8}M|8iSP#N4g|j_G&okbjgM}LOhpT_-{{rXcV-gHWvVnBy8;)_;-rPMCD0n5S2qPPJZwOohA!>& zhhI7o7>85WbKi#c0H%ocfRv~VZ$J)*;lLJ$A{5YtNh;s3z8Vr^gEzs%6~Kh^2v|D+ zu4@4QUckZ0xs2PqLS;Cnr!Oow@qUctbYh89R!ESiX+e_r9y>~u8PhaE*!sS&9p4 zsu`8rxQb!99B>a19iDmr_vqQkGzz{oI)8@Y>w6^01kH!2%sxi(HTZn*WtOOYnM35iv@*Pr-0a3!Br7 z0s9aB_eg;@en8+5aflPIezL;MGI!Q5$g$5l>r+W03RJGoEs}gbU-IOkHn(BdeyT$p zOD)7qW;;o0D*2mt+@{t|p$k!q&Ml7xU}f z^04)%=(3D5wKr9^jnvjo@KuaxS=Ug1KAtuykUV8ghA+?^-5>Ch+Gca0y`(cW8bp}H zRqf+%)&CIl+s)4|Jio1K;BnccGO4B74JI`F72`gk_A8N_Q%z?o@|cV_^ZReBs$e?c z)dEAeBlGI|>+djWH+G4tifhe;E(-d8kif!pV(JVi zgXH|aIBuCm!7T8e?)`pLx8IMmxp28{MF{IhoxyiJTX%5`*|m;@u;ov(uhwreafjTf zb1l@CIM1EP%H05hQBh6#b>no{+1oi|zCw#(nI$~)d-eh?qGXo;g59BhVpr*T++wK= zSIErYyp5r-NUonL>o-qZ(^y~BBX|nW74km|ueK81!p{>FL?wP3Q9^H!d_gNz(52?? zK_%B;wCvJ!s0_0^P%WR(G9{4v*OHMxV@2nT zB5kZ0rjC!Izxt!|Te+(@SGSt3d|z0!TDEd+l+)&yj2l<>*dQBaYz?+)yGk)hkY7xm ztZFG_s9g$VbDDEt#P$iI4&zFs5S@70W0|1L#uvaOyu%s5!wxzFa+aND<++!4SbBO4 zFl@P=@jBjoET)ZV=PcDiBO%_T1L|!gmB=aXH~svYA9o^N=1Eo67ij@tVAbp6pnB;) zJWFS&q!Ff)s+yUN{^!~;(plZNFoqZ&ve{k!H-^en7dYLHhXXX44g}-7SFB^%eg^2e z)<@m$QVw}Ko^5mS;K$M}&2omK_|>I~Zz94^Pt*EZ<#U-eIkhL)Hhy>}LoP$OCV_FI zrp0!2LL@K~NYAFOv-*)=!+L9`NPFSHzN+~6RMk(Ul@I zP|2;Eg4Qq%(=ZmWfZBw~`ORCwa2Is7NrCTy33F>0po&D-kE+NrQuzViH`8INlrf~h zZs9!%zZB@NYR0`R&1Wbmqo9{8a$9GK(pp({x{CY>fM5|XAyJ&vN!!RHo1@G0=!N^2 zlACM(xcSYtb_;@BUB|W9+wpAj<%%UeG-<3QSCa^%!XGc8>dUUA1N20US~uwz&l1L4 zNE~+fQuOm2=1vOz%`30#D@o5a`CeSk=%6nZy9G||q0Z04vj$5UzPCTzk#kRYccH%) z&1!&gc%4M&)8cbxbqIhUcsj37`k%fQ5KtXU-n0eO@IUu0J?%r&t`T8e!CW8jE6jcl zlW)AGX8GzMo2Y>LssP3zuWsOWJY(4JftrjA`=iOlcYD!qGXD0Y^B>y~G$x#o5=d&# z%J*77To}Eh9$E_8AAUcWN`9vtt;)RU@L!ba#= ziLM*d(MMYVFNS2VO2Id1J8qX<24*&zpM&=~!`p-C3CQ<~jpEXibsLOho)Xo4SZAgCFjR5I;X@g5cSde$K80|63rti=_xz~rGSYNPlgwvjL&nn$f6N#^U zBJ%Quj@zX)E{U>0L?Xb6&fn164$s8O3V*LKfhj@%b%$@mO~?TyPlAvHdu$GafV>JR%u9=4YS!VeOoT{bcRZo;?UnvozDn`k}yP3Y2g2 zMLRLNlxLosqeVFD42L=I1iGKXGR3i5ASSR5m~fCp@7#hAeUA$USwbI0I*MFdGF|V{ zYIE@GijuQfnT(Z-=jXXfD`)E+UF7YOxEvk2s`@;BdX8Jm^q#=c+Ij4>BlkM;pt4bB zBdD8?0_anwmW+1T(9VBZG?k8N&F}b`bssZ_rnfXGX;DTKak(ZulN|mXI1e5hjdhAb z3=-uYBo`60n~+z;?yg~8_%%P(rQC{g#t~*%4>$OYsR_>G_Q7`Im>k2hzT_QIhSC?L zdis=JOy~ZPa6*%G~8(|e|KlG`X=JlVriC4V^Hbn-S31!>e&;q2}Clmni!Wv_L!86$=Z)vmb{9c zznx;Rc`woSbDH>~ry%4Z@s9k=NANk6y@>)Hx$`O?- z=c0W(8%fHKD)|H{FGN-ElFny)Lk2pyQ$HdICMEfOgrCsk(*7DaST%ugBfY47_3#!JrUo+{~jN45V$9FggUjvC4vU9 zOvXfXdRP-P*p=OG=w?Sqljy@*dktZwe&f~d0+H!++2y=8$g^lBpl8Ae! z=#5;@KBtQz6le10wg2v@Jgv(r_MNQ^Rch;-VQskt z39Tn)3X#B!uVvc|=sTk~j17~DzjUc9$#bEqC@p)QI2pG#vapjP@4-_0-P#HE-OZ

Y22#Ms6|=GC&+1(Dr1mKcH>v2A?3|6&kV4XJFw_CXMSaZq}aM73g_9}57JRbFR!i9 z6LPmtC-hw^bDCU_sQ|YLd}^ejurA?-7oaY{-P+a27_o@4iD^+K7342aewfbhJc6&@ zUp}-{zz-|zaB>+BD{^!M`G@6Ze1-_pm#q?l^@~B!=T$@_@na_NjuYNM5$es^y|aLf zb0f~^Xy13cp>!B2jhNt-Aa`&Rigdajn-%RvrwU3*Vi`HB2dhp&(R!W$R||LAL`%V!+c#Km+nC@R8~9xuM~tPbhrkw{AT@1kx8 z&_OBnZ}?I6S1Nnq_aS+~1?1bx?(C1)Iy~`}jKf|E+7b98=F;Lm8Qwqki%d(h{w4zv z4|2)+j>@{UQ>DkZzom7z`jkypoZ>Uvu#hr3j8?Lr{=~Pgbx>CotwD zhcbXvB{cvyCiI5f%*Hd4XqfgFgYBmwsxYR0b6B87hWtiHw zxL4jI)&5kz{k%t5qv&0ty^tX=nRzSR%~qUVe2Meyy!H-5xp=( z78#jqGSFjHDU4}2i6%dOj&$E&IQ36S8V+c2(ZriY!X`e65T!mTH|zeRy|1nKc)zU~ zPWB8|Jt;kEIv`{|hvXknD4i(m@bkiMYCI5z5gOHk!`xSH>*#I0rogB8b!*ATHEfJn zWpOG!__#pMJYo6EbYGn5*$?vx5uCY_7Y7}@U0cSUt(FYgDa=EF%p^{<)u-#OI0&a<&FbWRT0 zX4Oe3*q5P$LM2e{=5ZSV@ywKKOXm~`dJ=!BB99#%-A}ONB zfK6<;4Y^IqP8o#h7!0AF%ObFWP~*(WW0~kK9Kzy7H~3vpd8tFzo-3b)jT=%oJJL&+ zlCsQBna%HGMPOJ02LCDXBD3HeMlA6PztV7uSJ6uyGk48F3#JmvgoN0m?HP&c#TSZ}%PE0KNFp87#2J32z>`?I$!qE?U9JBXzo$Fldw=iu}xA~3afp`n=^YzTX z@vdA7IotNB&>mvUZT{*zl?h93D4cjjHQ`Tb9m6Uf2{G^Odd<3KUoXD6Zo#){?q#_x zKVkMtcFFLUj$JQ}Xt4{4Vru8f%p0|eu8+;jXg8Esme<-|q%q7HDTAj`1flwL)3bvV z1?TKoKUAaDi_x&E(_a%2>h4VjLK6vWY!mqUYF}wLzfs~;%YKL0DpgG`jVFkst;osq z*y*m3?I2>kFWFSMpo}+LUm|k=K)3_}J*&j@q@viI|3(69nh##sVb!q|!6N{vlWK^_ zC8H9NDk7VHOR;h3B!|E(5pf`7S}iZNJr2=rrvZKUQ7ZFuv;Z`Z?gpRJhp9tN3z zv#f5o&+$k<>6$k=?+Rh4!_t~BqFuLiBTY>gs|bUHMG5{!c!q{6p-ueMb`rMlXTcj+ zP;0?xX(`l*)URZ7Fo{3C7Lm4~zqvVkyZA<3r&?g`Lyx9f)D!u6)%K>Y7UCT~jyc7u zz{o=%?r##$kqK14?kmGIv#wvY($2La)ZYDp)->ZQPPYmj#M(T+X5V!rcm@y|eqGvH z(5OpGP&1|P!1ceYf^n^{s_Ho_a_~>Aj(S`6k$E}M*T=#x73ouXw7~S9iAR-+35us-01kZ+k_T`Kh0kGU1+0c8pjic>L~y)t z9FXX#ZNEBuWV2e9l`EEX6BHY~k6vRh=?A?fW_BcNyhKPg6DO9rSHsKH)|xL!OztS; zeKph)9gHFn{;vAGI{Yi(zP+MtoKnN;M#U&r0!J}5_ZN4dxmPyW$;vokB5E4x<%=pj z*1Fu4ji<}t(ux_a&yf3{r@(oLo!^yMaQz_>A0eyb1H=?`vgCeS*jmAep7e9dCpaI<)!K?R7a}i;Cppiv$&I(8aveqw)P482{n{2 z`%87Q$m+TYjh_-ytx5C!tRocQqTL3XG4FP>!%Dd#zDpMO7{%lYc_CFyU&Kg34*brhzDWh+fY8QlbH5+) zzu7N+Y3})!Cy~=LCZ*EtYa0!s*j7DGU6~uedsaNA16_6)Sg3Xe?y%X+c9(XCG!D^2~a&_~7mceu$z>+?3aIaxa z=9bHumMN4oR3JMrQR`m4FCV5WuOJO)Z2S{JA}pD>pRll_kiMRW*cZLmu%Mee>j7289=6$P%ompZcH6!DSO;d|ysZn`Ogyo z8~s;QE0UkC{RFNN>?r?f+YFd`5y2-sfURP$en$RyfryRQf7IsehmxR}MANNOrz^c& zO+RU$R&Mi^v;@7Nw^B;?fCkB4^;3fo>uYL0&U_Cc_ z-eJqHxJE&GL?#l$G?VX<3#ctC1~)aSE0?#L8ph%}03NZ!A37nxV#Quc|AmZ7Cc|xQ za#9aIy>8+qR3c2;0@5Q+mT^M=?-WGdS||%O=%Me=?`cM1+%HVa9Wc;R zEq5@uc4vWmrsaDbZJ5YRLSRzTHBGx11OWxp`pBj>u@+Fxf!dZd zuef|>=hMsMnI*5@D+y>Px~s&MyNvCF#)e*)h_gV#PaQkV>8L6~rZ zQvR@vmZ)~QT5EI&3uaw=I(^tq*4#@v7jhmCLZ^u(B21x)M>w70L5jY9vbJ+vwRKwy zLruR~$BJ0Knbuu+{!mJGi(R1*>oP6Q(P$*Sa*>=xeAmFNHOau^yx%AEKVo0;#+dj#qM-Xw#a;;cuFBvBj8OKL1By3T1OjuaY^Bdx; ztf!q!5a9aa2ye6;dgZL~>v^f*6VR1&Kfi^KWdypGoc=Sdjz zFb>ZsNR08XA)aM(R`N!Yvi#z;VJF_J6>pvKfi}S-!pmZc>tABrrN!}GT(E zX{@G&@%}+$oK%ZrcucynjL?Z7gXBtZEx0i1?>3Y~k}ismF^M+Csgj zw%ocxfZk6t^~`m2QIMqyvhd@m@ZR5*v-q~x>zuRcw7h$W^#^)gH>^*4O`O=s?88_R zpeyP1;)U!eQ?HWT&mo#|B#U%q>IsN-YPxl^GSKVfE$IBKY@b(aCt}j}DC{q$ z>z)jvPNun1n9x1P(u#PK)64LF{A>kWkeJlCxT*n) z)h#e$EbLLt`s33*nQ#iyG(_hnp2l9O3H!xzYdMysL2Xb{;G|BVyzQHn{iR_|Dw`{FqsG03u9#70~05>h6kt~O8 zmo)QKg?jeElzO|RN(SAvD9va4efd5}D5S~~ZiB$)qmUggCVHBD3%->z?{YiO{3|0Y znz^+Z4(##8|1DPCwnM4sgon?ckuJYA+`Cm9;Yt1{rBfnQM6-i(rWgn+Y;+W$H1Vp^uhA1+*Mpk(`!6-`V!&i1-8U{qgDtmlQX3ZR_ zLd7P>Cr!2&D0NrNmG2=4L&JYQ{))N$++&Orfux9H?P_{+4^Vu|HGLIoLNVjVaDVjI zS#E0dN5J6$kArZ?Dz@(^DaZ)#YI^o()qVslfumhe9@dpNB+`(Ef@Ca9qdpDc6F&t9 zi`vuFb)ypAZ%zEE7EM#(^DxdUf``UNCgq&^ZbP#&>U_RUSb2%RGBXSFi$(yeJcobI!`qR6r8Ze8K zm>2ao&4D2`1v&|*g`tz8(pm_W1*qrkIoYwzg+zyM`YDU-E~f_E0&=%$mCy;rEvojY zuW!)8bX+m4qM<})`^8h&UYhO(U02(SGpE=e3u)>TBM~u!BYKyKX0+)U4R?PjqPMyl z*TgS8vg8`r?IQ29jy5eVe2#|N9wAjEV}0jYS451s20t}Tk7}$?%K~(yfIgCt$m5T0k3~H79K8JUEbA3@J)NG)p^!*gnfz_l9=neX*egV%=x>4`?)b^F9Q>W zz(QwB6lc;Z8wc--hAd%%F>Y^HlG0%;3py>JY%& zZ;nQZGPO0tea^G}cQA37A3AWy&xW9eMR;%XLTza&Ej2**x!&hDqObkrKYHeo!Q`@jQ{t?TN@t;=9#Bz0J!=;M zzUvNORd4Ti0XaXp+e){%ha@80hQZB>A9Pm6D^$H6V{W(5r7(%nbUHz* zH2>!-ZIj>ND(?JJC)l)lECfNI?edf7PGkbeZjRe%N|QzXkhZMCsTvUQeqr-;xx z-3-z`skv|P-0`ZL2Hm2c60`|5oPnaLm9-CJV`D&qmddS=N(;*Y0n!pODG?T&vUnfQ7<2SW#P1ha6p z>QiWJ`SA@Dio zp6rtMsvFc!`YtfE=jtsDCssFQCsr~1=dQA7ymvq)U_tN-eFO!TUM{G9auz$o5VEhi1vMW^~PS3aRbIwsZ z;1!zxR%Jfdno)YIb4vT_FfY11IG0i}NU&^z7R=GE)2Xa_N5H$D^K><;{2F_H;vDjH z@C;d|YK}$}uN+3<9OdF1``)Fh;hZ{Pw4mLXyymZMaxlz{WKVm#|KKE{g0D>?{=b-0d`9s;YOUxdZDr2 zI=9Ej|K3c=N2vamqlZSq?TX_%<0$Wb&GO8v*x4hh_4dGT4f**7GVqv z$HuT>L%x0#$7-&2XZmu*f>7b}?N%v@UT8qPaacwlo(v&@ctH)#Sm)~&J81LK4L09z zz)u77b()DCR8Sh*VTy@+jTs_cBkx=}BL(Tv(~BbRstwX|9{{uclFgqzwxjKEl8s48 z(FQzB_CP1V$}fy-X%t(7ZB?GoBOsIA`tNiOf#M%P_w#fk4Ztp1uhm2n= z>W7XsVkOzDn4NzwHbr#M;qXZ#??Yag4d&&-;U%Xq-wuvD9{)FK%o)0cqn~>T%4l)^ z=+8-bhAaYRaL;5?b{zz`QcHxAK2LMdG;%K6ewx0`CdtIyr^@8Vmxq$4lit@v00*RI z+^CaN+0%I(&?z!f%|^8Ieh~h18ZAqzUM+G)_!9LYT8eah($q);HQ1e?;&XX*O&CM- zBuyc`9p>*(Eh^{bJtYQwnq!>c>#urc_(7oLhh10pu5?gv(@XMq_IqRT55^8S_jMHFYJ*MO;7=Gs z!*o08@UmGJ9OuJ?_hJr5Rb^B%B!cWdv?Q+gle_|y4$D0yZrQUhD&xJx{7+E9`c zWIw`02jF&d@eLbi)~v$K@UAymI0I&L)fJyeOPeac4SniKpQM@ImXh#GoF4(a4f!0P zG43^r;ta=5*{tb2Ty8UOB(L0jxd8a2Uj6A`5I@oOo->-aH`vBoc;|(~*iKb72~Cj# zp^`8}ADjw(ZVu}Ghwh#G#45HwS9Y#oGos!GWf&#pm4op+=L$F0iZ`oA9KuTqrJ=iW z`q8*cOj1NI+PsG`+Or?RYNmIbbjRT?*1QY$tlRSm4MnGB6gsN-?Y$W$1RfiQ->pG_ zhO@SZK@i|2q+Id38L3^uJ|6kq$}c(A916?gI#(CLC3lY){$8u&G-0rHkyIS$9W?=i zVksKN+5pcPY&l25bY}r=BDUw9-NLFgb!Nr0^2>VTnJr_c46wrmjz&q?2et+2$!IhJ zF4=#qnI6)a>Aam#y5Un$a3Fa^lgr&Q`tEu3tMVfj_In7=;^2IQN=$l$2l69ImkmRC zji@v%iJ6mF=9CiK(-9_@u=`y^Rm}N;6yKLr*1-$rQ#OCuH2ftGNk36JSo|h>r(bdW zD2rr$&Hm@im2I_J_g%#!DVKd74dOwf60#Y%zQCQ9(Y_n@?cx+W?_Qd-JSz|0Y{6*E z1n^nN`Gxsz$4{VC^L2LI^uEb7vNJmJ{YxqeM+HML)lBrLTru@ zo}<-N!&8zR9*zzr-r(W>a$oiFW{wSGCqCK~tnDI1+4cHN1Z|SaPIMOJ8evLP8)&9A zkS^x1uu%(4IV;O(UVNkBaXM$*hdLiSrPVPGnc1P8f{oukJl3Mn*9d1v1#2?GrGZD{QS48B(cwl2HomZ+Zc+;)311VZ}3!_X}O?iR|)SUtLB8D!y&o4FiE&5+*YQ289Del-?cw zZHYA3ZIW|*oV4ZfkKe^CN;Y{6)b&l(S2D0Q%yL<4!O07W)z)=>p5`5#MpFaWipIa^ zYvw|WzPo>y60ytw=n7N(38y)pDPgr<`S~*AP^sGyc~1^g0LYhT$w|}ef>8lJ zjKy^n_FI|d?hagQ;|-l80VW;EJB^jrDjn~aCGK4w!OmLANFSi&D)j8Hrn1oE5|+{3 zyQpId=4cChkZteGNCZQucO_c|BA4?d_`_N}W~mp3;Oyodo?F(L0IipNJR%XO65VKqBZ~|MalX zl|7Y)vWe-5`e5y)SY&{Xa^(jx~{LXFKvl zxSvTmb=>i%+B)mh?rzlbo)k+e2vh7Xm>rN15^PR%!zjgB+^)x zcq*LZ-wH<~xY3XN_1Ef|Q+Y!p6{N3^=_KHQK*`%*!6@;Z>9X|Mz1g>`4$&t->k<@* zH}K%Oruruo1pd=&&X%LHa!YDNeGm?z-Q8KH3_aA6 zr$XKgw|mZpzO)}? zrE9FiYmz{{)}@A)TaKwT=-47yHS@tAX%O!ek0(oZBqG-PVRi|6A);@_8yz$!kqUd6*gAMxc>Z{&M_1Lb-4?Svz$@`HX4< zsZlGnFFwW@=v=bJ&G{+3J($}(n#uXUKSX zh~4q??pO2K{4t(~@K9#@ZCZ~>y(lLAFJ$eT0bBNKH#p!l3r2k@EPWL40{4??U>*-v zt7E2~qWyZX^(oPbNFEwtQ&f~>>OF+v{>*v^)Q8?#AelO)sw1E>$#)30(R+Yi)UzCg=^K+Z{E%wN~vKTwVi9- ze?hl;I~#t*9@M;EuM8d1j|jcKv-3e^X}F#j>Z#Fjs}m=7-^Am+{=>evLtCj@jr-#{ zg;q{-;AiUe&0LG1*(ljIjj7#zj_zznXX>Tgp4q3dBWXzgWq^{YB$qOF4Fxz-pE&5L zaI6rVc#*_8=_r!6|4}%QVELV%xrfC;C+?Q3(2-jsq1UXn{Wkiok~oSK@-#8eI07%Y zUJ%6zw$4RilkC-O!F!Yl@ojULgfS|=AO1~9zqvEvQVDB5r>=18#3SVS$@Lx`a>VsoPmG`bkiUG_?fZkA2*MQ#G6MWnla<^ zKM`Kbwg+L}#Y1>HS!nsnCH3t3v5({>=XQ{gmc;8i7Hr{K{iAE@b#~Yu^lyfuQG&T2 zN--TxVYNSPSUA$w)5HQL9a6Ze9)%gxvo*Ao%5kl2>$yP+U4Dc>#;y3Ru@*sk4>G4K zKy+ZnQe~Gj2Qk)-bTy{gzXF{7qFJbKdpp z4V15YIZpr0m6yjeY@TM#^76|r{BQa*QzBifzV|E;M-#XZ0a{P%Zl-Uyidg#8`wFOt znmp-9U&Zin6`t-&6J$}|=?f@6ryZr?k#ij)Y@+Xsp%wcpGo^>x%7b7}T)+Bwm7tVhDwB=GjQ1>V#imV7=l-^Bdijm# z9(78j;aI)RE{aWru;Jh=n3-8f0zi^jwQc;SY4{ZbeTi%52*o2mX&4p0?gvkxyzvy{ z9-DM_6cNU3#m(uK8!3Ik<)i8nmg{`XBmoJ(Ts9^}LV3`}+b+l1*THX-t8O>%+qg-6 z{MJtqLF#4KZ_9MU>sICdxojTZ^T07^F!ldtI2)P|(5!kwrS@*P_rM){gW;!KLb4bOM>1J7!7xMV?{>F%-Z%E(=nU9+ki15Cv6gV#+aF`u_c|ZQZ9>{SX zs=RK$+%2Uf#MqQHFp|$%>_Q+2oxHNY%QXdgoKMUnwa?64>Mwkqq}~*J7g!C`ZVevk z%eeiG?&_|6X_+}gxic6UbJJNLWZ5$JP@70RQ-7Fe=#&gMY}=tmlZ?tyEO+t7`sF}i zq>2JX9Y3L5e0{{JdyM~pmN|R|G_uXwEoWy_m3U6#)kw_l8jZg-=-TGj6?x8jt?%jR zudIW%dxh(1BM)D#upWNQ=S*WR2x*Qcvb_0ar0H1`Xf!=M0!F51VjN*^3h*?EMXKHW zz0+V79kb;g^)i}TS6|r#Nr=2DDt|!8F(q>fI2vvxLZ`Sbr)tSX@->+DSzo){#-_Hu8rCO*?!gF#47#cr`*Z znT<*F*(6r;8A7hW4PvQr^p8L#m%W?$I6C!SW@wnix=S%h$GS%7<`qTfPVn{V%Do@# zvUjtfyAYbCkYn&k!u7vRB8X;^2q6Y1L&oQ*lzPP)4^!%HYdXH*+GF8D@vwQD_BCP= zly8u_OFs}wussajZo!wZ|1`z9dw(9{fZRcrdnYToa+z)-(Z;o zELYdBFE;&1DX{b;`PT%O4MGN3opP>9+rQigGT+{c&qB_Ml9HTLpL&Ab75dZdXU1GW z@VA0g%EfYO;{EQ>aNVrjZ@-vI(Xz;1yFc5~GpU(JKs#g6vV9Yo)Vcw&wvPv~x*k}! z*;Kb^MoO4MYAkjf4}RlE&wlN%B0d5i`@2wG(`+RklyD;;5GJ7EE(t`^nW)5TQ_aD4-tg($3Ta*e*%bOkDmoUm49lvvd$@Aa-rk z+P7WbatUH8u?7nB@)KWk>olez;- zX#^sgrVyTC@!NBA27QuY&~GzU)v}XlXUytwyrR|{n9<+6kFvq0vwi!SpPlHfSw_ph z7Yoqwqv1fLgPai^HmwjU!Km9SD(aC5vWL;_%P?^fXEGPVr`JxVk|nd5jy>VmxdGHh z+~As@ov(i`lOi>xt67k)qFOx@YE%5<;A)i1#BoYcRDyG3xsqF;G#>;+Pk{U-@mQ_; z)dz$_m^OO{nGcSUqNbw{njCwO9$zvj}*~_muiqsQ&ynUlupPi0OYr=bj>rMz77&U5)eBKG2YCA&K&&NTWY_@hGgd zO@922{xn7~bhYjs*?XA*Dr=C;^oIqk95=Tx-GCPI)tyooZl()yY@y434Cj}Z6`QWg z+ZszMz&KpUPzxfb^YQwGgVAy}GAC!k>!y&QNLOV3!8|wn#j=UAT9ja$j%ap+IpH7$ zA4SQL=!DOg%G?#=5?Bc#j*~cyQq+y|H&!N03XE2N_{mY7-BbO-RdCtdr*&WP_5jV;EP)#lDCvw7{ zZ3gKS@wTW=k-*+Vcs4NueUa%ylu16ZB8We_`_)?uCkQ8l!0F;jTduoay31r{RX`j+ zxB(o+@2L^R=k9aAdXUV)GsakVy8dpuuev9Cv&S0IK@!94!(D4rGVQv@6^iRXJ<_W$ zxM}CpX}&yAbM@TV1@?QJ!N6{6tIuQ?61bHoKC)Y`bWi*g#dhJ4kHC>r6&oJWwFzl# z)To`up#lTtZRDEP&j{>gT%6`aRBY2+&3Gt~!2_k50oLk?zUwq*6Lw zUmRP`tE0i$XRT^4FHNSqebGhbO4V`@{_l~Zgv^lZz==P8qAq7aJuR?Gb>FojU5*@! z$zGRDM-F4QUCnDAEreYm3qA@*csNcV zmsz*FnWhi&nnU`ITLeDa&?n#!x)w@#`=6riao(XO1gITa{M3ZJB&T$n11>L1OaMqS zZ50UHH~P=b>_yroxrGXE!bRA^qI(*i{Q-D0OH4!&qLAiTDi0IDTMVdp)j)=Mi3v9$ zHjuu$-Co{0=`H`@XNWg>Hk%Q_Goo9^%|3PQ6qN@!A=S=kmbZ@sYU3Wq$afSNgzr1|-Xk4N&wAcv zQ|!M2^O_FsI_O>Wj08JP#Dc4N^GByt5@?iy(97cqtlLHAHGCxaS&kYfGJgogeF zHW;?z6a|qz;#lU1H;DR0*K!ZpTC3lgW3&Er3;G#Ph4*2kAZAmtipm$0kuw=wuZke%;TSqWfl%Z)mky$&Fy$i?}n?|`YHr*hpRn+3|ZsYwiC6-zL=l53wDZdS8HAm2`}ebkz!e02Hp3H>p+$$4EB z@N&5cCsl}_2Tx@pw?KcjWZGovE;;GTF`8xUe3UZ)&YtP0+8^8G-uSBcCIJWf2CljC zPVh0K8JwCxXBJz_Uv3wR7M0S9!8hN<`P(vdcJ?B^xqa}5j+ORqun3A0oQeKQmZq0Y z=(xRa^}N#59Jxclzpg6ArZ>brYVWQ(Y2c%g zAmn|Nq@m@J{aOlL;$Ss1Tv(=?K60cpoo)$%$HOfJJFU+9 z4uFSgzo;La8as5~A2}xIJMxL)v1C=3($5n0eN!rdD>1qk+G;1VZ z$_f-z@j_*g>4hcK1hT$1s#)B_{0>Z}suoSrK z=4O-<_jihijkgMXdw-OzkHH*d3u5NDw%-m=tT#hFGX&gfPXL=}S-&Tj z8g?)6ov~`U=zR|)-4+;NKAFE4@OWT>7yHe;$Fe{a^)X2x-kaQpr(4)8$BTmfYsXz{ z9sQh55oDfMVnQg^W)t^<-1ttJdcck%DzAcL5TWbKyAA6Kz26abK|9@wgw*#b+lhj`bBrhHvbxD5Tm`lmZ6~#v+>0gUhFI=*tk5A|w8BXWruC(?VSHqji`e)Q#F{yAx8xC7yjyDspnje>PiIZIe(+GV#X{1{jvTIvn-| z?}vnSVv}{<<)Sm)m6=0qrgmmHbXSt{_X&4oR2pZ-ucJ~BaBu+w1diS)v+p`aF4${>{M9yMeKyRY39tW7cb3I`daQk@T1fLMl zqT1nKffS2w>0 zO89fHH=Br}?X)G4V?{}XM&C0IE55jDL=jhvFG^$)NSf^eK)xCzAKBK{vJJ6!OKSf# zi7S2tgI>&G5==QMj*6(;o~;MpPB3uDQm#pUgT!LXP{U^v6K^GA5WrGMwJ zgTR7Si^rj8_A6vQ5bujDD+=QsT1x5D*TsBzbRd|O70Y6~n|`bK)!UhP`bV42VR%Q1BIqZzLEW)ZpDz|d@Hp--y~s%a)(p*ls32omzc?&4 z|JT7`^C<@JLrKS80LE?dvb*ySh!|dC>U5Rk4|iuu{`LrkS<-m!+C(bExesiM(AvM- zH)-)Q!vrSz(ujwIYJ=A7>bY{!@;mR!qHkQWou>pY7q6g;I|?K8ub$#UFGfM@c=?DD zoZj3v? z%k|jQx{+AhlPNUyMH7QqW7w$NzcyGgSL#c?Ndw}htm}ujEJDeHcyIwi^xVDnsHPz>T zGMKx#UyRL-yDnaMd-IST;NRtP`=Y+bv}UTeDH+rXj0wNGNuaa zpY9Y!3|b|x&1cve7qNO_7O-BDj2~`)uxL@aW5*Fm8{E9j3l%xNZxlzS<|j_7I3eBz zeczj_&Y$0@f4Q&UEtwKAyd{2MWC!Jx^bEWi78<@2_oPy$asInxO40%xr%sbw2dYCb zG+D;JBYFSj%}JC=6M8xg(Glz`EgR-koi~O9kT6c#OrZf8sCc*&Uo{AcF-V_g*aWvP z`waNcAtMRr_l5ceA#o6LQ@@X8>;B8b5-a=ZZgkvWMCX zF?G?0xH{}~a4`K)OQ*Bs`Bb5E?63>s;(a(2yarXDFUQv58rsh5a7N)-Clu&xoY|;Y zJAR!>_#y957p@`RLw^@OX2;oJj|?H09yBUmPPVYzDj+2$8{e4*l12(3l>~lVfBl_3?*uGJJf>|JS~GG2dxO zYDJI(uKt!MplP;xU5$7Bp~BIX_X2r`v^h`V>jP7~(~bQx32_1dCksv+GK;rsa8IyY zC(Vb*5~pt}f4Ck&9(q{sC9JK^mGJ}48pq>C=FbF@2UhVnA7W1tG*Nc1N{~b=^mrWQEiYd8uqfvd z&Dips@89eA*SMW%L8&bFeHHE8vhwe9qcw?AK_%ltm&}k8rz3H>N3aBI}%bTi|sW?oD?} za{h6gTj2L49Jj|`%eDI(W;dX@Xb&(4!~o?_gHaTHJpS-hQq!W!TCLGEfx$yZ_cnZ$ z1V;NH;E$KY4w^i7-kUZLg_pkr{;gg~FTi$?yNjbryJz_6|3(BLd;3PhyG~n?N9*H3TxE#CXKe39>mRx-ep7{V=Mv@BHZk0Qm}(X%|>3KK0h9-@y-Zoim#mQ2fPzeYI<=~hVwC=5~Qv-oWSFMeoPA7 zEbG6Y`j)?Y`zsl(ywT?K6Dv^mp7`k_nRY2Xv8`&Fw{?H?;CJ(Ta_!gc#T&g&pQ}pZ z>l7FjbOTP(o)KB^l$2o6K`eIJdHx^fJ-$ZrE3yD5q%k&I$5zWvG^q6)wJT;H&>tGZ z%a!oopbymHe;(2M?<@YQMCuId`M9#ts0}Ee5lY4u{z>gpw`)ZY}H?VStjb&EC7e#t-Lf=rG&yJEO>y=yEj|zB+J#?6WrUDG5U{w^GzwoEaqF&1{fv) zpG7Ni2mzMsIRht}!4URCRq=MQ^Z*C#>MF;3sIA`wBWsDx}ci&XBY zq&0xNO{2zJOTYd17Q=Wh0*^gFCg$}bSa`7@`VqU;A%S_gJ1PfY$S+3ycqg3ov6!Dn zV^OaFroAWHF7_owe<=@H%{>jSu0ym)A-f7^F`I5e=r+&umY;t_^zAtl#no=H|Z^C_2WI(yd#i z&biEg_y?^k|JIk;VKuQcu5FUCQr*Wv#6w{QWqg-c)@1w`6k9MBbsE;H%;8v@K_G0- zqbFuJrc`A@_}){-NMwBz-ZE!UN#8Olh21pB(>ZZqcf74Ck=2lOv-Q))L?tz~x0}{C zUlPS?TzYJZkI>3R9~PfvI-qGhDPBpSBLz|%Wc=lXexUK zNxe;X$UA1%zi3iEn^HqPD_feGruYiy0F$O9d&RnK%$2aqc5{H+{O*zn4W5SoKtcG~ z@ff3rJ8C+dXNmoPa`m%>UyLzR-Li4l^WS)YvN5s$!JKzq`6UyD4b$Hmpjm`)x&I@VI-%n55FRXRN&GWi%+1Mkx@qR`08DO8*0wCM9uP=`lLQajy z4Lkzd#uNYB86?QSJ`iB0;_9nUIghdqfzZ7#;L~Nv4DSNdag5Ns_?rI}n%nmxQB!`S zNho#`fY$|VFA51(tfE>DV{=vuTSe*Czob$McZz0A&&B=VA~5a)c3?ZfBQ zxN1M6yQ|jKsiNZzaazNQ@XHsaMY8gg@&hNa7_j8jy*^Mc>1PyS?_=4Nr;%t-U7A;S z;-mj8XAKs4o(PC(vl`Wq3@9hGCCMuw>^w7MlQo3hxDxkUWB7k`eRWh+Z`-vrNQZO^ z(m6;sNC^mtl(ckr2@EA6jpRs!bcfPiLr4pfL$}1x{T+VstnYo-^R6}PEdFA_ocq3F zU;ElSl1o^tqV>R6*FHM`Wd#N7srl2LD+D%|@fifC7*FXu1gO8^Ajl~8{SC_%PW10H zc%}cHUMBdO%zHgvei;1(kQ~w#ogj0}$Kqb;Cq#>?3oK|KR1q&@Hv(XjjmQ0A^JLuu zGp#^vWS6PAQNTpa#9?e=BA45D%j%QmKi~MN07{$@oygv<&A!(eU>`DB^P)^&8?erZ zH;Ci>*KZY_1rd5GVC>&u9EIJ@y=?AxVR zS4+G11;4?y=XzELvB76F;98`T(kj1Y4bg~kE+7C8Vu4=RWV4wVh#s-W*JZVQtR*P9 z#VOSP7CCPUl3*6_Ca#1rOB(RTTLwI-%*KOanqCrTI@sENNz!_+c7Ogmy5#v$=c{~X z1SZDdGOcz%;nOUP%41CQ*A`#NEr4rU&$6EI~%+Ym|!^P#98*0!H0_=^5!nU_)yO=Fs(Vf$fc zYpbo4Gulnm6qRhS%h_Rt*=h{Bvwkx6-g~S3;@#)lxPy1iGWW*|?WaWobI*Z4ZdILE zyY+tj_zU&AO*5WV`)iXs%brLqBCBX}_o*d_!mTuFM2mN*^1_1JAfE_SM$jn}L2o2{r^12Ps z-gBF|9bF$_%BtC46Kuclt0g!8`FCAX#~h%)Iy0tbiJ1{2cCM44o0M2HMw@4fYT&ez zQbV7x6Gj$Uj5RY#Zg5+@HzGesRH~;}U^mkx$ zlSN3B`16PP$F4ovAfQHW!?yT4di6s3a#53Rttn`d=AQ1b!+CeAxMu0&-T=@J6}wbb zRpRLCMj^QXG~pZGb+2fkCZmb_==puu|bd+A@r}R)Sok z2uRBWhguD=NusgoqvR^@_r$zcXewXZDm4S!+fs5dlq_p~G1JXD;q$BjWhDu4&d?Qc zTa%-f@8hep$KsyWPr8R@;1(*YH}R^L|1x;;6MoE;#c-xZpSA6yp`r1iWBjm~W>1X! zr;)U-WB=|w-kSDYmeoTh!Qvqu5scYCpLa|F zW^L~?y;d>Ss3iJ0$r6}EOoANhY>j(8$sdmTV~-k5*N+y{O3QzIy#<0p%0`OOl1FRL zbA-WxSXp#=D)+<%uQe58ODi;RpywDAu~yL=QOG8t4kC*D1Rv~K zj|d#0aBQJdo__y+p&>z~Z7|P>_%DeDfS{bdDgeF}1eQo>20PJc>=H5>Gz!?vajl!4 z?r~nknfC5X!Vo-nr>tE7AMJU3jt*tE+@_(tcce$oiI{>z?6pz1M`296oIQV3bd^Z2 zp8H_LUS6&}XLoXi?cSm=UL8labXOL(C=v?FetntV!M)mJCVY#wI{Rz{kR5-Sf7A1s z(A5gR-bJ1q%skwxce;11F}KHwdo#V(EWD4-r80VezjzgI_4JfV&?etl&fEk+ zsnusCx9z!gvG7wcEzZg#Yn5EM4^qRw{ab{b?A4tNgqc19&%JSNXkX@`%0_d@_BCNa zUL8%JC|}b&a^|mZ*SIz^Dpq-I5v6_&T!Akrk6lJqZ%B?_i-hPsr$u{l@fIVibU z`ue_rknn6P&!=FSb)&e3C|oYnoFM0}PZ%{n`Ss>UvLEHVvz?@B=FEm=Gu1srk4ux0 zw8rA$@Hd7nYnM0=Zf!emErrKQYtsfg!b_4Zh0b|VWvK0d_Oc4b6=1okqordK!a4Md zx9x~kqR{kS@ips=H-rm!jY9T`JH^whfE>fiG<-(yLu90frRJ9$x%YWXD+ojz`>CyK zF(RkXT&9ieV4N?1>S5xu&z4`$E$0nZsa1yUlf z&7sW=WFyKel%*X(VnM5i4pZ-sf*I-+Vy9y=7U;jdw=!JKF_)rV`d)<2C}ii^}4e?+e9T@+{R~gm^SwS{yeOJlWIjqUEjsNPl?O)W`<>Y4ypyyD@={m;-qYKVfQFVhPmVcjq51&wa z7|zjpO}d3xoAP(cJ^g|R<;KGA4#iHmJD*n-?6R9;mT(BKs`Gs+^h?{Dh_E>zxGP4H zx*Zt(7(P2q*~XXs*dP47&si=|UX+c6l{>yz5WXgoiOF6SK#l48ZHdb@;0?FDttc!> zp$$Jvv=c$rWZ5>*?~-ui3H70M6P)T4U6%2ZPynvFM&h-K78B2SJE>|sipe>MExv$K z-HA#81z=!ifQ=rzSS~4asd>Om)2>$5PpH7N!c`#E^SO@y&Y86OsLP`l4<`zJW=33i zP<2r*L!L@HtNEa5NGn_X{I@B5r!b2=Lq{<-h`bFOyi9hhAugOKRNzYbEgg2U6U=yt z+`3@A3-dN)UuRj~#{J0hN06~%3?GMUjl$`K$Hq?6?tY$yDzv{S%h#(da}peKIMz>f zi41;kQ994vg&)j4I_|udCfcoP6c(O066DVve$aMvU=g=6afw2rKQ?E0#3;*Iwl)|Z zw>cUxkU(Ps)$-V+O^HVVhLDxFn*#0QJ|@&JgKq&8fPL6J`^1==>#A$AM7=fHz6e{%Y7ESiBsK0ZcK8!f>|wq4<^H>VgGJmpG*QfQorS!Txcl|t z)Dice$>sq8m6?}iP?0|=tts+>UorCPW5^`ES!#41)B5lU(UIw3Dns>G$mbkYR?DCF zufX>w8mGCx%RidO9_8#J>pUNYn=9cKf82X0N+Tneh}@qk6vo zyfdFkk1lKioU}@4Ewdy`&73hRcALU!1_cX!kvZ?aL(2NTbX& zqeqzJ=crpY-#)t_Vd^tq0Nea89vS1xY5YH7q_QWj0NN z#iBS1ic~4sUph$X96mek-9Td+R4|Z<1r)y8;ehIlCXk6(=buS>O_B9suln8X8KnTM zqlp9i&!-)KGL%w!`}yS}DXTv5TcjTGOR0%V%&CKKSLLv`LzsNO@_WU7sn(e52r^pI zd>^&ECyMMpw5CldO?GC9O7Fy0vZe2~jTnn3p^~TnN$QNChBgl8y2FR`wxJd;_>{~t z-9%oSfXhlrf9VQ^3pM?9=J=8#(s00#bI1ar2arC8&Sqf7p}MQ9agHtWZGrVDjQ$Zr z$v9~wu;65Ca%7HGNMG|i$mGi7_T=Nu-(*DD79iGl3L+b2Qu&&=$2QoTBNB{->X8yX zhh~*^GCX;>yBG_bm^q$vzO1%L-%ZenNJ+CMu4F*@n77YmF39}LGilg~`a+pPeL|Gj zjg=evZRl;6poXm})R%!m5p-L(Sn2%$_BSP^z+u>=*X9LJX`PtGlc8Ju#UWe*U{|P9 z*q3&sLanb~3kTHY4>$|68jX5UA*W^_MnmT5M;GtL{TepAY{G=eC6kQ&!N0b-p8y?Z zyOS5e8e(E$3%&o3IU6=1K<3Nce2%;TtF+)ly6D7Heq&ck?7mcB#AfK(SNHo9R#g1w z_%Cb{I-+Gkr*OAT^$m$J zBz>p#?iZU2FHjA*b%0@=$Yz*CAK7XEVe{9%SQawdvQ@6?<{Y zMWq@%yh374Pzj<*jC46tnn8h)=4O7}WzkT0*x>Sy2k4QqdxDO)w3w<$?bExJOLLom z_~h4lT{T8>*>P?`HO38RUTN#u;iFeCyPWd310>u!@Tr^4yT(?1?K&GXUf=&ZgF%_# ze}pX#WpWRoY*8GNn0Z($>YTUTC=SPBFEfv*n^gr1;GXR_83Tj|PE7zkz#K4c;LDWh z!^6G*6N!1hg`pg?3V>2G`$cs(hwVnxey4|xH*cSF!Li9q&Jb~Af5}kl*A=%d3)WAd zig}~nt8adw6s`Edo#zkR%_j9Y&-feaea(Ugw=K?3u3BsY20}Xdw5LQmhqfYL3`)RZ zSbjAR(7f{2pVtvS2Os6yeoRU=ZCeGg)?jF-{E2fGez#H?{W9|U*FndKyQ#D%^i(D_ z{yE5!LXDPqqKoVno`jW_=lonW^ z>*VRDq<^^Vm$1`DP5`Zjibjdr*AxT~R2&`Z=eDRWQ*6*8w?o4`yDmBEfG!VcZ3pSX zD04$0XZ9BuPhWK*zM*zGT*|c@OI+mWFb^Bcixsya(Kh{-3ec96GlVr8roLwI^l~2I z`Y%UO3Qcf=w-ne8`X0x~xuuu__(bIm+?#Pf-kX2E9r7zU4#ioBtpR{jT{0Pj=58Dj zXeGcqTp0J>=sF+3R*S|#mp=K~ftOPn0t4`cJtV$lAHL}BN8=UJ9f8q83e2r-Z z66fYTPekF)Jeg-48E=Flnf<^)t{(tHnIATSxh(}m5uk6*Z*IpyL*dYnGk|IK(s>fz{-q09f1S`}eE=QNN|q#`Tp$FksWF;S#tO> ze>OZqP)qy0eT2Ut?x=s*L(4Um@1*Jy3$LpFCt0%F%o!`A13gNnRf~VYJrWdYx$+-# zB_91GrXzKtoJ_C^Y!D?=S)KsZXkDq497>M9|Ds>b;l-N?XSf$1w86KBHesNBBC%r1 z7(^=$q_hV5%15R#NNO|I|2B_GWp&;YlPTLAVd(jz)-J$@B_Wc;@rJ>260$!E9d#f* ziXZZt`!gpZu^&%Fd^_SgW8g&6G|o;>1DQwM!c$V9K&fmwxPxBl=AzA7*>=gZjI### zW!cdIys0v(;t+jQ&!ctu^p_QvA z!s)P&C%BK4w#+*;{GmuB)?TqUPQRsOgwB_I9CbYx-G4&yy@(6x{MJ<|6RXuNRtpSz zRBp~rsja;bNm?o!H_3UyjpGB>gH&>l)Ya9Nlw(@KKwNjpmIup2;3Day!Y*2l5?5A z|013}Xsx}?I`rOx2=RJa9N1kfK^kER!+x*}_EDj&lT2?I@2D$rsiW$ZCJ9c#>}xe> z?adQ@X2M-W%N8~9Pjs37htAj!06FG2*@wsROKtX&i?s^sYN{_u+!pStG>!#<3501l zs;f1$J&SmYQ6M%|)Ns3b{Yo~0$->m<>TK9Q)^!gphTCGbG2{kIJF1>W>)sFgv-R3G z5zQXoy+de@87l~w@5Pb7qH!a;+yIbzK6jcFy>$S}`eCbK<)~o!XK~jOEG2?#7A8IG z<3WezeFxMfGl6m`Lx4W(20T~-UZ;BY{Tng?Edl{;;L`JetyZy{_M1h(j!9yBG&zm_ zn(T7fAmV~YT#APH5J}KaOb}+Ie{CzYHq4fv@1EM`udjB*8DR@!TWGm^Myf>#*)0Fy z#Cw&3{4ri~lGKdVC#qOWco$Z=ZmmNiU@J|?)Hk?X%g)#?(Cy@?g1?f2iB-O+bC2FX zp$3><#y!rC4_Yq_0TaLn2#8&FQA~RA=d)6pE_O=!{Z>~XpbluaH^mGN3>ycFE}Y!o z;ON?uKcJ6z7~S1`a&4Xw?CQkLTDUPY;e0D`Cq-4JvWyS(A z7Mus)XAF0?sLU?UYD+qdc#4PWRDC6Y(phs6^H?3iYZ(JjRE#L)d)Fw}J7)2>pcEq) zuGF_Ohzyd53}cnZ`>mnU%BkEVXJTbE?+iPYRa18^bgq>p8VjXwLS9ELulPi59jbdx^oO^b9j8ncq1p7Fi-_zB7-(ijCcN`9_RO(A|%$BXr zZ26*Ky{gMTVWZUE;(Gc)yNzDw{1ua{?lpm-aK12G_3_yb+>>F2!MB6Bkn_B&y9=1- z)7lWsb|*M{<=b^`9E`XpkEhPQBifQMnK9rCr5q%~b4mBv2F$6JgPq1FW}Yv`%V8yW zmirNoUtqRoS4Z7=i8Wi&uOuOl4GCAMB^OaIF^X^7p8@mbe+xCC40~(X`d9L-dnTHpEMQ@D8?#-yjnz{Qq-=39-y{?o%k0 z$K&!z)>Jh|AWU#{c76afRo?O?RwQM128wpD82Kvon_eMDu{22Ihu~smG%#Iu(BK^J zi>G8M9p+boK0rDEBuPKIJIl%fvK6!uRU`krlb1#*C7MQJN$dh>f@Dd0cQL%fZO`Os zlO?(*BCRg0PPoOpw)51Rg}&v{17QS#zzPd-x-eDv468ncYANu$@64>oV5^3k;$=6E znD*u^9OH9cof0CF$F7x@@|@G#)$-bBGq0mYtbl{1Nsuiihwz@M^>wm)QcE&d#>Wy; zL*L%r=`@M29|xN=1$A}HhNt_+r9S^)j}PB^AVm-jKCB z1?<2H=HL52+*G#!T{j%CgK5N0<5|PK$&p;aHP085amIq96WyP|qfWAVmji8mhjWIx z$FRs>DpYNCp;D9`#&G}cz>V-C%~=B9*aHjvDQ?y!{*6tZZf*@t@_FSWv>P+9(aF(L z0>VLtPs}CMA2b6JNrA=o#t=p%%^G*EA*bA-vo6kP?8$+j`B%pf5Vt# zJ#Vye=cV_Yw->k=mWL7bk%=DiSTTEz++~RZ=!%0PNObS#a?FniUthG@&DYk4#3}NB zr=5HHfGwfS*%<@R^4o2BiP!BnHX6@;v{5I7g=HR<<967GP_8R}NvVo$tKEW#K;T5% zSI6e?bYz9>YVHa8^1efX|i;f;@JxxFwuk$0o= zYRiXsMF>4iFffcl5a91XTH$Bu9CY(#icF0H3fR|d zVe#sG#SRsSG;j9JVQSnmjK-$Qh-6;vatrsudGfIy*2;8h%&~3o zi$nb1%WdGx6*OSAQnjRJRoMIfYAfTda2dUD@NA87mbDUdAy@aHebm12g_SS6VF&$+ z1)&L9Ns_kVEZGF@{8lxc2gvQVmzIM~0%-Qs5R3Rff|7%|u9C^zhi~ z*fPWx6rIv)mpMw{;%jNrMl-4yfD9z@-QXvg&E+(RwJ<%5;QSx@N$HyI>@m0g!+N^CdUhmYtB zCDM&hhuYf*KU@jyjyFi*lSTTo0)}@r`~DpcbJpkuN2~olp#s8a_;AF83c z3f6CVIo$o31G1UZur^643DH-<(N;O(lWuQ5PNPOzkr^7Wl=o5F)5P-M%wQm4ct|wJ zN0V<@JY=(b2|%P6wBHn)%L{zflOw9v`RAp8b|Rtm9sK>33g@mPh@HC2*V4sT z;z3lPji%4_@5_5lYdq>T+~v0tvSlFAEik36Js9PHeC5_l0H02y*|8g0TaOlKi)(1B zL~K=bZ{+5~i0kN}lMEj2Xq>^;)9|UzMZA@IcW;l(YZ^|IpJ9+S=q(}kHb%%J_nfSW za&p;|1st%3#wzExoUr3Y1cEN3aausry;eViO#7a_A-;{HIn=zmh+qK@7V!DV7~mOo zui6-=muwx)C%4_V9fqguC`sdnUCqIrsPscRAw(cRo@$ z1Y}Tom5)a1+~Um5t?lhy`$E$kGdJKNL9^>CR-pKTw_8KOScynizne+Gf?*PH8Bz;M zeSkCr@;uP5_ms1=WG}r18^pEM>vv$>P|BJ$|M}r*{@(wtGCze-%Da5Os8A?zw4MJU z(D!tnAeBs{?iWA`(csR*?5mIzA|llZAhIVxvJSz8t0s4j)U$^v$;+bpM711ux7QuBK1wuIrqT)6$((o%%{tA z;eeQkwO_DUNEKR)(6>V+%cmcwEW5D<5&^8OmqW^OL$ZNK)*y=;3jit=ajxA_E0C|)y-&N*%$1n_nm;cyJqHN50B8H@g| z+l&T9^UkHZxj+Lug5XjA*#^*?2yu?7bk&2`T)Ap}Ln(vFllE}0(S?vF)DVTVJTpLlzm~F}kKRAQr zp*^@~Lav)o05W3D$lzZqaC{~TAvlhDyg>@a+4$V~<^GRHd!re)_@?dQ?oi~#iSS<8 z8W#Eao4N*Lb8v5iNqbV(?K;yNxeBSirAomrsq;E(4M1IuNhE|nadgwsIbEL2-{qw0 z8x-@ky!jYl$t z&YT^9G$!NI%QfioHCTotZNX@4!;%Y=c%WQ_Gf{lp+bsY>`A9wmJXLBpCRHU=DZG_A z;oOaag@0GBw1LvdM{S*!0O{sS^TF3Oi-pr}L%fPw!9^iR&$kqof{_$em*x%LPPMtkU-T`C7>}306BYQnV!V6w8BSvm+F{ zb|fjMJ6AXtbg3SVPt*BM-#--VHo{+V95u@qu6Wgnd!7I&P`-?woU@t>W&(cS<7X@q z`A8goBdd0cLk7~BHn=FQ0f6OZKfY}JzMBEL%`_KMdBKOp5Z45>wLW}#$`y;}>*Qv) ztaRcQc*Y)`kJ|$P`{J91q28=71l;s?B%XNV)ARW(lP4R3yC=X3P7@gqpmLhZ+hZJu zylM*hzDxycmy6a%)ym3|2`hn{;R*qwY>PFzsdOe6NY;3OEg8uV0!TQ8;?>-4r5g;r3TVMIa~&e*$)s@s6ZfFTjg=)pfll zsxyHx1fp{4KmeH!5(&U*oO38crMmqs={P8Fl{aN@#nvaJXESW$q(r|RuhAR$_@n`g zSQ|}TeX)SWVYwX`od1p(9@m#Rsb<`bOECk64=}j^W!(_AzyfU~F^5YZdHH){%ynC9 z+QNlfB^FZ_XEh67M4P`86mP1dclJu~w?e zegpqS!O$tyCRXS&60kvaSRXr;;X=jMR4e zy)B0>pi{F5Y<-0Z-UFaSyta{_roel!6u^`e%g|ihnaHD-YU8$$GcbgX&SAtzHLyK> z_&dHoCWv!zI*)0na;9$`yWWrC);zcz=Pco&REu_VI*;6!xv)+wh}k>KOGB;{m39?* z9(E>0fy;ceoG69I(+{G>2HoQl{Q%lZ3&iu_ISIKuMP1jQ&o(yoMib5i^p!+d%EyAO zB#xFl-6{%5N&&2c+B_U|7ttf8<3YwzKr&v;6P9>#u|op;uU-WYj~wgh0>j zY^gskohT&G%i}e7`m>w^s?W0=#G~sMZaqG~;2rq23IH0<(RkQ)vrv{=55qxyrIV2( z+7T7(Td~kmYmPjWhXRCF%l=XQS-;~Oh9afsfwTBhqQmUu0uJe%!5_nMsAqe~Qp#GA z#p#`mGp}xqv83h~9NUrBf#nN#$uX^t!PQRQlpKTLb{ZW2lam{lMmQcC*edMa?|3vp zabadE`j@g3&Q&s6MeU^JTtYAQoP5=26q{Any_BB(6|ejD<{eAxg2$(4bj{m$rtK$#?Bi|9}89@b{0~6@b|3_Y7Z+SMxWd{Q@vwjUNl}d3Y}g=H#KkrQU)s zO6_TCr&AAp*xr}78gABwnizwmcJgy%WER*JIuDjdY@jRxuDKLntJ`>2^&! z1tZ+1gFZdHmOIfZ2j6?U$Ge6{C)P1*1;wcRAot#?JGV~XWJIJ}oliHGDMwb9DK~!b z^ZMWW3ITd1?u||Dbj*v?98{~xtFNPDy&6Fr9Ps1O@!H`ncE5u1_hrTw6kUEo#IO%% zi##_zQ=V5KroYQM7xdrF7?s0GTI8I7ZWFz5k*TRb!pn<{w?<(0Id@83z$zp+&Z;YQU#drZeA^WQIX~s_>djdzM zShkL)p{eWhaLc{&towzd(({-XEH2ofZMP2QV6ImyTquK>8wkn1keAk9Zg;iY;fWY= zQh&8kz@0sl=~xP8RI1-^^lhqb%8A~fC%9-$Wr+2 zJp8lyMl^`856|uWSDk;hQ)hP>yNhUZD5}-H?zX#Px}LNDNyAzA`rT zWzRfx1QVmlpSVH_%ziggao`S3sshCEg8XXw)w$M?tlo=mx|x*d7hQ2ob8C;HO_PPH zo=Sn%FSl-6XkqRAjiO)vs#@bDdGV_*3>XMpZqjlpPxHC_isyY=riAA`xI4>8|LE<2 z%;#*UVlzu~7?ZcZ014~)TE9wC`|TNrUkd#K&G37hH&MWs#T|z+HBGCr?P*-QXO8*H zhqk^9wbJJxb``bqjn?tF3I0wZCa0)ejNnJEo!xi&!IKDAyk@U?p_1G>E3SQHmgv*A zA>F85yR!d&@$O2rY6WORtSgj*&if&O8@*LC!+;n#myR^%Q|J9(E=bixSW;TSDsi{t{W#=n663$+UJn-R^bt<|6X1IJuxIq}C6cpA;h<>` zFp;hw|5{Ksn&MA?rJK=-wAG2xD)7X91C zntns2OAIULaaS|8Gy?}P390)#PK!77MQY?`9~p;8fzv4-*fuXy0P$y1JKnD^E)cAz z89SE|yl5I{gD7w&ti{4q3o=|2W1dWJyj?J?u!3$YEDuHC5|JOVl?NI*_dat0pw=e* z-s6UW@pZSy`V9D}g&`|LqAw_k9vXg-VV0KFMA!ihU{m@m>D7p58@9AbbV6Yc(AQ+)TT$ z)Os%hC>G~mmemg1oellJUK6)QL}@n|sr(22C8{39aw z-|`tj<%tjK2eqF7q43Yrs2A7G;h@WM2lJW*bbVWHiZbu;h8XfN1^Z5OVS4o;p7pKt z^}$wT17%B;5+Xx3^7RO90e()}I4%p~VX(iv@+tC*Hi7*U|1I&8NBYu)a{~w1Ag*nX>lERL*Rp6gNAm&kOe!6=xKrQ2s^Dn4L^6)^&t?3hnEb=m z0iM}7%VB(rjZZWccid){GeWwBYhQga!+aA=t>e+6Gd(_E^et#bGqhW)pnZnHNOUcy z9@#GizW#4Jfr|=R4okc_&enK(^HVd9Z#rstYl=E+G!5*HzJ61^Jtv*naJnmo_}MJ1 ztogvndsVePaoXdL=%=9gyg)kCt6Y^ljX?ga_C?zOR}a(Dw0q#iL@jV11SJln#G03~lI3Co_H` z%GJ#kM7?7WSMld2NEB2nsn-9&UGA&C<*|yPgJ8v-TjMcDMz2&u#*H;c#+9xpjK-vm zca|9K;@Ka}5jd2oe`0n#W#Dg!@2|qU@lagfbgb)Wg$)Yuskhw-yt1^m%DB*3w)G?} zgJw&b%!)@c#kS=wciX_Rdzql%)7Ffqm7FpKj0gmz#>m%Gw1ic?-K=M(17v2mXa-zk zT(nEz%jFf7nPy~YlW<#W7uVE_A#BA#Le(WEBSRmd&%M>5LW4V=`c5%iYJpt%xjqi# zxw18Y*#}0li@HIGFt}b+RP@E)x8Q$1ix3cahf}3Chl~s-|1{RGgi;iw(3P`OOa?y2 zAld|lPwxD0-u;-hNN>6HKP-_dSj5g$Pf#=0<#`5lhygFuG=h~!iSE$8_Hi1=RmU4& zZCruutUurKWH=%i)UY(-7pAHg5GA<;vPTd_NHL7HBUh~$Xz%j^z^RD&o_$-cZI;Kz z74@lIT;~OPo_f_VTFj$Fv?xF{X2qoUzNwEH&iLzI{CzwA^US`&u=_D9up!WX6+Mzj zc96`?+nQT>!xkOn7#AqVo&W4zurf1I60St}kOl0T=4xHzY{pi=cJ}8N78POB#~b|w z{WM3a&nJ%TrbQ{)#NI4&?2Z)YCV4ALs$f~>M|j!6MDxuz>efXh(Ja>5BZlKd=j4fM$@uwR{3zILC1BxDhe+Cu ziLqz{SO6ub#z%o^MD$ZN*Bzh2apxl zqeO~djMcJrv;EUk{M`or=UEgG;Aqy_xTEw&;_lt!%#~b`J0YJO>GO_EHpKu`kS)59eW_A)gN9r!WC~;o^ZT~Jl71SQ|8omNRBBmuyeJZ3Otg3GQ{(RU$CFj77TUJYL+P#_^j{pRQU8p&0aZq z+uYj{e8y4PanR>E#RX=&l4AHr?$w0VZzgGP<&of>;rcS@o3tz}OOya6+4KnPXD&OgwO|35m)rxo ziIBCe?GS5yWi0e5^Mf`gmibub!|w9l2;>ouxa~!qD$FOAxyO-%i0ec?M)z&v-jkxjzJq6Me((1qzs9U*37SSl zCm$h^{}JVey19W_UAGK4=KoZEo`X!`qLQ)eg`OlzG1jIiUkG1QQKr||)j@w(o0=ai zc$1T5r0nB7os*dzZnjjS`ER!)5JB#VaEN4OKbsNs@e$F$z&(Twv}IPclq_p6512&p>gwhBwX>w zw^u8hsJvzo0M&BV{au$yy_+sDzEy^RWWmRgWIJRamcRo%xAfO`f?(-?dxgI{hW|VR z2*Ty^N`kjt*d1DqTBoUE*aJ35}@k(x?3#YRE_$`->T$_%h2pi))bSfnAx z1-@u^Xi;J|;p0iNZW;#lqO+^A7!bE9)eQhN*>i91Q=k3a&4@qKkhHD=9!L#BR*)n) z-`NO+`I)EZNQIU+YGYhtH;$$B#cJ4_Jl#+)U)WTl8Lq?*F@=}^oKBwi7c$TyT`HkU zF4B6|N9v2mv>mmrXc*?R#oN!E2n-?QJ(j%dTCO(rIHPGYwb8}?>xEXxMnmMTk?enB zfNTK;oV5)tM$&gG@KOEsv|2$1$ZnJ+80xbl%y}QxVL8HV`)Br2jKVM{VYJ#SNv+7@X?oH!~fwzi0KJyE+%2fS;$?vQr(u)2H)@-G9W)IugqEW|I|MYOfc!>C9om_ zRP;|f6bL>un1wuhZ$7QEH84Hzuv)NSF&ZPa5m$ATvV_GM{@J>7D48PxfWMWgy^JfG zL)+cM_t7Q(kCV=v6tF{7r8+#whU@9``+oG{R#nrd&uq)S33pqHfRzMEcYY(qQm`erM0ga<*@%iKQ3j{9NdVQ`B`K5-(wd?aF)=4e z8j}-{;vtI$N0F&>qj-)j#{mF5OU;cT>=zD7GD~-V6u022hS$@IfeD3?C&61?z*;e5 ziV+y4CB4=Xxodx+%j=)+>wngrw1JuEl7#`dSRe|AOZP@#H|mi<;e0cDp7n1baH_>#&Rc90Vr@Q#O$N10lOyx-v z%Jws-1t8M_6rg&*(TroO!dBfSAxpXIDEQ74DCMo=#V&*m?~k@&FAs?|6JNPmzeuE~ zTTwVRPqtAncn@VRh~)88;eh905fpurtUlNM+|u?s;bw8idX}$U|0VSDC2B`wtA3OA zFDy`1%%t>EhAPs{IuA6P-)k8~4)R9mHhgho|AcYJT|tB>P)hDq;j+cgGhYynioe!to2K8781Hdw5u29H*$Olo&;vjnbWJ4 zoHlDDN%eKmx%zSoi)WSAIbE5JS9&1t-=h$yV8oUVvy&|~&mA8KC>D!LJKTFbbjLSD zQP)EBMO;hjzSk=;*kAc@F5Pg%Ksz!Fd-J? zkpqCug6rra$YzSUaEk9Cb0UQ>hKxJjDpK@*8)kkXhMT1GA$%lie3nNzT!=Dew^30k zD^Gar6Q)q=taqQ_S>3XQT#D-vl@<$7xNxFCL{+kAVJ9M3M^2HZbI-&_ldZ78e7u-+= z^Iu7N<_C@y36&M~Ut`X<2 zt81Q9{lz@|A35=_%(zbfBp7RWfd$>745}O?F0+)l2iV2Fp8QpFf2WJPx#){*^NXJ%L%9O{V-y=C-m%bi zO2i-M_<{ac>u(5C-!VH3!1#Wm08axmodXotHvIR1N-U>GAr;DrK5QtS~kZcGy7maI$uhzfNP z#^HOEc=8AMFE~?Dg&e}u@5-f0ZtzIW2&%B8wBKHpC1&pKhTWuv%+yqXsT;RK+_V~x zy;Kmd&`&i*ex`WRU!Ijp72straJBj`4hz(MX}YrW%}j#ACk6Jr>HZ&GUl|rvwC_!c zfOHQ?Hw-nDbV_%32t!H=2+|B8CEeYPqyiEmQbV^O0wSH#4e#chd(U&vIlSNYeBpT( zvuCaU`lacq_L9_jjr!cP9=Ytu;=0-*)sHIl!~>%oLIxjl+QQx4*^}Sbgh#UHgH^f3 z-v3EO+}Olb$rUqE6+P8vq9c_o_(ept-N(+DYVqivK_54+LT?2aUlV{0-@wqwh=SAz zv4}s(+>s+K(_LqrzR7!1I+!HDQupJP;7zUH>I6u91_V(${SuCiTC$?sfwDjc zgaZ5Q)`i1^Xcz}>Ew7HPd7K{^9&Oc;N;1DrTmmK5wZ;Lrr}eMrAf+~C&}8bRE&D7< zM%F&6bFFl~9gB!hn3K1baf#$D74JTD@bs^4H`p62QY&n{4!WS%FosM=rI+PZ57^qdJTsx|Qro)GSt{9n^hs=3q zVy$(Bf1JSDO0na@tIT9sj%d>rg8h0+Ad|1re`mR6lJ?Qh9CdbpQm3NgJlUB!GDc6IA zIO330cfA!}Hqj(YWz9SXXPe7sOasbn^k@u8QXx=s%ZCrxbM}?h?f}}YBk`AFZ|jSm zfvWP$$4;ZM*C@*2=3%J-c&5JJcC2b(lkKsfxmD6xDf>2lc*wxWJ6q7YGpDvn`kve`PYX6N1p&rjP z^W|SLb#Ndwev6V|->+=T#7LF|Zq%o|?8Z8ZQJJ0LG*A{MV!6kF$D~8z#60sCYGNt|37;aA+32B>Eykn-qXS%K%>ewYy2h#0F#?Vt@3;V1FGG<`#JYd=&K>e z^Vh4ZS^+c>UYT$?O43?;nC>)b;b5kq9x@CQ?(7jdWH`;lY<_TaI`~sqkT+#Y>bwfK zRO*ntat7>Jj6ya+ZJB&Z&Nnh>H?;P>>Bfsb$eI?xGFl}tLKdipPKaF#BOgt;#r~nt z=k>iY6R*6vb(7En*8V_B0qyK14UXnF=t877`#RCd=#Lzt<`TI5)0bFeQ#C?ixdVW} zY<)ZSubW3uabQ$W!o(a=>OkL}@_y^JTE*y?s3$=`TZOdBG<9KXk___?rCyM&rf0gQ z)kd$K`|bOHSht;-tl18Y8trZad#w9)ly8PQ@_G4K5M7&lMD>yGAvK$NxPr4Cum)MW zyia%@qzXvVjMfc}{S(ExgNNdCW+Q?Jq^AJFNR& zO!;>7KOEz~uC2g>#1g^R3Ubn2^l$^Nhk%uZfcti*kA|DXM_VTqoDA+H7T)*mYS7OI zY6$@yhvg|WT)k!6^grzMj_tYJtxbLU!nbo$`&m5F-{Na{lN38R zQ`W`rFJoT=O{hn6HqRI+W51{-d6Z3n{{DY3o+N}5be9)bN(p<-f0$bUas7fDlxrrb z0@?kW;vIoz`%7&=5lrHKic3+&I#|g;mqwINgCC|N`}A`~jO?@bIxo%R20psrY$513 z7uArid|71qoGZPQ`%`r1I_sKWjt_Yoi(LTVtqP4RVt#m zXy7Y-7|{~wwM?l=q)chF@x2njV#L%RRXA=FxKkRNKM61o4EhO(p4WOv?$^)awzlD< z?^tmGvE%=lDF5}WqWYEaX6$f|l3UNjBl-Ee%`T@}Cf!vD(v@jVocHL0KXTX0=s1%y zZAP_3d(|V#?dhI7xrQaHL&ogrEqL8fLG5@?Fd;3U%Tm^cl1*@CJx90m!A%^}SBn-@ zG+z!Fp!lSJ6(iyD$dMAZKjOIOYvB9`Sj_TOV02zOMBf8$CC2zox9l_*dV1+_*|i*> znz)$GmN27U%#r}Goo<9HDk>=2@bWiK0CVTvsp!1jw;aBvJuu%NKa1@DfU6!pno|?> zGoK^-t+7^C(LQvF0)YTz*w0QtC1zYO6@ODwTMW>}k322Wx34_p($w|Uru$cPu_NrW zYH!X9UjltEarYL)Q^bWgROzHB2OC&LsWQrWnPIe)LJGZ#E8HTzmu*TPH&pwu z{V+y;y%hQqWFL))@Wt7V6w4LAG2*J#+4k6dST*l8O!@GsNyDF4xAJ)gXq(7lj&WUR zIPS7skRs_3!D}?Z^}cI|KIAacHng!WG*ivu8Ol-gp|~#I6+ok^!>E>1`!vrA})AwLZC!(b&M zEu@ege+Ogmdju2Q@BH@T`j<8iy|84lS~oOI>>oUgKM`QUT4r$Hq_X?0U*x$Fm#1ds z7#msNhe})D@b*vrV&4iyN_LFf3WccC1N##dfr}r`uGEI?I}}t<8$E%2oZxPp=xWAZC9-R*SCdyT?S+K{^icK|5Q{Hv7`&(U!>zOPJfBXJw=RR5V zt+?i*_uKUEuh`XJzkp$WaEgG$5WV0Taxpj~j0+BLu!M=%yde|$79fr|h3mrbBL=iA5$31y=mrp?kj+Omism8z7 z_|JzPbn~U3fVW!vp#<9|Iw+_F5>6zU7u{nv9Yk+A(i_YGPlzaMXfmIx{}OQeH2vmJ z?qEICQ7BLQPo)@r{5+LFnu^8su2ih%-PdV2eDR`uTRIl=k3nI*j89?Z2ZWCMm{)NM z)4cKabzQ9yhQ1A~x^ltgz-f7vh)`l}4YF#qEZ}*?zhLf>?)US_Q86Byf`d^-;J^_U z9m=Ga7Su>ce}0Tqe9c zl#n=d@mqQwwH3mm<`TB#z+X$6fAq*WEu&eA-p_(~>_cFcoZXo4%<;(Z3v*nZ`|R|-#om) zRdaFmVvnvpnIO;!k7|>EQ8kMhIQ>E!8#nzqbc8qg@(#PjT1zMbFXHLTj(H4$()3pYFU_2GB!c?eypP?B24p^$VxGs_^!Fq>KnT1=M@+^k5kLR8ho+Q{MPSUGoF=x6#^8MHGYkv>2Oxf7d9CHgvJ;=#^G+Gk1 z!35t`2Oge3qNXS(r}^o}4TAS^^{BQ@{&mmhB6raIWYV8JY=(|%%7hH)Bp8TdiOz=` zrGj)@M>Vv z_u?4UQoTSki#5OeE9rB@9y(s#7c28OQ_O`zxK(GzUl%SR7B>U$L}iX;MK;A0kL~8HyGq@y?S8h@ zrCFTje3uZ%B|I)VtbNChaO?8ZxrD#w z%xt#T&_tdZ)+P9EJJwm!&end>1PPYF#$qXcb3jS(PIPk51E)9^?<+}@e@E4=eH?nZ zBxQ zKTVCOK~X_dq~{bqz2?XrTay7arCDwjY96EORfR$QALW%W`Y%~UeQavqYL#WOXmjL@*Bt-8^O;!cu@IFM~cV@#?AW_JD3eZ$f`P!zcgFB z>~dmUnpJM9uR4Txx&Ioe8C^?C3}CiV7F9e?8dr@a2>zhFH6ohyk%0c|hO9uF8VA_#{bcgY zuplkl%iu(q&;Ap!^ryUKKe`)Z(M}clTlhE`62U^n^yXPYG-J8!#NHd9KACU5Ak{O+ z?2;$Qw2cA*jX)tq4G#Ic&3|Ow&P92ew1S6s;XJ(vq8*?4gP&~tkh6wi@yErAUG)zq zpu_W(ii%fgupY%9B#sFJIjF~;NdyF5RX9nh$wrgXJP1o3ic_3im{!cqKqS(mgKy-+ zdB^OQ-#dV%Q`B$eq=-4U6BOjKna+VUh&?X$?P{S&CqqJ&iNC6dg;$81VIQ&BB#>fM z^Zcd0Y4V&liBwcJ4D55vt{(q*jWPzm zNwIePx}83?Ht-js+n~_ejn@dF5TvE3zuK;b+^1hdFNwwsSUPX>t7+7(8=IO20B1Sp z@y6)6kcoZ+U$Vm!Z+(-;nIl^fq@Wy(cR>;XTmTY~&gYPVNzOmS9{Bd<+2p)ZGKVg1^h$UGH02u$GFCKy&%~*wMkQh9{ck7E^qX(Tv-P{F8vege9IcRS z$u2YH77d{tN@kr#h3(;-mB83`!)Uju$3 zo4BAFM5@h|JdrN^ef2ku@LeOSBo-zUuu^JM3n>|?j4M;j(bES1hrdl0A<;P!$AZe* zIY2x5MuV%fVEWPm7!m zp8jpD%6VhA81u$XLL^^8er_mX6;#IZ$HmoT+c!YXvn1PG=*tVGiQyiz*>u;GwbDVv zIYtS)4m^#^XqHM24wdjPBOs^?MWe<(O`=MS-Xn7o7Z;k9yWyB7J@n%5Z@2r~^T8A& zr!9BX^uLPUX>~|Pj=0PzY?Hss4r`zLZ-%b2U2S%#(E(K%Cr zr$;i^WVA)E^Mq3MGTIt6wP(j@lDJSzH5^hwADUvR{(sG}iEsco+;tzitiNECz zAO}yKDgH7WNhMowJRPu%Lkzd22P+ASB?6%@@~Wc8xafLy;h3v)?>>%-U03Y_DTGeq z>E^P^V}epY!P??Q|7(W(_sg>_CDNP_t1U~yg9vW`h9ky2ZaXy`7nFlQwMm#K-LI;} z`fQUT4{vKi+P)-upX7Mtr`cxxHo;nVY>J{teo0y_hx0nBu0VqS5T^AmC$*5K|L<*BO81&8s& z)YPC2&1EbrZ#_H)068ApLs}#-BYjm-`ou7(SlFfi$d?F6L}jSQxbtKc9_pN+Kwhe< znK`m>bxrXm4Y-EF;(LTlf&GwzB0bq#H*Vn)h6xUU{pkeU$(Y&flQoLZOnSN-NBS&45r!QDK-7L760&4cD1PLv2D+COjUEdles9Oi`@V?%AoxSZ}xXw-U zIs)u00p!Y0Z7jN;tW-+UQ{-PnILB?#lr~bEdCpVQ>5GAX&p7rB3Kuk3bsP@kxlLAY z48F%>s|;Y&VW6O?dUg#&ZVdt!Pg)%D8y9#U0ocI0&}8DtDj-+;zsTZ0pCu;o|9Ysa z|Elh|vOoIvjI%$Itwz7+b(*npiKT{$5-9*V{KZX%DoF!SCC$;z@qwaz49}r{YFK4F zUEx_j(bVW{%Y4p)0~_VdAc^rqjj-dIS?ZEM7tl^Lgb)`{eUryMe+g08%-h_=qj*{0 zO9r!=8QvHW5HK+lW;#3>N;}&3QOEon(eujbjo2;&43Bk_d5kpCBh%|2PR#%KK2_8Z z-aXkwcL|YP2P%S+fD}eeAt9lxoIGL#h&PH-uEnMU^Yi#iwL2}(xl`PTo^X2*t(}P+ z`%zUp=BB-}*<|90lxy};dFgjdrhgd7@@ZhQBomWo8hB|cJh){tl1p#eGmGuk+g@bT z6l~0=lF`YvD$NiG77dUy5zfr|8cR>h6GZms(YUr;!FiyuqMv6kvbR(*qM5A&S+!Mn z^e=zgG6BZACV;NCt4SAOIjL=l&zZV1XRzYn7yHO3mfkng@^iIR&N3}~!!R|K^WcpxYdXxFK+m%t1 z#gLdw<@X3epJ#wBVG}s|wxgkGDE&=oRpW%Bs<`@>tBQ{9;cw{l-!6_8eR2o<#V;b- z{;qURkBnC!viy>VWC#hEDC{X}WYcfHKC<(#NQ6%TU;3?F9+1UmU37M+U0;=+o3qib zM7>n?e%o#LCy7OqECY{_)vVz0%5M3s^($*7=8X!%SFP&$aO%Q3i;ym6Tn~$10*rXE zcHzt{u3n+SYk{|NgF1RSWpoNFtWlHs*^p=z{2*iHB02G zKc;?0FJ*Qls&}RxI+l)iE-5F41xXtr-k*50*W7H^!8PSB&|g;iMvls_5s0+`?~y*~ zeLj?x|3BuGe?QJhNl_LSIVuEx0N~9Ukkr~_R8uumSrd|183;(DI3r~AfdY#+ptoP` zjy5{m@?px{YD+VW^;h6ty#ZR^EQhXyOsW1PKcy-hM*sZe09L5+pyzxI;Ej znudE=^iHiUnut}z!kcsV%f*q*9khfr@~&W2r7yRr|V`=K@^VLyI@z7NHCdWBEasNGCudJ z2OD7gd1n1R54XIlh_{~?Dr{LSpGS#4My(b5HWW%WhEl~jmWj*7_afaUv847aY(+>I zwY@-w7C?Q8jp$3Wg)~%!|8SX@H4UnqH^oyL$R^=h_vO+3Ip9e&dG@`Jtd6teqTqr0 z8KFk*1Pjh70D`Ok*liy^j?tqi=OVPr0P~W_@eoh_xGZ@~S4LSu=xjl&!nA|=Ks|@a zYGHAc%7+&#A}xDoOO=~l72)#3+O9&lR+w~HZq?RLVw05L|Ks}$LINTuSP;S%>Br1a zt|qCGyU~QiYp3af3&yGOmsa9IRbfZk^QC}f_a5PC&e|^_wH*~ZWqEzCsMMvO$~-|4 z1$kKs_M3fro&F%p6}wn2bSI60*b$1*{QGP1z!C(N(8qB%(?d@-v!RP^-cEej+WZ8% zQ58F4%^G~Y->1n4?2~bp)cV(rc4?^s@b&!UzO09K73CZo^@Q&oVol|iho~)Qxhm)rvg_w7 z*pG%9aQgoy*L_dhCn}^<`1V^j#C~c@6Uu>SW$^U-C> z@H945?5)5Gazag)dgXA;1*UJWyY;v}c7NgvQ)U=a)KSdbl{#b>G2zOIlOPUK&SXDZ zmc7OZCrEf7z=0#?9`(B6*DO7y7A#t9S9+pCZm-R}9CA)kn~ESr7k99cpUUzShFQX3 z=6l%Vl&$X`Y#zljmBLQ(xGW{+CBjwt!qT;VjAdaAf07*KW&6WTYB*X_Wb;|YxQ&5n zzgkau2U3H@w|XyE&9nLA7v9@jdfI*CCI0${$awB&(Ld#6Z3c@{EjSAnMg-!fpxWD_ zLAy7t37QOCl<4kE>P5qzu~lNPVcvBo`8ETnprrDQ@Pgt|KeTfT6Cd`ATl!}aLusz9&)qI+oSf5bc?)=;->#Gcv!>qT&!z`e z0baH=ng?Gnh|fL=4vXHWXbh7Z!gyp{1CDKTS$tunr)MdmXFmqq#$SrR@#$NhcgOQ@ zQyiBhPBFr%%!V4sNdc`!RQ31%Oz?spcGaix%C>ntMVkm(HgOszVE|ZJ8edIU-iY~Y zGX3?4>gGt87(G`lS`svF3tfc;m0W~yI2nIwd2$VdMY8P2o^LP;Hl((?GeSrX>`iGcUfO||y)O%;&`>0&f76qweAIE3< zRe3#;uD+yG^KVoTkF~Nck-bGUcMz!r*(}2VejUHy{mqWm73Y4EzHb~^pm`Mcuc9r8 zrYt_sNLpM)taZrYlX2=llgr+>1oEvxQ&;Wi=P7mdcc7`(5I(+sLbj}Xgil@_uDv&r z6vqR#7ok=}N|tusm}sk(^8U!5J@NtyDnC~sG8Q@1c=o3lRk6LjeHR86$5cx0X+(y7 zh*CwK!B&EJn6g$kp9KmD3qye->SW-285(X?xY8O{B>Mh16X&6X@Q%UPJUVC*oz3Y1 zv48;0iy)xpy&)pvGiOWG4r8GDzQUO+dXU@$C0K2Z2p3RHfu`{V%P||~ z=gECk*W#Dsk~Z+ZTzA{WR;zTfF!MO{6h@dN>ZL)>nA>Ut?}aNbhKav&Pc!MtD_2rk zB;*i@T#(h#;gHRH?#{28*_=eTWHEuil)ik9(Y*^YTm%SHeN?RNB!`cp(xrs=;uIPM zS zOhFjtSz=H(bK(kiSeia~omLcx32+;ed^!NUct!rZV}wk7>W{#Iw#rCdU{L(9zd$N=E5{9p(v+8PH2^OGPLpF``vjdXY&kN=452{1l3yfXyJDh7v|oL@4+Vc@f4n< zOwUX&C9rs_E4tAtB~inIqKg?^T08Lzb)UzerbmvSs!Ev|EC~y$+gGEfahD|cq&CI87t><( z?wxHFOL~grWzLz59MBwP>^|-~G5^7UgWWz_lJqXkgCd)5hnlFv$CmD*4G36=I4!*J zzFbQ^6(Z$Bj<6M;`QW1RD2qDQjKcMT;41i`rsyFG-o}?j&$)<^oPw)D2n`c6xy+t5 zFBZ)mzpEvTj>IIGZhnICc)bJshfz62|2eXv5ig2TlB8ZxY%x|%c08kewMEQUmGv6- z+fy5{BBDRIvU&n23!+kq#XGXpzpTH6PJNU!8IEHKDPMT%P9t~%+zQLGB$y`{j2tid zT0G9wpcio1G&O7_w$+I<1h7uriN@vQBDxW3i`d&G_)EO zRgGJ^{T;yY++Ir*258@ulG2MMCtK#8WQbT*`b+4>Nn2{v)HFJsCf2$t48U8eIG&c& zgakS>A3tT^xv5e7&gYRK%LAVR33>-v0nbKcq;(c~*q1I%HONc+R`MR394@ z)$dV|ZWW|}F%a!n=9!^Et9!XoM%6+B34gytLWb>BS)BYfI9*1Y80@9@>8F32yZfg~ zQ^di%F5AM2U9Mf6R)5*C zI!ZVt4ahkxrRJ8^@DRKb;b#u?4@!n0Hly9v|4(sRNhpF2J7` z%)1WmO+4p{3)bKgbX@%s2M7*`Vq@Y~Vyc{B@r4omKpJ``bbfAj%fFdzb6HJBPg?n1 zw(q3q4~_0grMf@92rrhFS1Mh7?-2v@J1f&hFIM2IONnN|yqbvTj1lxdtwPS^=eBRicpnHl`edL?g`glce?)a?l{m5-hUn)@X ztCBQ1Dxxg}==!IJ&$!t2rMN{4v_ekxZ?RR*-&wRmR_8F&fqapJdLSUhEll~X_Q_*2 za~KnDj4Tw;rp5hq?ljRgfD+l+b@_9w34ivB-tI!Oj4tkjSevwhLDpUG-FsVCqkve* z<)G63M73M#74&SHKhaWSW@#zp@1m0Skz~o3CRh0m<9k#5+grS2IA`dathmII9Bemd zG58eLZ@HtwP}7lO`UuCL1n=~cubsqj-g*BbOn73ak8~y~@#WiYa)>+p{OvE}^#u>R zS`IeTbtsUC#s91&ii9)8*>A__2#5f9%fmst03WNxHE?-kJ0;CSG2eMFQvmTk8h;32 z62{=_*FG`=u7E!#UZL{x$xrRk5$v)bUswht1JjPu;(e{sal|%LtqjT)S`mzR0X1wji?)fD?7{&2&jz%-Ka$=)^YcuBq*{oBta?`${Mk$!o?Fy1w{s-7 z;VPysigP`Jw7`%z$_ud~phv%&oIJMTE zy@qGd{26@dkCzSQab1x~=h@&ONiqssa1a;$-{`D=N0GKuC4goqCaGyHtI-6YlGnRgHlyF&l9VTQ=T-c4OQB)x6 zBq2dm&Vw7n`L$_5py~S=a*s0Y0q5yA2MS_dm~tl1mH0gAJ&2W=L*g0dT`~MPI(@u8 zy*|zM@Yr@1;ev;p_>^R>KB35-=y+qRR;RBFh_st|;4kvpiLdBH5GstHGQPX`RHO}G234hVKFNq++0=+LX7Kw+{NdYmsDxmzz)(&d|LSN_KJYVjG64*x>m>P?j@{czOmxhND2XV^VZrl09m!knB5bjj8P&2<1-q$QzfHef$sZ+eQQ4 zLQbx0B-mR`l+OhJzBKSd1qkc1=U&S}M~8h>4wU0yn94sVzW-F00?a@F5)Ifg$CR6H ztm$=a*VWN73-}$1#G?PAZx(g&Ic0rZ+l?HVywFSHuv6Lu05)~h?9V#XgkCe!y;s8 z-8S=|?%0#2ZYz@G+4$BiT_$gvByg<*I=DFWLM5)wwXMs5#6yA-AVkO<$C%UsSJx}g z3EH$20 z+F}>?@jglMO?(E!Y<)3*J}wq>hwjmTF6+oRyX5i#lbqNio9&00u}y!^%T%=7n-MSa z9WN1%T)(sQ=Riib(}{ANS=^_O+i9IeCy(ZaFoyG1o?Yvx@CdISbuigzjqeL>w?{G3)@NJVC)*y5-H zVw$~3_?X~NUZlZDMRRpZ{IISl@Y)Pj(DOghJ1FLgj`JQ=f)?2k?nz`hKVY2+B49_X zs7V;Jd&&wR*KT~ZC-QRTsOe)h>*wJPiH`aa)`=OiX^!}g4lbdWSdgx(YSH&6!5UP) z55A_BBRE?&W@g92xyLC+uhyeG3?=%7QUc9GXcHJPK8O3BR+@Pro8P*SXU!FkIpJ|B z@LZ!{sB%giQP^zKZ;W0RFBZ@7SvPI(Q)#ehEb z0RaorWT~Lwp(c?#a`#LX3&Bazv30?#+-BY5LgUum;BKoiR85ha`9d~5k1%wtys1Fm z-1&)x8Y!QCF)I}Ylhx*tMayZ)vDK97Qi6=gx{&_+y!8xinpW~=jN}m^%g+mLQ4i+j z`qu`fMrxl3JwvS~av$NtDmOqP%MaUSZvH=p>PTw$@LZLu{E0WKvvK~-$)S%w1 zIBKeCsSv}ptx~fDWZ8u<=yhWth@eB&#*81 z>8^~*Zy8Uvd;om^MD)$NK9KTtnEO+bD}WGW(KYpB8Ql!*#HZuV=AX>`H5n0=?4Vfd z)kJ;4ld#H=pMYPFvd-t^CME2{|KhOP-5>k@6tndjE@>=PwUEfgcmO6v*-y+0KW9 zLDoPqY0WYo`e%9NSvChd$Haz`U(Bh-{6@!ys^a?iSd}iD)EXx6s{Q9!oH|SRfT*O{ zEf{^|?U)jgTZ=2ZX&oS46++1ho2c(~0`-ziU4XNk7BW%V!`+aIMQcT6WJnF}g-pA5T zm`)|OY=I|1N5DKt4U5z*uyVAL01Kn9;{RPlZ;^zW&2F1$`lrJO1&b)iQl*4>6*8@} zb`xR*3{@xtZgkP*#^<&sRO9z)3Ntr;3tIGcYUZcW96t1w3clHQ8(7w>$bde_5UA@u(J&HCq2IO26~8QVL|X`D0k zFfk;bWyX`u19IapnkURGa%&0v%z5rDsAL4X9k2Q{`A4~Xh7aL=bm0!i+F5)cTNF5d z{}jGD_n!|eHV)}jP#xL8^8$!d3hOzACeA_Z0>r@J8P#}vSJJB(#KNeYsP9(&%gr}i zhhL!W@octzOeGWrA)bB(p*@7?LCU1=4?wKodYa`gLF;}LZz6?$>PJ`R&j5HjWo&vd z6E2T81_1)?G*WMrSvNi|^AbP73n?tMO!;Pfz-{Sv)IOoB3qkV~=j>}wB>AmX+;~>v z;r=`orU!eoi0)-%23YwCmZ(A9_Fvl20Rck;mZ+n8d65JOWc*D%m~werB*;k-Fg(nm zuxhv+i&^o2PcaUr_CNmC)S6Wgv56ix%rk>sef7d0cV)?b23_YPfyeqc!T=gblV_7P z6CX1j@Vlk5U5CGJS^Hev&@j2CrC?M|U=K$9&nXkY*)egENUcB#Kq8+crSD%!o9Hg* z8^imOa3^@R6D7vr&PPA`p8@9xm%DF<<(`IrWB#pc7#qTne?53o8eagQm2^$`(^xv) z{DT)0e=!=ei zk-hEBA@jXucYxQ~2hT2fWq8)Vx)Obvw$H8Al>im{sltC*(gqZk0QPzHM{yJnMFbwb z+U=fD5>sZT9Ipa*cXvRqT-_pxDils{-B;$na+Q7#Ptvk33wtFf;6=#xHU!AFR`c!W)o!tCJSkVqGswVJ{q&3-k%tnc8K^cem%tV)fKKJXro&I-8T)t}B737hEr z?#dYZDM4UX!*5E2-->BK8=u5U%${7xbgR9ed!4n7W$6Bw8TCWOC#h~J4(w>uHTcJU z4k(EHifhLWi>svx{F6G7EicJoq1CqrM{sR0$De+T3MEUjmvBYf_pFcIJZBBJ_LHG1 zS#LE1VjGK=^SJr~$Qpkj^yvGahBfjfV>w}C*KGd6k8a8YHPl8TFkoU>1;B*(hYDto zdg>AvrkY^ov`1}ld8|ks{*0u}nMoRjvI9U8Gb>w7bHTM^tYoTxTLnbsRn?DtHaBX2(FjaRl6)z(^oSvBRv0;z_qqZswcl2~Jf5=@L=Mkwd{ z7mV1?sday$$B(qPVg6m9i-dlD(L-XE1VB|fK;DQJ(=&fwQa!0eyZ_dp$me;Y{Jnmz zl-Tn}h+2!H<}gCG`yuf*LNSE-C)c)thF20$)yOuK@90U5Yx(j-c}4VN88eYEZXHCWHKM+6jA4YYF+&(@@vR&{k90U`x^8gb!$EBpa)6BC1+l4>+-@iG}6czTd z@uYlfZDW1kN46xqk+X5IknOX7E{>Bm$rq0xoAp&tvV=N)SFYZrYryT#c};>0E!}{Z zz3qytA4TqKezq^3dkK?jxpbTz%}qZBoy==t-c2M6tJPP?3|o2RngqE;2*e{G2peRzR>Ju;+hA~)FLH|pL%^r_X+ z>cIM*^@vQ%{7cDH`Pd!htXZ=Fy$|C4`EB=-vV#dTH@-WfNr+|iSLh8;LM4)61sZ`Y z0WeJuLtBADN4qBXIJ6lqc{wBwpGaB9gU%~Qq4cH$YjAZ!u%^O7^!b`fU<$vKOT0#r z@3HR5tI%)o=$CX?V0?)!xT)o~5$3QqT5=Qdec@fT5G{l5(j{8z#s7`!{dcijG!9}z!;jZ#3`!g+KtrVD((eYRb<7$|1D@yXuL~K5H~u8Z zGg$p&j7NboPY7{f z#q1@mdb=IKXE!1uVn7_n(7;mHxVEz4`7`q`8AN0ZNr)7)r29}gtP9m@O<$|p46LpJ zor0~#u4}#&SOtX^xQi<&-1c|(K!Mz(pOtDVg|O*c+L+NN9><-tSuf}wA*=zhDB;kqSntqj{JP=gB^`IdNBLv=V=%aBeNUeQnW7MPQ4-48uCu+IEd8p z+q2h1)*k~Y=w7|-B0!1cM;}$=dF`OrU{1*0A^+PQ_gsEw_b<&-?aRZo0S*J9#bV1l5P@jQL#nL z3<^R_7xnq{q5d*;?8AcBk%MLfm%$^GkF5iewc1xL%{d@IICgg2Y{jMY6=_=Z0vYER$0_Aucn{$z2KcVH1)5V zdlZ|@{qE+4b^Rs^fx*gp{A_H~m`IGoymWh>YCN0rcH!OZS|zWdGHD89-6$dw2G8Kppm6cr*8TD!>+*}LK07)O_`iPB4|Z}Z z6fT~Uyz4tp9WR*<*0U&1h*B!UDaFDuIrO8(e+k^WHr!C3Y%jIrSx2s)-dXJy? ztxz~dYxFzG^JH`s&=2ar|A4kwlP#Sk;9OzciVPCE7o2iF(#isRea`nNYDOnW41bZ&{xx7$uyxC zJjn@kA6`g33~_=N%PiT;~Y*nGJt9H14yIrYKV_6 zVKb7G(51BiBwUAImvb^y|2Y>VCS3Ole0)K`1rt0dJamrQRLm16;eI_4UMgVS|DIl} z^|gVuL}+^K+V?-PDY=e!9XU39@^iTSM~K!wuDhdB0rAUWpu(Mk7@TcQk7 z(MK3~p!SPC@U6cuP<9>^^Yq~Q$=23Zd@`FJ9AHBighjq5GCyBDk58FHFsHh>IEM)d zuaf~ruT=WX^rEP@MW^p{yRq0cbcaZx5-o=jB_d8MD&hO&THjZ4)#K1@r>s96p#yzz zWVB;bAjnzwoH{zF#S;Aj^-Uy%;wM15)JcGiUALY85>eDiT19|$&Ov*(6VWEo^N zZ!%YZ3#V`^ZHJ__tzvvxEyeLOQ+;vAnMGZAeQlR(Yx_wVQA*jKIVXZw+K#3@A)c+- znd_JL_1T`=f@|MEj;IeWdv<~dJ$CY!kH(reKx4qab4d>$^XyQl`+X0A?zKz-=M-cd zk=h_%b2hZs&qfvzeg^r+Fx1kMr6|rO3nqxVZDhv|6ht{qF~BF@X`DCcB%_`M1!J#A zPq$dT;|t~B&V0m5q=ILq&cu__N+nk|C0%p!Iye`jVnYK+QcJenJ*4*4Ms*#Cq58?BV$89laY1FVejyf8=m%<89_Q;-G$xGqS z8<73@q6OHj$=y!*qNcL zK>=N)hEb?SJg%@CrVf1jc9#kU{XX)-vgV;u7ONxoIv8`kuWVRi>DEHweb2K5k3rz} zwIFy;E$(x&#CISz0DYrEWZ~-+x=^b!dG5UP1_Khqr}mWJ+mgt7SC_yxgZhsiO4uY9 z%tx3^yBoeWTby$2^wF#dtSBfm&^!;}GKQhZqCu{#+!`!{RTBwJ-Gn-UA}@^G{u4`_ zRFzDB>RUh7?oC@__bDzn21+%|7<16v90ptZt#uwOTtna)2}e>4xqy%)>Tjik@><@_ zN4Hd5c0>ZJTIEZiHGme#614b#biHL%m2K2DN=v77w=`@*X%M7SK)N^Gozl`J4FV#q zba&ULyGy#erOwSWzB9)8-j_25fA|ACu6tc;%{kYcfj}nC9O9CQ+>qZmbQa$!&RxsJ zh+Zmai)Oxt_`a9MB`K0sBT1DwkZdE&pe$9&3aKh&0hDndz99gg+G`u4V13bRIW)&Y zH-;^m-{WHBs{65>%%|1QIQSpyZ=)U6E+mjU)$j8FO;%eiIi0=|gQ4($9TsM^WTO!B zXXQ45W{(yU&pBvTjdCpgF=7H+VdwfG%H{8h{3s#DA7=%{=*E(d&~H8ML~)!_mPCud=f>??*$ZqjfN6r7K>sq@o;9K)bb#Qt;&w9W`1C$` zucjZOnat32m`YcOF7}C$RppDy*M)cXIAhhyM_Sr%o8-S2jZWX#*@UHN<6~w~Ou|Ra zm8h`?HX(toNw2uuY)iZrXHW(cc|{R20Q-0&J`}nFsxSrW;I2%LmUu_U`^J-j-})K( zXn7el0u{(eM_GFbMmMJ)=O|huHoH^4D}t)aYTl33SUa|=TX{{m zpnv06Cc1BAN7s3ebVR3+*5BN1^mCu@eY+o)SDZ1Xi~kN7?~9G_9WdW*vH5O2V2vIR zQ=mO5zINS@S;M3d2Hu=CIPE}yXKrU@J}{9(qIEV#7d>J2Q z*oAJ0|Ku>K?WySQ{Kqr#loF=J6#AO!USx(?31vC~rz)BT-xAcM7LJ94ds=6jDE9)F zRm)?}aK>*J0-#GKoiW%7=f~4<|^a=GgB4XH3D7&a(|fcP;-;yIa#Y%av%>EQXtpH0DOc+z=XSTA_W0IALr zj0iwAg=BTX6&OpDk7sH(;RZxT+l)U8J%5AU%=A(p*b%x3@kGm!QT0|{qa*9fNWFRg60n%$hopTBaIMb$q z({jN0^mzE6^{)uIIG z!t4cTY)h~5j$hMxI`W0t1cB`(?D=l>lnq4rI6`tv$2no>4gb9nxl3aAfN5d5X7l2W zF+HfReRWx+KCtpz@MSWFY@*>E`=f!)Y_n^UxLJGFoA}U^MBG_Ds)%H7c?6J`TH$zf z{79TAK|#MkZ=@4Z!7ig)@k{h@D&N&$rPGcAe$IF!Ue(s?J6~#W*kLKHKNJ)D$h?Mz z69AWUC{tY~jg(K*b$VU-eqX`P!t*)`E1nm1j@R>po5Ifr^8H&XX`^$2d_{k{m9nsm zP#+$$gJ{8@(3>QLUuLDkIA9KA6s_@M_qvU;P%2ZxlFj)TJ0=^oNtkAUBB*S})aFZO zf)!b`g=4bdqm>kMqJD6fV&XiQWO&DMl-KRtz_F}Iho>mLGMv``+pX87d;Kgj-SynL z;pPdKuf&~=hFH40I3CZrTt|^UX`Bi@j87^jvZ@i7{uhT6I@~UpaK626OM-uD7UFZRSL8*=vq(h2&$P5@RX%(7qewwM7;z&#oD;%Erg#9bWDS)%wiLUOTbzz# z{`Uj5M;Oxj!t3T5x(D39I8Cy)Q2BA~bsQPz6R^O=3cI_&?xm+@B{*!5x**5o^(|X1 zNhsMQ7OiTjFJ*d1;wkN{n4zGWM3`P^Z$KRx4DC$;*;PJ*4O7)QZoOx?YieuLL*Q)j zq%D26{~Dw$+BSRqv4?<0g~7tCsZfxu*W4J!@Mzf8b_3xcGn-pJwgUsCL*2Vm-sWH4 zmgt$5lXU(qG3RsU7YR<9rt%*7u`BH$73LB$ACGpO9mZR|o(spd2xvKzSPB72zdM@sAN9qNU6U?j8~=7LkWb21p-$8X=9 zLvrf+BR8U_9*zJ0RHKAfiF6=+AMKLc+bGNR=P6SpcD}+m1P~?n*bwcCXSs&-oWCL> z5qX2-ht_on^&y0!3c!i}qfC9j@}Sos)zgj}Vo2@F3Lhey8Vnpb?(jh(*)1ry08RlMA|0Fo<@1O780*eP;tU&!5WNk-x>FlP+Chu^lS z^qkGn`0-eez}n|{;_QL3o7t;>-&ztSly6HJf@x-0^xIA9Qdy7n2jzzkZ1Qz-91^)3 zRiqBTdh{dZ8>}B26N9hH5_{{)6FU=nEpAS}6pT4q<`^S8@ib|(YB~n%G_4!;?||Z_ zTuf^e^WKNK)}|ZaJdg(EE3fbExD9diL9UM$YXZMuhw8uD8&J_gX$QB&ZwMsHK!6F7 z`6WKM^~U&b-iY=%{^f!V+4W}((Ov}C>|MfSJ;r6Wi_yF22k>N-rP!Rh`eu8Ul1|xj zxHvSz(#z>v_$E?JFIaWRmiqX2Mr&@tH^^$((D_#4yxQ}hMat@Kdhn+Z8N&y6<8K!i zR~y`pdJ3@?G{Y6^|Ef$dYYDH2icp(Z$l%y1nd2WbNCdYqJs!fma_v6r-NcS_8`Qwk zI>hf4U#K+<-QJ77!60o}SFKu-5sa!9b`)D0}j~(xzYVNx-3dt-nB~j&*}BU?(%jqpRFJSVMVx_ z%ZJle0|S#!8vK$(Nzr$ATQ{Rvqv=0G?$r4HZ56&9DfR#Eb(|s@Bw182%^#lo-Bd@r zy~ZD8lY}BWe_UX?)$d$Htu)ql=q|vtm)UZD*!Cp;auZkQ1e^4ggHu;!Fin<7OC{AD zKBh1KnB&YZD3Ii_U3&M@6Us^OTTo9Rhsm+M0$Ne{2P35nTd#h-H+lSb1zdC_U)e_M zw|dTOZBLG;O~E|v0j!_!KFlFFKWp~?5oGw~HFgb2$T1r%Dm$8i{%OfJCTPK;P~)NT z0U0C_@M2f)N`Wb>o?1&xUPRrX{049Y<2e9^Xl`$ZtD z8D@`*VSWC#iMF-;FOgyYCok;(i~qL6(HH~_nF(=f9@lT6^X}!whjW4tGg|PNM_{0u zNUvkhlDXA$2v;5_`=uQZBij;vz4ln-YbAw4v25QLCb0FS$T+)40gibBh~1wBx00EO zV_<3+6+@}kgg5rJ0TiipGq%WuC$eXQgT45knrB%4qopqA6`xzi&^a=YlSYrfqqNuzwT3*X}K?MvBSa>MNFhCYg4p@<#AsY#Nex>L^A&Gy4a>Z!=adV>>2gq`l zhhsY?*298YcZx{!z7*J<{_Dih7Ws@}NgQf&OyrE;~Po>z96MsnuIa0 zFih;dmLN>gv6v@KL{jlIDm=_?#oW8!)ig8>Zi~-fM-JyRmieois`#7S2087kR7TRz z`Bg@mM?FTvXgvE&^Wu}GrmCFBiDWZyciN!8OE`L2M!Jc3>sC`|4>1d=93s|2P0VXM z5B2m|37<7Vp{-kfIpJSo2-A@Ga%WjK)Oum0Y4AX(hfrNN4keoz^d2JlFTVp&sjQ%9{+AyW`9@^W^czo2U;jnUW z^Cd@7mC>ZN%PK;k$Q1f9`2CqF7xH82==H9hIMsOMWdYux+0X02TxonrbDw^)oNFi! zoruvqYKb_sJ;yr$VU9->=TnR2pws($Yu1%8%EuI!&Fgvm{?N6qsTx8zNxtNF-D=hw_}OxI z_wX&Yas9dft?vOz-Gg?%_i11dA@#sqFL?-)N~F&JoQPifAW=A~{g(6TG9NWP_ceG; zVBbTY@fNRcb?b`u$GCg;K1F;ixKZEetp4&w5`3c@lUR`XShesrxDeOSK++~RLpr&R zVJev|Q~u>luDyVpuqsvxe|b#Ri}~*_^~p^Z%R=)IJlgD|R_yZIAhbr^_+@a@VhAQz zg2yfj_$O7p&KKBj`dqYf_K@MWO?w~C^j;AmzEU^}C{YBP+(@3RXaOU$hKFzlQ|mbm zxe(@v%Pm`Em3UL(?2&(}jQd!#2u~CPFKVO8olLKRK62l*Rl6=Y2vy)K7I9EWa5TW5 zUEKx<&z2WaA!Ewrk0ZZvb9*b&p{NwBj@a!+#hr}dd}Hi!EFRE5eH!d|ezVc|oM=Av z?6bUS^sHWl8jQD#IMcRSq&z4N2%~7DFU8H@6zd%uq+M^n;w|CGk%NGwn(0~P@3gO_ z^@r56sr7>rN_VrkpSIddFk#pbb<=%O2+uhM@IMAs;L(*#Uj&RBIl;aAg%6$G z-Vhcd%^4E3{JJ`I+lDPbRh?oyd}_k;9QqCAwH`~v7)d-wq2NWyAVcPL z$RUrqdUqNdmZ}inp%U^H$`zWa5P+Eke`G`_mp_VcU5x$%N7$XPoV5I$8cBB$3?2uK zt>L)BJ*s#G5>YtgQae%z>9TKY=95K);h;Ugh&27nk)~)M?j|tC+`X4;+Kui=Ts(4h zp3x#}b^rsRND!ecxNrb(8g*IzxB;&>;?=2GUg3~H#kJ-n6T8}wx5MShFLji_qE1T4 zlK5E;=UqOpEL(!Mt&rmN@<~&ESWg?bS_N|tzgT7eTTPPrYDen-|#}5iWI$^1=a7` zb)sCzutls`#`Y!ao2+QqI#FznPqsy8rIvmc8PakJn>F}Hnxyq9H<6bQx& z8CI#Cp?qZddDEm>k&xDoA*5-c6GjAPHc0e0>0MU~*=Q8Zzfg^kzT==>z@tEB0B$@iUJO)`3GoJbp#w-@s!^x1p|OcM8`O7MnY-fSA98o zCTMqvFh&w!b)RUwzMjzBdHZOQ>T_Wz<62Ga8w`CR_zvs(?IFFUQ9mZ}fJVlI3HsO3#hN<>TeuQv~o^P+>++q0)1`e)w>X&9lN@6towVX(=O^$eU4 zp~k%XA=wL+Cb(x$WV+^&0wl&fv{yC48ZvsGHbr|E{?JmV5v1 zwb@UW*swS0ETaN9lDJ^cOSu|Kb24@}x$Zd}Oniw|zY@_K>KI$&U|$LM|I|WhGVO?< z8ASf}dLsKdCQd^HXh6$P;;ZqCeK2n>wRf)Z;U7$bf)+MXziPnA1_7w?Xrj9No4G55frkVvVMy=p>DAC@G zs$dk56zV7hf%WL=(1uW10&RUH?q022S!3D7Awpa9Li;q$UN2jL=%H>I$>4BC)$`Y3 z^<&-U)0@)CU?RBGiC_KpkAE>{-$wJ!#+RaMP_)uuBc<}+*z#`1ipf#4*)(>cA`Xzg z@|}smpivpL7yu0v5O@hVIxo{R6_-F7*eSs6+JU+KbMz!R z5M2NexXOgE$le&eL+eZML;t}J^~WtT)h#$6vZwdOmj#c8y)Zxy<=p&^86H>Q4@<1J z>hEomW~ve=Sq+Z?f*fz|-<)zqIH7;ie#o%IilLJ*%`V$#?z1qo7*Gsk;miN>-?|JE zr5@txZk9ELPeoy^mjYV1kI0~vl=|dXXhb4~EQ(q%cz5jAf`?=2A+TXNvASy4I-6nl zd=8|prE=lX3tIOO^qKzc#)~Jma~Dxa_e@1kt&IdzIpCUtvU3V}sgpjjucD5GP=I&h z&lJNXUz!#JcEki+FkomWR|WDfhr@J}5rrqbX>b)xx=5*5ba^H!OZR89zhHD-ymUfJ zRj(zxB+f5AeqayygY+R_$eVYgI&ibNDrQ1P`XHrD+9qZG*MPO^$}!}+@X^I{rF#ni zg@L)gp%0P&GPc6`_O(2sVamUF+T~6>Pdn}-z+F+jg8|gM*np*q=2&UQQxsr6`F8)KQ_bK_@ zI&L(V>XKXK0g)`wd}#M`iPo4;FO62a0l=DZ5=;>%nEr+e02hCl$fAM$MBMmB-eQ0I zla>D8f2_49ClmHdwjZE3DC0BP>Q+5V<>q{W!dmSB){-oITc?gHU}_CJD6DP%H}5k8 zU<7Nk1*A}OLo?mfp2fuccMgD~@PGeUQX9Y%J}g|xvNlL52;G6D>Ef|tif+PLx7rpt zMp70KhrFwlREh=PxWdjMmuquOJ)crsX*CDYl-c9nD98Q|E!2dj zfaG#sY(4#aaZ_4Sm1Lf*(1!OlFV@6DKx5^aus6KbA@PJmA##6#d4TQy+GZeH><;0l zQ^NDBJ!+6F)av1`60d#5N7QHoQfiIjwx`DPDHX;3)3vUP`@=JTj@?XVYgkJSPyp&7 z{0u9UG0v0q!i;Hbe-G0{*;gUARhKk@i-(^s?w3Kq@_Yf2rMwxU4y-{$#09+VFK7;yj=mclQ`)>iNBh6bTVQrX*GC;D-O~i~{Ad znkhITsmABa((U0)rV43a#FW;*smNaw2|K6peAYqBwmg43y}~4#lw~3!_AXABMp4Km z!n(3wD~^W@)W~KawCwS)n-jD7V|A(BeZW8%p71?s=_{y|$OY2eiTBXGC%{GbU+zu1 zKq}3Bj@7*cBGW&OUGSt$DExHr*xGu(^;c{p-fdMIF+-vR({e{i>@!UfwL=P7Bc^k} zyig~EdgH%gLI&;~M3t(0BT$0kPp_AoZ>Z>CDE}G}9O2F0bnzQ3VRlrVFT87PkoEW8 z>G~w8Sy2RAT)FRQM}+~$;g(~WxS2bB7*hw4FHdJNzL4E<;z4|UZ^NZuZ{Yr-zjBiT zZb{hX0VsU(wm_mXAb4@wEYjDNrWoTg?KdwNEoRn9`A$u*OMxNyFAkZ5!|V`kmpjg0 zf{`;D70?4rK^p^Ie&LIP)pPf3DH!|R0VR%2vBm~336acVcmOJiQ=(*S-daGUt0EyU z6;+$Z8ms2UK@JUW07YPfG0(EI(+e4L7X2$kE|$nln$+Lxm%Jwf9VZxsClr1NE<~x?P&0Ol580boQvafutRFBPq8ST@ zJFJ3)LjpD7kU@dcKP_hEq24R+71Ov#rbR$yN0JJ$6n|LcA^Yi|KHlmObDqZb6FrQ_ zCZeC%h7&n4&mZB6pIknNIV>s{p?R6MNja3rq45yTdGjpEvKf_C147;(k11xl}iC0 z%`>=SQjWu&&n&zJ6e=j(nUkO4{&=(n(-rTcw4_~XAuDO5a;%8LQZwO#(}NWyf-wdM zP)=iitHgG|;y8Sao%eP^1j&8?&$nCtC41%bB;bM>FQiU*@#uT0{ry0E#w?U_ud=2A zeY=*#rxcv%HgVgQBX#U3v}wV8+IaFeD@gq2A}Q=I5AZEeibo16+v)qLTjrKDI3p`FHFp<(o(J{ zC_d!{JGWx}uUCmLf(~g=s&lj2$DuHJloimpxsIO4m0jg9PslE!Nf&owA=q&{z?}Kd zx|{|61SV%^;eQoP0_H8)60UDDO{5(V%y-_$2grTB`Z0_IN)bZ{kYh}p2#zZm`|FTn zcmr7dZ3%+Qql8~;okd^*72B7QoVw83XaSLKHPF6ML;l{A_8mM&hnxl)4zkBd$bDYW zhQ)}BwPNV!eOw*Wc*sn2!W z;|FAx{x`qXYh{Wc0#J_7Yig4&C~&E3Be5p>qaKi{E+`CjM9^t$g;-JpdHq%Qu+yvj z&px#Q+OOMh2IoL&8UO-s)M+~ZY6z~LGaeK!WmokTMzwe69h`}mE!i*UIG!^kOY1+! zF6p#GU-edn?Oqi7~u=bKV8?2 z(v&zsb`7~157K$KXw-uTFqb8vZi21Os>;@UxFZ^V>CoHP*aF}8z61nOib4lQG>9lv zaLlVxZ(S{P{v0QaA*w^w(?E=c3ODiYWi*P}_LsFdT9-tua!w#J2#Xq$OPg~qL*K>|HJxV9!IVDXk${b(48%H3K2{Zp(sNw0B^E<(3`IGWP@u_Ui?kTK=3K+$EK| z=yn*rt+MF$vov2~f;*R?_}cI7h5NCDtMeC9uJq1%?qach4o&i+~I&f7D#mi%lV@je;Z8?np$dlP{ae(yaVs z;VF?yfy5Oe(p`@_ntcE4m48@2nIETyk*dL!p9xy9pXg1NlHn%6HIdsWY^E?KwgNoN zlUZu#fIOWYQk76GJ+fg2L6uj=YX4*EznPK-+1RKFC_AZbnMj#KKL}!_7Zz) zgLX&&UcPCZnLrrnoxeF3_3z6a$LZqutx#z%t3@e6qjMBnC`8bL`JYZPgo63rC7}AU zjSAkRhlP=W5MZBQ`$&_hlo)Ft_SM@GPQ1;2Jd~TYYj7(Pt|7L;71ieg%;fO&Lkh$BTufPPMd(jPffGX0kmGt9E zn~e0sbQLW=k3E&?F#&-uDON8pc`ygA8P~Bj4Xa(togHCSm-x7a`81xWDBTC+%<(-q z_KnXe5gco?MpYP`I5v3dR>+`2X|ruRjC5$VI{Pw?n1@}!{7H)X0(`Nq&~PMaT~lVUGjI=V0^a)4@n36{H+LjK5eK-Q4lTY-C#YPQS1Bk- z%q2^EzB8Kl`We5Om;6-P1w5b*h-doP?=TVdV>?=Y_nL)+;32p(T1v4FsMYN4o@_I* z2LhWdUG>-NX@5bne&j3ulno(pFgT$-m2q-DbY3S7c|#3W!9wx(G?p|@;@`ZMR{Exq zK-^xDgHwS{>SOh`h?hi(SyuUzGp~`;=|!Pbh(!d+3b%Wu5BAG z(=nWC4^if!S_wdvSd;pVnIJ8>S-)Wh0n&T+Jv-fxrs$v&2T@|ai~PVsbVvcU#o-@w zhI9Hv9zIDibJX_8T_mDb&%|0qC_TN$&ZF9pF6^;f=8RdIuZ5#p5E(GV?*~j3X@#GA zY#6h4V@>$-!EbfHM24WUiae+_u2Pos<1+;_H{GoK9PocbW)7FCIv~g*``Y(F zjc8C0VjB31Z-m;qSK<3`o3RBfUM2x1Muh%A)wyrc*Ij#rBSiJj-z(ga1I15_WVJYs zf2$kBU~AGI#uTKB3iwsum_QH?#~1FmM?re&QxxUrRQ8*6bap(u)*V2=alx*R#Tle( zpk#9ui5R2<|MclMzCOQ=sB%D>1Co=ciRi^w!JNi0(1=-m1K657ZLPq}=kzm@;QM%R zEHwnq%AQwxodgjCJk(ZznPudBssc6I|4*AJpEcDRY~wvBIpqZ3IRyBvzXMqP80+eB ziTrqz(}&J9gnME>v(CbAHhrB*(-s)%l!I}W{JV)(Wj-P!9}eh98^pe)p`sZehs)Ms zL*|REDte{^A_L@HruaU_c68N1aPn2Zp@>2EW7KWJ&QPO$y}E_7^zdN z!7OCZ7Fp;g7}+BAgc_FQO`H}eE!W>1R-!>EjwS_CD$xq`Bx1%)EP`jB_`a{eR&LKG zPhZEiDBGXSyClE=p;%Eu5!khRRXL+&)qj7|rYPDQg8y345~)IJAAuc*_w5Hypvv-q zrDBR(t`}bHOjE{LThF5GAjz$ld3>!qhO*^3CNw1cVl>9h_E`-C@^pBK{3q^m`I)%^ za^&!-ruuK>%03|ysC8Ggd@~s=X#`6|`n#pqJgR*)orO~sHPwmAzmoucOnRu?A|e|C z|Ed(N`_5ZJQ0GCMkkGT^X}$1GD=s741Oji~jkZ(`QrDIWMu`#;h(jTDmI@+rwzPC&=|sgSuNJc5lyN+p%KxTH%c;!JBi00LFMxcT~;%HcedcX;DDr zS$D-GlDUv+5rtEwCxrP6Mygz6&rz^iQuB|%1{>jO>MtI&n{b5hn;1ung*6mnE3hg? zn8P~DkU{)iRQpv4UhGM{!}PWLZpjgg+rM>E_Ejupq?{-lKUaRTKLQoiybH;5NbcRM z(+wZ(`_cDx616!DD@@<;0UNz`HN&TzlhCsq;2rtQB1fp5F64Lmh3;)lh)fss>5^Hv zLQg{u-ppb>9dhWhXxfwJer6y9fkR0=Ho*;m<)>qSoV3WY_?-Xje!KnBt2zkGNWYrN zzfDaBy#s$FIPPd7bj*QrJb;9d??? zogjdWn|dH9F)x$|y0RAsVcU}%kpN5L2qSJxnLEx?ZZZP(J0a(B;ssm1Jija6b}AAR z1}S086{{mY>wyiu^Bbh2KLN!=x23r{EjEe zPczp*=5Xh;ezLK@8994bRCd3~-s`GOrHimILns2@E+&XFSE#J*qbt>+F2KV%_S%ld z{gGd8Oxq$a&4g~?eCI#n-rr8kOY2~B6G`6oeaZR417z(kXVgng_^eSedi|xNmiGfx zvdz#MRA~($JkWJr{pxlH7;cnAl>{7I_~A4JzS{7r!eDB#2f(+EChI@RIv&-`4gfn( zMib?I6f;3~Oh7+Q6Y%}Lb3G`D%pR@=JX?+CMz3dRzfbBfPUoHjIR*L z>g~`8L4BMH2EO{I+iZen>$rvw-Z|%d1@9(U=f}J#AMV+7T=UVJ>RaDIg1iJ>6s_wm zhUznxRW?l%t8w561vEHQ6E5kDK%SS3jyt-^5cMw_TG<>fW;{`pOpx|O!nE=>*2v^g z6BxH0fK(L$Vv~e{cK+EwkKG*6@s*|+pb-V2y?<(VY|REK5Nam$yUeBk~N27m7H{1@y5dO|#j1hmc@}q3=GS&iuYgoLul3{7%xi;Mt zxMjZbVEK?iMKqRy!+AfDkJXX^*j^d8&1N2NsH~c z`9cORwWGRk&T3>X$$(~mn^v7IP?KErZ0DeCa69oA17TctECKK?jT0U9;&vO z1QwocL5uF=S#Idj4P8_MufMrdM2)5nfTN)UB8V>i>>R96Yu7quHA4(RFmj)-@E@Pv z=flmtE!ThM?BePHl6Nf5PjlGJA-@3mVo&n{^>8ZLdmeHKc?dh;VEr1mw}vee zBIWI+nEpZ8w(ao?ut%|ur~9W4e=6-~It4WYDH#h>s;U+XGC`{xzqzT4AW^)!-d9Q2 zC5t#_O#$(ny$V+L!x@>Xm5ay^U+icMGwt>p4W{fKt_V!G#lmGz23=b=Ble3>noX!G zGBc3Xty-?g_2jO$gLdA~Vb)UMe1IjRhkHNeCElsajaiQIv5vqvr=st$c8ARw?<%_A zlxjgqi;6zOJuBVq>DK~m#gjemaH5eCa|R3qrlFQNNxJPif6NPlM;3*cW`_AoBVk6m3e!@UX9Z6 zUF>DFTEEegX>pO9{-6zidf%i;;l1 zT9JIn+h}`*;v6G`19At=-(N&XXxE0Z;60qGri=d-0HX>!NQdKD+ zbfQJbLv;*cP0?UugNP(_tZ(IQk#t0?j^G5Y0Gen<06yE2hv* zu2ym<^PBR=BXm;BA)B}Q#AOu~?=}>4!b>O7vB4xw1bjmGZmzZ=|Cs-fgX;tL0To^V z#YXGheyU=31~}sYKw07DA}5z;qxf4EN+}PJaFokZDp3IV)IH5}4AEFipUQB1`j@KTu``$d5#{bmH_c*Q*QS(OfK>p37dD(~?wS$`43R}0@+(BBgTHIFtfQz!Q zv6<7Xa+Y}-!5mXekIQ#_(?;!B=maBn`Qp6wTrC>np49a(9MHmDo;GYB0gQ@^VyONG z7lpef&v0YgP6O^z;v1tK93if3<;7O!&j%$y70M*N7vziO-j%XbMp}uw=c)hRy`sLw zcve0mwhiq`-@Yy(?V@iorsFNB~`R&8fc>~x4Xk-*1;>Z*NV<5(z3v(X98*SA&6zN@Ia-r!ipi{S1dYxTJXQg9BMw|yPz3mbcjokApts;O?=vnIUP}Zt(I^Q?e8kaT zOE{r?eB7_erq;@3cPmD4?EX=X zs;Xe7K(8A$>3kVE7}r{SNk8e>8OG^cc-hCL-;(Cqxw$B~)M6&u2rj5-7)!w|td06G z*%=U7Q%5*np}jvd2j&+81M1~nSL>X1!;+fbI&;^?)sFEL3m zTjP?p-)-8OGLPkVh)knHBB>kOtxW$07-JeN*^YjR^(S!J{4TlgfU3Uj-v3rl26kip zHC~g#e*uX=%;os9IGkz@DE%c{9dF1Jeu;(rp~&Wex{3udDQ_AUfIB^lI?tjxrHCi5 zb~i;l8%a7JbRIJM%o;-K+Sq(GLcg$DC3ytbAm{ooh7h4OHlf1sny!NEXW^NRQWT;E!zj7 zlW~cR%y~>c{~WyAQA|=%5LEg^beYtl9QpW%bAUj!=b1`er`9Zrdga4nisUx`>RmKo zCkaEnFlWr7nmUC13R>fg3e5!hu(})l2y*}k+%f@omEYToOOI&erX5w7_)|W9cUF-U zutkB#vIj!ngO;2(9vwTU2rWZ2wCXs#8{0A{hrH3Wbb9$nm78P?r!b8R-H3~uAR4Q& zo~H&DC(!gQOWfP&v$2tzRNO8l2RtpTC9AG%Jn*R!D>O%M-9p-OI{zRy4^g|Hy?5S2 z6-8hxjJ$punzllqDP4fq@3Xon0Wio^E`|z>9g(fVWA;iNz0FF^f?yft#bDBAu+aS> z=LX*608`l?+3HEoJ}w~{#dV%n-e;`HGF&kS(dx=bUGt|n>uw^)Md>yYv7Cv}_WnM`tk-0j06XKyJkIG{B> zW7gYB&CzPRf2a=U+gvso`+`Qem9fRLOH%SZdA(MWzE6EO&OUd}Q9)tw@2!?^92RHg zz$3T;Y^(BmU=U74t%{Y`>_;`cG70&QP+&%U0t2B=aaN@b5)#oQ3}NCQzu^>Y(4fW{ zdKZ|vb(bMjwr!VoyPxRl2q!5730_wL9li@6v>d+aH8@>=s+>1H_*0-f5z76+ zHiY^jqGajmC591L_9v01TXPZP$R}YUdnK&RUjj39@8AgL9OF0lGmD>Sl1J z`#KWu!zuM9;EcnuC^|TB`bwWE0&|7a?)DFmP|(K+oX5UkzuP}n0czm>qu`!~D1t*F z=Ek{S2Xn0jX(iKdQ3atr+390m4E5=bX_zj@I#@uBg>_rdP-@w3?0{ z;@_Oe6T3%;^KnAOjWdoYCsDJpTm}yZXj2}WnXqcCevOAR`as^`f*onymWg(0F{4k8Bgbr^Ia5wsJSpEbpe>AM0c|J*hWCC{Z z$osQp3KcS<_3b`Jl|K|wJSX3N` zZ?a@)DGP751xLu~Oo+fR*#KrQi(DWF);;(#5|j1*MUXf`?#|aF^$Ta^?xe?-JuE+o z_I}X0MU<-H+)S0Hj{TwD`!xezK^va%B)X5i5x(XGULk{?%MS{k&}k2kRd3O_4Z8S3 z*)>v99$LHAS_df*s#nbBa-F3d%&Cxv{+6(%D|s-lA-NhWI5~KdS_D2-O6H5cO@hF~ zY2tZ}nz~XHT^?2Vq&DsDAkrEKn9y@g$G+ne5E6z@D2lM>(N;LHmDKkNM+%rUQTm-Y zd&h{y0=UEXldpEdjZ_a-G)#@0>_+Ryc}?$Et3x)HYC*lmHrzVp zVAp|@zZ_F!jN1WGmQ5WB*&$!0_oQ<-JKmIix2e_DuiVQG=R!=I8SD7=j_dO&_C0VG z8GwlRAEkZgQ346eJVu8RBeoj^uXob+h<}?FABUrC5IF&d<(MUxS>1fGyvJb4@VvF6 z{%_O}jYZLpZLzwwx$$Tzpm(Ab_gNo!4C#1abkwLBI?peq% zu*dLql2Hx5WApXlp#-@lh(SycI2Jy(!mREE*5+2m3lAPKjjg)0rinl63Qi1M9Nd!w zTUcRzA8OSa$7dNz=y1g_=(1X%K(jU;aD@^9xu;`w!^sVe>J` z&bs}KBAxjfUcZJtHtGVHiH-A`^bYXuq*Jf{o2eU02i4?$Ca6TMrF+;5CFwne;^^HY z2=@TTJ&J+Pm3Ic0g*`rhOEb*h$T$8`NIQ+ZQ;)VwD{6>TVOr6=_O<;~g_ho-stD;V zl6UhLzH2SO(5FIeFn~J%>RU#ZY6%m^NEKRc0&|_Ugx>ul+ty&tnAoQfra)QKDRi4J zxXQF5ph8!4F7-V`l_%f*RWk$kOqkXDQP5!ZV;qF zk?!tLx~03jJEXfCfgz>4yO9Pd*&plQ|2|lYeO(+l9!ze(wy&{?xr(ItsFI9eX;KEzQD(?oF>JF6%fjhpY!{5|X zGt8~PPG&`u)!KC$DFAnjVZ;xYmi&MZMr>`egmRZ?(gofySV;F9JZKBbhXW7GlojTU zad8>*`+ijxu|LqZ#e6Y<-6>E~19){hkpMKqZ9D2Q`UV+{SOzk$Z|i*h3DO-{|5}k4PY_^) zJoMX7{q4dtR7a(T&rvRkP8db+O}16?gJ4?IE$aG^Vn^GXXTSAa`hMO$6cH_>9>@?X z?y%8jd*)116HXHZMAK^4uee&c?B}b<=(-Kx%lv*34={ZL{r&?A(ZYL^bcY1j(zgsE zfoQ*w-(5SOUY4!P`QwxKlOj0x?P2WSy;}TWj!H~4oDEMUa zAw8=%7)Fi2GQyq{5MmyGesWiB5MjXCt|Ex0LROgvOS9X=ako*T?PuvPD8NgFUMmYX z;J;DxWKE{59@v}S!s}?23_!2p*Yt7e&tfqMr0?c|Iejv;%Yvk@-O}| zLt-9=d!gaA$ACt}{?pyS`ImXtf*u!0d*J;f$9E=L6YhcND;Gcg8q^!4t5{hN`?%wj ztV9YiT-VAndsqz&HS9p59vbSnZ$Y!mV3t=EF5qgvYj_x3H}UJpGM#ND{-<^l^z7EqYv{h6%pM%hu^T7Q0P(mSaC*w^OIR#*+%G?w zFYNZWoOl!yv)aTm!Y9Aw_zV0nC_KOq{A5-)sT=HMMQK6O72^>!3{&KR@nvy~>C+_D;jd?Eb+T({3Hy$nY>e+b=I(|gSQwA6t3 zvyA^E0h{wTmIIygv)E~};deac7HkVl2KA2xOSTtH=lR<#Rhd?bF7e;j1QwHO#K*F_ zSN|^%F&2dk(P-sK&QI2+8f({X^8c2%$$>t1QZ=pxlbLwB<|LUjkvumqQms^B{t)+Z-T}vn?h_vi z(<%$reHoPR<{!s~i-9>i-zRkz1V^|zk};6Ntdf25&0I(4iaG@Wo2{29V9%W}{4!UN-;@o3<8XaU(Pnl(j8$WUE47d(zRbA$Al9i51X@2jyf z)d{4BFqCaXHq3MtW7Drv)~)L(^Clg~6uITSh`ZnExqL!@b09|;$kR$|MX=~);uxeo zx{7}K4zN%SkLIe*GoRekE~0F%1LxPO_s?G1{7DGaMVX2wXuFy<9TbV56pR!OoQ!UYMt5R{3+u#Df&FVMt!!D zH-=Kd-%KS;JPd4&(db)f5g@6*_MQ@dP0Q zNOhyF56aIuF=E#hide~cm)-R3WRnaQ)Eaj_f`}XxQ=6AY`8&k9lW#pfS3%!%zXA>G zKY_784)B>z5tF~Si2)Bjc}L!Kd6tC)C#<*Fy^nFVP1Ajbi|y3-t|XXWkpZ(Uf?Hpg zE&Dfsu**|-au%k7K z@#1mzCV96$dnNpfZ{`JO4v5_h$GE}KK?C`Khmhc(F7$JexM{44CvKjDfE0MZv%iYk zaNI?~3Vz>yFztNB4H*cgj*Jz2yyaK23NWRXXG@t`D69kn=!_MY&n4!I5S;7w!nmy$|5k%6gYx@eTzJk4O*JgBi~wI!>CgUADYq zpR5kH=aP%hWQ=A2RRwz5C%Vcsz#YNjVXiRZ5Ir2HY5N}K5!0T{EUF{X<}jeC(XzR~&MH&7^%6^aDV zxifv!Y|DxO}kYq z@qIZ`Wz9+b{NGn^MufpAVi__=4g+y;G}zR{q>qmTfs8X+3znxOFH*u&5P_2bZ1VsM z&yQ+AR~km@D^d1YK>;R%+N@7zQje99mMDfk-yr_+?v~T=eB7kp?dgvU_FMfPf<=&G z-xPB_>}a9+oTP98f+-(R`Z{BQ{lX{uC2lXTM2ro}BowYHZ{F7Psm?8Y2Pl|R5)K~} zI;3zaOLcTS6ix&t&{2452E+cA=hkl$9r~7&_VBZeDpZ*j{i)4RwznO$KpPH08BDAKBB%9rN}L zgVCa^RAE~UrzoP7kVVu3nLRI~@wS&PD&iad-bIgymk{wxTYF{yVi`p_!i*rr&R7rf zk?BPC?*bkrccj{SQUjF6c9)9ZLj8;()0Bgstz=1CZyYoX3Mnt>;yEkd<4N#Qgu?^R7!y&61 zy`c`;DFX+z7g6&Cfih@3|7alp^=xH{!6*lHG2uRx1AIZTsQr@@&JRFmlrGOy8lDB| z(flE7>3xdzNz`a04FITOnG*ex4Hm*D_gl>ZB)Ws%8q24`{1i{;YtBJxl_H~J24?_T zu+QFrFz&UNb0ad6->mi?kF#W((wN;xLUGf)kA}hSZZ+&%v3~ry;&b7D3p}ItyF{1pS)N?m3cF>3TyyNxJI~i<97~ z{zDAhvxWH+(U61u`I#1Y-m=Ku`7{Acv^yDR)UepCB@z%Wu#m@?{qRdQ=dvSZk{Vlxm%XRiyh zMOBE^8(t>=1-hZ7AJ^KymKA1L#r)$CcLIe=Bz;Lu`7)xFl+~q-S6#dH%nX9tq0U}{qzj`GMd|X7H(tsGJ&7P zvu)s#b;XE6nBvX7`+yVolXMKw#k#2SezX;=EW}Ar^Kf*PUL`d3NrX zK;gB5g`?BBk|-jb@WTiUA5XbT^Tn&NqLdWY=3_7J(qi$DsT*xR<1aN355+syille1 z-qz+4A5PZfipAMvKqD4XF0Bs2 zmEN1rCtCY5H7W04vwh7FNf71&l1~~V$x7eACEHWc+5pugM}G)2Ffw+?UH6>llhvA;U3eh^3)L2LrVcrw&Ru^wSpZ^9g|?Wp4D5k!Nz9vuqhUxAIHNxndXprAVur-{;S zYwEfunbjoXtxXT+fj{=_3hT%cc=@{Ytg~C|g~9MMHuPN0krVq)XxAMu64J&}e;Pps zvwzfF&yx}3x7}z1+Aks?iMXNY)$q4ERIY0n@`6IluCtJ?LV~7f_Q67WBQ&LcB=qO` zK_!QnwY3?JVc35n{d3dtq{T6Bi2Y1oW1ZLOi#G87cV7YAhPvMn zh0?lKlUP>c0fx1XtE+36&?c_c&p|b1_(aoE@fGKb%=6`AwD?bglM!k2jhhSlC}GSC zStisy_;nz~&zf{^(cUkH{nJo}AfgnLDij5}$;b(aUp4PXtH21;wiPa9(K=5QhS`$C%;9dymiM`B4=%X_-`1*Ub!Tg6b0Q=M0 zQt5CVM)5QbWUXm)Ay6h2dnsg(NUZZU!!=%S_o)%NI9lR!_yRz!_!@xOkimLQ$p01v zBx#}_BV__KtOWy`pV|w?(G-xqLzwe@WVkgn7+K#K{w%_Q0MuHHFd;j55IN%;u!L`6 z_s`a};W-(24owSrV9F&>X2B$?mZ^TMKAt%oB9GKAnjEIEBk%kWJuY|!IQ47Cu}J;$ zdT1o{$Iuz$-38u2BDNftJfZ&{f9{t`j^RHKAg>7C7k+~*dgyz8D^E=P4QW2&c)Jm9 zalqDK>zpWZ2%`O$6fQ3A`)(f=|9SVmU_xlY>brrhu{2y*-|$6KU%7MvM_8xIbIJ36 zu1!C=EN&l}ztDSph-Qda<$cxNi7uHx+sfp;m~M+LXs~=$V`lV*s?QI1cLxb9A|~vn zEg(HmU-2JVu`jwF@jX|NGnneB(Pmhh)}9rU8rDtm?sxkpI}}Iao0!O%;{PAHQwrkR zJx`AL0AP+y4^*BK`nJo7+ppI5$65+K`PAEDBN7&Yzo;`^mC$$}+<`pYDch`zUNp@q zQiTbYrGRXcFv@4^9`7#FF$&=V=30#K%tGUmF+7b!+I|!W!0N;CXkc^ z&Xi1m^8`x3zrXz~4wx!cQDtoxqb2}5FzQC-WFw+n)@G~gRd-hwg-^DmV2~uLJ_6dm zcwclL746$&L3>12Kf8I9tVUf50>IE4>e+Z;3=58cvHx2Jeue8buA!yGYt{tOz=tnU zMa}PWs{m$JsNv``X9BOW4qdS5m(gAYih>VPQ#K;yJoWk~iO+#fj6k!#LJoP&a#~=6 z%m$rf^7*Cq5}GnAb4JXs2{1l08jPa_uF!LBupV^CULh#D(6M9D8;wNn z7X8Xn0n^QhzOyml^lFGOoZ#sXIG!q6K(S)gQ-a+DrrE++ArW{`2n+j61W2w3V&|fM zh`A=+alkV!JY)~^+`Zo z;P0Q!R+>!@j+>X~Ffy-CPR>0?jhhj7kB?cPBAC^Ubq2b|Lekg)D$3#&E}q!1CHrnz z+re6kDG@5N{jlTyU^a(8c1pTSJNU1`>(g)s?|}CjDI@|1GC;YqK?W>LwCp)PCNmZv z#pE<=pil<#xUzEYShA(W3LHRz8D6^kc4R9j-9xi_fvPaM94O%wZ2D!W_q3q0rYA-v!xq4O+ z{K#;vK8xvUKgC64Okx&m)3I-#zs)q-Ueq|D^mN;0T_!GYo0b+khKDd_Y5dawX5j$y zqFJxH&35p`K%?%r=~SV>T9z=U*;=fvP| zAFi*XgxAkBKZuM8WLie12|lbxR&OV0Nb2Zh>KR5hP%;~*{O9HXoMM8!u41}QYDNX# z6I#_~-!e9=Zs{4~7(=@cnb6BpFKZJ zF}zdYKnKI9#o^#_Ty1T052sLFqmgwotW(OdkarQUo0II(H@kk;aN$0FC~vvu12U0V z7LpET1^M&BdEO8HNhlkJcJ8%oyP7!%XrX2z47q1|Nwvy;@&y=tqA$2qFh~FvGb1|6 zjDW~18Eh1S`DuxWD%uK>4xU%V%O@#fjYkvIzFWgW-rstJ34Fqd+oUjvmH~Hy}Dp1b*(O+TKIuGd>LDUx~ZAxrK^VcAC{8gIVf8dxZoLS+TzH zAjsA#$<4ss>Rake_u>E_i3$Vo5bgW^NXe2g)E+AU9o&>JS^}W}@9r+EkP^ ztk}u>oii~lD;$?andHJhg}*W<>@Q5g3hp(^9mn4K^!2Ysi(@e`5}vEm1v6k6)A^r; zG-%w+M{L&388^p+hGbz9yc$RiO8@?*PHKj&&w-vz$QkW&$SG zO@?4eE8QaGYI3H4HXolwT84nV0!vpZGzp<{;~DSj*A5&*#qj7t0(j;~8T#nY zsa5Bdxt2EBhM5z%KF#B0mj0gWDie{DGn9P)WkP z>0Xhfxgj+c1FNd#9-2gaKnsQt=`mZtSBW}_|7OP@?Gwn#cwZB|e+hH@*RO25evwIj zoWhLH@RTn^bc1dX4x7?uRTnLpOeCQHIbQYIGu@gO=99teZq=7s0XIa|Yig|Mxytfb6xr2^kg?nYlIb~RRdR38kA zY@bn|%&f4BI0%A$25xg83RI@sSt~4&!@vm5fK4S61$YK&d<~t8tEq?8r^n3w{ELlHrqm3c>!w|6=EhkIiGUyfEg?}tvl!nzMfx`7SwHli^^q`5 z{`ka+GRBS7?JrVTwYny}&PBv^#$*YDuS8zrGtA6=U&^~x4Qh}kAO^cmJ#-<+zk|dT zh(~9|N$4#y{A@g&@vGdBCUe&N71(s_TJj|YN%|VzQ1ejW8R=>rIs0tC#Io2*I{bYX zyHF}>fIW?L&~Wlu*hq*i33O%m15LZK$}D1S#%tR-OwKYZDu;j{9usg4RWG?;lzk)y z{}u~yYn!omCVrtF+QULOjKxf!xVS5jI84cd4In5rXu=2|!gw`5Gm1sA5V5oPxrlwC zL%F;}iej-Y4+0kbGzF55s9ITb6op{tUbcIv~%;Iie}k z!~a1#%dW-(5nq_fQ^Y~Gink=|x=TW@pyr^?KpK$UH6eri1H*n-YJ*KXKgQ0HX;@vM zHeHN_8cx}E=Tm2gzX#TavA)4z=b8C?KuXdCumu#JZK^+NgM)BFb$q+H2Vd&3z2JcP zrtCO~@ov6+byb|i!(Kbe8^bq0&Pzz`x=Z%cy^oCI{mrEU5ZL*TWn-rhl88Ar zuciY~P`f)Gwwb`+AIi_i#YtRVgX?;SYyLef0OLc}JtAObL)*m4HgEPL-FPRut9@g$ z?p)r&JCM|Fkd|{Ges-AMM}{MPrsr)7O#9<2dSbwrXiF9gR&MWsNI8lYOPjROxvE$! zsH(qpq@nL}Rufxz!!CVD3Cq;v`Fu59OP&9gan`PmO{Ew!hvQoip$c`D-&Kc}>qur3 z^P9%Am)YQ#-U=C#iL_~b zUiuWFh+G-WAP|pz;j=OKGiW@C1a=puin)l|k2i?8jb5_7_dUR573H^E_|2GC16lVr*{0VXTc{qMH6CFct^90M>`(n zBlI)h?HM7-YaVUWJ-{C*iZ!jIFB9t23}ZZFUPT-%0y`h>GS0c5;E>as@7R6amZ8e& zPRKW$R!WF5$!HT13nwTJT=NNYwpyygxn6d9B1^SyE&`a{yHq~z*XE%SCNCKN3ngOF z49|ziMF71(;*$NOEZF_e=_wqj4wg$K!R(dXWD%HZ+&R+n_PBd{d-boZEF6DvXXi>3 zVKoZ7{BMyC#VD5bvrmDH+Ej0Xl(+GxLvsAef(v3uH%D|vEEO%z`6A&EV!iIq9du?m zKD{xd4@SG+J~bjz&OTOuyZP>j&-ERK>Kf0{wdFW5gR-h{Vd-mzM#tRXMnmS`C9gR^ zsMw)-N^xp5cE4K{ujlNfC4VHE?uH~~WbeMxTtW?3r7r+Z9qsdaQx+Bul?Nr-7|%(y zCGsVSmJm!n-0R$W-L2qfMtA+{_xlMU2dh+pbaVA@cMb=icbW~l@G#Y!9^1Bbp48zi zd^Y_tgG;QSMH0Bl^-v_HMm4&F=jL90lcRR|>Xm-7fh*+>%f2TR{4JK`)`a!uH`ZVo^0*8|p+=rQys4qSYztj<1vG&rm9d@Kng;#C&7+atF_ z;kT?*Ju3(?1-%rc-kjofn>TxM^qivJ>T>bi{SJ6~2ErqxbCS;|eXlT?vd&t)u4)`% zzxM*Hsc_6?@UvHMM}qJmFxh=XsUjo4*GAwhjkG!7EkuU=9SA=!|7cje14x1;N4{D# zcy4nBe1|5L7jx$Ogo~*QeY(b*SUld)H?mQ0j`(-l%eAgVg{csSIZ!Q#4I6o1X_R*Q znJdvNd)S{Jr|I6rXZ%Jp$Pn_?f=r+|9@lGsEr5rEYJz;SE(9Ov=GSJl1eDF;UdP@U65?V4pZl#7&A-DdG!m0tm);k5$!hW&Vf@8? zSrtCFsM_+PZ@dMbonf{@Pi3V|^I^?QV@tf%uOq3G$QTQD9dw+ZO-%|>0}6)^IDF3O zWO*NG-fvU~TodG8V5|9vnG71qnM04$n(pv&@BtaAOlO)%WZhZ9d8lOXhm+Tbvx>Q9 zWA1tj$8y5iYvI30*RI#Aax8CkY~{sL?Kjq>O7Ib@bivsTm_mh<>yM0nBsuMS&B}#3 zI|?9)WY5X~Gv)~qDpDDQ|7M2lxxxgC$|$Sq!QTR|mh`EX)spS3hK0IQYr$WgAM)#B zw*{Y?MC<4DzuxY4B<}U`2hb}J`e7Q9LZ)c?#il+FP4xHIqPV0IhV@VynIl0% zN*?lW>5(QA9OSDJ98%VsN}3J0)BXzBt#o6w8%9v3sMn zOe9X&RS42k?Wa&{11C{_J!XWXLV0q-Bg||Vj5I|m@LZQGPBUglv?GFGlu5k7{MeIZ( zLqba7@y z5Dev&tISZbj)peGvco@Y(adgsV=>n#*(iW(w#bHEj0wMQ8ecz*cubC|G2pNz;+Xvo z7y^b?+)fw(jcrcBrp$++!Ro%M7nx+u=}?x8*BYSJyS=@j?12E3{b&H2l7@s6nqZL0 zRCyG9x5W%+C6rBdF^L^WlHt9QYTBO>pZ;4!Zf>X;#^MMo+c(#$%j9)Tx3kT6ghwVTq&qDl5w#qO1FbJ^&dDcg@_aby)d8~}6q>3eizxyjJvN{9B3 zB;Ep%+nL^RWaTlBj}DltMItP(@+WTBkHZt0iWW5rUVBq|W~g~*w>f_*vBHU;)tXTr zGJUl}of8h$BuPergnfjS)yZpSDxOuNDr-}$#lcoONwVKTRx{c*+Uh62N#wu&Qx2et ziTMrG*-ToP{J;DrgSBm$^p=B>?qrZ@02Blj^*#E`^+)@iWRt;CwHmh75$RReQRlRu zFJWF|@o-D^xbB~LiI{5ue+Yzs=rxsk-L0tmdSgCT;=6IOKQ_-tcu5ZW33dlho=)2r zaHqVyMp7{hb;BwHM$J)X@^7nm6OK5y%PHGqoPRD|vNJD=F5P}?T3Y0gDfxRh<@?L} zeTV_a*Q*ED8QOziq2l>>mpnf-cibn;KF(yM4aTGPxew0P-oH~JaZVT$DfnXKFlKWj zJ^5yc;R|5!N4Z@GIfz-ds!3+5X(_dey}uqY6(wOyx`5}$3o{A3mg(YX)z3M=shR3e zI%?{3v|ZR2ADC_Dkc!N|1Qpu#6b8PoO+XQ2pFG5}8`?|w%PtlLZKO#JOxAfvwUcrS zNnsY7BbeR`k9XqpFYPsjlHf`aeZLwz_pLcW!iE5It_arE$vFsLrJj?$Vu?@QlqyCr z+H{jSaV&O$CS52l<ml>*-G2?kpyo)n6QNip7?hKkwzB1;oEa^TO2Ju?~~`WVQ8O+-FJ!y7Y3;=!Uj ztZF6gSCw>GTU8I(PN_L9J8Z8${hix_wsD&9z(9Bp0rH#vb#aX?!|57yk@VnNd{r|} zHrk3;(ViGd&_UMnqs>p3H|UIM4?AjDWlO&4v7m!o7A`|^AOZ*+1DXpaLXJ!uM;2Ch z?XMwS!opW`B1;HH_#VrP9Fxa)8iqZLD2beN4r4TFum`wEx6>xM2MZ3%rf$~}R{zS4j*bmAZk!x{C%jxAtIGsMZ*9h7SG(k9~vW~UR|U)@q7!SAh0o=>Ad zdVy01t5fMCYR!^y5a26+drCe!)D@V<5Q^K)E6#6FyZIvvmb%r@_F`4m zEf2$&0Re?stR;M~zs9YF;>4W1{84LtK%eo=6dw=t8!N>4dI;k|;TvKD_qJJ`wh$6*sDa>aD5|M$*_0=fgPi zE(Oa-tvZW7p|~z4T>w#2U)4SGOkC^O5V12z4M;Z7`7fi60NxVaTS_iNHHC5z)qnkt z#v)Jk#}w6&gBnwk)Rh{mPASw+fxx*k+0Vu;&}YZ$CzQ!(V$y^r`mP431vN znx^`}(1nW#5GqTTCRb4TUo)2fU{jDRa;w=i_zMnKA+B! zi}F;Lxo!-k=pJ9$=XTVP5WENx%G1(St{AU5uiST%@JObyhTuX zotWL1g^k2^2cX6RDfTAM=4bfm$;_`$7BG&wc)o{YP6tV z`PbXUX14D4 zfJ-Cv&tHt>Uh7r1=pl^nT`D(xFkO@Wyaq3fdGEe<%@ti^3|qsGkl`c`HCpw6^p_@6 z5A^4|T~Q`a<$m@h>({lJsezQu%})k-o~=2y$QTehL}m}laT}#PY*7nF16I?UnEQCR|=N{i&KAD{GLFE;f3w$>=cG?e`tJIZd3uXzf(z|7IteW*A z@kk3JyCQ_09gGar=d!)5y`Ho@26_v7Xbe zr`}E*;I7i}urkuNn_-8H0_JzSjvI)jKEzrhOzyb(AzxZ>puvYKZs1|3CA}N((cI343xe;f7wZEm%gzbV)Y$e`}eSeN% zjy_+Pt)Ctq<73gi#c*wQ12b=;!qL z;;)(5Dg&k{R8Cb5EF<$b!dx1*DDq@z5>kIU3nw4Y0&o$7{wOLb4UqgD(;@b-&yg*I zv{hGE562ia7QApgp|MnBgw)hDITkHhn@-yA^TZ>Go8g>)hB04-AxR4)Bu4hFH>czg z=t7pRqtYC%9jbNpk_!~F&0o_yl)KH)k2?gekd@{3zpvwiZTbLuy?*IZ6$TYfw^xHR zHtxQae8qu{VibA)4OhKWzb$mTi|(SmjXv5|_3n2EA9!+#jlc!(YDHdQGg1I=dy-(z zaF;*DgaH{9EHA{zWCpX+Pv}RfaWP?NP!p(2FRwC63X86lGOQJs_{+U3Z{2)mT88sfTt!gJyjT3uIgv!r<&-o8201MA5@FLRlK zkeu_k6kkn)6ocU29yY?PB7bWbDwdIXkeI!{bJ67IT+?g8WTU<`1q|m68+PNV;rQ>5 zu0sx-X$(f&N<3eL#Rum~3H*AijkxaiKqw|=VtSGWPJuV~r`_m7Kv|~+U`IVqCzl1jqf`w+T7@+#w60(gqU z*LM2c+&f05M^%PxBwg=E+-|OGi(ZR6w z*A~zA4l^>rtekEq-88pWSL#nl5~@}cKDhE79FzO*Fp%3O33vNtP64@&>5S(;rJefC zOB^Pcr@!ESWg58bj8B5dCnBheyK6d6OupmE^F>dfNd>gIny&l#wtz?W5b%@#DAGM% zjKUvGvPh7@toa{X`+w0?vZk@W5qUe-0Awgf7GIeL>t!&j;n)-7HP#wCyA43KZrPp~ zf=C-VykZ}OB5$|%GQp}b7;EYq#N(39yGa~(TiI7R$!W6u=yJEvB=jnJ^eD`A@K#gm z=E%Mk3ibB>@rwY*nHC#fwd)C2io~S{=cNGQc_d^qW|aS_-=MbDe`|iz^x(ojukLO4 z1ZmA%J=P*+38BP|bA&pa9RZUBS?10OJ?^)^*>|Sg<(&uvKo)BD#p#elu(Enz@pL|4 zmBa{m@=Kdc0YngObBq^rDFdl~+0>O*cyZb8J_WC*@=YiIx4=$wR;2(9e#b#m`3CgK|pg2Ze$G1=&=NqLZDGaTrLO<>Eka|)b);x z95_%33^lvnhKr0IH*pl$kI?B~=!nhT-B*exV$Y^Q61U?VFS?Ch_fiP?%CMM$%j?BS zt+FHzZ#ArM2?lk0IQFsICr*gr~=J8wWz z>8DDyE@V}(@4&C=D)h?BRRl985D(}RClO(@PZ@q-cir$Ijlj6pHTAZGMnp(_mUsPb zy^zzIysqGRZ=B#SJXd&eDleGv0>lS}L@(JHzr5%3we<`Fa9u?A=W9$LP`(27=zf~# z`vrG}BSNoy0grPw?u|k{fLNc5);!ylT@T}`{zLbEAgd#i@>iCpX#og4{;si{;^439BU4zM7to$rDWm{tM;jbm? z%S(2q)kQe_Nl(m#2i}c54WWXaVSZ*1s$&KF4QKFc0FycOGstZx<#Pdh10W4{@0 zcW&^Pmh~keVjQve-Q%}t-~v*6Hg|Y71kEo%;PJk@3*Nw-$RAZo{H&otCK9MkYhaz4 zC+{@%U0|XnI;YrT%x+Od>X@Y(ybKC+R^ zt|NfraK!M3eY!AxNFtaae79S%V9Pn?+snyM#{$(T1~SZpenxw((LI$^r^X2#-xHxT9CKMPWubkL@xZOcF5+u!qcN9{n7CNmdL++ z(0hCA7iq3I;pYSMbI4Gwj1zePeq`Sj>UXUZ2}%$Cw;$V1ouYruJ=S0iaZDI4Gw#LI z7`*45` z!`V^g!ot$h;maC%wi8dhA4z%%BC=m5X--IUzq(6|2;3kPl}r9X9{7nLc^+3lai|FS3!8Z9Qp~9_O_4RgK=7ZV;f8PPD}C0-BRO z0hZNyk0m={%v}jsHHaPa&$Yb|h8JN=NJZy>i0)sN1&D%w2TkZz1g`-|Rki@K));ik z^5IoOC@>117QMHm$XCBYx4AvRt1;~KvfJKP5m5G(5kFoPVS#Rg5pYFI?>biu5|4ix zVqM<&V69Pg%iDE-YR8m)ha(+nvcRHXF|Q^O>Io~C()SklE(W0ewJFBlz;sdh5O5vT zf+O%5bDRixW~s8XHQvvx8V_XIcso$yi&SnUf!OWqLqSF{p@5seCj+_wv-%I3SyNR% zbP_~ye-1}tVgzhKao41IpHZNhc3LjkaZLu~)&Q0{^k8nzd{N*Jo=O{k;VBT(AcN9^ zLf~7mpETtL+X=XPY1*Lty%SnN5b z20jbX^^>0#K^cm*t2E9IlBR2Y;@{RX-7CAymw;SpXl zfu^EY@g|-Kf!s>h^cJvQ+X95xfxxEY@p;A_0BW2mIvHOWdl08_z`KQ{L(}t@SdA%> zUWbAU4HZ#@Ut8rPxHb@_Q9N$-TPn{H?w;@;itfjY(bkH7ti+K=5CFS1rNW30CIb|D z;qs4AsNi16>h;DDuuG<;Ed5cj5tfucws8zW?8$3&W;G0{1AQ?7StH+6mKu>oXd-7% z5B}Uq8H~2-HHH2NhJNo)+mVFnOF5())z#>w`w-Mc6FXKi^|oneP;j7A*h3Mqc-pMz z>S^wfT{}GPJ-&)Q2dc}?aPo6%&%!HKgPjA6B+}DcnWRE!s=Nla+)w0^7yqak{=Etk zf`%It@B0o01o0=&lG}+~adcWHriQLqc+r~6EDkRm*pCkUXsoS!8Q>x?_gx|D_PTgM zad}RtIc-+WXMSj$9qHWx3~wRg=8~+ypPAa~uX;O}{$A0;LK7G!Kx-?IFies9+MC9i z(OC(opa3229D<90?gi)flYtge`*%vjnBDbLf`h<7f zgp!91JsE8uZ{O0ANUV|v^rw+1QIl>Lv(I*=NUN3QNzCiBS@{d~ z(7+NU9cCt|^AQ*)gakn+P+-LNWQ?zGO9%9C$NnXEptLgZ%qts-^5WrDNhUmdzZV$d z{Q9v4Xq*)W@>0?Rk#wJ5=!+I4AshH)}!4=IYuddUv# zgPk^=#%rA?OK@3V!w*a+?`8`#Xzc_&D!LArXvY%aY_UuoeP!6Dar8w$8(CTJhB9oo zr%rO-k%uFkk{C@@WLJ(e-)4x@!Ff^?e;es#=Ru~P^@t1<$LbG+`p&@e$Q!nPLVy4w zG|OqPr?O<8>e-aEO8gFp)`{r=-Gmaktm^Eve}yZmxI7p26|X*(MUL5YDRe_PVHw)E2}V}bhz2NzX3AZnG!ff)ET#MumrnboI%*qy>70?odR3dd@i=b zSOXU6@T~oWlL)3sbZJ>nhVT-AftTZRCd4Gh@PbJO!2`9`k6`7hm1f? zqkbGlqKp90t3HD+IG|`EtBji{+_i)kRpLk=WH9n5am8!WXjoOX^9kHK?oV-tDI@(% z8p)71KnkO}eHb~+kH*c45=eM2!soPP6R2s^8-EV`-0JQ@lNY;U$sZ;U*-L0rk8*X%{+sr zfYM?B=O4#%ZQ;C%8a1Bx1cqsFk?`34_=fImQalL530;aKDRk^XYizU@<>T`HS>xxS zI;CdbYb1VU%o-5SRR0~a`v$OBRZ%uwgRyJ)6;gnZ7YaH-1 zr6y9}H)o%l#0QEc)04IiM|u$&+T(`pEtKwDe*KJxr9c=YB~WCJ2l*`2)ctB&$bejxgmJ1|`RfeEx zW@AI}n#~f32qB3x==9^KMfjq+jeE{42aUg*j;?jauOm`NPm?dW-8|pukfZm!#j#2j z@1a^x#kiO6q)b8;W{g%Cr|lXd3by7bdo$;rdA>O!G?GSPTU9KHlQm4RtjR#-5`N@xyTSq6VlX68L$}5<=7@N4B^u5VKIsOv+emWj2d_` zTrfmBi(Ucbvqb6FP&&@Q=e*v(CER*|6BHMs^)@NvRygIV?SbRT}`fJ2SaW$z{}A z8fS1W_P$JAr9!?gsTPyGW4$d7boi7Z>es|-d@&}4`6?wgvcrzp*D2@!12M3i?r~j} zgbr$x7Cly03TMD3{k-RXJ;}2mqKC-r_c482zBv{DHW*PH=c{a|9YL^$vpA&h zXnrA8wutLd0=(VghT{4XACNG?2{0H8fbd}ON#x6}7!C)kKYO2=bDepl1<$8+fSYlg zI0YhcNbd?xF-`BUX`6RCRO>zZEa4CSmMIxj6dfEmGhX=FoVf5*cWV;4o!B&LAAUVn3!tP-90)toysU$Bwz&P32yy(f9n^cV~J}CF9($uKbznTur*aorOZSDqZnl z=5$$?hNIEtlJ;aR*$E@`XK0f^0j^AG-AXuh516Z|F(bCJQ?A|i`ummnM7Z1W^Gte(=(1!xyaH3v5-iRjVE>zAD zg_0tl9`tHhRMwvVi*k#_5O%x5RO}}xvj*(HEiv65e9PHIbR8?+K|~#tmrK$5qM_k| zbq2%8Tlf<~Jgsl^7&kxvhWDm6$+oK1P$uhb3`#HPlH-bftnE!UfqR3YB>S-p6@74U%IZGem2$1_~}r`WCa$?tz1)?yDb3VhX{%s4s#nk`0OwNY8o z!(4mpl;8NNog=rxHIJ`JXZ%isk3Bq2Dm zWD&~e2yQ-uR8PpE&y0r>thWzw_Jgr==)51~)inxK-^t&0VUAehJJm<8F)Xnw?7ZFz zAe4tom}tu(lIfV5d8yMZGjq_GxX5z@MC54(bVNVCC@~ik+cq1spz^KgzWLO6bi2Y3 zt6rj%f~!QtlH+&bh_-*}BK8Ul$^M_a2>Rck-_M>%L2iEY(G{;F~&OxR@e)0a#9P9CH(Nx~9_u~@##&*FYOOEeF%PtKM`9ecoN^qi}elx8P*LKML8O+ z7iYTeu%RRI5|K@o(HLT?q&cVpWnM08*S-BamKZ9ZnRxfb1JxK`n&(jY8b|)Av3$GY zcR4%Gr>^j`fYV~miwp5z{w{4z*bbuLqG9QQ!NJX{?}v$Sn8IV4b>fTu0XxCsegd4^ z2@{C;vy4U6GEM$NJAS-71iP34j!!l%F$@xgjpcAJHGyGxB1d`xvceM}FUA8~$> zaS!rI)c>}XEQX}H`f{BRNEL(+Sh&y`N6=!$m<PbQued*il=V2gA3sJAygLv#>e|}>I+_X&zL{U=_hzWku~3JT|F<~& z5G2k~AL9S$e|tdxZN>llA@71j8a|o~!P)xYz`&GMzI^s`eiPhap*6jLCtXn_7OQsn z7(P#Rq*$u_>c+R zUWyGxKaOM)!YumQSGug0Ngd$pvYA|`#N9v4**)F)MS#vV7pS@F7KBLLr^LwHoV+7+ z(CVTfv*J{>|9$0x?*@TOiG^V1ZHh+Y$MBs&H`vz?o36n_z!{m%@8(RkLvc%YXT`iD zAoH5OKmPwlm4E3cFPuOF6w0$nPASA*sM%>xG%=0PQS;XyIOvm}wbrIlI!aG_h`Fm) zNo4SebSA<`>K3}E`@TAfiLB=W@#Jpu^ZO zUck8?5#G@+mHY}TefIc1beas z+-2J7C?yNIQ2p~YW*VZo&dP+HZaNinlfFH88Y_#DEJip)NIU?;uOA)W#iz}7LH=!! z^4ZuvJa|7Tm=_5h3h58`w}ZfP*7Aljhd9`Do!Q_gUaG7Amlqi_$fnV3$`&K3lwB;u zL%HLa7G2wq>ANKczJE;TEFWQ-LUeqVBoUjCiJZ>7SPU)JCTB%WW&C#jp7jFIvz|;t z*Cos0nRCIbs^;IgJvpb<{s(_7Zr58^POHaFtB#4ru+Y0s@nxyPB@i>5^W=&$Y zF_{nZ{yLeMLUKKm_wh?ZLB}-J^={X>LyRGxvgFi-PFGvzN4FmT zd?dMlb@ktJ13q!U=EWI&3)r`*sHB4)N$AfPRcvf*xNiH5#4^HePgL)NNX=HYDp65I zK1d+mpkNZZV!Ya>F4ViBa1T@XaM!eam^)k58$n_=;m;h6g9`=;84y5oHMvvQp^ED_ zS3Xr`4}`t(WI{zap}OkoQYPc8V_SYT-nOs&2v>P!@5 zM$}A`+nFlUbohP%JNFo9Kdr4+mRBvTlMyDNfjx-A#$BMgpTC^e<`bRx-rbkG>fiDtK2MYd>kW7Fbt; zH!HN~>IMdE$|C2=fL~bSatxY@`4d_DAh)hKZS!%S)!ZFpqTug(HV5l@sD|4NGW;qR z#uOcFTNJN(Y>S33ACHhoCV${Etq{)o<4z)MG(+M*(f|z`5Xs=+$blYgR|w8j&aP>Bwcp zIc}oA62@PfS=lL9me{k)N z7M~U`#8IoLNLqS(WG3Kuh2As~2JX=N+?g@+37e>Fha|4SaOeUH1Sc`=abjj=gaCv# zRZ~5UB}z2B;a-rLd{3@A+pd0gUc37o!Yydu1J6XRiW6sQm`?I4xu{S*yg|=zb4L)} zQ~6bLmv{j?+7WYT%(>H4^-ouJ?j<6}sb!`^wzYV~nWP^K#Qd&AKq_Vc%rNt~0v-Uo zXVz^ro5Q)lGdiQodpsp)^Ih^j}j20EstTZnf>k*XTZs)=V4TOy2KWq6| zPQmiE<8Rv#ar$_53W9Ra*kFTl_d|%_jAgR>c?3Vah;!C2XVLkpjZW9l_7X$TNVd`I zET>3bEBko?I}^dAQ_~j%1=#|lrs~+&%A{t(`L0<6&WG$rWclk?K>RKL_;9~j`E9wr z{rL3u0mN?Ke~<-8ss06(O@b^vhB?}KV75+n;0@9sy=EWC9cNo=a^voDFgGvq1;8CT zK35MPV*Ve$#~?(MPe5c*Ga<)3mng!~mrH&JegviS zD+2EJSYCFP$MQ9oSzo`VR-P!+M=3o%$*CKppYk%Zx}YKB`!X!sx$>h&>YGo$^Q_;9 zl%AV4d9xT05aB`}w;6a!bZE%zy8J;Ad7u#8ysC25MAKs`eka$%5WLxw3qM^H=<2S( zqRA-Y+@o1pVOMgd(o9<>p)MOJGJnv;?FSibEFjA>H#K|D2-QY=VtN?`_@zekjAR;Kg z!3U%;-9&@s*9D?$;tmml>2f{MI+g6P5 zH9{s2w;rHnH9L*+GoE`2yLFuYklHtnLc|OxbP@_;1?9ZYcg3@aL!xB#pp3Q622J^dbk4fO?ENuHn2b+~-yC(vb+=$t}EQQyL zWxq@Pg^KY7Bgf3HcBHfbCFWx&gk|?d1#G)8BH@B0 zrm#rR5lGLRu6R#?)P5Zh1X+%)K^H0qBh zkt70Vs|TB5VB8@**4!pqm9~u7;n?4GqlwtksJ(+2{;BWkDq$9y_VS-o42r|fBg4m= zwEZu=6yT3qkpPQ9U2Ltsl4jNPhwWXc=}J(my3ri9(y60L@AY-o(R5K`R3w) z23Y%ihI-=hCE?j`0|B-@x8(z{$J+TAb2@47yoAhb|8W>IxVU*Zw&sQ4a;)sVmlE`H zP=z(n%B2X(TB(?wIhj&dekCm(iqPdZ#l6klo@c-~WPM*EUy}JiRm{Bs9&1_%o5VdK zdwIgL&&Owucc<#pZnbDlsD-LlTm;N*(94x70N|dD(MW1toS;5PhE=^){QC)pWiPB` zj=EX$5hJVjdqc}_biP><68kKKPT1-$g(#14s^=lS!+e)9whl_mRa^)cHAWn^Y~gyr zlJeSecsRye-}g4aXVUG zvKTfx#*OCw8GwGv*9u(9K_VS{9|(ha%56C^)VKDNhO3-#*ufI}`Q~~3oz{<<1aW6% zPmz$HPO+PKy)oYv(SP_2W5J1iRvxnqbfCH!ns^ITEfBWK7{cy1m@&4pse;5b@XZLDMI*MTKBnQN4ep>WgkAY76bJr)QX>EVe#4nWicNmKlYh7`qX) zUI^IO{A!xrNSiqNiG8vh{+P5&vQ6jeuB88s8h-09frXa=eV&K|+nV71AJej9n>W}c z4vt0-RX3KgY$4j)fW6*ipH18L*LYw4@QOu0w?L~i(FDtiYPjJ^;VdJ$o5@!T zqo3nG7KI&nn@yYVIZhBT$QyOOTD&@1_UT8W^i3zmhLeZ}ylw6|ZeP_`Lyxn9h_qUg`w6AA)i$*#_(d@4~p`)Q7;~rs8-c*JH!m(YQHzIz&CP$Uyx9)~` z=X^L@nL{Q2x3&bkQ=UHX0@I#te!z+pS?scxvo=Y>p%=#jm?owKV+i)~&Gq!TK~g(i zEKGZ%$6>HPXJ%Eww$ycb^7)*3^HdgXB~n8HTFjH(>{2O_i99Kx3R9KKb}u3^0)x-F zN>YMXR}(v9Ql%p>;o6~kF8$iX0ybH@oSKOrY)+@D|6XKczChJtEm|M#g@AP|Exq<>y78I=jieEv9}W4M4oU4r0*;+-zRB z;OHJCIP{Mp;vwAloxaZ$pa7Pb>&0P%X)~&cL_(xTbTb}XgwEL=;8Crp;hS48R(Skx z)9-7B`E&;Xk5Cf!a~T?tu9N+w3k^>UXydA5!UL|>!WFhyrjXodw(@^;iF6-Xug>qK zTk-!_EGVu^yLwPLIAVo#zj)NRPfip9gRvZTu{$jfl~dSe`kZYW+wZ2 zdSGlZx^rTYll}#9)MwG(u0l_T4IB~X^ntWvdGrxG1EdC)&u}O)qucyyTG~{IS(sugJ-yq8)YZk~Sy&gE8 zj7vsi<2NX0B7EhQuc!7Z`qN~>u1$Q95x%H5Xc!8!7Ov+|g_xJbjQq;>Shki=VKvZu z|B_4cgZ?p;7LvwxDv*`Ra6yRosOMlF*+U*O>u5R;Mnq3${kAwUQxz^jz-`fbdnnYn zUo*HdE=!NfN7z$iR?05<8Z%fv+l2AM#l#6N`r!YxOoPPRzpLj}7L>`u0khB@HQOuYgJRF7T@GgnCf8Ra;cDM#U0)Z*zMZJ`J$g7AhU-(A;$*54oUq z?J?5v(ktg#P9I4G37jZgzdn(^UKs~f(r0Q;2-);;S>dw*x5p1Qv(7Kn+(U8D`LP*< z!5}+O3!rmdtC*zE$Ul)GwjXQ3jbHNgxt;Sy``MjUp<*1V@>0~gAO(dKUAm+|u3gnN z>ARWQr5j^K%5cEfphbZPSe+8f=Zx~=)r^z0gJ-S8rX-Q2#sP$_$FWp)d2cKtm z*4>nhRa9n;+#zSgP7PqAN;$XhQlOMoF{M?0+b!UJXiXyFUG82#m|*s3_f#)}89Y}n zG3+eJ4^|!S2$cF;<04??N)$iQhRma)7?sr5p7yjD1m`%2i750_DlQO*=HF30V@$8^ zjf#~UAKyL@n(BM4^^r-IvMw(2*|beA3OVaODQ*-?unL3nQLO~EkV!W(xwS_HlO-n( zZMUJ7FSHj~{R<`c5FIgn?7Bn1qSdZxr`QFgCzhLRap-1JaGUr}Eu8I4+OMyI6Itc= zaHb`Bol$gI#iW*Re>&WMxK{>r*yT7>I2Idwjvr+uA8iIv-FBz?sXTQbdq^GAA!5MP z`}$%*huGY<7BdW~=Cge8GkMS+8zX}GY>QRwb9u<+wySa7uk4?HE?r41t1`+noQD~N z$wRuGw3@C$9*%*NM3RjiEKx#kKExU~aQw9@WuW|mk6A30VY2qmGmEXgxBTBSxV=fp zmnv>`CZO6qacXe9GKLUz)f>QCcSS!x-hT4atDi56ExORqddziK8bbnh5T*?IT~VLO z7aRS0yD#po^=X{nK33d{xK26KHVKrXa-@qV@l;7va|RU!wh=XB5}8q%iGUsFvgq8~ zzd!eN`g7n*qGYfJeqbLL^}ow6*7UyE|4Rb4`rV@PLBiVN+;tnA@)m*T`bk>bopiBs zj`2Cq?w(oy%rxtv8cNJ)l_c#oThs?{;U)0Ug1 zyf!)69mE9j!X9d3$h3l9TC%ZI+g^GlI3*@cRTWIiUCkfvtUHNHj3Rj z>EEo^^HxY>?PAIB{1gnLNA9_PWo5dhDl}e*xiVrMgule`#*Bd*@BShjp&4iq{c4ZN z?v-bY?BBiX1tgDj$|{hV0hAXOeLPbr46N%A`0!d7IwoPGGg%Bmc`HwRmHE2Q2?>Pc zz~X69)~2HTHJ_?!OxUd7#S~Eiw;q<39s&XBO)5>kO!Kh;fSe#yqRjS3EMHL6S5p$0 z9^GeSohahY!OGA9DUfkl`CO^KF4x#h!V*=6V4pDb6so|ArnCkktF$EvRT4y{!a~zt3@+Dg6=Gq}YF6$LchcMLHPL9k9RHg#R@pnyoj|qdnwf$x zvZ^%k{vwah!eS@^AObh1GlqD{M8*&{3tGX`gS>pCY zx#~{NXVEs>gC7}=T0i?}%OOpB7?oPbFo~!*TI-Ev6uxG($Ow~pR<7PJW(oo4f=VZJ z@2;g8s!4+`d0O);QcDu%fPNxNI~&%*_pVz^X%aHV@H$p``>lJaT<-e<6P3C?s^k@- z2*xjRq>)hN(#2MG;*6EwTU zsnxTZxd{H-e1d*b{Ue2GJY1(8ah7Q+=}4BqNjn^7A7Hv9^y!O2zy)mVmYpcblzMfTk4O}Y@AiZgCYcOvxp5ltqbb}?s3NiMK3E~wbB-sY19 z7TAspv>;z>e9L|A%+aIG`i|-^ko$WuvVIWHmO%V!42_J=JZJ?C$_#}g8R9-Hr<{O? z#w?#huM^;qKF^C+9#7Nkh={;)gLwQlqhk}|6KDuMSEZhu!|sNtD~m{vh&}pN(C5;K zXRn?k8od}z3rQMuQmYU|+m`J7l&0Y=M*Mt0<8{eKE}T-**5gCaF2A779dgMUH@7j5 z(j;dGWvpLXd(aB_&}-J;@`xmroF1k^N-(ZbU5(w@iP)?uI@*{FbjHh?;wZ*EE^A@ zRaT*FUu&Jk{@ua;KMPV*oN6dS!e_ZkUkGZkXx9TtE;|lh`yu<)w0{Cb$M#&LWQql} z0{?e7g?Jbhl|CFNijOWqYpaAP$#^hRq*v`kCrLSW49a5nwVyx-Zbn(09Q$zk`4A6; zp}gQFsg{vZ#}kcdhnJ|go#Nl}f~D18t5x}>5gJ~-#5mx7@q0-6&1RE=&>6Y5j850C z+>_sG4PnOgBmoUS{%WhNq5(>b8DDFue??^I0CoF}`utNe zK@;c_Vhw*HDBg%q^Ge5SGo=WqFrZw=!Uo}*a5M93(Ys?Q{gDkjFaoY~@3&_Xej~wu zx4h=kyW}!3ngwXFf4-*ZvF4Afqp={&4F=XlDH|YGKGif%L{re?Fw3ib)2H?lmtH9u z5F#4DF+{}vks1!(G~ViiTvpCDE4)g~+urUZ6%1*I60ZNz zU{g@qOTm+HOl+9ySxw_{sXM%G;4rt+E@v^P5pjK0<5?N&BtPJ$m6MlU=2xgG`4t9IYRnI*l^;4#;(T;77eIQDyZ` zJysHzxlaE(cOQcrD7-pi4yU)?>~rbhGVzLjj^%21*9MIM9WIWT+v2aaCI~o4AAlj~ zHiPMdL*DDja0-X+vm(j%TjadRSN%{{c~s;w3!0UyDSLJDyOh3WFXbj$<;&Foh0Rb< zVz1`8Q!u7QC2w7h$`d#iIhk+_X$$W@&83JX zgej^Y{;AL=kjaRnQLScEjH2Sv0d;7??cr@E)DWZ|LhZkfO!zuqe60q6pg~NrJEO~d zdEBRL-1FU24M`a{M~oYSVXi`BZ2Gjk+i%*fD-O;Rq*+TVq9#=ykFXj5nEy7qdARR9*@+ zv%N`#D%g}*h{C#X%(cBCmmKSeP2tBK;_BV6lRl|c$_QoCz71V;9V~eByzjYSmv%>@?QG%5wYi^8SRwL!)Xweh2kv_BSKDvJ-6h3hM> zB}_!W($`wn^aYN>%HEgDZ@Snu;bN4gUuPhACc5JcaS~zw^63rKNoViVqu4rTS*hrP?K2Y!>&Wl(5KIy9^y&bdw+ zT11!kRf?e%4FGq!dI*KEb{hTN80xPulI}IOkZ@xJ@&aGI+Ssxv`W*U9n&`LIu?@uZ zf49B(0-cH#>94i>hWwqYm+p?Y1=k%$>>H=a>S2AyYnIz5#Y+k@;@ZMna6BB^dmxiM zb-X}!!QgE?J{8^7&rhjpGgfn+fCC;AhClZ42v5b^=CvkCb)O%8`V#xox&%E5 z!1Sdm{k{Q=L0bTmHkGO7QBI~>l7f9WDtw^Y9Cd@l?-+ zU|UD6JBq6NMWr)llI4YGZL>JBA1%qgyY85rv`1jmD8xoApZY%;;0Ck>F;GF?ZHYa8 zd1|zRh`D!ANr|0HoBEx$+L8ivr?HO(bDNtL(j}% z3A!S0?4OD_?@@VSTd42j#MBAc-ig9<+nyPn1W4L*M{o<~vQ(1xzc{kq{kr45_;e(J zs|8B+K}n6G(<+?_N#x`ZiDHb1_sG_bFoOv{Ornb9w+fp%Ha`pQwv-H@8E+uN=lKeW zu<=NDt&>qQxO`CpeXPr*P&7gdyhb;HUw|ZTkE9EXRh+s$dY*tRpFr-to#%3YKD$ud zLNT+4O!nvc?sU{GY-#xf#P$l|;{THH%wbGx9Dk?OOs4Jd;u6rH-VUt)_KU_vvKF+qdI>!+E+N7%vLMD`VZ73}Y zB}7{W16kIEC&H^Cr+_F6gii(!^NyP;NDsaS!bMSNKOFx@t#K$fn>JZU2T*aWsZnfX z3{v3~Wf#O(Qi&0Ey&E8K+nWBrm_HofE?$pQC!xIT+QhSxnmoWNZ%tlS>(Rh1Sq zmE3x^N)j|}Z1jU~mMqn&nb7R->4uOOsEF<@&%`0;g1Mu5}uQzNk z+lRlL_Y1Ubzsik2xE?em`Zo;#BXwG!;tF#G#11bf#Iow<%y;&{<7YHK8%Crkx7G3uX<-O%Mcrr<-nkKeEvMl3xQi zQt+p~)`eRP{GmVS16O>VUO|H^HvuE??WR+`{QHrpqL>M9pvqQzDMaz596%ysE(&d5 z-5Ss5yqA=w>6*%4N?x{wa3XpqS3CF|zOWBucE|{oi91JX-Sm%W1s`h%&V7xO1PS7u?0ej&tmuEu375en1C!}o*W9gK`ycO8ue&4NSA7ljYL1+|VPG;`;g z1AN~r2&E(l`!EHgz(1X>thZIMpV~SE)mR&GbyTQ{)8Xf_U_MLTMdYtFgB;k2&nx3D z9SDRk!7%ZG2YS(aA+305$>TC_T{0+zDPq^=lVkqN4JH~I26Ie!ZHOL8yuIt z%F984t>sVW4zj}t7JXFD$BGKGfBzuENTnYoOlCSh!@Na-!E1Y!>xwc)rQUS(k#;n# z{e-VCJ?PoWkaQ<+>Znx~pSM&_u=wg|x>N2T!uGZYc|3hUywXq)Y2l!MZ7>0vo_tffq$|;MYeT{t^^5!{Tf9 zF}NqTWPc=XqcT2d*IQ1Ns`8okTzYrkr*7;Hyh&poVp=i^h7LR&C4=`pKd*=xPBReh zVkC|PQ4U7RDfJ8g%srSj{X?TIJLcvz3tW))I4YTy)0izjm#HA7XazDKKN+>7f{CCb zs-N?wB{>lxieNA|h_Fuzv#s1zM{A-yt*M@~L6{X_f(YF#0N0r2pNzQTVL^d` z%)`}I+J5@-ff3K=e7A_Eig^!YhGoN0hs4#_HGoYc?{AzsWE@UTM;UNu-4j2qR{C`1 zV*q=@{dZ{B@=I{K)8u@VqaR#l}*kpIByk-^6d4{c?C zhz7GQkTcYk zhr1q=;4#0UMesVun>Owj}yYZ!0)1Hj~wru=l?OVi0t$Qiwt; zN9I`ZDh!3IrARk-48d)xM$nV8<2JaG?Yea&f6x|O?W2jC%HT}VB)VFgoX~TB05~Q8 zt4UY|_09`*2kKFkC)A4*Cr8Hm2_YB@AfxhWnx0EpmCvJoan)Ny`KZyZKmVx5s(q0? zGsm*dxhIbK5M*odwLk2G=GR2VQ&MeRm^MlB`Hx_N0~!IR(IfiWz-tpf(;g?LL|_nK zu_XR2hqzEwGdZYyo09*WHyP?)(r%zF$`W_{PLQP-zh@1fAEGJ@9p6(eX8n>C%3-Vx zRudE{$Bk0>?9-X{#@2~Ys+M@Ryt`>1J&og)?Zew;RAg=`sghylTzdX{__^40Q1RB_ z8L^=i`D=$T;+6tzt0%60)f+x7Y*>e7BhsgK7}!wUpUeXkA96S6Uyo_!+b#M-#!2ah z7$P)Wf*i6MGxdB9h(XbmEridrszIKSN;}aRe9d-`CW-aS9US;y&Akki8z?-w*RwZm-UJUa)>~2ZN518=Jo3$x%#$J)!#U} zBPdazw5+u`AI^BH{&i2?U9ON%F&w} z-<8uFs*P01ht^`_grg>Dd_n2%u4;m}(navNh&Y133T_9Sy$48wmv7 z$fQV?N{G7@pvlys6LF}3**`~eMP#nyQw(z~JJk2t#^C8hO1BH~MtoNC3Z|7bsZYHx ze3fJd0(^#j{@dKv1PZtE26kz)p+QTU$OB^|J4(XHNzh-oUY`M z;=g;!00o&J4v0aJ@xsp|+`TE&uilGs4+hRLie5;V6rWJ388UaHaZ8bot-aV7erQ6c zGl4bhhNakT#wPdoNkqQujTK^VPq9{Lf-7Jg=DA|#Z@ENS;}G8dqgWqXgdbzu4zO2G zbeU3>ox>5bl3yZHe|a;`*PA1y3o%vt?tWnBwF=xJ7$;l9W6DI#2Kn%+w!1n?5GT({Ly7%WEWNNw4?^*uOvNRcA^-ebd3QsXY;yf{#(S1<8Qs2y#lXq z6lAB1jOwsASQn_^Ok@15;#K)vpQ+44SO3A~=tLS5OdleU@?k~v5)1$i^ckodQYK96 zs^%F{xh<(|3tf{06S(kcsT}b5BH>`M1Qoto27KPDz|brhnsi6sSibHYA1y#N2!T%I zf@4XQCi;=UIfN6CVO6OBsq&bHNI1B-*?~-3x1EJ=pM60UcjHs9lKQ9Vv~w53ry-+_ zqX|8`4v2(aK@BIlIi!UgUoP*JjE&p!OEspi>hKd40fN<4_hgt4L6Kd5#>p;p=})tl zcc{qlf8u}U;F}FX1333IYdPcHv~=z97q+0_x2`Y&@o+Pfpi}xZo%xZ{?<~RIMda;o z1++{GmXGAFp$0ygdl`Xwa_^yX*2>{-Dy-wk8i~fW@O417v+jfKd)`>gs4rptwz;FlR`3fKaaJsRU65$UxkkK1W{&by;aRjvs6vWcW;r`GPToQ9dVCLLqdvI{N zJSYc{lxO;~mp?b5o&OX4aVrN_3Mi0Sm|w7|G z9)9dOlOoF>-f^;Zs{davfISdrlxEQ4-H$0Yr0Nj#R@9lDRpH{hl=pvH6^it$=jFYD z-gVk;%)e$dC1a2S;z0`1&ze*#n*2XB(WGR;m~*n2>)f_Z2ZJfpEQnHT-hJ`Zd_MSF zty4b*~({VUCN%`ihLe8HQx-xEas#{s}vtlAMl?E%aP`ohcng50b z-0g4nD~x+yQYph)W%@9xtBXAKnx5eMF(O^ zJ55JLJiZla7*$)nY+QR#VFDKa$h-|~dVUicbFun4pk+3i3ypwR9EYMAmX6lq*v@g6-neSJwYn6-}O2-@2_bz`951g_mwUq4smbs%pJ>#>%br3P>ISgR75h|EJsjSh@uU52ph~dwHzpm z&PB8fV<|)IYNFw+C8Xs*A|e?p;x3kg;tNmSS%eTM4B;A&#R;L-^RWb}IU^J1aaCW; z{9Gx7URjd~gSSQ<7#kS?72|oW2HkEDwp~ff^e1O4KB5NziF-W;sG9?F+phHrBK8FF z=bS%9*d8^9D(R|0vkah!8a|W3=Q=8z-F2w?<0FuuG8f2%7LAiA(GVGS*08>LT@u5F zOu0MrW@oYS>m4Bc(#~fM4QKS;Dyko@JwS!!+8+>mjzCyzQHrDIXSc!CD<3J(zsx>^ zG%Q<5{(c^7;1KDBa5s*L)h*Cvl*zTQg;_#du~0e)l(Kg9G`p^(yyRs$ths)ne4~5E zf{#wAFT3Hbg+V0-om42t^e`8sdHpomF1z3b#a5 z$jy!wbevSF2dAKdOl)(v!*?;Z{H<|Is1A6n9(igS^VJ*}fEqe(0j6U*)O&}#0U4De zoO42K++vS!*c#9H4zxr{xStMA@2gc2k9o6HMt$)!i>KwT$z_!!FQGgeJcC5#S<{B~ zF!?EuM)6M?&=i6DNV@}Eb2=UDf&EG1}Uf~ zt>}0n9Lb*7+%QqTusoqv6P@2AMc&*5a-w(DU> z*SMVN1M;i=?tuZhrO&qYz9;?kGbleI)}*7DxUKW{BhtVU1~p#BlU+%VyhG(OXET}w zD*teDcMem95~b`cZC6q6JGXQ zm3-+A*6WPtZE8I++0W7pIW-q>^wnxKZ}s1E>7LlV3Xa~w2py{*(BkjNosWhX&W3qe z{k@3Up(pOqchiwqREktYI-Q|>tAb24{O94u#mg=dYX-tqk*(dnUWXYdHN*9LFBRQV z4#|VV+J-t8dfY(3DapfPWV2N`@)lMg)s=>p)*!Yl88zT_8<+?NYiWr1HVY*=nu-0B z#N`2_dk9JrT2O&~th9~eUHqE;xQu-PY~11j&$jG;+Ch{=-n41Z6#)}E3IKHBt?qrl zpp5|^S=44OvSc<=@DR{6GeA54LZ)b3k@@?s`Cjc`S0Ujn`tEZKgUnUNzCm3=l6RgM zvchm;dMiNN3H3wKY4-dw(@Qi?j;F`}k5H=&7rAHu4M^M-*#w*&-=Lo$ganE9tRCB zeR=O5t;dLLz6wnk1V<}!oXwELOO)PPdZ;G@Yv7Y)1%MkWWA5~Q4(jaO!yXL^=7K57 zELzB2$0SZBIi6EhRFMAhA` zr43U1@_gp+MP5@aV4EI}3CQ7W+CUAz(nA4M8#EexY2flGV6^sz(0JlAR>@xwq>>1a z(}ew2T3QHj*9A~=I_ctILH8a74rSMp;p7fkeD0hJvJ`MLSysM(n>q4A&kRyvU;Hg) z&S`KExBl}lkaAS0`^_O$>8x@8Sg{2IG4c(sim#*nd8T7)hBe#=lC2Y841Ik2O0LDn zi*IdZ#yjFZvJAS(pHI86b3pG&|FTX<7fEbr(j)!*N{#SfXDv82QYm4krb~r>3HITg zcR=|AM17X?DBA`rKL-lwTgmKDZ)EFd8lQ2#*^blxvK{8vV@slMDGgq!N>jERnX%I> zZF85A3@w@c;E{FviD;0;Rp2U@P3j>pNrD(UX$z)3NzCR4GR*|!vYwFG_>NVZmfJOb zsc$ixK=<_i9X(>4@LG#CEz?m@{$k!o$PYGCy5il~Gi2e49K2sB1rM%d^*7WQzNxNQ zcKMO*jc>OGK%0VkRP0f*rJbVcBRzD?RFeUw?}1#f_S^8mmp{USEXz6_*lfGuwhXe> zL5IJ7q}(iEp1<>Ob!sbWv=WGa&fr?@3bBAgC*>O&*mKkVa*DyGa$@}cLq~A7jsvZ# z#(2nEmdv+69V_?v(EEmrqo)1(w%x1B10jHY6rfzkK|Ui)c;T%c?!Pq;uvDh`UtE24 zSXEo}EzJ=Q-3=lo-H3pMfRuuCcS(mxcS$Ra!~q19kPhi?q*J=PyL{{1-+S+S-~0bw zfxY*dYs@jn7<0Lj^Y(m)9*p&J4+c=t7T>yOqz!fL=~J#WKi=9bl--zlfRbI(9AfB3 zdM!;gmqGPuUIm^to7kaQUBm3zqP4}0Y!L-W`&wr}^Y(*NYct&y(eiuk!Kp}!7&AJSn9QZ(y8(p;mRQ#T+`Dn@VcMO>E&ch?cB*nZmSG$}{eV2;rm_O z?hC`q1slx&Of}WIAsA41V#&bgdu%$SX6FP&W3ji(jdJ^49m>ABeaw9j=H=fu+jQPxk%8S6s|-@5@g=;N8A4AG z1YP-H#1+(cMuoLB@}m9ruGMM1_e+vkl6Qdfa~&M7My!pW*?ikT=L@PthX3@(QTpA- ztjF_$8e7uKwZJyYi>&Doti-;(H>Cx5zPl4Yb>1D7R+XP7vJ{+MC#etK zK(6>Bytkg=_N$KkOiGNa!}9i=%z4H*Y``+hXQ~j&!%WeSu<8Da>ZMy8sH_G=MPWbv z>cY4l-=@Eexo-UTcy|g`XZs0w^~n_4yxcyyx$8KC7Y@)F`tG$!2!7!L8^$7K3$rhu z6`|*T449_t%Jpx?wJ87L)-^%(A&Of+q93gC*?~>#EJ#^wZW#{zl;TVTKU?-e`1>Bu zwh4Y=joeiMSKO2$oEGj=~AP zYk2A5@Dp5#VEyA*@gyWehNo2*{2^HQ{ws9Mk&-_COPl%M9>)^o;j`8)y;Q;|q_&xe zE!cd330(Bw?~m9s(CDFs{cK+;5WOjA+-Q}3TV}hMA@9@CqQLv#3dtNv4aT2`6huYL5ZdQa_De)3jGP7}BZcD}I4H3Kq3h&q6;zHwZ7!Z$XgnMaC%YBCZ#l zL#tv(TIBF}Ae*b!;q8tk=kK4Zl2q7t$|-sMcc#?v)!zbI>e7}1&0p>(`_GG9L6)BH z8(=!DR{ZGo3wS?-X?9=-9+$H$HJ`}LT=Z%gw~hwS8mA4scKl2K+R6}^ec8WbuBFg* zq^Zmb#d+0%7DmUmn7ni)^dEWag;KGHrU(8OP>?g8fyKV5lOaDa(|Ud$A{Ryd825;{ zB{H+0Y@Gowa&oehVCEGgL!||0T$>*y%dps0=#=V*GC4`Z+txyf{Gt)(z4P_u4sxp)#5dO zE$BVA+icA9ejV`FxhW4iBCSZEA}AJB=MCdBS#-D32EFSD#1xir+}=;kicv0#dDeN~I1?(E`|>b)XnXx`Fd% zUTBEN3FRqmd92@WJo_^h>Y1iSHlN!&6=>1%=>XDO~;BN_ZTKFb4RWw9%%2F$RgLr{Na2fadHtzOWGGCy1t{Gvi;X0-?2 z22Y#qJ6qEEu%NHVEe=saKJW$z(DPH~;?GN5kp1o-_3_5)3B^nXNcCKX`0G)0{aESy zBToqjUev-3wJ{>6Z`5Z(@cOY_3VC~tp1ddy!U@S}iIhAgQbHc?AawzF)LQQ&TI$s8 zA;wB;8F-3$aXzH(Uo;x~ykqdZd}5 z7g^xj;)f#k4bZ0A*3=MG8gC98!h+aub%fv4Ru9D?D*9P+Bm+sd0X&+WV%^`%cd50Y z1N~8A_Oo_A5Jxn^ZRo@(M`VANzWt9eNeM4}3Ul-6~DB+cmE0Csk> z1S8Z+I;;JTNI5Fle4(19GpbB@+h%I+#(CbRv?PQ$rS6O>oe5+{yEQFbK#e0RB<;I2W}L7E~@s^G1}1_ z>cql~Ty??L7;8~AhjHpAC-QN^XG1V7eDQ}c;pxAHU_*dDqf(k#C7ZJBsxKY( zFfE5SmufEPQJj|p&~?)3#Mn;Ac2D=+rF4v|#)6+*fzdCGEsv|4?zaa$y6IlzxyrtH znq(>L7ZdAep?}jQO$>aEA2QsVGX&=j;OYf9ATq_Vhr~a`d{MpTG|*!yNOz97R5~ob z#I-$cb14}WufAVIP<as;I9b4S759GKS zUt=+6qMsvw1p(k#;nG>+nv6e-3lmRYGyPs4D|enHTB}mBD&YQs-JQTmP2#MjrK{e^ z!_5e)&nA3BfymWgWT zPOKSdP5SjMz4T*T0R3ik?gx`P<)_5({Epm$tn4O*fmblw`sTZ3@pG7FM>m zR`e%zesir6*nOJtsQW?lr{JxRw&f9xy<&O*O=ZU^@R|chYxoq!#a;;Q)!J1o*&6t# zv&^IUrisZ$s=?A!GpBsmd}pHF)Fb~sW|Y1zFYDOW4cXumNj41UwJdR9(95F{ z@odaCG^+30KUx{qQT^iJxY>eUx#djd71_NvcSD}b)>~lb9v&$5ywx75d+GVH2Ua!=XPaKeA%PLs|=3ob5cCcuko06GhtALKER1sR7x2u zeh%$pI@tF%35D+3K;N`4q8PU#nH0t+=AOumOZ|1ZKS2fy5F=falwN+D#80gJvSoE` zC6W|4RRlTO2&6G{T(_PnTLHQqztJrzuV}B5pN~VEQTH7<$ny1W`f{1xr%(Q-Q}Fuc zZphaCkVVt^wau$;ryK(L=^L6e{eaK@Ac%a$kl&-1@rcE*8)7hS%&_Ra`!|2a}rLPy1 z(eD|pbF^wdT1V1^eHB<=9>t5>JdG=&54`VKdPz}`&hz?L-Kw*0n5b96&*aIqfI#cb z!QqYLj@URLZyreywOhG5U~6myYgs?f1L=IlsLZP&*BKG$?yIBD>_0&R7R$zgk{k?} zBphlMvV|*qsCzTgiGH)gp47Pfs}kfhpDaKQT=l1QYtK6Gm^aA7bo!KrK=w5YaMgLE zJx;3ks;!o;E7*C+^JSR+ExW)O~%5buDygIN7mqf5UANt?7_oY z^Zgw|jeT=gr`%%FR{=6f6f%7-wToI#Y)!p--RHv#fOd`N!vu3iwnyX8$m^FuxA-zd zlq(|olubUZl|r9>Q?8t_LAijPr3oasc%*Iwuf*QjwFNxs7XiM@hx9yYo!dd0;D3M{ z8S+1SxN@08LQ4t&#o!4_<6?L24=HXK;Q&nxjb8)YZ=vxnr@2$K&dOb{;nknWjj~d# zB>cC=p^735ll+tQgWe%}E*I`Ew&p4L30QabQ{9y7wv$>$u6~%vVvV<5BOzm*>Xgtl zS)!2*QU?3YaiQc!?F3Im!p8y9a8r)#3BB)kMY#* z$-jMlbbrFGv0qK%GK3OQ;ln5xfiGUE;6aDQ3FW?`yhfE)!TW5&_#{wFT@#F9U$G9kea$WzCjyobG_#kq`^M|BHLO$`f8A7;L#X1+aBdZ(_b{^=usny3vl z!}r!{FrRx(m?sFTlZ%W8c`X#i1);;C06}Pqf(YUd*;UTQeo#ayS`J))m=nOhqXV2R zuVtxkVwVM*YP%(9K_VA*!vZ<2OsJX4{&MPx?JcrW2S#eSDb3eJsc>1!7;;DKdSC+y zcRBL;-s1qaNP&GXf)!&&(X=J6h41;IlT!bYK6d~IGe_-=-1)SC{Z{_(j@{n`7S-)3 zU55>~mqE}u6&;Bzf(_QU+g|R(n4$i3Mv!fxF$GqCdFwHTgxK2;(~HS)H;FJv1r7<# zW$!|@W10(d!Rl=Qo;sCFAR!z^d_)i~WjUp3@^C_*{=@7-^AQBL{80(3RnlP^dp@jk z@A&S>(!%1jKdZo_Yp7{86LVuvG@bRR-6IFh$e+gjqFDRgI3oH4Q88|qB|F>l?`-#w zFT2@c-4XLSbwo*vtMF&^8&BCYz=@@uyN6O5*qu$N`gSB)al`n`@szPH69O7|;<#we z2#XT}-dgU@Fj?JqdYo@z=WjF?L<#h9Ugdqc?hrI~l(ncLLn9Njl!~1vSc7KiaG^j0 z>ZDttJWh8Xij70XQy@n9uf^q<2Q`y_oZ3Dy#Nb_TRIESkue9DIp#P~u1j$ssm9~$Q zuyMuBm82Gs3BmpBS-w6+5rH4mXuUs=%Z1X9hREzV=3Y%HP)8-|nV=SN^qV;w`Gl)k z{If53H{}f-meMO7GdRYsR9Zo&`gi*g5Pvr~Mi!?^UVb+bVSWd0TGppWLhftkH zcWzNISUc}y$_jVPi=a`5SyS5uUG;WBy5Ec!Yx8=Z4GDHdWBsDX>J6$NZH|h9?#@-S zI-EA9|J~IP_ROLmtLTs`y;%FPm8WkhbWp(uUN0^UM4U_`E-L{oTKP7p0FA2KqJQQH zmEvU>!3bkQw#0B7MfoifAB|T93umiV9ptb7grSy4;`6+6*6OztEq;E&kJoE?Vw?i~ zuN>7n1P(h!{B4R2LPmYs0z;}?wm7mMtV%aKJgki(od<*w{01$uxJ`vK{Cr)08Z5~f z+qJnMhh6MVS2jBH+szzDHx`+-<(6622uZc#UcW?w5nOw;_ot*Sir*ZhR=OB=+0$#`(lrcOM><7?k{<^lU?AdFB6$G|w7eMqu@P5*MDVAw z_)^>k_*cXui?GB%h#y8{kR_Pv7YHs8zMGhn5@H6&WeSKkbMMVIZzlHL>HR^Tn zW;zJsT2cP_M=frspV~@x=PY3j@r-nn!9P}%FVZa0>7vj!WIf_x zVmkj*&0g+X5MkZcoOdC8N|`hTzS6HahTkDS=+;fy%hgu~Iq9svLfBNouLU;H{3fn*GLg0D zsPr)F5_%JmW<&s4sHuIFR1R{V-Xma(8*qRTu3Fqj;0sG6u^Y$y(p| zEo!vv{PlOxeFsZCTjusgE*^csXw^=nc9?3a`IBSZ`#5yjbtmtT68->}{0E63sH_}0 zB|9lnsJO^eBeYa^Z`LVBDO!A#GD$QEES|zTQ;YqxgKRXc4`b*yNhjj;y7S2@)E0*R z)?qYUkdr?aofmLa52UsEuO6?1(6RcMu0vvD^7v3zF%cm+&xL-_BDUq|H`JVW67ar$ zqOKSDg(fr@3~g1CIrM*v6s${lFtd&ZkxI&YvS-H#Y)iQ|y(_D+Xx& z<;BHpw*VG-I3#_VVSd0b(QnY8m7rF~WqE7RhW+_`fiNU{YN^UvRj0;Qd!!nBj?!M7 zOqtjuaU7)Nbk?oSC1q)Z=^8xS@n_-^7tcs!)l5O2Bdn>@I7QknKm%gLrcEG!5-Du|xR>^Gxd8YAg=$7N5_k{sbc#j6@=CR?T1E!B-pb2nG=1WH*09Px)Yq zd41%`y|SdEKq~0!xsjM`zKWPBU49`(vg*%WhQQgm9t_m&m`86JMH+`~w?JJ|7D5sh zP}j*`^ZAaJf#4uc$!OMQ1dnP_5Lu1WL_VeGAf}7G@{wCM^!YRm=VuEdv;%o(QdKQO z=+Kap_ezh3?V~IPV4Xv|;u&wRKW`W@1X}342jz>|AE8+~4ie0ukW_}WyV@#95$;-h z`XEj1z2z7-R&FY*Q)R`y^r^D*_<%`4SvXHtBsr7Sg2nbY6f)x>IYYwqTRWI&D64z(FK! z9j5J%J7~i2CMPqn?XMVf+elcPO2m{p0pdaPY4{^n!9_^zcx0a}nua+v-^frm z&>lQjVk^VI=R1arg!6R4g4_^$)`Qa;>AwL&c?ZkU%72Xc&HgmlZCqEdc2+M`i>AXnTt9U~a`QOgvhpzWDn`3%n|*iM59MH9JvEvq278k=Hd?R^ zcb!gg2_sQIkmaBg{wBx}d3zJiFw1ta0>09>LP<|ipuETU9qkqv|K5*R{G1Fa7~6kA z&mubXE^(}=0tQYmZ=ZyEbyNU?72kW0cT5r!-Y)tdO|zPbXQLRe-)v=VDLK9@qeY_-G1y7`3v~RPo6jEYrj-gmxXqC5E3CO^mHw#~ z04cm9Tm?y1xM_JD4*KNBMPt~%u8)6;7`hv*2(B4c%UTe& zfyl^3S|T|{)=Vh}~6kgMNhQ81)!^DCo0tcq|+9 z@VGb2>QL~{udIwAOgbmQI)2m*bbkhOe5aRW6*6^)T#I>vu?qgFBwu{(Q8n=Y#cdo} zESEF~f1`x(H_9@vkxHkG0r9u&&?jT9dv>e{p?-BtYr-VzeneaU7+RDjtF((I!1;(m0hBWSv)h~qwm)QFSUl{?iE2uu!MU2KTb)}?$eU@wlJTReb^p6_9k=JV9N!9wCpLnD z(aOu4hk9TC+|Y{cAf03?csUlsRjw~_ov6k@)0gE16<_Al!bcLgzyfg*Q52I=@da}D zSXytE%x9eIue|-?ULN`CjRPqoY{jW~x7}#TnufYk(-&k)E&u*BzdhAD>L9RPK@3TJ zdB_yMC1URULUQ}5L(0Z~Xju~UT;?cM>ZXtca-`*zI7WBkms!XEL=3Rja)WRR72k=z zh3D&@KPByF)7Gd%6z5pJn)h;gUx-f zq_%?lAFociNr(uEMVHrKbx-#4uiz2F8gZ3{{cj~O3pGnQDx=H)ErXq`$mhK*+5HvK z{aYAQ+IjLs!XR=Q*=t^nguLGCies+vhU6BMl@Jg!O8%j8OfApSlTE8ezXuI*IWjJ7 zSEE5OLpTx4FByE~j)>F=7Ksvmd=s!+RWH~Yc!nlewD22o@{_ijZ=@i25bLkStz~`5 zml=sysJrI?Dx=$b#n^vMvBQUh%i>P;LOyP~Ra1P4i)EnCfeRwB-_Fg%^v!L5Fqc~N z=)0zY#q6M_l)IWd`8)SsY0oKS)P?w!xx_A}|GwQAJb;@hTPSVP=y0i#l;e2DL*m;1 z0X)gf$H>IJMAf zaJH;98mM{v{CdB2JJ<%-HAE9bUu4pJ6fN`Q`S~%bzNDB4?g51~pcaeCp&4Zf?|d z*{5m5X2QBMI4tTU1F-0GC5g!pH_B5P@ta!ax)heX*@B;(@%A+HpAV@QznOCDlIKY- zw~8TWr;@Bpg-Aex-|Zs;?RF5bAnWTT4MewvqXN_$j)S6>;@Bf1d^PW~1Mo;bC>#A^ z%GWX3uFrocj(NLdK#0#ahjI~-(ZX#c_11X9%ChAPuifWnYLSFq%a7;uscH?Bf40no zyw6Vk^p@GxE>KDOZ>aEve(MG{VQ99S|MJUje<2@-wA!+IQ?f8&ecg2Yc+S~rKPhry zSLOw9X*Tu@m9CzmpLC0vd~QrMhjcO_!O)e3+j{-r5Dm<;=vnV5u`o%w`{PKNm0S;; zQ#^J|!$T#=(B}%PNQMy&wh6D(n6$8C%U_@uFyGXXnt!-?ZyoTeYk@OP=h78GkIc!RS=KwQ#JB ziqzIL_S)n zTpp*--1M(mlt5CpRq)-VRM8FKAiQ$4=wWNP=j`clI)U%gAtjt#3ZAy|=?^;Bi4E^@ z0LXD7Pl4nXaLU^GqYZBlpXkq~f-hK24JE;E=WLA}jXW3}uOyk%#c)mi5FbyeqP@6$ zqmet*wKK!q`IfDrtWbX8;$As(-?^s0j%l?<3`-WZ<|7TXtPpMSDVGnVxB>}#>#xMh z|6_;3;q1_Ho>n|GzzX-c-0P{^Q~BlzQP*oa^=-d@x-!+5Z55hK*4JRsu;6&$3(pyK zBU;4lEx3JRNUPk5Z`D=RB3&1L!(EHFK7rzrd+(RFJQoFa_C}QxbBTa#Bi1k2MdruR z^zPB63Q25VPMYo(hiFxCUp8=1BA&CHlZoaysF&y@3cjb7UGhG|5(T01V@qWPWExl7 z@>%T)m0IA;y-`CEN9I#2On3_)^|F@BjDVbB{QJWNj8{tLvQjWBl&V)O4guvf_SywX zcY?7l97D+}9?H$Vt*D3Q78d=Byg-D*?~%_H#|C;~6bxwuTdYMHxR=$b<%wWAjDWYggs{QE{!9NzI*8B&Q6upW5dMnzwCOQzQun7ig>0jjLf0l)VTpxZyocZ$joxu z=1xI94*^fGAH%w+2bb9n#oaNk9WA+WzGXXfuQaE84ed-mg;HKgf>IM()0`)KIUr#F z$;lPk4lU9rwUjm^q;Ipv9*4kDx=*Gjl=pW@p_~-nS978qW9@zN2K@L4U`adm$yOsU zZcEn01j2ioxj}Adeznc?DPy;z$M)%mdaWnK($zhy@yLh=VM3BlZd)0(<3Qf~?2hcW zSg^0T_p=v^HKHMO$DdHt@IuxA3nK^QM%f^!eGKHD(V5C^m!(TEejmK`Vj(t#2%#ht zShXP01eE_<^y(SG5LDXWcIwCobN0f7&wKF|j|j)BLU zVLy4A@zfKGWz2R9rcm+sbO^%!4m21)nKDz}f}QDn;5EFBdb~a?AFHP=dGj1$-`FGu z;TAKHT-VVDP~RgDS4&Jja*5e1YWJWXs(@R;eD(7wooHzM(iohf>yj2&ERJLt`E`R9QJXk(I%%@YhVy2x2Q)F^IhWaysn(~}7N zNC6!(P$O~wyWF6h3U-iA8YH836Op#3DU`zNBWdR==w|><@~-h^*})@S?~Ox0)|h ze3hMXk~Ti@W`o-tVu#Pb5J`Myg=4`OE4H{!v?T*G8a!a;X(EmxcAh?QFq{Xgg;(l@dBXVmJ=0g5hD+Lq-Co1B5Q(KM*pWd!gef`XVQ<*1A1nSD4A3MG>S z(a|IePcD_lKie}Sd6*D6pDurxdzY*?mkbQ0Ui}xE#G<&P)MD*Esk+(p7sg#t$8%bf z_8ah*Pz(oo#dy`H(125Me{@tai#jnw#%jBT^vypM(%N2bl?^;x7PcuE2GM!NBJRs6 zt(mTVHa{t}>d2@_MT`hEAc8<@@MV@pxzIS?VZH-LUJpK0!hHqVjw;rv{&2F{mvwBh zd-0l?S)O#{NGnLcyh=slzd3CvZl_~Sw0`$X9$0U#4jKJ1wIM$F^OE6XnFMK#9I~4&f4GNOwY7o_!lHQ=iFoGUj@|*Z#F0&v?tK*FyIq#ZI_38Gk z~Bpj^4=QixVxw5hOKywwp{8cKNPnsWX=R|)^6Lf03)1@>+DJQ|CW(kb$vC@gA zC!ylT5SSm`M6q@TfLcsmy>J%JT zYP$`C5AiD|;s|M?-+3RdX2Yeg>#TY57%GXLIADXngDCfK{jTY_DlL|vNpATpjLm81 z9O2W#@xfxsO#g3h?dBFGOPjjU$oz<=Zdx%h{=v>pJoJ;C*!Rh20^xlCC%g|>`ee$^ zi_$LH%7i4#q5dmDGCF29D>Fkhl$_FuHZPACxx3rm*MK1iL57{x%(47aP!z^9Tnq4< zzm^IUCLvIsSddruRT9cX-LsZqP$o2aRnjG5p*>`QD_?_ z8mm$2I}GK)Tea%DiV@F~-)X#c{>>~~S>ev3V^kctwkYZ5RznIcZLFv+UQ{Eqbnw{E zgrh`C6b#MaFtopD{XvIu5BYERwLS(^Mk+1#2k8*lv-I2dS%xbeX5pf)LW&NKQTUm`N>t0`nziEzl)pTj-NQ)zMlOEHM zUISUIXBw|1c4?=xZ;f5Gx%4*TWfaj9#A6JUeL9y#kF8O#!9xk1(h(uUxu2yXK$uXS z7#g5O=YLq^xPqjiT!US2Elr~qVr6`>Ear|wSV%7$lhnk+y?7Gi@xMP_Wo}&MRCG_g zJSL!j*c(%4^;6V0HY2bzcQcZdBIcGn(HyXGWXvni;e`Oy-yx^hX?VO zy6a+|E&mnUNEp|$zVsNOT_fUfTnePaWkGt&bukXcB33hH##J9CpYKi`=-dMDNl5K$ zoy5$&G!B7eb`bcYnP$>}=oo%y-GKu~xC?nN`7_jc*#yhDxe< z=x4J+arR%aD`JevXh!P}-Zlw9>YP|LYU>g?92EpC|5UnmlUYJ= zq}b!iL8|9>ddq|_vt?~){+Yqo#$3X?1jTVDc^WzFEoNiZ1+{sGegm7H+JDa4=rQ&@ z-e1qtjGSDrkfHMEJ_<U%G2^tqmD+nNJeXZ7p(CO9Eso{1VTK3aK4|c z^{9G!7BVwlCgChK`cA(=4|%=T%`^h+`h4WV{pR1s9caKm z!RvFqB3nbF%?Hz=mZFWp8*va56_pkc0{Pb2=b#Z%t+ZeoyFBp282-GrsXLAjL7bkx z=ld}6X2)hm@Ll5fIh;sO%K09%O7MfC=Blg}z6m-A_^J_VfdR0ZIEpwums^(;P^>hj z*_(-Jj)Z?Y{o=B7h`GNMDAa3Oo|It}C#O{Ox=7P52UAC{qp?c(U2PWznLv9hcE`aN zgqKh#pGBOL`-~y{cl&?&Dvtz%RI4m?cjRV$W;&zbHjH3hhbDED@VK&NB`V?RyEH#Ang+>D^mFwW zIB&lP$}l%&kRgpe^;iG4(D11M)T~hu;4263O^8@E9G%`;O*V(0Hf@bgS>C{T^nEb) z(uiG_1-x$l&Uk?;xS2Y80ZSQeePyp3qU`3taPwj8G4EZd&EQ)aAx8vJKt|@UNiShC z{itvSDjyLE7=h0@9wTJt`LbhKB|w%H^axT>{W5SEK9uz;3%-iwOLc-1ePngfqkObbb+gAV-7S*8T6mcyv&5T7(yeyDrt7t$_dWMf<(mNr!2u~kHGhtI)F10SXaf?t zgr$&>B6!S2i4CyspPL?i1|-bc0n-&O?P5CRL&y*3b{2;BKRhoA_o+8kcEK9w zDy)+()bDN2$-K{WYi#Kt)MR9->0oqWuFHTrc0yrff+>_~R z83SR*@)c@uh#3o6rU?5gAFCX31Ty2!L89p>=-N z^>57C32SyUl;U>0^E1KO0i7yW$>s?)H=^CvpNxqjO-?YVOm=5mZ?f5f*DxnM|6iR- zZXMqPf2tO|2N!#A&x?8jOf$*{XNI5l-CY4yHy$mdD93{dgs+ZhIxJGt3G2UaU{&ol z4QneFb-r3;B~*1Pm+u}%Bijxs^W(CRFrJOV<8K>zBt<%jfRhdvLs|{c07pEd?!DXv zSEm7sDx;NWnRLTmP2jP<;!b1)~L&IKpzKTp4 zt~^Tu%{ipA+|DQ%dzQ=WfTqdoi)WH;WFlkC6<;LdvxFJD*u$fdDT*^g?6xJ^ z^$}IygWt$JU!A2F3WB?((3}`NhQ0 z*zKOd7vJ7@;dXe>TB{@fPSFmtj%!0esU#Z~Ib(C-RDo;)2CI_-2^aVH5kzsz`f2@xr43<-nsNQE=I$@s9NvO zyh!+}5q~<wz5-Y z^1sZv{}@CAT;&y*^*CeB_AlMqU!>Zw?J2L2YN9K(R(}aJRLtitqNe6wxQD>YSn?3LZVDlQA4 zXP%QI{ee_0`QD9!eM&nZ@c|z}+|cA54Xjo|xd|)k`F#)tkD|%4q5WF{pQ{IfUH2io zD*FeEeAWFV=ni*KjQ+o;RJxAWy@=C|?XuqKze(?G;DWOMPVae_RwXpx7_T>Hcd}yV z;U7CJH|E#CtUEzHrJG@JVrgSC)Wz(xOB5CKrQ?t#Dl!Twf3NG#ScqVmE>I-}(#}@X z;aWn3LV|77B8f?QF;K2wU<=Z|O2Fspd<1i-^H{%osqQNnQ8|flyIo?Q0wW5Hx1-eV zZ;eS@8`~xrYi8<`W^@*D@s?yUj1SaU{w#xttK?zWAIIq0lfA(aIreAnF^(%I;vuTB z>D+I_B-e4oS_C^^X`F(7qLH1yI-2=WhPLfebk(;a6(FCP92`>nWpKbde6Hp59Afv%jRP_Mv3QFd*BCJ zL`6MG2}Jug+;Bp~1I@W5Ntxuh0iJ#IAb+nF=C@za2u8t1S!5@EY(GtLx%{$T^iM7>3c^nJD(sn9W_C(vJQPzGJE25MRd@Bo64ap`fY#4nu7|K5Nh zd`eUAx7!l#@lzP98Jx%(FL*~%%d@Rund!mkbJ}M))}tn;Tjt<3Q0@Ys6um-8kaZ_o zzHSC7q;J57_M15YDB)f+rH>@I9nKT^;TW;YGov($v}fGyY<@%UpFw9eTQ*#PN-(pxRUu0y~X2u+Y4Vu*0YOg3N2bl)IKsQ?2lvrysnR1smW6qS}L%S!n?yx zc8iVv#Gjg_27ESCw!FP+gZ9SnGRz>%hV7;+-V62bx=f< z)FeVJ+e80r_{RElU2X!{jWbh{sA?q89^PwyJeVmD0x~~3Rsp+oRceQcT~J4`5G~_= zsuvLtrSi%`Ypk-9hZF0%iU&?A8@OpS>aJn60Rz&#IwOpEh7J{#y0rT?tlSTg!J zUX}BN9t}KlA(COqF$g%eZ%L4c{V zYe5j@D<%A4s^p?60ZGIhxWCdBaXF${h+!~^P}PXc?j7CJ8S(h!Z*M6Cyk+$pu<|Yp z?TnjKyPZfC&K8@sl<4I=jIbVXE47} zRzwKKOzvH}h%`rRN=p*h7RzxCy-GV+#{ZkYUhFM(%2)W8<3`0!5`LHdvgqc{GxHOE z6~07LCSDFZx*8HR1k*Jksb=j?U<8V5E#4IKxg8;RH<(a-xg#40%VWntJnu^5FB%6h zu!~*M!XlsaUzv%;2uwHVL6BLGSUnDW-J#-oPyHPzO}hvOuQ%3!!&MRF6SEk*acdX^ z)xz^ktIYbwmS=c4XgbM>t{~ep-50fl1|ij)4_mKF-vfV2An#0v2V{M^qAaZrEW@a> zoyA1-CH(4DN;T)qHP*)Oia#MsFj(o;kMs@G7_ZooEbjXgRzbluM5FbEBAq zh-2`okeUTg2c8HcV?Op5lI&x3DE{p^?0oO8(Uq6Xm!0|!3KaH5L*M*}1PRi$f^GgsKz~mM!$5^V0NA9?08yxTtPHTI-oCzU zFq5so)LHZxs}B0oySS)J4@*a>}=*&GmuUFV^FXJi30{U@@)}L}j$ba*^2qG3x5C-^iXQ#!i`AEul4th_D3|BJ1+ zjH+@CyM+>yiHOgXVdfv={7Rp20}w2B&IyRuUcbfd#qxjOJwr*V$Z5Cvdy^OE zvpSw6Z?F1KRZ~q*w2z-hH7(tqQ_T)uUzAz zi5y{|XCNcj_$xwX(0%)ca-}R{Zpq1gLoLFNXMjXi94dx8&gj~n;0iV}HCaE&pM5bF z66hnwEBKEIs6q}+eTo3{=dWa^jPA!Bp+i;pkhZm7WZz_qoG;^%?9d&F5h@22bnl%>h|^d&)?y*f#f&nl zA~ua#kt>aa*E^}DlU4S(t^qP(U`#`TpT;J)5=ZEp z)0n0fX*(J-QHa^_Yk1iZSWMe6;xrdj?V~&9H9P$}XmDHmwFG#<4#x|epim?+{@~qZrZ8a(xXN({kjE3gRhb%F)VC`5{$ycLyi&Jgsn2@0%X-k z>^FrVRkN0p^R0W$1bR5t|4gT)&L~Blb6*dl%g9*U@SXA6@`)`nAMPBrgrGak)+pG8 zbS%bD*c4Vtdn=?AIqccPiRDoSD&}8P$}=j)B(z2byZ{qDE12jL(&4P}E;rreHWb26 z%CrHOd55%{N|eG*2vAQJ{;Tr-DfVrT8sUk5+KVzi&GCScyKp^l)%(WpDTMou!;-<@ zW1mnf9xlz#h!Pf+HT5e`4#Nku!Q_f$k%1i^6Onh=5W|vQ zv6w+NF8v6(t)b#cHE!Q|0vo0Z(1sE|1Pjzh`%Sf}EF4frw;&k>{Vraua|)ZPJW7FW z)uEC6@2BwVdorPl8#DVL89N~UW8iE9Cu(y? zz3!N-w$3Vx5qidY$uMKth+5y3> zg*|Dv=kxJ2@BG-!Kon%BQr&Bf5Nj8RLwh{J?MDL(6O+)4)c<->j0jMq-j4nP%i3ar zc-^0$^+M`cgtdmP%_~sF(glUN@tS-Iec3Vwd(!-8lnd6aQjfff;&LY&R(}5NE+)~B zLV^%lQ;S8IiW9($dW*zdf0wfL2P+XvM$Hi0Kx7(5E*W({w9F(lbNkxXe zfA%7PU=~%K3VVLAPDg(not*V;{p&?`S)5S&RzaBzf?9Aja6mt!$X!>_VxXUL7_=$^ zjeA@Hk&YmhEyA>c`{QF5hP3r0SDj=a!a|vEwWt|HHcOu@TsCHP{5%5kpl(_yClRYK})u?D4atDoq_@(nuq z7anuxDi0*A-eE+sjxc-Uzg8b z1exf$Zc#aQE2lwuz3Oo5JZ_&vRI@s zC0A!Js{<1|X@+h5)w@yAusP{woE92BfGJEy>h_c-dRrIYQ7Mi}1q6Rv@ZxgVV1gW*5HsYzyP1CF*3LaleK=ES z2iOPm=f1wgvVcgmi%7j*cJI5AeBv9l!M;YG4}_k699FYprneL43zNz+jg6v0t- zY{$D`d#QB@W`3(;yrMqtF!Taf%+cwnj2oWha;+S^R^DD*Q7$A^Y8S8r$VOcbP4TvC%y+QN?;r#oVFEv ziE5jUpyEzU9l>XP0&5W+g;Rfnj9u> z0h;YaKvEvFUV@oxKa;q(We&XdJ0{izNG)Op_fF=i)Qd}j5Ow}$Pv7vUu)t#mzsZ0l zFS6+s6{HXnR6%s)@AU|svzj!zGP&p4WY#3zQ6|^$Lsg{_eK( zNF3%rde*j`oI*}c4wL-qbLLplh0Xt=`JviN_#+nRp<*HfKLIwsb9FQcziPzuqoJBJ zo_Q136Tjq6BGPc2Y}s|@A)=6&Wm?|8Lp@#)0t*kzCWH0|gR3}kQ)BM!BRnW4{O~v5 zo*8zTb$e)r?jQ}n=voc-^qn}oxoIacS}VWtEv*&P!x*9RXK0u-t+9hQG8s*8&5EN8 z;Uej0K}L3lseg#8W+Hmn9Q7h_Z@d{+^ejKQJ|9T#cLNdV?@@vplv~X-u!?#B z=;rR31w*(gjm|;J)`{`rQ9R03p$LK}C^v?TFW8K4fqaFF8)qLpm<*saAbVCbi%HJo z17I)!UFPPn>4wi$!B(^tg_fZ|57* z1gLjL-y|81=fj#Hc_>JIF7kaP7;78wTM3+EY{Na0Kj`hj-D{)J{v`oI-Ldon_>LmW zk70k`>siL)`sVe<;|zS-al2U5wZ94d*X-$x^XrWD7L(Ouh>VD1<_nb^T?GA}tcOxEMfj?;COsT?${{A9`z$}i-#br3Aw4vj) z{?!W@RHRo`2A$MnLOxO-qRj9g9oh|Qx{+)Uqat>agy^A0#LEO3QSESkmK%MmP+zOh5(r0Wub%Ilna!1I0m+lZiFcdXxjh7lQWUzHzyXB414`WgzZ zF7=r>{aCEShCnt5vw(C199Q#GxB#5%KM7AOzIGVY7%d30{PzCt)_$$~z16$g%Wrz3 zamR;pqLE-*PvidA=Sz1UuAVcQ1PTRdl&thMQ`~kM28|cpoBftJAo(F23YJ7KumWSS z%>n;kPcaASLAc$2N=2}yJZxcjyoB%SBI?g_rF^?0qMektWjsq5eC z=Vv{7U2f$e+|z|B)-HVLX@l^&wo}vVq6N+kysMnlh}$^s*30K}&E5hRDyO!o!3~6k zQ+EMjf0sv3 zr^<-4Ga=@KKRVZdCj)a5*{;c;IZX)pHmMs83X2R%LJCA}ao*pjdy2NxCS{!~h_6dhT_cn|jJ3ouWB zIBd+JBwMk`^F?@1+FniAGzTxQ@REAP5f_$RFP_o+R%|5{Ogek?5Qeu^Y$IMz(s9#L8cu@Qg zYsr$^FvS7yk3Ry7aQy+T{KKwO)wpIKMV7-g3MXCWzm8(9c<~$!^gAEu>U>g4)X*Mt zooWQ7JXQ{-+gbc{i2BZCh%++%(|!0`2QZvU3K_gB#K z+<)r(aM(xYHW}t%OeQuM;Sg_AVA`K9f9%xVm%L~h3}6(b3m`ywCH;8oW37FEA`{Tr z4*Ly+Dx2VD29giY^d0}(N<-`2Qp96Ws97sAU?PPX9j5$UEVzv5Vd}F_D6v& zD?6L=S(L+4R79pkHGRS*+zY2-g&t%86oTbH`r`81wW9b0&;Ud&`JKG-w(m7)?^N3;fdY0RM3tIQmOK$2Q+gTmBa!e^4rH;ZcY#-xPom zQbPJ<;GiMz30RDC{>EcWTD+Mx+E7TubKi7#qNr>P z#?{L<%D^@}q5lY0$CIBc=rog{U*Q+)I9A13G*UsV$sk-U2w;=y%|ydHc<()ZLeKiTy$_cS*?@E4+P~0RGqA69g7S z$)NjV2+V-^6O7gZe|Nxul)JTrobUDHQrOg~|IPJzg(L@*17N33y);cg0!}H=sbChL*T0Sw zU@}Zqb^f(p)WMwW5$871pY&p$KSmeg#tn;9^V2-QREj}<x}o`JLG z;;HKg28C|nVAdw9;Cas`t3KH-_!|9b)g^)#4~I)mf%*eBX#y#<9|$paW~}2$F64)P z6n!$rjr;E?zQflJ0xdGVfO;MPTVRPWv|kfbM*6~}z1+QlqX`I$yQyiebOj-| zxXl<+$zd8zZ{NyYQz+;bZzAc;4QZ592*h_Uj{Gg&qSrdknkdTtkLM&S?wq5eWxX<; zH!1d0+x>OBe@E?mEksYZEXe(a5HT@S!o*uu!(o5)>TS{=52BN1)MJsvZeaX%eRVEl z)ZHfjyZ-ZJ|FT_otq%Hy3pL(OG+BZW8?cM-c&Upg<2@o=B5>Y2u}!`4Bhp5oCKPqPvGIx`HNk@?ZX zh(fSk7{GwsD(XB*CIlw0Vo<*oNl;YC)#;g=lJY~gFIEs_;gN3_NV4((2(3U5@Mf!F zfg(;Ozb*gQH_JKKaRQy&1m55nRsj2Ulw8q}n0oPKE5F|8$ZSgJ`!JN&RaD$kxysh7 z&*jM{0oN<^PdxVvk;*9W#0fXp&WBlkJ>kCo)tSCk06cUPNX}0onw04^)YM@u!eyj( zRC3V2x?8~*0&b~%yl7CkB;P&?z*wSG7y7`LUz-zrG1)%3o_UWoGL@n5L|`B85rK1tJKk(W$l&=1IA zLV2vFE?nA(4qvd3H)Lb;GrUGt@r6R5ORdcB6(2qO+pWP*sy|Kb>IT%!P+Nj4quFjM z`OSt*d%{z6XAB>-IBXOeg9s0mUU^ z-*(U4S(=Gx_A54c`T2@ zVS^8pxKuu%TdEX%QTcRSeNvqc1#y7ioc;ESk`lugY*AY5&@-IdkrF&K85F-V5LA=^zqJyZLl`;qt3HRhOUY#8+0*DZRcVHs#c$SZGDZKh)g(Pp z|8;Bgch!P4{*(8op^-LMBhA5li~1W)i-234+c#&%#wV*R&xekzq$te!)-Q@qys}2~ zES}SF(%XlHbNBBAF5vv9MzIY72mS!mwyX<35(aj}mX=4GhXmNfSjrhP$L}&0+p*`B zjNG0lbKyp{dIC6&<5dqSIBqucW0Ew`8USj1nP!%)zsH~Nzl zj-no)Qr_MI{rwb(Kk)&BvM4STfjlRbZD7sKC|j4mISe#QfD#ppOPdipM`x!USxxGj z?02wuLQ^F|%SSgzrLbHC?kO-( z0|uAykn;tJF{fv0w4TX*hw zUNd#QiUsH1@|}v61QDjJ^iOrXSx3gS^Ikw#f&4F^7snaVkEX|$gdSc$sJHot%}-S% z5XqMm%)e8kUot*XgvZ}y+okaU8ft_6xL7k=wSK`h8vev`&78gz_QOQ~|GP+#8u+uH z2DN3SY|lPDWjgXSP2837w5$tjJ9n70gHvNpHVL=nU|g>Vm6$rity=9bAMD(ej&q{l6B!tJ^=i@WgO%-o%QA54;5}ww}xZcgm!Gq|^0GoL=TXQ4S zt~isTb!-V!hJylIBqun4i+sq`zx0!kPW1GpK-)56Lwd%>bkXPEL00wFvWCBpd57w> zO!?3N<_KX@A;C?Dr5p}_R|MWe+E3qQ(SJxA{%81n57f|>+RuO?HXVsh>N=hXh%T_` z%pov8fSiq5wJt2CrtvUmopP_8r@DF1h6|8di}N^TEv*;dHeo7=pw^IOV4VZng9~Cb zGCS7)te@SAD9he5ODJ7?dIn|+|A}IF46EOxv_iW$D}XN8+bBD5r*Q=c^C5IAo0LAd zORk`!xzC(ug=>XcJDqgTbCn_T4&C+-z+nB?a;GWBPCx7#wvEJ)A$TZ@5dRKL@=7q- zXbp2d4Yt7M8oQ^X;}?AGvDMHMzxYc|>d~OlWgwlkQ+Z{O3Wi_$qzhGj4i5Jo4Y87k zdv52kmGFTM(EdoBm=~xy?5>~8N*FU~8qbpF>)^U+&m{amuGT3!)Zd)GeSv&(R%o;! zY7|?sF?ye7B3cpqfE_&}nYi_#f;NPr^9Vp*o~)pPa`AJ8&lmZuziN;apeTJ{${!FC zhW{q{hM(GZuR6apP@3RVJ{BhK7@A(H_1EFBhvv#}TFz1__fAd}%3y3P_T_U=6{S<` zwnIQXW+6vK1H(2U(XG}&0Xfh<7s!OXPP!}dFHg>T@!z7BoG~FMM^z(BxM;Ta#UzNF zQFTO|u}{cQg6JviDHdMjQ zynV=Q;hJ26KqNKcf$=h#UYi>pcnAC;TRtboc?zmo&zI?hLRpEPF=As24N+FJhr3H_ z;)VJywR1S7=Qzwu_d3VrdfvpoZQxMT(kd`8ze_;JrBYnJZwSE=NSP(oRXP9E|6=b* z-|9b2F+0*1@yzn8EKU$1-HK3OJDgEcE<@!S8X(ybv%}-FH%>5B_5w@a-d3Ostu~I+ zi(G_EF!c`)NDLVWmd0i_CzN5S`DN$`PK!u1s3AQnWDWT5Q;mAl!^%-ll?N{-P?8^5 z4*{1Ea6Z{8>nPGPCX9f<6%M9BPHGm+PCTF@fD)Wu*U)>!GD;jI|CW ze9xmFT($}%JwlNu>=Z5kx1bx~+Dwj{k=Sagb<`^5Yj$IXXXBFgiJ}o^V|s4GhO>=JY$!9wQ|Hq@CQqI@E~tz& zV~7%<{Dowb@o#8?H7&g>jmc6dAMc5bls-y$&p>domwZq;8a4jJQ6sJRWaC}3;J>>D zJ^r{#v87%80x^TmzJnP4$6fx#xu#1Z%QxyGcvNzgZH;jU23r<7<_1t#+S6yusK4cw zxUqv0k&rJD@xmK;dZeYPVvawjCkr0Sd2bFDpH3~F-uaH|CK2sc1!ncvQdyS2=axnw z5mPn@x~jA4s(%Vk7ljcaN6G|4_=<1O#XO*SjGH`S8_u+UDAs8~S5a-(>Mc8B58&W)#!7e40FRSnoy}`ah2PTuhJ)uVg~3j z0{jPQvHYU5-AN3u5%tS=i90WaglBjUQZ{&@M_Smjk(NHHDVIEJ^e3w{X%YS}|KseR zA$7CtGn`5E+URh2h_zxGR1w22N(SZ5PUnmN@eEyU4DOqDb%FRsR?{p6f@j+Qkk|-} z5T{`Mqryh!lzvEwhDiN^&-wy(_*XVC(Bc%hy^$$#UOM9P?9waO21Sv-!-8S8Dj?&s zKjggL)?IF!W|AF?gcx#4LOMS4rK`%SOa9a6o`)!4w{`==tjZ9m4sw;!4b&T3dLD1) zf{*g9{h(nzZk=T2BPw;fffW=;z~3DcMvu#`Pa%!vBt{` zLG1!XcWp(+C{iX3=Ub_SA}0a?F{lYq-W{r(8+tTt5v6zW>4eMg?1M8 z-a6zOfAWhZ$NT`eXSdSMGeDlFw$KOMo?Wbz*VXt+m-Pcc@;nU0*pLv@_#Fn!mXiv| z2yrj?G^^qkkr?9&+qA96JqZ@bSp7YA4t?-&c0)PhZsRIa*d8gW54>!* zVAjgvR5aTo$j5x!kRVHeMS=nOOBwfOLkGz#-rf9r))sJ^9u!yuoVT7kNgxl7dDHP- zj1iN50+QPk1fi>B^*JE*y;)>S_gUo<#5LOxuSlP$j-r`(Iu67-9krC=7K7BOYQ}O#%7Bx)EwWhrlgwVy{-$zqmy5&`FqIqVrT@IMmoFt zpOQm2BT%sUgWC6!wuCmu*jT1Z?r*mEJZ=Ng1hu^s?0`2zF3b$x3JTnD!R-CsEbW%< zi1~(n4d}25u^@i)m#dVL2hDGdj&AcCBH#aJ(+*F?@X6zrU2)vakLJz?Unp7>4$gkT zr{3d7%uqvuq?8}r0!GzQ0sJ0&hP%sc?<5(qukeNcqUXxocVLzQ@9G!;Vod)Kpx2>}9@TT3F#14=BYZGh zu$Ec7sXsNGQ(hli)-N}tCxW@Unp(Ksa!n)x$v1eijL?BxXYq{$IWML~sMu;KGk+=? zm+HqA{qDy^m|l+3c7yATl-7N{=nrXnw>A!wmpP>t{V!}Xk7pR-=F@slKAM_lRd+rZ z<)krbPx6)xLuvoF`KYi=mCCfdC|uePc?4g2;r+ry27cItVGD@aMY1^7#d#ittw@<6qsY-khl*JZtQ9)o^0uF@z;5ih?WSpmlVU)PM0kIioh^|!&)Q&fd&Fc1-}^8Dwy1ZuJY#uCcNHZ{Y8HP)i17p zmz_#Gb!YKZV(UY{_Ie~kUU4~S!(AjLWoc6)-g1XHF!vm7M9lkxO@;DAsD)LcKX^u zcEKvAUdEdLrGbT^#iekR41PvqZFiw|2&4-})S)Q7F$D%fTeP?dT8)Pe5~-3$_}cU& z!Lwje)&gxE#9y6e`TL{}<(KY)Uy{85(O(K6EQ1Iwy|_96^$|F-!24S8Fu;8FmbUZv z-qkQ8&DM(6cVpbB3m_~}6gI(N;FlBe&Z}nOfI+4$A{~FuMJ&Q3G=Q8|MRMt>a%xq< z*z)20cXtltwh(l7a*a?LhZXq?>_NHmkS{J1cyXyDj>iGc>Ej=*S&{$R?QOrEgb!8n z;OQwQBahg6{+u{Ef;XUxHz~rGEd?04N!$jx@Fw=ve~){AlwL?)?X!jJ+Wo`lXPA6y zP?7Yoh~nBBJX8YYQ5(5Z(+!v+`V}kZ^j4JRk4_06Y>S#9^Ho+7UMdcF1H(&1+I6Wh z1d|h88eUkmlu3Yhj44Be!BxywZ!$d2$o%_ZY+JhX0Z zdNIHDw<}KI@81f4S0<(``)9#zaZ~Ls;@*7a;qzwLhX!PnnX*BzST(s{57u!$p#X!I z3ex_nLQr=D-e5WeIytu~Ha}-%U$JB3GbV`$W+_(;_oi3dq5)4ucDPRiF*rRxxrd~A}5FB8SobVHgAVuUecpIRF7G!piRy`RO>tsxpEbYXq zLve%k0s((?-ouK)l}Z{_#zKt6c0BJGk4GN4=i70!kW>bAa)ahl2;$j89kO z)^3#ulY0>~i@n%Nr+jeZ(ODUxN?HZ~iAKSvw%4;5Z8z^7yO5OznD4myuQ1 zYM~57vcp_1cN!Y#o~q_z_N489rTFHp5c8eqt;wdd)goU0Cxaq_VBZlOQkN88$vfa$zXbWx(5Q%n zryO%E&*)L3aLPT})qewlz|c5D#q>P-$E*oXrm_Y0}$G$Bu4N?09T>t3~=$BCf?e=0+dMd@XGknz`W@JoqZHb9E>$Rfjn+VA^n8!NI$hlx{Y|IM1y(#n1-(glVs=E%kQAz z;;ugK`vsXyo6|_4lghvi1VLGI^(01}cEN-#U=z^RQjb8l3Wezqp-yRR0gp95|-t}UCaR;y)TaMX>R0=Kt zLjw;E98%Km+=;RvZ?zn^=Tx7MyDYiCPNt#VA%}*&N9)>!BjT)6p`IKqcu8+B)>tL= zAS|%;rGJ`FsPpt^J+({;U*VsnI&xQpSHw&1tg*h&x?j!Jy$35SIV452^3Tj-{Fn%n z_+>c~NX4SGx+oES>>#}+!KD=Dq>b0k$qQSfKwm-n^~5~(`iLU<7pu~Vyc`tk#MS>) zTmNIg-=)O&bt@{&)f7LHM}o}lKvY~f#iA)~RC&u%K-kTSLb@PInil{Ie18`jb-mIk zY|5n+!Ye_bW+FL+XZR=w7-M1=sXadEeVmaaKsQhMl%VMlE27o7w(-Im>i{pr0W+SaI9?facqOQ_U4E=H42zrVF7`OQvU1J-f*W*3q~g@ zbW9^7BhUmW^}hWYR3aS4z^zLOI_>*$f*NF{!=QqguZs?|4a)R2?8t#aZMDXY<^qu% z`Aa*$Gn{=Tue+T+Lm@gf68!N!`<6yO1Vpxbp{X`2HNU-~5G>U~ihXwwA-IVFp^ppC zT3>$b(nCGx2_CPoXbX5w?B>rC8X=;^`fCJv4UH3MUj@CP;ue&_1C~`qq<{q2eX(d| zDQ=wcWOm@k?W`esgqKBykJBO%v<(<{`QohOE}#4U;++wQm#)&0emO-Di*UGhD46~*4;XBZPSu=hNB-guS&Vp#* zQ~OVv{VXtWG~lBg9U0IRA_MJYY*9l)>_ad~ALl!1bVRM>y=w8ztzr2?=Ez|!Z-yhE3KCzI4Z~2C~ z+q?SPTRG5Gpq+^zf=x_L+FA&dr_X~MUwakJCr_&6k*6X>!;`&lfL5x=S+fQtS>3b4^u)GhGU7c*wcaA6De|kG)TlaA%$r4gY0SvMDLmxY*$ZhZm zUQs`|hL*2_-%XVPOjexpk*-WD)a_}}2iBoDn*zN?Opbn)m;iq-RsQbI{TaVHv^ze!D-c3DY zmGqoU|8Tq0ll5KO5p$H8csR9iAXg}c15S@0lg8~Im$u|V(lK-CK-O+Vh)l_7Z>Q~G zBl*}K_(#nByaWkTs_2^g}Kj!;`Q z>pTpwq$BjyiOg6+TiRkZATj3?@zu zJ8U|gPy?2`{+em+y&UWs;w#V_aSVb9#(|Ts(8A$fma^Gj$iv-41$m?N!CB2%y=(@U z;=^2cyvAFv_tlULF_`b3g812;KE`-r?=nD2AnlIiQXIIfSm>10cVclcK z6E!28kVMNsMgM(r{PhYKn1wsAj&hlN-KBJ#{rqIPqGOj`cmr1J9N_R4K}9)*DvlK{ z{r^)m_z);R2SnfjRjuvFaE6h0*epiPqvUl0mK`W|969B_T66A&KSEt>v(S2buEJnk z@5^Yx-1R{hdWJYT46gOFhS8oB9p9Nrz~_|gE;jWe^NJ!0f(h(Ki6PkAEg{}4yc=UP z)z&-3S+6m_eYgC+5)0R=;~w$|f0^-sh*|jof~8bW**w(N`1n#9B?;2JT9~9ssK73~ z)#GchI-bSswjVkIWJ-epD4F`#SQ9@!=ujrm?$D>B4Y^RQv6(xC9H$}yELTUSmjKyT z?)m&1sl2!50T==3<_6jiQGFuC+?r#=BXuc?&;GdgFTvvf%+jIq*af^3^S?dw5Ze?~ zFa69p{d{Qk+aa11CukE+L7)ClLGgcmFes2i_Ug<-NS1!oD%#eNmCP#Trxf!^4;j)n=P1a<`Q``V^vIcf~G@j>EENpBN~)r0&J9 zF-QU&u~RI7a*{5OU`(b-GPG%gM5^~>Ycws^wR$dZ1z|C%}><25fqf#gVZ?& z@dmNy1*Y!{%~FMh;iWR@oxmVX0zK+p&8H$Ol+jVc7ZY4aF9?$B1B+K8etLm~eHjp9Nw8E@F({aAQv;9&iYMv= z-iqOLzL1L6tKIZ^+FMkAG;xQKkD@@{%$5A%1O2|RwRr}KJs3Yo2qBv$hGuKjlyc}3 zzAM%Z5kCcyUMy-rqt+OZ_Dq4ny}ZvZofXSC%*{t$GP$4`f79wIbi!xz@s|=C9+UyP zC8h9KE^nsOk$Hv%$-un;$Akyc)Hic%mk(g_sz8gqSKv8g% z4%}P-TE=aPpIy>$$BDuR{zpI3fZq{QX3z$ybVYE1WhJ2}9eCJDioJG+G(G`vbbPv2 zYudls->js_r|7ff!V_nWWa&{1Y)8mKhSe&+@3QN>4)Tm+X2cI{bMoJ|eQTX|;8W}A^-8ZW!GjU4!@{Ve;u z?aF&I@u#o^BogSP9-Le>44D=Lz zhboJZ$`kK4q>Ek@oMDUnp|!t@eY9IvS<&=rZl@wk-^WfvZ81pKMjXs!B0>e~E3rcF z;*8)ZEF#80fJaZP>Vm1bR zGA1(eU{@Yq{uwRxjG~Fir7n(`ffDvd$;vu18JH|eyN%qws%bdC#zLmr`}N3#3Stk! zJJ_+@2-5?$@o!%|JfsiA3jw01tlY`vcew>PEtPv$mRtR% z#|DSxmAAHS50bnpg}5lXJ23lA)xZ1p#t(*x`TV!f>Vz84*IMf;RqBDMIp`g( zWx_x3BX1mb`IZfaRQ^4HzAA;4;fSUZ1d-4}ww@rEKqG0*_o0@Bh4`O#8w!-HKM4SN zdNH>zNdCm5e4Jz=dGq8A`aw-;X*+Ir>uNZqQwMy5@IJh6S~pMX<`t;cdpQAa#l;^_ zmM?W%uU<&W2g30oN+@nP^hrG4k^>3nu4K;QvHUQR1v;s*{^vr?f+Cg7!ZiuS_QAFmb95tDrI|mDPUj&*_e@ zPY0E^%wSxhz0=M&V5N3gYR(V`9;zC^9BeMyVr*lELRm-fBZ=Ueh)q{rzb>C6Y$o%Xr+<)qMD3Y6D=|vEpM79sa zqIf+8Q|?B_iyZw6E;9`RyjIBf5N&FI8JYpWp}gJsS(Y~RQB?O2myy(1#qQ4JK&;QC zm(ROilUHBE_C0)_g1N|^NG_mtnfj8z6#OGcm%*CdT!Y*%VN@Cxi9*oF0@duoUpFD| zaL7zk*reZ}fBfd;`^bGW6`z+~5%<{NiK5yVTB)U&8H9CdmKN9wyX!qT`xX47OX{}H zP@+ z$l~K|5n;sfnW{C-Oi{a_zSF9pp3(ZYR9wrhs+N32y3<2OqSa>KI%f=a#Nw*0b>m;HraCF^<76 zxlc!l`pa^)Zft!66zeQo(c7d)10$$i__;@TCx~u!;#WR3Q<^->7P{(O9s*J~&il(= z$Y(kn#q{lERsUW~U+_&w2AZ8W9T)ctsr>pY(-lOr%>=_NsKUhV zUeSE9n08YieJmUjvzse$JUlUv@v|zPV{CBE$1`(z&%Dadx@7l1utl_nhFfb=IuE> zo&FXtwl07R596qyGc9b)x3eWMRsILki~rJzcK4m6gd4eau!Ap}GXlO6LNg6mj)MI{ zTwOGU$9tr-=lGbrM5d+E_uQ<>$;qs*n)IFT;J6bC@SgP3)Z43={%`+e+Lt9RQ9L?U z#E$zIHFV~XTC#35jj1!wY|-y0smSE6*G;b?NlWgFoBi!5mxv$?Rmb!wl0j8H6lH^y zZaUPQrY=~ zr--?*e_0r@A>kkl`VOKZ9;EB#QP{ebv6;qam?_56ejlJC4dJtULN2`b zRAT5$EGCETQ%iMj_-DRyG39V3&ydf~s5uU3nSFc>mUVx>%?|+YMxqln?z7=)4t*AI z2#HF!lMqMN&)QLX-yHSYF1kO-l!eirBOaXnE4{dbpiNZRckHJRayJbT^WxJu(?4QV zJ9#n7h@wEcWFq?zjmo(l;yTDxKVy}DnI$bv&J8n2oQu6f;2tBKLO8-e?VhSi|MOwI zzs-1+qv>$`pp=J@F^Up;=ML8~nfqj5l3(7AJYhhfeqY5rce@qL4Tj#GO)po~+6Vlz z%^atEQ-7R4@0LS#13loSa-;T=0o2!K+>`wTYR?6}A6g$PH{SlXT1a02E7;HN;?t&e zsVON%J&&{oSmySk!T-*WVMUyOO!2TIeGk;;f)^!zrZLWZ|AyeKo$-X=RLx?*UBs#x zq5YctrquNEU_RNaUclgL*U};)@VGzOsDsWLEA;KO+@^KKUI@G#j=UcjniVAFk`z%m zKl6X;hB9{Tr>Vw9gXcc~N;u&IBgQW2lwNXUifSGWMUrx11_dd#;TY@=3(`YTD5OOG z{Ih0_;5t*k)>|~x;5C&AGOm|5A zF4sO--1IW*Sa42uHq1;L{NiiGkGhGB!{E zQwIAU%-@YrdA;D7vvz~c*9q!A9{FFLeweM@Dx53e>Z*}zU$#hxj0|ul{M0uYHnrRb zB>uN7H1`|o-|PMg_pcLj#Lixb{!TC01VJWW4{&3)-pLK(lEV&Ti3g!1ya!-=D&-2Szyqf{lPRm=+{g4}n}X!}%usoR z3_&}Fuiy&fJMeNRW6(BRhINA-k{h>-KEk&NUyc*G5^XHpnK*vk(&dq z82oGtvtFAg$jd@njxM1%mSk7PR*ziVtBMLZ1KGONp0##f{>v}Ot5jvC+%_s`rvmuA31(ii*4$f9ZnNT(4KTtl8_46q)Id z94tAfc#B2B{OS77P`-vttwgDx43UtR3;e>5O-fTF9ji3|rqB~BX#VY#Y={z@`h~cu z_h!mB$5%&dHOYuj8rCXKYNcOv&_Pt_6YK9T`0dLO?!8nbuG;wccH~<&Urw$lRx?L3JzbBl zg7jT58rDWt7jEqdU6nBpt63TQ?{fZkNHnlR;4B7BvvuwbHp^PU>818GO<5!wd4iHY zN&6<6e~f&LGP&h?zsVJl!JjCXv~({Sa;;|R0xv=@YG>`TC`Lgx-rv78wGVFS+YXqo zF8IJYXJ_2n;NMYi|ED&}dy~Tkc6Q0M&&#TMe-7gSpk)|4gs%utx_z#yYWK?LNro=M z%+9F!?r`?StM3GKFS=5AzY8V-g4sk<^o+vHvgpoi?}p6pfCc0WnbYpF6l88xpG9O# zlusd#r>AWJl70nrh#?|w@~MBtm~=jxa?e{&FX!!!JEP@NKBVmIlaW07sbOU5<{86p zWEr!b1%t!48NVcJHD$YO&VjV9jk%YV0&}Qg zRBc;-LcA8|WeJQ84Q*cBoc@_MSkB0KTkO|&uRkVm$L;K$(_4Z4V;LJ;45aRsvCEBq z{VrKx$XRZ9?%M-^0zPCcK~Ur9jY)Oa639v5;4-dL9;Y`DhX`%a#?f85h=r0(C$mZQ z=lJe3C9!CkulfF(=X*^C`zW%q$guJRp_Vps3=-jHn$A4q4&t|)PTMeel;%yLb9q{@ zn-*dcl37vvQK|dp-!L@ho@n%3Yq3$=Ph(nQxorP^F4m0N4*P|`ZQx{%c6Zq;RZKb@ z@d>BMP)A7O)6}l@*f)zF9;9&9ZJyMzihLqqAAX$t-8LBcE5U{scwN@H&>j=kEPBk(wfQ$h zL8#j`Gt@W3>dVxqibRze=~@9Vi58v#vNQ~H;>j0GJ0!E0%m_40Z11L`0@pYB*ctzZ z`xm;n?@TD%0V03!gA$EH&*P~<{VF-%W`Ua)KL8Rw!xnYzhSGO9am@I@+Y4q%X_UGy zZwRIft!z;R`aUB-zO~~iZUHq7sc`p2F9GWwY*A2fz1g^xQ=9EOUVSL>=axY#v!ckC z)Ru0g!CRPAU)m_b&lJ@APM`F7q*A_akP%*RmRko>-r3`a2Oc`iGk_zxOfr?7REg%|CDKSS`4oGUHrTy!|oDS4=cs{pA9^8Aw+!EAhw_R$oy7DGZZue5JheIzw|zM?fQJsZ68t}Oy;VR}QP(a^ zY`RstLFw*h(+WsPr*tVuOLw=@-60??-AG8MbSSX_K@gCRGq>;iU3}+1|0Oqzz1Es* z&M}_x#O30ARP)F(b^#KWh+94i5<$q5BzlujlwQEMXMp$A8;)9EscaIQo{~HcB*a2I zNMK@$$UNEa#p0YL)az#sRg{rnw!zyS2a+y2fJdZ8K4vUQV#^E6i_BA3NBp{{B{Tj; zZg-hGnx!*<@;58XF!uWuI87|f7tGZ(SdZH61%xhd(m&l}gA`19xI#uCxQ;FJd!-Eg z`_4Xt9@lSex!G(((1@z`QNz;^lpP+XR=Ez6Ks2==3!CTK-;wtlSopN=(& zoI9PrBuI8Ca7zd0C^)JKAz9Qtz8$CBlN=-s)c&_lw#$v5UlLm8xwzq73ZdZ$j3vr9 zhhy=?&fy31_;e9JAsi}BU11Kc>UzEB#rip%cXjxS^73BQ1#qRYc{|9o0$!RNE; zXwFKPAdWO}ToWZkOR4>imj3r4r~jfYtHB3^_m;roQIladIh|YI)qvWnZL7i1(P>*x zZ8%g5XW@JNEdY3wLFwXW@J#Z}cQzOL5FbNY$G?3ahj_x_g&u^u9|%PGWDame4{Ac{ z%JXv`RisPBg9BDn6rD=mWRf+361a%^3Ng~Wj3=`Lv#TyH4`C~gA;KpiT3djqov;Fq z0e55UoTZWM*K9bEyWTx^=b(;O27Y4pVr0juVN@fLpdi9UWWx_ox2hi#NCY9#Y65E> z=9?(vc;#;%k0Dbcs|K2_+0MfCF%p+>;#r}~`0K2leDS3KZMyh;cSu0Rrq&mR8Z69y z1BaG=E(P(~i;B+60_r3B>w7M|c9*Tchs&zJTi>LS2A1bq&*`21J8)_$@f}mmO>b#_ z1;q7~iGwn1(JY$%V0p=w^k-{m*^j4_c4v>{D=O^Vv?x+{6eVfBBYn>MRqJNPACmf? zvGY|Ie-Fgk|7a596dZFDH`VV#G$e|QdkdO$XaH)uNWcJtEDbTFyMo1STUY9b*`j%q zy_Q3ZV!FE-eA4TB(z!p>}J z!pnn!z)^HZ$zU&h71~=G4{G;5YOvjClRyes>&_R<@koW8i>{8UZxsAj^Yj{QKD3N` zfw%(;fX~VYPdWOHRfNMnxqG)p$w$sMgf>E>!uH)qy2wu_I zL`(NUA@R;E7q{|<`O4n(cn>1+HkXN>3>8{T6+F5L+YlY2()s=r)S!O|BBr@_1EBgn z^*}W8#N`5AhIf~VV^X-(zh#VXCZcdN0QLjhoWxXfd|Lg>L-S~68#GS*-b@^JDLyCv zrSVpgzw_(c+K{zgn&3Q_T9b<<3=uOLaA6ftC}=P)EJUDh1qA#~U{;f7sBLlHCM+G= zv!>vXYc&`U7-!<;%f0s$7!vApH~mU_4=QBK+si*E1LTEc=%-*FrA;8+(J5(khTMHh z0&lI&Xf|9CSwH@HN7;*jYuWWPiuL1#V&g~o#V{YUNy?bl+S*pux;Ru~3oEfE2fKn2 z)9HNWw6HJj=h=Riw%f$VAWX0{3ox5^%CQ%Ep~zy$tqp-e2%;XsmFj3W8=)N&Xr>^K7OT;Bq$UsZNyZYw*j}r0M-uD zAS{+aqg3Ukd!oQkS-E}#cvCuovkwYQ-QP2#P-eea;z(ca5!V*~baoIE)pV(I%(d<>^ z_}l-!b3gZOfnCBB-%3*d&rlZsXGNOx=<9B<$2=S5ZABJNwMnU;Xv@?*i#VqFjgqs) z3+oElKgzuJN*Cpg{RYL+x(7QP4cOs~)Xd5060nf$JR|o^ZP@jbhAKGm673rop~>+k z@yBzYDk!`0S^M_43|l{c;vi|{eaG~JCZhKlzIKiz;B+^B^c>IHP$dtF!Q$VYSoT~u z)}D_?I11;6cN?G&$G9aQ9JaVT3V(C}RI{J8Fj7WlgeaZz`}STibRddLUjn79z2(C3 zk3M_j9O}*sa+RLM5nlektqWHn7nld%4P9OuP~vW4BxRw;bqwB%-N$7Kdie33U!ZL- z1N)*(zFtkYXOBNm@PYp)OCt&i`Z(wbx2N0!jJSl)jZ4Gsh5-`7u_yPAcwXD~3Ar!k z6T(HBYw-6zDsd1r^Yn=$?a#~=NPXzXv&d+uwY>W~p9*K@xv%@ax1B4WdvsJGI;jzj#@)QNQT;0?b*2Us{GKm&7=8*=#9<;;!_ulR_o{hi-Z0qbWZxBz1D2 zU{po#47^{y_` z4^l81Y&UgW@B^wqg0Q2*_KH2&y99Zn)B`gQ$8ncv`i(>Im1 z@`Pk)t-)T4)8w;#p#qK{ge`{fd#-fqttSLTP}hR6T#f^-B^bA8V@GUr<_!pbj#?wD zc#Q9!hF?6gNCFeh5s-Ia8O^wXIb-yT?ub+#tzno$o$zIoa-%;nj_~XA zm+-fz3p~dMYZB~$jxQBZbrx47MIQ9@Z;F$Dy~wxo3+#CczBL2>*{3mkghSxxC5d~T zr@ZUR{={!!mrkw^J*t(F|EsW*qkKj0seyGdmo8= zf=shH{zY=0cg71i!OjhMf4Pm(pXgIy3;@_yrh5-5hNsFR$Z@hBMg;(h!^SowKBR3G zb9_MyN0D&u`-`rJ#UH1-&&1vKCj{Q6DX2YZ;bs=0&drrnZFuXcd~-U>_ns5Yp}$}5 zI!Cr~#aT&szpN0z$ zE3kq<@IBFhikD_a_!>@3nC>zd%;kP#jPNe~n{HVaY}Byqusk-7D+<`9v0c#f?l!y8 z0Pb3ot#6D9>8#_x<9iI$Ei{`G(`lzab>Yoh;9DTNb6pFn*LQTsZhs={snzH^XQ)cd zQD%(MW}b9v%ip2IfgPs4Q~GIU`|>eIBDeWNg(bVI#0z}~ACh(wov$>=l*wQ4d_+EX zP1|9EOK!!0Z%$Zd09BSo5Mq+>VHm}E)9r3+cINyQl{T431R-nm3{Mulx%E)@AY+;V z*YqqOW*T1zwjl=--CaNN>mgvM_w-%oRmowDUb{zaa+^u$eLGIN^~+tJT=YsqbSU$7 zFG3Co-$IPK1C~{<;2M_^WJ)PF$@CjZ&kNM(g3l6$vwtvGE z@pF~97aKbDBQuGLU3-?pdaXRwkZSr0e05bAt^m51gj89GU3&k$dAq6n)_OFJ7G5!u z-L21eg*SW{{NFHiNSV_6Ns>zK2r&lm;FRypc{N9w|T5u8Y}fq)U7N$yd4#ip8%E$`Mld2FYPW+}daY^Y|VQ+Kt6 z&rFKECL2PsPJ`)^y5e!Lx2vsRvK)IcoPUH8FR?hAps@uMcPR~WFAYvlPc`m>Li{|X zX^qVM2;z@NB6=+tc-?GG18;M-q5@{{N=4Q6rz=U_WyF{PBY}rr+@_t}%9#>P4EGHG z?nXCaFqWJV3=wj`3t{}klfY|ejAx#I7uEavSjH+Json4&CP7O%TtfH>9?R%1^PKjcP2Zh7%*-F|8CNmq;_xjMmZ_K4Q33~O(zs>U< z|FV>2loVdsfU1KXp*8}YjS#%C6ifnE{J?9Pyk27p#eUC|tFJnig|Ye-_&OX;fBQb& za&6Zs>utTl;lYO*qRWZ_pzGBC4seM+;T^2MM8HsPDu@28j|cYJ{ZRN??hfaZm?kjn z`5EOs8KeDKf5>g#$Qo^@6^U)uq51VBqan$JZ8EH9D1dNGckC9FB#RE+Kq$G(8^V5` zCYnP}Cb*(Z-UMT!@)#&6kX|L2d4Y2$n#pYy&E8mpfmp%RanaDVZfJGb-1pbgUwpzE z#2+b{#NPLM2fgC7w&h6OtKk4kn^g!Y-4*jpi8*E}1yqmA=@Fub4H{7cp(iU)j5C&I zS(iS{>z;)TM)(j=3}lQqp89;RTolpf>K0;`1I*qPc?YjAlwG^^q3t1uLBLbaXbaGS zJ^~N<&L~7GUC+z`FBX`6g1?I7ovY}Zo~_a?054s)-*(pS3_5|wK!emmgP((+5lp!MxubNp-m$QQp(5Ww(>_zw{{iC;Xs;E zhpWPtaUGanEDCdvYZSVDkS8B3;?5x#L$CTp1zZ`K+1Xz*6ZjgZ$+%Xr_KMnJ8w#U> zCmb7(V8e)ny^bg5*wyf^D{5-SXI%D~=%I?6+@-3UD#o3*s^*iCMQdZmi#QJz7Xv%L z{1e2xpuqx``uY%06iT4Qz7jAM#KzQe_}czK^BO)thu-1mNtO6r*76hRvLMxZq&K|L zq0LqHc=3G?otX27+81C-k_eeWHrL2!a_q6Y390!ZJpd?P76yN5V<5&PAq8d7S8ez~ z+_8z;qC~s^QqWDuM-Ma6sk%(O5r?2QGkgAwWSqsH9Mk?rhj)g~opjEj>9HIZ$1SiE z{s!bgxeJ|w*{k^g>A?!cX(gnh^T;z?5Dll6!vV#Exm z8k!XR*AyCZHh1odZ^D%VCmECC)82LS$4s?*y6mm_DhLCZ(y}!B-ppZmg*`ofdm`b@ zfy=pYzW5sJiWx4YSE-@vRA|(xw72%)(|K;)TsVTKMhwZ4pdBbEqI6&ASxNoeYUf@2 zq6Yz6A46Y%9>3W4IlYNkmR=aIB7kfV-EeLbZh@eTIVZ@Otqya2WlvBx3X`_J|E#^Csb~V@uDGExbcZ+)gNgR#iK>BNG6m)7^OL@Y zHFS}u@Mhs86cjF3(jV{e1Ih5aNqG*Jl1IMc<;+v^j3pE7UvVTm>Xj_Zo8u>RA7JvM zwn2C+b1>QhQ1V@XzM%2*UvL^Kf5(fG>EQkWME~!CZ_5Kw)5|^JvxGnwMgIu%@A0Wbu*KHu+eL`Z*C0QC8 zG1d7BXJgNG=+s&Y^xp;;8yPTT zJjqm4tAtKe4Km7C2b5FvDgX^TaZkVNUc^82EDPT#@#2ovTNHyRU zk+TYML=bSjW@~Rg8LqG|WmrSIOq@S`KdF1VDFjYUFeau^D?@!Wqz#-cQdi%vi_1M^ zpU?SY8?_VZ2r~zwAcwvx5nb`WvaGs2&EwrlobXfstf9n(M<{uQw9J!X&_Pvx?_!Dv zE~Oa`UhQ0duNYl|Dr9!|m8Q^ZiJaqtx3}Q}uZ;B>P@Kdmma%zW^yiS-;0H-DwDo|g zEu{goxb5_Oelez#de2yT%i!YQVf}wU>shiw9=lr5+r~w!a{Lx!xH^a>D45kFh0t9E z#)W6-$Qhg+Vm=s`49KbrdI(*2=b6lo(yJbX#iKKHwqb)ny|Z?NDT9@vUU**On*W2Z=m^|N-RdcN zo4~Zo=QwS;iQK`NbRVS6%Tv184Yv>HbxR&W@rau!EAc@vlX-JMQF1ly-#ICjy!&>E zwPH*}Ok8N-jrz`-bYufbn8z%vQp`2LOp^Zp{o{__nHOYmmg7sgm$AaqlZRWFYx)YYEk`Z-qQbVKXq7lwi&xNUp zP?d@~$f`_D9Xm>Ualzs&BLam_d1z?5Ea-bp!j&z}s$%XZo-h94{AH_;LcfZuj}eHh z{D`(iT+_wTTrXYHWCg#(@VV4ASU1U79L;Jdy%(C1sEC{mCv)rRzQ_Lt6bU#;>VY&F zVbPT@1@9ySy}3xRYd#VwvP>2a;7X-MVhZfd^)tE15xPIQ2eXS45HFpMbQp{$flq;n zoln22pw<}b9*BEGA6OMs;uZ1phiAl$Z>Zt>;>Wu@Ph4%Ku+ButfIF_W^devqRXTiY z&HP2u920oLM#d$;5tBTD;P;a?t`->vlCpB!0Wi^CemC~EXN{UJw^_X|;uE+C;obWrQ%9;B=Y=6srkFM7`av z_P?vR{ekDtwGGdK`z&#NhBdL%&MQ(CzrMh|<2NNooSs<+>b{K1U||~SQ*4i|Tt4`d zoZKLVD5D1d?Kj4IKl;0P^g9>0I`>@zh)N-&``#kRSpR zDqSxp2XD8nHHgHTbfueD9Xl zmb`8bn*=WgK%H3OxZ*40!TzmGXBC|S``#-}YehBmk>H=n6TY$};3N{#t7n?|*$6!8vLWHk$)z~%% zA9UhG_LZ+Z^&?w3S+!LPs0>C)kc}a}Pr43IBgJp%kQU)cssK|hE1dOtQHoTACL-%6jnh~jC2|H9d|I6Ruc2kYq*m^C`>Bd$vu0^{ zV&`d6sc!lVw1NZX!k`*t1(+gx9%UY>TFiz7lT(lx6>124bP0mrY!x4s%EdK-!#}F8aQzRYA`tT%->g#7gnh@B@$E zO?`nR6EeX@Dm7~pfjJ6~VFED6GX@-#xAJKqNofglJC0IRpHgR73NaKP8)~W$Ww{$a zbG&=%nKS=MHKy~p9q2Rav$^APSuy|kbW`Of1(Tgv~o z<;4NAood3SO=e+pkX&&@TBj8u12i)qI4U^GwRVSR5%;R&V@EN1u$0dEEH6mfWP zFgZmTkv9CeqLM~-GToCv`dorlzivx-`75${})oyQI(K znWFFg6py2-n6Ri3Z4t3!)^AREl$kPevuNnyJI^CX0Us?3#YP{M*W=3XVCzvisrENw z0{oCH(g?)ZCYQ9VxQ(@$bhmZ~^RE(U{tP-Eo)S-VgrFpt^>7BZ6zWCu#t7ALJ#b1b zg)lVq{U#(a&SMG=!X6-5pDFIr6&z~c*mRi zAmSlP26CekUt+~@a18~(OxUX9Nn8e+)jQ1zdHj6hsH~sB4!?6l!>1bJqpy0?1k4AN zg)df4w?JpbZLX~8CuLB5#C-y%m;w3@7LrPvwvij*vfLv0$e+ajsk;@&x(xEdnw;y3 zlnwkkcX@I;!MnZFgEv&umYX2=Y=JqL@`@$5d+ga|YDFqbkO)pDQ&Sag{$9s`?uiBo+MR5mmh*&|co@tn zY>0QmQ|0!QG`k%|Hu*}JX`_TUrH}b`B4KB+lPkLQ+@4=v2D47j9xciJ%4b_5x|nir zo_G&^kSOV*XF}~E?&gFK<3S2lkGFx?t>vU2&h3>O69PbE(R zy%~vB(Ets%T_2wGysVBS5-q|sQPeWGJ!j34ZeQpkO*(KPoX6(k!#Q%m=mnY}NzMuf zHco@@wUUfxFxHSq6PARvpS2K!mj6G>@3fwVj-;+?=+JlFSuAzkGhtEi6flo{f%Br-x9Z0#LB(g00oFy?L z^9Tb}T0|l;c-)Z5kn5^@b+{9|Cm5e?gQ;9H2pG;p2&oO?s;H0TS}A)qely}`41^~U z-BIBPyEryTA!wDxD4(H#IRr{tBaV)>kf7efrEX}Izz zHe29@6ig$XDd~0TSn`Z&m%Oo$XZu%7{e23PFE54#`G&TRv}zdX)Q|zWbQS@-J`%cy zua=vQjuN^({b`fsWCK}eNZY`kNv`z`0|3w-2@VCAmP;{g_l{<;{~aXtE}2B?863g) zCc7^;jrj+F0iH3)siH2y#SbOtKijNw_i=2n1S>88T?k3NA9GhmU0uk8^h&6bD*O+X z>!g&`S!e=c>mx$>_S_O8g*}^{6}t?7tInCF(c95`vQsLT-q81fr)E_xJ7lC_MwBgz zidge1lry(_0_%dw{NIK7v>>+z#HYw(NZB$}L}ERmN5E8uLvYnK(e-Fe{0ln9->kL# z`#N-qstHn;h;ddI6=UkF=kt926BBQ{Dv!81hlo5F)del!JtV+q%GFprL(Q{ zZ6jeZo$E}fBiEinaB4ezJINAI*t;N5pkM%DJvQBxJX}?~ggO&UlboL_cnq-La*QU$ zM*t538b(V2i9Ao7vMPh=L>~RQl+g z&ot3nS@pOn6;%>)t`}vPnnFsb65_N0yV_c@F8o6#(k8z-nm4GF*HKb!NT=~b@{;-% z5z!wrD0B6V!Y7km_advfB!2}uUj-`)2S-WDniMN^qhKN+iUfKyDL=xPtV=)(#loeK z0OA?fxw*X^o&nnz4R>{r$1Vy(RuW~Ke6_6tbALkx)LSFHsYiY-L(T?|I#y0k3B4b7 zj{w9dxIGVW6&oa)6KPLJSg^NFfE+B|2Yxn1KsBVh_sVC+%lTNdYJ;wRZrlJ*OJiK| zGqsy@lrfo1lV&Hf=C?Vcxri<&tRjz-(8wrlc!zJPZ2jGf2nsBxZbrSdTR!-4HTol) zJTD5o11J;&uxBd=#9Xv*lufz__&M{iXc)#3#yY9)*8%V!0oHeqA2H3R`mjlT^9>G~ zU=>4QVv@P4)>u`u1|S{b?GH{bo%ENstV|q4$iz>GMiL7LzF{yLmUz*KE6C&U4=AlL z1!wc*jKj7T;B%krIgLq|MK&C|OIZ_3scs@~6k23aGm+|fMPm<#GS|b_6rMI7w~5GX zHhqlRdmWu%fbEIk$8=z{tOSP9KSdB-5YVNTj3R1M`LqN%_^ckKXt zMqyGcjc|ra{fkN_AwV4!(Ke}vtA5_t`x_vyvx1)UGqa%~L9o{vku5~0Jq;hfE@SrH zj*qYN5O_OlWt)FHd?hwV?aODV&G$?GS|I1QsnG-!vMLv5yy(j{J1GyRsv1Gu=raczj#X z+2cvDBLWzUo`4rVDz_LfsUnaQadIq?vQ?T=?fl51$Y->;bE5Yge9ZWu@pJThTs*(p zc0}{8gJ1vsyRiN`rZ)+B7KFclX-R8{KSr8vhN>H>W<4Mv@LRo*mD2pH{DN?uXp}Y4 zWuireikx5ms<4%#VzQ-~c3@Eo`Q-#dFEw4wN5BppRlNRrwGbN-(87kSq0tx4)0Z}y z*oU`l^t-jN>j_cPFHDqCxx26+bt04gAeWAT^g`Z;Jhhi;2_hg6;tE(;o>it_z!7~O zzgv%VVV9DZ=znE0^Sa+;={Y|E%Qb}&LgUeVJk@gQ1s@3mQ(BE8qc-;)0aQyB?_)z1 zf3Vx1RgQR7#TA-|lMJ&%Hm*U>SwU-+&>G^Y0+7fz&EA=o;_u_Y$pPBS=a8&@l z1^wFm!|~<6=$EKx{yo=iN<kXFSg1?sXpbj{ZqZ6tCpq)|OJR=bcrGzs zHh~89!$_WglbxJbULaeXAE6Wo&{by5_ddha>OF}!@HcA#6!BgjASHMu2Ss)Uy0grVt~()l?X|N@sH+nnRS$F53mi40Fg7>;WB8hV~1Sy_uXEw);}0T{KTf9 zRZ0TFmlgWLQjkd~1J@-Ok#G&JNja}V&x32=@&Z{C6e*g?(GbAkm0bj?R1MW+|4w9I zoGos{NluhtWo35OKyBiE=@yeTX4cqmlDHyDZW52R80TJXqZk>Mwpp-9RnpG%&=lI9 z;a}TQ_$K2Ff|VKsU#XbaW>>l|(F2QK@ibrd;*v5=3|kc9XeZxLkuYW2cjnY7SUS4r z#BeBdv!Ix##XewP3ONQo;SOTGlDIq55G8Q~=A8S#8Z<*cJ91fXPJE;XEUTJ!nhhj0 zGeaOj{!(uVVk+_XWXy9=(fLch`eb&npK=U#lRSc%x}lV=XlVo6Mc6gXbG0F?q94pq(z8op^{ z1oPJv8+*VdX@TP=)1mbnk$dr~`A2I0={G;Pz>DQ0fE3&^O%7wW>}0%A=5VIkb~_Q= z-pAq5MW@(7h({SdbuGo-3RR${W*wyR&u3WvbV0;+K7FpZY#O6JK*Bv_5|%V~a*?wz zGYs!gS$}21Tm-3*0Nsr>)$KVK9}Ye`7N+9X(arq}2U4H>hBwRlQJAz@Nz88y+)+t0 z4NuU5n1CdvP8>`mc|aPWs19m;0G6dx4{;9jQi|=r1Lz5&j-#oZHZUx-0A0mL;Ptkm z&1@dZ>5;*@>$_ogT z*r2jp)|TfHEL?$DUi;V?XE4&XRl za0?3V@7=^Gmp7k&Q);dAy8P>?Sb<-8;INei=#?9IztCU_8$lkv;n|18p%N#lHRqY1 zh zLf?rIL6~!OIG>Cb1X?N;9Y`<^`mA?3%$*xSu=&^iy!tp~xMGF0l1Ro43MKz;djt<@ zj=8n_BhUjqTLrP&U~ppyG6}4Pxtdp;k#Voo$BsyR`Ypo?3g`keHvx3iL*xR?24p;w zsi;3YAOG zUljG&IexL7k@vdz&CZzPQQ-P}Wzl`I`OosQ2Q{m3(ttSyGMhjqION1GR$O-0NU&O` zqx!qUDc&?7hIJ={I|VBVp0mAbc3TewQk>E9E}xXU~Fb5ii6 zk0ZU=%jLyJrTHrJ<9Rt_Rk8CD3f}_+qnKsG(_l2@H&{)Ub|u{8s=HsWC&NC8GQe9L z9MGUS_eFn+gs$RGu5z)oFtxGFqM!~R&%U$9X6@fhwhPf?B7W@}a`wwRsLYQiY2H4G z%s_ba0+Zv3-06q#e6)9K%pmY0fO05OthE&p^WTB>7!jQgox`KDamlU@2scLH zBlq4H%Z>J*SCUPeU$A;q+C);9+ABTb`xC>&9#`VKq>`(SitTs%N8xnv0WKEVohsC5 z5Sb0;9x&kfhp6HQ7SrReoVETTi{fT`vH&>cmH>}z3Pn~4=e@$;bxu-c_pL0^qfflp z5MH(U_|1TlamydHjwe8-kXE7G%p5$rL4sl|YiTs&vzbK*KA91sz%!K#`gpNdz^2quNo$2>v%Y!)+QzIT|M2-LzVdA^~*#fuPnNHt4}bZY~&Kt-wYoDexOSI z3g!FVC&r!&m-lalfW1+;r+-*B54eUC{G8T8sio_vse?BCCMGJICh=m&d|{Ct(!?i z%ftQ6HW1KH@tSqkuf{lV;n{a^o^?avl@H)u(BMB&*^v3+O`vcJF0y&aZz!XC(rRG^TpJRR6j+S=HF!e)oirC*R zoN3!^{$zp*C0@(M1%(;>p7bxg?_z$80)~v4rh=$Gz2{6S%7-gx-uJ()xha8glF zFuKcW!?e;&v(arH0*KQ3Dx!y)r+?Qg^>|16gn4aXw`gZ%d<(V>GiF=Du}xvIx4~DM zf)Xb_RmQ(B(s*;wCIWb}@Pc_w7?jg!g3<6&0NqrNh)p+7v6qfj^{FfWmp?x??oo$j zRzy)_Z-*%Uqu(ncaBRiakH%_n2FEUb7FuL>I{LUq)_tow!{kx}g;p2Z%Mm3)#X;iH z3eA>ZW~98oU6?B-`!QY+&pgt&ia|}kmKysmOM?tH5*~yf>;yxSNZ*o?@png!NRz?{ zrwcrb&$`Ya!iTfSvu^2_eq1XcOOb2dXQD4%f~2{$XA8s*%faKh4{rmbqR#9jV&Yq; z791}B>3)WgkyOIVH8k_t$i5pj6&?T))--VQf*);`pTB(PS-;OSHJ;zJY1^j75g{k6 zJf;BVJAzFX@SxqPUBNXE8Nk-Hjt@S`+dz`na&pe-5EB!GJ^xqqv!Gj?;B~#fK44A$ z&^0A*w0-3N&<0u#8Mq)7POZ@J8Tu7m?Fkhbt`O_`G_b+xpy8jslAjytw_v=VZe)-D&G#`u5u``C9 zP2fw)v{!=n?}SgdvO5HUS%7%Mz}T#tj=mKaz^&)V?h>45ATh_Tr#xJ=oML3LHT28! zPUWxxoAtz1E}iOY%=HznKzPygM7ogSIyM>K^Uw;|HVA9xMZ~<9>ZrofNW%(Y{SOi# zK~NU^U6}IZ&+Ud%7Hd6tH6tKs(RR3|>vo&71pCxW_cQj=HYV-YFR`14WTNgFQ42ac z^s&JS;5f8~_4)kiCx$CgD#+75J~2A?41@LBwL&v_|D1UJ2cniW*+ZATl4TH&1%p*7 zj4+o8_)LFqt)mStbBD|SzOhtPnw^nF@f$N(T{fRzZc&L#cF>{ zyx+ZX0x13?fF!~UXAfsT4$jW7v|K<*{Ef%#Up2b$hBo)#*$KKu*>2mq)R3DR^I~8i zSJ#6|J|yz1k{;y3Bm$N9;uiIgw8}fv7{t{oVGq*Xn%8BN+j8KnWyp8!e5E2`n7suTfD0VCPOXKI!5Kw5!dNSVg;>eJ{KH5^VM z#uA@E!}nw9pg9V+)%&#T#A8+RL9u!N#XIuPztYkVyH|Ws$hBW>;O~MghM6RPlUD5S&C8Ivrj zrD~sX(bUeYI673yFho@1ozNS2-Z)%}S12F*5gfh~*jx^}y)Jo1JDAOr*2~F17L&9% z_;KPFYWl&M(s4?ihf@oMi?YAxk7B$cvHah{J=aNSOn>G{?pmZ!uj!xs{3j3MfV1L> zXBWcYtoXFlKP-1sF6L&hI4V9rO2s&mWO!3uAC>XC##}y>ay#*l(5;&FAlHmW0Dh6o zQxFO0r^)5KgDdq0%sspiV7|_P^E?cKSAi>G*@-|-BlbPmt)Y+%8l=2;N-SQF+y}6= zS>Q6p{a{a4Jy2@6E&GcG9wUV&&PczD7E(OrIdwX+M+hzzKX2csahAfk^Cm-+n#dN_ zakL_K@9n-k0i42-uaS?(0IGd?9kjZ)5dp8F1RW3q%ahVID@qgpt(cf1Yic) zZdHflS$wvyrYZHVTPEMz-zf~5Wx(G%6jCPx?YfMmemXmpi);FW$lZX-Wt7_)kgd}* zt&)PnV2aV^cF<2T`TnVljsh9Og1c(|GMFa{8?Olow_nvg--=jE9jU1H&l{lTU#K7^ zZs>Us!Qcy-RwREqvu_&uf5{zzS~w&|)l@D!hw!lyTgqnKqN)S6S5+(TB%w=dugXOk zQ#>-`*K%a*G(EihQBognE*aviw}$Ki8)$MHneXDh%lt$g0dh6VQHpJogQ*18#P~+b ze>XBof-Yp=r~8~f%`oq5d_@0?-81m&a+v!~(gHK<0abUt3e21pf#Y^D5Q#SSk{)(Y z7gb=X%caDBSRB}6j#&p}FdpPfAfaXhN~>gWfb_(00o&ljVuHTx*crpK?X88=@=UbT ze^rxgo-G}M2-T(ZWuyD1$mPpYmF%}45dOUv$w1;BLL7H5v#Ee4JYUFm+C?Da_!dqX z%yUYhEZY0{>A`5=^2lqyt9}oHmAF9aAIc=hw9Wk?>Zoqn;h~LOy?KoPvjWf|qtQ{{ zI6%PAxpYp?q6IQ4b^>VT3WrscOHW%ypOq+bpUaT60lyi0 zYiw<+wr)bgpY2tt+Y-o){|Z+=F{C^h-$1DvTALU__j=6&0`0KIaWiC$~cE?>FYFtlFU(tUU( z$#lR`NZ+|ssfxa>cQTg6qy{dl2O7kdov7}Ssdt)FFljwL&iPuse-zcp1@t1D3fvgJ zAegYxsdrM(bL8s?ENcW=Te@L_NnBRYkx2_$2=92nFP^jj(inHLxEI$QrzAsy8`*My zNm(wV_a_o5?~c;q`z{TO9=fr=zUX02rERam(p4ja&+x*qTa0uDUh*^K65S%rTnJEU zOgaNGJm}5EXcCx*F0%@+hN9O*v!g#Q?uU?9hm$C>eVq!9=~&-Q+eLHz9(?iRV#o83C4m7m$D@m~dmenTUd@Bs?MSvm$SSAV>)o-~2eeG_ClPss zF8xa%x7Q>Gq(!OGE~Yj%Wls~TM6|7#kN#mbvNW&J1B~e*BDuf-Y*kA>&Sz*vH%LC% zZ(g>dJ{Osdd4trHjsM#lvbrdksHD1ap&Yjnlh1MjW6`@V@-OmAW0Sl>;7dN!TM9!Z z?T-sY-@*gc{7LvTZ#twox{cl^BqmZ)EmCVNeF~kt|90`1I$# z*}LFY^&h3JjZ>{_oMzS0t_^sc(Q(mYT?(SA^wfwK=u;Bx!T+<)iU<(y2DaCiRR${x zPgNfNK&M>YF4FO`_12dyzW4dMnJfItuVz~B75_KE2AC%Wl6uB+xfYfu=t+x!r5^Jd z0i|$an8JviYTFMKXB2F1uOG^!qD?}(S;}ZAEi$JppiY@zD;?Txx~~xXl)hg)yUj43 zDkb^V!O_2P(=n6Y7zy^~39K9#I4Zq7NrCGugyH4`Dvq^01VfaFucz=i zHN#koolkK}M_w>K7~$}252L&FBOY)Qy_G3Tw-jR}Q_$)4%;g%|fQO)L1INCU(p2b6adR$-k{Q9+oGODqlH1T!g_b*?$dN*!K>|rd=n%$(H@1?0FOkC+70a%R{ z2keiPeAO9vt)ut|^}!k{+ppz@j~%)WeT1*mjEd_Fb;!GEHW3}(G`zB87h|`J2=wOR zI#e%|Pv^FpE_z{=g%OsCa@&9~hoECa^qvCJb~_jjc_C zRmnbROsOAHNwbhOK6w?$2A1dZNorR;YgJ+(NCh{?@gTO#c4as1+A+AuEkf*~gBq+n zO^1BMfet_l3II~Cn?l~N(QWwC39!{W@ zfrwg?tHXaItyE)~&GxIzlYT$gy)dl|3>!^b-gald(*Lf^GR%j^F_t$t?X{QJ z$|5+e%dqslE<aWZ#@Cq4X%OE|zDyiz9sutZyO6h=*%<6qLMR_KoCu@=cw`|30YZP?L3L8b-F0#ZsObP)Zn^v!y$$d?zHHnx@hiOD$Ej^>(Z6)dQ(3yNxG|VnMVEhIjE2 zOmK4;xOENlg{77^BE5V>vmzGhe@hQ|HT}Qu0Tl622qxC%bgSDNQW4iOxZhl30uje( z`D?Xb#+r8XkF_LM%cK})0}6e1^I2FPwRShPWVxKvwEU`q6}8tDli7asqNBuQ@AIJ( zd8%P&iwNF)-tB~HtMdRn!`pyf#h zxymeoQEZ;p?M6cu8s=ynm^cBH(cc;P1AK!Q(gS2{b^%kU#O^b)I3gqiN8(j|lW(Q} zKlefNuebtd-4jSu6ee6WaWW}`_YK&KOhSJTYtEZn5$+bUfKy@Az5gkT+xwXPGP7Db z$_FjsUs)BFKcxS*zfx~c(yNENFZsaVfW(x=YuP*zh>3kqzU?ps438`Zf0KXXRb6FZ z&loIL!{J*H*RKlu*i7{^j1a>lu?0f%iiIVqXFm!P?^qHZWdKi>C>;Ir^viTXs}`{h zo(#@^_x=CJVuJS3BBWZ9+c$iGo5UlOA>CDrlFp%sfYZn>cTGvv#FtDP7u(p2Fpm~$_)U!_&D5rk|X`9+=# zT{^{&h)^!SSu?>D{;ToHYnN8z_H-ui#)I`ep_+wPC2Y#Dp@Jt|}yW68ctb|P6umO+L) zlzrc+8&Qm%WXqnN?CV&w?-3&VQkh8z&ow>I>-qJ1e%t&>5G9YG`G!696|QSQLaPL-<2=9-rSIlG+lx?Set0a}d#&9@Pn- zjQC}sn@R&)EK$q7A^n&flB1igRHMz*^NDtiQ=U!e<>BJFk@>s@-xaxI9e2Ib_>Xrd zx&xlv()RDM^~F6dPGGa+VlBX_Cy&2Wsa5y%)0b+mCPSay+EN)T27C8`Q4vw$Yb_xa zcVj9lu_DWt(E2u8MxgcMv8Z=>LCWNoLCPj$42U(Qq=XDRC53x%M#o+pgXIsF1~Dtx zhifT?6jkI;h^Mdk%JDGudk0iHd0`A_;W;4YMCWj?< zHp`{w8yXp6bMNj)+7hV*5l`SQNNYMel{n+r@iBvL+|BK#QOO+Nv%>IEp=;JTWv1>Zy2z=cA{`z`Rhe|W7y zzXw5Z)}lJIR5oD>m?GX=pr4ZqsKGTDy+h88-nlH^MDrPZS1 zp@i&gYV!ojXl{40UWwWi(!~R+&M$Y3jd`wc>m&doX`c}S&`yn8o>+>^Wn~6lA9Og}gWvIIAt}MRdxm3n- zV?X-*6&Pky_U*kigbvR!Jb{YvHrGb!6AKpLR?2a1g$cJe2`!Vg@xMg?$KJMssSL`1 z=2b2eI&Rzy=d;~@B!zUEf390RA!S@RLW#$YNBn~)8K~EBQ;Wu00|N&js=-Gc#4C92 z%^By`X}i(e{DQ=Jj6?WzheY@a#=~lu4?K+wKh755Zx5uxgoO#F5ML9}yo?W?g6hh4wfNQA2A&)D2va`%Wa0aRB zv7CndeA2;jB^vhFJZ9wat`TgfEXsC0qfV4d8xNk;ZXepl~stAmUgXU2k6k^ zz`@E0N3#bFtGW$PO=g5&&F#!LoqK=YpCI}uaV|+Qbd^OaO1*A0B$xT3zzF7%Hv~NcYwx0yWt`E`FtQVd9{_bl zXa(9Sa1$r`gYfS6{X9A_Fta_)X6B!i zSd6Tf9HvTA{6H@w4o7qZ#FZuF^U2FOSGTrGmhI4)|4t(R|HGI6!P%$#DA$pw%)sB!Q?c_kv%Uo?|8$bkB0Vr(zvdyAs``0 zn7E@7`?;_(^b>{D9S$VYk?3#D%JarOFXl#1?4@3FeIiZ!q8Jo=2^tu-)KOv#PBnZB zysqIgfYrCvQDP`h?Xm$+)yfLM$iaBx|GY3jfk;mEbW3MT{It3SA7ZuQu-%p-EIex& zN``}L#sgTPIuQR)Cg^lyg=b@oGRS+~#gZF`*`-*_z078{$Ypon-e-BSq6=_$J=5nI zP>1MI6C^7(*;)`=Q^;sDl8LK-q7@Xdm5nE@AJGi42ymlFiYJ!cwnQatpL+qSOni2P zazJ&D)tbA4uAVd4QssDFJ;TFyLHgEdNO)Qfg{Lp!Ed;XeVMDs&e!E1@dBTEM&Mci? z{v+#lRg;ZkJ3DF6|BjRUt%Ce4snFof943^N*H5cn5xug?5&e%_iG}lmEQuSp{(^=V zDT9uSDt%2hz7*B1A`VuMcle>iV}Ku*0Ma&NoOtPIh5bBvPT-2qdo2Ay2wRWPM)-Uu z$r@WmVBTPw1&8dR%W@u*=8=DL?&br1lFUu&$_R;^)9AqFOtKgJU*&i7h7&gLjMfExI z&r!!xRf%JK>C-W#45FG83BNqKEb3^)j~i~04rVD;?lo)|-A~9iT$rzTtgIz2@V?)= zA%DLmgG<&!+B9F=qNL%vdrEAan|1^%jMy$AxU84DthkaBm%on$;UZ|zQl%rO9xWb& z=F13b23yRz7yQ)Yh6d_%^Cp2<%gm8*e^cW*QsmvY{&CyJU2#;$#hsU<{e{X?;4C=Y zkc}6Sn((?iMwrJnRIA`&=+zXG(S|}T%`jF;6z`=$ps$-inaX>w#7x_^hlKa{PVO?c z5Q&2S>#`(7^#@-MJf=105}HoNJfE#=fCK`uZN^IVf#H6F+!<~!cb~SuYl1HQ@P-wn z`F`oHffjkqVKvK6+Wqh9BTTbNypOtLR|^nAFT$fpurevR)U+tiRAFsC@ycz8BebbZLL z-6?g1LMK?USp%zXdTPfAkWE1imSa8yn$PX5(u$HCVbouOm)IGeK5n%i<-|pVd7xnU z1_Z^XlJf;ePN{cXZ~UW!Q@Tny|Dy0lH#|O^;rOLd>0EE^2PZyIp=rcy)kOZ)^c7vp zg~nblxH4HaK}NIToHebOE?PDtD^uu|x$QMU5oPplZR=!pZktUXq4i>SF4%$RMqdVO z?q*n)Eo|C$N(iGyyul}uu_vF9!?tC!_OMo2c3-GUu>Z*}*M~EQG&RI`Du49cS^~vi zGejdg=gq3)HC6^6e38j=m__TiSs%i)r^FhQxz;|pYM*vK)8Wi{J{kM{g_bWnb~D!h z+M?Q(PSNA_@@h{S|MNs0xhU*tSEeI!nELckZ`{?Jy&z3PjTF`&hvMQsr$Xj7>L`u$ zI}|+ITy_W#2VSxO{(bgf7`F6xl?_nIjEF;Xv&uY4B6i{xnA%S?raD`<4*bpOx3BzuEJuD<)Yo4=D3~L{VRHWb0?Ke*P}U$;Jj9zutHvSGw% zr7Sl5vo!7|{csU}+h~c7K2iZKM6jbrx5M`mFMn3FNZHY($4he}rXa{5pHIA+yrnT- z#`Os1qneR6+=q2+yZWKpY6}0v9`-|=Hp5YFZ4`geRV?sLgR&jP$-3P+WoN6*ApEV3 z2ddgmh{~ZTO}oW2QF|eEt30-7zqoLD`9KjqE|?In&jnN}oTx8LGLbb2;7MzCqVOp6Wr^PsF16YBoeCF6O+v)-WMylTp+dFZjT= zjRp%FXl1r*l6QOVmhhNUs4gh>$~g}X8m5&lp$|HwV2sip>HcAsVt3rxMddWA2Y)d! zf6E#E&r$#)T>qjQ%djDzTFZJ}U6x2Zs_R}L0!`M8TxdYydb`EzYyywR$sH1A6>t}Q zQWUp3yv~m@Jtxongx~Ht1aAv(cuWW|Vx%P^C9K#CMfvo32)-&Expa6|PA}@BBwfKt zKtc8q0unMB%WPAwe0zLE--@by^F@}>uj_qbT$(4Wx5mIHd2vW42dd&(rg9LI=MVu? zDj6y_c3x4M0QX82$UnTRvcC+}1beqdRy9vUa? z#9qAiwfVN)4m%UH$TIw?bDwH6MCfM{DI=`QV`!-p|3E<&{&7g&$3CG?B@w5{0HOYW zErVmxd&@bwk(PvplV3U}+3^S#w99axLIFgZ;d8lTo>>3~8q_vXSARCCY(9IUpAa%6HC&NJ)E9UntI@_3~kF| z-$=HW&?Q9fG!^PN=gs0omZHQLD~zple>&O#@0%q|+4d5G2nx;+l+0xk#&U%o$LXP=a9lkICtqJn{0nu@r~;|59xMgj;`otmkn1n((4f02?iLh4 ze19(JZlMp`Mu3JUpKWg`*XKnrBI;dfKBzMW=AK{2wAOD$p0zfM*gfVX?)*~c`K$}w zahp%^Vj>ChBC?E@#_Zh`d)5<_@l0WBhkRVCiz){yU8Vkpyux)=1Be}o zYnJtKd>BTj_#HbtkNse1=gH7vjBD|aL1D4NW->-bkx91hz5Q(63_Xqu!2;Age}DfN zKG)dIJSv8NW_D2$W{ZrTVX089+GH`su`!~P;?x)6+>x#=&v3B@cPQLHn%v9f-a^)wPwEJ7+wehtv{eDAURN2m#F9?<< zKW2-T(~+9Q>bwk_E`E(!7?PcFTh+UjVOvG+W;}ZTOeOdNep!UV*Ds@K_lmwu&Sx|# zEWP56yU*ce>Bx50bN+L4GSq_Y(y6<~(PumZ?qfcW4}Z%P-i<)zjm0B>!qd;A8$F%S z#nZzpQ@hMXcFJhJYxC%c=uSOz7TFaYzlBTt$9a-RF0JB5sqt3=Yt>giH3)KZx6<$) z$llJ;Cynf6i0$FUm#EW3d4VVS{m=i~4rTyCPvW__={A-LJvq_MS=^80vYOld{$vzc zwO7=G3~+wbsdg||B%Wqbg|-%WqfwZMVPO>Pi+)xmA(Yp1XV3e5Pnm^1a8iW#UJBhY zT_r*z59UZaZMd#ATsUDPUBn}6Z -Авторизация Твой ФФ!

Вы запросили изменение почты

diff --git a/auth_backend/templates/main_confirmation.html b/auth_backend/templates/main_confirmation.html index 9ed3b47e..a9984bcf 100644 --- a/auth_backend/templates/main_confirmation.html +++ b/auth_backend/templates/main_confirmation.html @@ -6,7 +6,7 @@ -Авторизация Твой ФФ!

Регистрация успешно пройдена!

diff --git a/auth_backend/templates/password_change_confirmation.html b/auth_backend/templates/password_change_confirmation.html index 7114e9b6..264a876b 100644 --- a/auth_backend/templates/password_change_confirmation.html +++ b/auth_backend/templates/password_change_confirmation.html @@ -6,7 +6,7 @@ -Смена пароля Твой ФФ!

Изменение пароля

diff --git a/auth_backend/templates/password_change_notification.html b/auth_backend/templates/password_change_notification.html index eafec0ee..697249c3 100644 --- a/auth_backend/templates/password_change_notification.html +++ b/auth_backend/templates/password_change_notification.html @@ -6,7 +6,7 @@ -Авторизация Твой ФФ!

Вы изменили пароль

diff --git a/auth_backend/utils/smtp.py b/auth_backend/utils/smtp.py index 069927ce..aa81c9fd 100644 --- a/auth_backend/utils/smtp.py +++ b/auth_backend/utils/smtp.py @@ -1,9 +1,22 @@ import smtplib +from email.mime.image import MIMEImage +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +from retrying import retry + from auth_backend.settings import get_settings settings = get_settings() +@retry( + stop_max_attempt_number=settings.MAX_RETRIES, + stop_max_delay=settings.STOP_MAX_DELAY, + wait_random_min=settings.WAIT_MIN, + wait_random_max=settings.WAIT_MAX, + retry_on_exception=lambda exc: isinstance(exc, smtplib.SMTPException), +) def send_confirmation_email(to_addr, link): from_addr = settings.EMAIL @@ -11,24 +24,35 @@ def send_confirmation_email(to_addr, link): tmp = f.read() tmp = tmp.replace("{{url}}", link) - BODY = "\r\n".join( - ( - f"From: {from_addr}", - f"To: {to_addr}", - "Subject: Подтверждение регистрации Твой ФФ!", - "Content-Type: text/html; charset=utf-8;", - "", - tmp, - ) - ) + with open("auth_backend/templates/image.png", 'rb') as f: + img = MIMEImage(f.read(), name="image.png") + + message = MIMEMultipart('related') + message['Subject'] = "Подтверждение регистрации Твой ФФ!" + message['From'] = from_addr + message['To'] = to_addr + + msgAlternative = MIMEMultipart('alternative') + message.attach(msgAlternative) - smtpObj = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) - smtpObj.starttls() - smtpObj.login(from_addr, settings.EMAIL_PASS) - smtpObj.sendmail(from_addr, [to_addr], BODY.encode('utf-8')) - smtpObj.quit() + text = MIMEText(tmp, "html") + msgAlternative.attach(text) + img.add_header('Content-ID', '
') + message.attach(img) + with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as smtp: + smtp.starttls() + smtp.login(settings.EMAIL, settings.EMAIL_PASS) + smtp.sendmail(settings.EMAIL, to_addr, message.as_string()) + +@retry( + stop_max_attempt_number=settings.MAX_RETRIES, + stop_max_delay=settings.STOP_MAX_DELAY, + wait_random_min=settings.WAIT_MIN, + wait_random_max=settings.WAIT_MAX, + retry_on_exception=lambda exc: isinstance(exc, smtplib.SMTPException), +) def send_reset_email(to_addr, link): from_addr = settings.EMAIL @@ -36,24 +60,35 @@ def send_reset_email(to_addr, link): tmp = f.read() tmp = tmp.replace("{{url}}", link) - BODY = "\r\n".join( - ( - f"From: {from_addr}", - f"To: {to_addr}", - "Subject: Подтверждение смены почты Твой ФФ!", - "Content-Type: text/html; charset=utf-8;", - "", - tmp, - ) - ) + with open("auth_backend/templates/image.png", 'rb') as f: + img = MIMEImage(f.read(), name="image.png") + + message = MIMEMultipart('related') + message['Subject'] = "Смена почты Твой ФФ!" + message['From'] = from_addr + message['To'] = to_addr + + msgAlternative = MIMEMultipart('alternative') + message.attach(msgAlternative) + + text = MIMEText(tmp, "html") + msgAlternative.attach(text) + img.add_header('Content-ID', '
') + message.attach(img) - smtpObj = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) - smtpObj.starttls() - smtpObj.login(from_addr, settings.EMAIL_PASS) - smtpObj.sendmail(from_addr, [to_addr], BODY.encode('utf-8')) - smtpObj.quit() + with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as smtp: + smtp.starttls() + smtp.login(settings.EMAIL, settings.EMAIL_PASS) + smtp.sendmail(settings.EMAIL, to_addr, message.as_string()) +@retry( + stop_max_attempt_number=settings.MAX_RETRIES, + stop_max_delay=settings.STOP_MAX_DELAY, + wait_random_min=settings.WAIT_MIN, + wait_random_max=settings.WAIT_MAX, + retry_on_exception=lambda exc: isinstance(exc, smtplib.SMTPException), +) def send_change_password_confirmation(to_addr, link): from_addr = settings.EMAIL @@ -61,43 +96,58 @@ def send_change_password_confirmation(to_addr, link): tmp = f.read() tmp = tmp.replace("{{url}}", link) - BODY = "\r\n".join( - ( - f"From: {from_addr}", - f"To: {to_addr}", - "Subject: Изменение пароля Твой ФФ!", - "Content-Type: text/html; charset=utf-8;", - "", - tmp, - ) - ) + with open("auth_backend/templates/image.png", 'rb') as f: + img = MIMEImage(f.read(), name="image.png") - smtpObj = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) - smtpObj.starttls() - smtpObj.login(from_addr, settings.EMAIL_PASS) - smtpObj.sendmail(from_addr, [to_addr], BODY.encode('utf-8')) - smtpObj.quit() + message = MIMEMultipart('related') + message['Subject'] = "Смена пароля Твой ФФ!" + message['From'] = from_addr + message['To'] = to_addr + msgAlternative = MIMEMultipart('alternative') + message.attach(msgAlternative) + text = MIMEText(tmp, "html") + msgAlternative.attach(text) + img.add_header('Content-ID', '
') + message.attach(img) + + with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as smtp: + smtp.starttls() + smtp.login(settings.EMAIL, settings.EMAIL_PASS) + smtp.sendmail(settings.EMAIL, to_addr, message.as_string()) + + +@retry( + stop_max_attempt_number=settings.MAX_RETRIES, + stop_max_delay=settings.STOP_MAX_DELAY, + wait_random_min=settings.WAIT_MIN, + wait_random_max=settings.WAIT_MAX, + retry_on_exception=lambda exc: isinstance(exc, smtplib.SMTPException), +) def send_changes_password_notification(to_addr): from_addr = settings.EMAIL with open("auth_backend/templates/password_change_notification.html") as f: tmp = f.read() - BODY = "\r\n".join( - ( - f"From: {from_addr}", - f"To: {to_addr}", - "Subject: Изменение пароля Твой ФФ!", - "Content-Type: text/html; charset=utf-8;", - "", - tmp, - ) - ) - - smtpObj = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) - smtpObj.starttls() - smtpObj.login(from_addr, settings.EMAIL_PASS) - smtpObj.sendmail(from_addr, [to_addr], BODY.encode('utf-8')) - smtpObj.quit() + with open("auth_backend/templates/image.png", 'rb') as f: + img = MIMEImage(f.read(), name="image.png") + + message = MIMEMultipart('related') + message['Subject'] = "Смена пароля Твой ФФ!" + message['From'] = from_addr + message['To'] = to_addr + + msgAlternative = MIMEMultipart('alternative') + message.attach(msgAlternative) + + text = MIMEText(tmp, "html") + msgAlternative.attach(text) + img.add_header('Content-ID', '
') + message.attach(img) + + with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as smtp: + smtp.starttls() + smtp.login(settings.EMAIL, settings.EMAIL_PASS) + smtp.sendmail(settings.EMAIL, to_addr, message.as_string()) diff --git a/tests/conftest.py b/tests/conftest.py index 838255e1..0c1a37a8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,13 +33,11 @@ def dbsession(): @pytest.fixture() def user_id(client: TestClient, dbsession): time = datetime.datetime.utcnow() - body = { - "email": f"user{time}@example.com", - "password": "string" - } + body = {"email": f"user{time}@example.com", "password": "string"} client.post("/email/registration", json=body) - db_user: AuthMethod = dbsession.query(AuthMethod).filter(AuthMethod.value == body['email'], - AuthMethod.param == 'email').one() + db_user: AuthMethod = ( + dbsession.query(AuthMethod).filter(AuthMethod.value == body['email'], AuthMethod.param == 'email').one() + ) yield db_user.user_id for row in dbsession.query(AuthMethod).filter(AuthMethod.user_id == db_user.user_id).all(): dbsession.delete(row) @@ -51,18 +49,22 @@ def user_id(client: TestClient, dbsession): def user(client: TestClient, dbsession): url = "/email/login" time = datetime.datetime.utcnow() - body = { - "email": f"user{time}@example.com", - "password": "string" - } + body = {"email": f"user{time}@example.com", "password": "string"} client.post("/email/registration", json=body) - db_user: AuthMethod = dbsession.query(AuthMethod).filter(AuthMethod.value == body['email'], - AuthMethod.param == 'email').one() + db_user: AuthMethod = ( + dbsession.query(AuthMethod).filter(AuthMethod.value == body['email'], AuthMethod.param == 'email').one() + ) response = client.post(url, json=body) assert response.status_code == status.HTTP_401_UNAUTHORIZED - token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == db_user.user_id, - AuthMethod.param == "confirmation_token", - AuthMethod.auth_method == "email").one() + token = ( + dbsession.query(AuthMethod) + .filter( + AuthMethod.user_id == db_user.user_id, + AuthMethod.param == "confirmation_token", + AuthMethod.auth_method == "email", + ) + .one() + ) response = client.get(f"/email/approve?token={token.value}") assert response.status_code == status.HTTP_200_OK response = client.post(url, json=body) diff --git a/tests/test_routes/test_change_email.py b/tests/test_routes/test_change_email.py index f67fca17..c5b6c6f2 100644 --- a/tests/test_routes/test_change_email.py +++ b/tests/test_routes/test_change_email.py @@ -9,17 +9,29 @@ def test_main_scenario(client: TestClient, dbsession: Session, user): user_id, body, login = user["user_id"], user["body"], user["login_json"] - conf_token_1 = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, - AuthMethod.param == "confirmation_token").one().value + conf_token_1 = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token") + .one() + .value + ) response = client.post(f"{url}request", json={"email": "changed@mail.com"}, headers={"token": login["token"]}) assert response.status_code == status.HTTP_200_OK - conf_token_2 = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, - AuthMethod.param == "confirmation_token").one().value + conf_token_2 = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token") + .one() + .value + ) assert conf_token_2 == conf_token_1 - tmp_token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, - AuthMethod.param == "tmp_email_confirmation_token").one().value + tmp_token = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.user_id == user_id, AuthMethod.param == "tmp_email_confirmation_token") + .one() + .value + ) assert not dbsession.query(UserSession).filter(UserSession.token == login["token"]).one().expired @@ -49,7 +61,7 @@ def test_invalid_jsons(client: TestClient, dbsession: Session, user): user_id, body, login = user["user_id"], user["body"], user["login_json"] response = client.post(f"{url}request", json={"email": "changed@mail.com"}, headers={"token": ""}) - assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY response = client.post(f"{url}request", json={"email": ""}, headers={"token": login["token"]}) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY diff --git a/tests/test_routes/test_change_password.py b/tests/test_routes/test_change_password.py index 72099edb..5cb2f862 100644 --- a/tests/test_routes/test_change_password.py +++ b/tests/test_routes/test_change_password.py @@ -8,17 +8,23 @@ def test_unprocessable_jsons_no_token(client: TestClient, dbsession: Session, user_id: int): - token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, - AuthMethod.param == "confirmation_token", - AuthMethod.auth_method == "email").one() + token = ( + dbsession.query(AuthMethod) + .filter( + AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == "email" + ) + .one() + ) response = client.get(f"/email/approve?token={token.value}") assert response.status_code == status.HTTP_200_OK response = client.post(f"{url}{user_id}/request", json={}) assert response.status_code == status.HTTP_200_OK - reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", - AuthMethod.param == "reset_token", - AuthMethod.user_id == user_id).one() + reset_token = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", AuthMethod.user_id == user_id) + .one() + ) assert reset_token response = client.post(f"{url}{user_id}", headers={"reset-token": reset_token.value}, json={"new_password": ""}) @@ -28,38 +34,49 @@ def test_unprocessable_jsons_no_token(client: TestClient, dbsession: Session, us assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY response = client.post(f"{url}{user_id}", headers={"reset-token": ""}, json={"new_password": "changed"}) - assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY def test_unprocessable_jsons_with_token(client: TestClient, dbsession: Session, user): user_id, body, response = user["user_id"], user["body"], user["login_json"] auth_token = response["token"] - response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, - json={"password": "", "new_password": "changed"}) + response = client.post( + f"{url}{user_id}/request", headers={"token": auth_token}, json={"password": "", "new_password": "changed"} + ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", headers={"token": ""}, - json={"password": "", "new_password": "changed"}) + response = client.post( + f"{url}{user_id}/request", headers={"token": ""}, json={"password": "", "new_password": "changed"} + ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", headers={"token": ""}, - json={"password": body["password"], "new_password": "changed"}) + response = client.post( + f"{url}{user_id}/request", headers={"token": ""}, json={"password": body["password"], "new_password": "changed"} + ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", headers={"token": ""}, - json={"password": body["password"], "new_password": ""}) + response = client.post( + f"{url}{user_id}/request", headers={"token": ""}, json={"password": body["password"], "new_password": ""} + ) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, - json={"password": body["password"], "new_password": "changed"}) + response = client.post( + f"{url}{user_id}/request", + headers={"token": auth_token}, + json={"password": body["password"], "new_password": "changed"}, + ) assert response.status_code == status.HTTP_200_OK def test_no_token(client: TestClient, dbsession: Session, user_id: str): - token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == user_id, - AuthMethod.param == "confirmation_token", - AuthMethod.auth_method == "email").one() + token = ( + dbsession.query(AuthMethod) + .filter( + AuthMethod.user_id == user_id, AuthMethod.param == "confirmation_token", AuthMethod.auth_method == "email" + ) + .one() + ) response = client.post(f"{url}{user_id}/request", json={}) assert response.status_code == status.HTTP_401_UNAUTHORIZED @@ -68,15 +85,21 @@ def test_no_token(client: TestClient, dbsession: Session, user_id: str): response = client.post(f"{url}{user_id}/request", json={}) assert response.status_code == status.HTTP_200_OK - reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", - AuthMethod.param == "reset_token", - AuthMethod.user_id == user_id).one() + reset_token = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", AuthMethod.user_id == user_id) + .one() + ) assert reset_token - response = client.post(f"{url}{user_id}", headers={"reset-token": reset_token.value+"x"}, json={"new_password": "changedstring2"}) + response = client.post( + f"{url}{user_id}", headers={"reset-token": reset_token.value + "x"}, json={"new_password": "changedstring2"} + ) assert response.status_code == status.HTTP_403_FORBIDDEN - response = client.post(f"{url}{user_id}", headers={"reset-token": reset_token.value}, json={"new_password": "changedstring2"}) + response = client.post( + f"{url}{user_id}", headers={"reset-token": reset_token.value}, json={"new_password": "changedstring2"} + ) assert response.status_code == status.HTTP_200_OK @@ -84,18 +107,27 @@ def test_with_token(client: TestClient, dbsession: Session, user): user_id, body, response = user["user_id"], user["body"], user["login_json"] auth_token = response["token"] - response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, - json={"password": "wrong", "new_password": "changed"}) + response = client.post( + f"{url}{user_id}/request", headers={"token": auth_token}, json={"password": "wrong", "new_password": "changed"} + ) assert response.status_code == status.HTTP_401_UNAUTHORIZED - response = client.post(f"{url}{user_id}/request", headers={"token": auth_token + "wrong"}, - json={"password": body["password"], "new_password": "changed"}) + response = client.post( + f"{url}{user_id}/request", + headers={"token": auth_token + "wrong"}, + json={"password": body["password"], "new_password": "changed"}, + ) assert response.status_code == status.HTTP_403_FORBIDDEN - response = client.post(f"{url}{user_id}/request", headers={"token": auth_token}, - json={"password": body["password"], "new_password": "changed"}) + response = client.post( + f"{url}{user_id}/request", + headers={"token": auth_token}, + json={"password": body["password"], "new_password": "changed"}, + ) assert response.status_code == status.HTTP_200_OK - reset_token = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", - AuthMethod.param == "reset_token", - AuthMethod.user_id == user_id).one_or_none() + reset_token = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.auth_method == "email", AuthMethod.param == "reset_token", AuthMethod.user_id == user_id) + .one_or_none() + ) assert not reset_token diff --git a/tests/test_routes/test_login.py b/tests/test_routes/test_login.py index 0492a43a..67d7b341 100644 --- a/tests/test_routes/test_login.py +++ b/tests/test_routes/test_login.py @@ -10,44 +10,27 @@ def test_invalid_email(client: TestClient): - body = { - "email": "some_string", - "password": "string" - } + body = {"email": "some_string", "password": "string"} response = client.post(url, json=body) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY def test_main_scenario(client: TestClient, dbsession: Session, user): user_id, body, response = user["user_id"], user["body"], user["login_json"] - body_with_uppercase = { - "email": body["email"].replace("u", "U"), - "password": "string" - } + body_with_uppercase = {"email": body["email"].replace("u", "U"), "password": "string"} response = client.post(url, json=body_with_uppercase) assert response.status_code == status.HTTP_200_OK def test_incorrect_data(client: TestClient, dbsession: Session): - body1 = { - "email": f"user{datetime.datetime.utcnow()}@example.com", - "password": "string" - } - body2 = { - "email": "wrong@example.com", - "password": "string" - } - body3 = { - "email": "some@example.com", - "password": "strong" - } - body4 = { - "email": "wrong@example.com", - "password": "strong" - } + body1 = {"email": f"user{datetime.datetime.utcnow()}@example.com", "password": "string"} + body2 = {"email": "wrong@example.com", "password": "string"} + body3 = {"email": "some@example.com", "password": "strong"} + body4 = {"email": "wrong@example.com", "password": "strong"} client.post("/email/registration", json=body1) - db_user: AuthMethod = dbsession.query(AuthMethod).filter(AuthMethod.value == body1['email'], - AuthMethod.param == 'email').one() + db_user: AuthMethod = ( + dbsession.query(AuthMethod).filter(AuthMethod.value == body1['email'], AuthMethod.param == 'email').one() + ) id = db_user.user_id response = client.post(url, json=body1) assert response.status_code == status.HTTP_401_UNAUTHORIZED @@ -57,12 +40,20 @@ def test_incorrect_data(client: TestClient, dbsession: Session): assert response.status_code == status.HTTP_401_UNAUTHORIZED response = client.post(url, json=body4) assert response.status_code == status.HTTP_401_UNAUTHORIZED - query = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", - AuthMethod.param == "email", - AuthMethod.value == body1["email"]).one() - token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == query.user.id, - AuthMethod.param == "confirmation_token", - AuthMethod.auth_method == "email").one() + query = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.auth_method == "email", AuthMethod.param == "email", AuthMethod.value == body1["email"]) + .one() + ) + token = ( + dbsession.query(AuthMethod) + .filter( + AuthMethod.user_id == query.user.id, + AuthMethod.param == "confirmation_token", + AuthMethod.auth_method == "email", + ) + .one() + ) response = client.get(f"/email/approve?token={token.value}") assert response.status_code == status.HTTP_200_OK response = client.post(url, json=body1) @@ -79,7 +70,6 @@ def test_incorrect_data(client: TestClient, dbsession: Session): dbsession.flush() - def test_check_token(client: TestClient, user, dbsession: Session): user_id, body, login = user["user_id"], user["body"], user["login_json"] @@ -99,8 +89,7 @@ def test_check_token(client: TestClient, user, dbsession: Session): def test_invalid_check_tokens(client: TestClient, user): user_id, body, login = user["user_id"], user["body"], user["login_json"] response = client.post(f"/me", headers={"token": ""}) - assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY response = client.post(f"/me") - assert response.status_code == status.HTTP_400_BAD_REQUEST - + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY diff --git a/tests/test_routes/test_logout.py b/tests/test_routes/test_logout.py index 93d07239..fdf3f1b4 100644 --- a/tests/test_routes/test_logout.py +++ b/tests/test_routes/test_logout.py @@ -1,4 +1,3 @@ - from starlette import status from fastapi.testclient import TestClient from sqlalchemy.orm import Session @@ -10,18 +9,23 @@ def test_main_scenario(client: TestClient, dbsession: Session): - body = { - "email": f"user{datetime.utcnow()}@example.com", - "password": "string" - } + body = {"email": f"user{datetime.utcnow()}@example.com", "password": "string"} user_response = client.post("/email/registration", json=body) - query = dbsession.query(AuthMethod).filter(AuthMethod.auth_method == "email", - AuthMethod.param == "email", - AuthMethod.value == body["email"]).one() + query = ( + dbsession.query(AuthMethod) + .filter(AuthMethod.auth_method == "email", AuthMethod.param == "email", AuthMethod.value == body["email"]) + .one() + ) id = query.user_id - auth_token = dbsession.query(AuthMethod).filter(AuthMethod.user_id == query.user.id, - AuthMethod.param == "confirmation_token", - AuthMethod.auth_method == "email").one() + auth_token = ( + dbsession.query(AuthMethod) + .filter( + AuthMethod.user_id == query.user.id, + AuthMethod.param == "confirmation_token", + AuthMethod.auth_method == "email", + ) + .one() + ) response = client.get(f"/email/approve?token={auth_token.value}") assert response.status_code == status.HTTP_200_OK response = client.post("/email/login", json=body) @@ -34,7 +38,7 @@ def test_main_scenario(client: TestClient, dbsession: Session): response = client.post(url, headers={"token": token}) assert response.status_code == status.HTTP_403_FORBIDDEN for row in dbsession.query(AuthMethod).filter(AuthMethod.user_id == id).all(): - dbsession.delete(row) + dbsession.delete(row) dbsession.delete(dbsession.query(UserSession).filter(UserSession.user_id == id).one()) dbsession.delete(dbsession.query(User).filter(User.id == id).one()) dbsession.flush() @@ -42,4 +46,4 @@ def test_main_scenario(client: TestClient, dbsession: Session): def test_without_token(client: TestClient, dbsession: Session): response = client.post(url) - assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY diff --git a/tests/test_routes/test_registration.py b/tests/test_routes/test_registration.py index 92dc639a..959f61eb 100644 --- a/tests/test_routes/test_registration.py +++ b/tests/test_routes/test_registration.py @@ -11,29 +11,17 @@ def test_invalid_email(client: TestClient): - body1 = { - "email": f"notEmailForSure", - "password": "string" - } - body2 = { - "email": f"EmailForSure{datetime.datetime.utcnow()}@mail.gtg", - "password": "" - } + body1 = {"email": f"notEmailForSure", "password": "string"} + body2 = {"email": f"EmailForSure{datetime.datetime.utcnow()}@mail.gtg", "password": ""} body3 = { "email": f"EmailForSure{datetime.datetime.utcnow()}@mail.gtg", - "password": "&%@#$@322îïíīįì3@##EFWed}efvef{}{}{}[èéêëēėę'" - } - body4 = { - "email": f"EmailFor+ _Sur{datetime.datetime.utcnow()}e@mail.gtg", - "password": "string2222" - } - body5 = { - "email": f"Email For Sure {datetime.datetime.utcnow()} @ mail. gtg", - "password": "string" + "password": "&%@#$@322îïíīįì3@##EFWed}efvef{}{}{}[èéêëēėę'", } + body4 = {"email": f"EmailFor+ _Sur{datetime.datetime.utcnow()}e@mail.gtg", "password": "string2222"} + body5 = {"email": f"Email For Sure {datetime.datetime.utcnow()} @ mail. gtg", "password": "string"} body6 = { "email": f"roman@dyakov.space\nContent-Type: text/html; charset=utf-8;\n\nАхаха,лох