From e16910468e1e1f9e6049f3ede4ef5cbf3330b14c Mon Sep 17 00:00:00 2001 From: manu-sj Date: Mon, 11 Aug 2025 09:36:45 +0200 Subject: [PATCH] updating links in the 4.5 branch --- README.md | 84 +- .../1_feature_group_embeddings_api.ipynb | 2 +- .../2_feature_view_embeddings_api.ipynb | 2 +- .../churn/1_churn_feature_pipeline.ipynb | 4 +- .../churn/2_churn_training_pipeline.ipynb | 4 +- .../churn/3_churn_batch_inference.ipynb | 4 +- batch-ai-systems/nyc_taxi_fares/README.md | 2 +- .../online-inference-pipeline/README.md | 14 +- ...reat_Expectations_Hopsworks_Concepts.ipynb | 4 +- .../fraud_batch_data_validation.ipynb | 4 +- .../langchain/news-search-langchain.ipynb | 4 +- integrations/polars/quickstart_polars.ipynb | 2 +- integrations/wandb/1_feature_groups.ipynb | 4 +- .../wandb/2_feature_view_creation.ipynb | 4 +- integrations/wandb/3_model_training.ipynb | 2 +- quickstart.ipynb | 4 +- .../1_fraud_online_feature_pipeline.ipynb | 397 ++++- .../2_fraud_online_training_pipeline.ipynb | 1298 ++++++++++++++++- .../images/confusion_matrix.png | Bin 0 -> 95686 bytes 19 files changed, 1707 insertions(+), 132 deletions(-) create mode 100644 real-time-ai-systems/fraud_online/fraud_online_model/images/confusion_matrix.png diff --git a/README.md b/README.md index 0bb61681..63457e9d 100644 --- a/README.md +++ b/README.md @@ -24,59 +24,59 @@ Familiarity with Machine Learning and Python development is recommended. For mor ## 🗄️ Table of Content: -- [QuickStart](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/quickstart.ipynb): Introductory tutorial to get started quickly. +- [QuickStart](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/quickstart.ipynb): Introductory tutorial to get started quickly. ### 🚀 Real-time AI Systems -- [Fraud Online](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/real-time-ai-systems/fraud_online): Detect Fraud Transactions -- [AML](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/real-time-ai-systems/aml): Anti-money laundering predictions -- [TikTok RecSys](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/real-time-ai-systems/tiktok_recsys): TikTok-style recommendation system -- [TimeSeries](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/real-time-ai-systems/timeseries): Timeseries price prediction +- [Fraud Online](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/real-time-ai-systems/fraud_online): Detect Fraud Transactions +- [AML](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/real-time-ai-systems/aml): Anti-money laundering predictions +- [TikTok RecSys](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/real-time-ai-systems/tiktok_recsys): TikTok-style recommendation system +- [TimeSeries](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/real-time-ai-systems/timeseries): Timeseries price prediction ### ⚙️ Batch AI Systems -- [Loan Approval](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/batch-ai-systems/loan_approval): Predict loan approvals -- [Fraud Batch](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/batch-ai-systems/fraud_batch): Detect Fraud Transactions -- [Churn](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/batch-ai-systems/churn): Predict customers at risk of churning -- [Credit Scores](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/batch-ai-systems/credit_scores): Predict clients' repayment abilities -- [Hospital Wait Time](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/batch-ai-systems/hospital_wait_time): Predict waiting time for deceased donor kidneys -- [NYC Taxi Fares](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/batch-ai-systems/nyc_taxi_fares): Predict NYC taxi fare amounts +- [Loan Approval](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/batch-ai-systems/loan_approval): Predict loan approvals +- [Fraud Batch](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/batch-ai-systems/fraud_batch): Detect Fraud Transactions +- [Churn](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/batch-ai-systems/churn): Predict customers at risk of churning +- [Credit Scores](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/batch-ai-systems/credit_scores): Predict clients' repayment abilities +- [Hospital Wait Time](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/batch-ai-systems/hospital_wait_time): Predict waiting time for deceased donor kidneys +- [NYC Taxi Fares](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/batch-ai-systems/nyc_taxi_fares): Predict NYC taxi fare amounts ### 🔮 LLM AI Systems -- [Fraud Cheque Detection](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/llm-ai-systems/fraud_cheque_detection): AI assistant for detecting fraudulent scanned cheques -- [LLM PDF](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/llm-ai-systems/llm_pdfs): RAG-based AI assistant for PDF document Q&A -- [Recommender System](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/llm-ai-systems/recommender-system): Fashion items recommender system +- [Fraud Cheque Detection](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/llm-ai-systems/fraud_cheque_detection): AI assistant for detecting fraudulent scanned cheques +- [LLM PDF](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/llm-ai-systems/llm_pdfs): RAG-based AI assistant for PDF document Q&A +- [Recommender System](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/llm-ai-systems/recommender-system): Fashion items recommender system ### 🧬 API Examples - Vector Similarity Search: - - [Feature Group Embeddings API](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb) - - [Feature View Embeddings API](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb) -- [Datasets](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/datasets.ipynb) -- [Feature Group Change Notification CDC](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/feature_group_change_notification_cdc.ipynb) -- [Feature Monitoring](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/feature_monitoring.ipynb) -- [Git Integration](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/git.ipynb) -- [Jobs Management](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/jobs.ipynb) -- [Kafka Integration](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/kafka.ipynb) -- [OpenSearch Integration](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/opensearch.ipynb) -- [Projects Management](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/projects.ipynb) -- [Secrets Management](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/secrets.ipynb) + - [Feature Group Embeddings API](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb) + - [Feature View Embeddings API](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb) +- [Datasets](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/datasets.ipynb) +- [Feature Group Change Notification CDC](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/feature_group_change_notification_cdc.ipynb) +- [Feature Monitoring](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/feature_monitoring.ipynb) +- [Git Integration](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/git.ipynb) +- [Jobs Management](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/jobs.ipynb) +- [Kafka Integration](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/kafka.ipynb) +- [OpenSearch Integration](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/opensearch.ipynb) +- [Projects Management](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/projects.ipynb) +- [Secrets Management](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/secrets.ipynb) ### 🔬 Integrations -- [Airflow GCP](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/airflow_gcp): Apache Airflow integration with Google Cloud Platform. -- [AzureSQL](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/azuresql): Create an External Feature Group using Azure SQL Database. -- [BigQuery](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/big_query): Create an External Feature Group using BigQuery Storage Connector. -- [Bytewax](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/bytewax): Real-time feature computation using Bytewax. -- [DBT with BigQuery](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/dbt_bq): Perform feature engineering in DBT on BigQuery. -- [Federated Offline Query](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/federated-offline-query): Execute federated queries across offline data sources. -- [Google Cloud Storage](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/gcs): Create an External Feature Group using GCS Storage Connector. -- [Great Expectations](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/great_expectations): Introduction to Great Expectations concepts for Hopsworks MLOps platform. -- [Java](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/java): Java-based integrations including Apache Beam and Apache Flink. -- [LangChain](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/langchain): Integration with LangChain for LLM applications. -- [MageAI](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/mage_ai): Build and operate ML systems with Mage and Hopsworks. -- [Neo4j](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/neo4j): Perform Anti-money laundering predictions using Neo4j Graph. -- [Polars](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/polars): Introductory tutorial on using Polars with Hopsworks. -- [PySpark Streaming](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/pyspark_streaming): Real-time feature computation using PySpark. -- [Redshift](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/redshift): Create an External Feature Group using Redshift Storage Connector. -- [Snowflake](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/snowflake): Create an External Feature Group using Snowflake Storage Connector. -- [Weights & Biases](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/integrations/wandb): Build machine learning models with Weights & Biases. +- [Airflow GCP](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/airflow_gcp): Apache Airflow integration with Google Cloud Platform. +- [AzureSQL](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/azuresql): Create an External Feature Group using Azure SQL Database. +- [BigQuery](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/big_query): Create an External Feature Group using BigQuery Storage Connector. +- [Bytewax](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/bytewax): Real-time feature computation using Bytewax. +- [DBT with BigQuery](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/dbt_bq): Perform feature engineering in DBT on BigQuery. +- [Federated Offline Query](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/federated-offline-query): Execute federated queries across offline data sources. +- [Google Cloud Storage](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/gcs): Create an External Feature Group using GCS Storage Connector. +- [Great Expectations](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/great_expectations): Introduction to Great Expectations concepts for Hopsworks MLOps platform. +- [Java](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/java): Java-based integrations including Apache Beam and Apache Flink. +- [LangChain](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/langchain): Integration with LangChain for LLM applications. +- [MageAI](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/mage_ai): Build and operate ML systems with Mage and Hopsworks. +- [Neo4j](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/neo4j): Perform Anti-money laundering predictions using Neo4j Graph. +- [Polars](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/polars): Introductory tutorial on using Polars with Hopsworks. +- [PySpark Streaming](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/pyspark_streaming): Real-time feature computation using PySpark. +- [Redshift](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/redshift): Create an External Feature Group using Redshift Storage Connector. +- [Snowflake](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/snowflake): Create an External Feature Group using Snowflake Storage Connector. +- [Weights & Biases](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/integrations/wandb): Build machine learning models with Weights & Biases. ## 📝 Feedback & Comments: We welcome your input through: diff --git a/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb b/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb index 47188f4f..b255003f 100644 --- a/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb +++ b/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb @@ -324,7 +324,7 @@ "\n", "## ➡️ Next step\n", "\n", - "Now you are able to search articles using natural language. You can learn how to rank the result in [this tutorial](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb)." + "Now you are able to search articles using natural language. You can learn how to rank the result in [this tutorial](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb)." ] } ], diff --git a/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb b/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb index d5e53768..19358273 100644 --- a/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb +++ b/api_examples/vector_similarity_search/2_feature_view_embeddings_api.ipynb @@ -13,7 +13,7 @@ "id": "8988ff65", "metadata": {}, "source": [ - "In the [previous tutorial](https://github.com/logicalclocks/hopsworks-tutorials/tree/master/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb), you learned how to search news articles using natural language queries. In this tutorial, we will focus on ranking the search results to make them more useful and relevant.\n", + "In the [previous tutorial](https://github.com/logicalclocks/hopsworks-tutorials/tree/branch-4.5/api_examples/vector_similarity_search/1_feature_group_embeddings_api.ipynb), you learned how to search news articles using natural language queries. In this tutorial, we will focus on ranking the search results to make them more useful and relevant.\n", "\n", "To achieve this, we will use the number of views as a scoring metric for news articles, as it reflects their popularity. The steps are as follows:\n", "\n", diff --git a/batch-ai-systems/churn/1_churn_feature_pipeline.ipynb b/batch-ai-systems/churn/1_churn_feature_pipeline.ipynb index e6d39c6a..bb087d18 100644 --- a/batch-ai-systems/churn/1_churn_feature_pipeline.ipynb +++ b/batch-ai-systems/churn/1_churn_feature_pipeline.ipynb @@ -7,7 +7,7 @@ "source": [ "# **Hopsworks Feature Store** - Part 01: Feature Pipeline\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/churn/1_churn_feature_pipeline.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/churn/1_churn_feature_pipeline.ipynb)\n", "\n", "\n", "## 🗒️ This notebook is divided into the following sections:\n", @@ -367,7 +367,7 @@ "\n", "In the following notebook you will use your feature groups to create a train dataset, train a model and add a trained model to model registry.\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/churn/2_churn_training_pipeline.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/churn/2_churn_training_pipeline.ipynb)" ] } ], diff --git a/batch-ai-systems/churn/2_churn_training_pipeline.ipynb b/batch-ai-systems/churn/2_churn_training_pipeline.ipynb index a922bd33..f0be6d79 100644 --- a/batch-ai-systems/churn/2_churn_training_pipeline.ipynb +++ b/batch-ai-systems/churn/2_churn_training_pipeline.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Hopsworks Feature Store** - Part 02: Training Pipeline\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/churn/2_churn_training_pipeline.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/churn/2_churn_training_pipeline.ipynb)\n", "\n", "This is the second part of the quick start series of tutorials about predicting customers that are at risk of churning with the Hopsworks Feature Store.\n", "\n", @@ -455,7 +455,7 @@ "\n", "In the following notebook you will use your model for batch inference.\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/churn/3_churn_batch_inference.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/churn/3_churn_batch_inference.ipynb)\n", "\n", "---" ] diff --git a/batch-ai-systems/churn/3_churn_batch_inference.ipynb b/batch-ai-systems/churn/3_churn_batch_inference.ipynb index b6ac1d78..643d74cd 100644 --- a/batch-ai-systems/churn/3_churn_batch_inference.ipynb +++ b/batch-ai-systems/churn/3_churn_batch_inference.ipynb @@ -7,7 +7,7 @@ "source": [ "# **Hopsworks Feature Store** - Part 03: Batch Inference\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/churn/3_churn_batch_inference.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/churn/3_churn_batch_inference.ipynb)" ] }, { @@ -451,7 +451,7 @@ "> `conda activate ./miniconda/envs/hopsworks`
\n", "> `python -m streamlit run churn/streamlit_app.py`
\n", "\n", - "**⚠️** If you are running on Colab, you will need to follow a different procedure. As highlighted in this [notebook](https://colab.research.google.com/github/mrm8488/shared_colab_notebooks/blob/master/Create_streamlit_app.ipynb). " + "**⚠️** If you are running on Colab, you will need to follow a different procedure. As highlighted in this [notebook](https://colab.research.google.com/github/mrm8488/shared_colab_notebooks/blob/branch-4.5/Create_streamlit_app.ipynb). " ] }, { diff --git a/batch-ai-systems/nyc_taxi_fares/README.md b/batch-ai-systems/nyc_taxi_fares/README.md index 4f157005..a19fc8e1 100644 --- a/batch-ai-systems/nyc_taxi_fares/README.md +++ b/batch-ai-systems/nyc_taxi_fares/README.md @@ -30,7 +30,7 @@ Also, you obviously need to have [streamlit](https://docs.streamlit.io/library/g ## Data -You will generate random data for this tutorial. See corresponding functions in the [functions.py](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/advanced_tutorials/nyc_taxi_fares/functions.py). +You will generate random data for this tutorial. See corresponding functions in the [functions.py](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/advanced_tutorials/nyc_taxi_fares/functions.py). ## Streamlit run diff --git a/benchmarks/online-inference-pipeline/README.md b/benchmarks/online-inference-pipeline/README.md index 6eed969c..b94b5d0c 100644 --- a/benchmarks/online-inference-pipeline/README.md +++ b/benchmarks/online-inference-pipeline/README.md @@ -8,26 +8,26 @@ This repository benchmarks a deployment running inside **Hopsworks** using [Locu - Run all the provided notebooks to set up your deployment inside Hopsworks. 2. **Configure Target Host** - - Add the **host name** and **IP address** of your deployment in [`locustfile.py`](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/benchmarks/online-inference-pipeline/locust/locustfile.py#L12). + - Add the **host name** and **IP address** of your deployment in [`locustfile.py`](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/benchmarks/online-inference-pipeline/locust/locustfile.py#L12). - You can find this information in the Hopsworks **Deployment UI**. 3. **Add Hopsworks API Key** - - Insert your Hopsworks API key into the same [`locustfile.py`](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/benchmarks/online-inference-pipeline/locust/locustfile.py#L12). + - Insert your Hopsworks API key into the same [`locustfile.py`](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/benchmarks/online-inference-pipeline/locust/locustfile.py#L12). - Generate the API key by following [this guide](https://docs.hopsworks.ai/latest/user_guides/projects/api_key/create_api_key/). 4. **Create the 'HOPSWORKS_API_KEY' secret** - Create a secret with the name `HOPSWORKS_API_KEY` which contains the API key by following [this guide](https://docs.hopsworks.ai/latest/user_guides/projects/secrets/create_secret/). 5. **Build the Locust Docker Image** - - Use the provided [Dockerfile](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/benchmarks/online-inference-pipeline/locust/Dockerfile) to build a Locust image. + - Use the provided [Dockerfile](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/benchmarks/online-inference-pipeline/locust/Dockerfile) to build a Locust image. - Push the image to your preferred container registry. 6. **Update Kubernetes Manifests** - Update the image URL in both: - - [`master-deployment.yaml`](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/benchmarks/online-inference-pipeline/locust/kubernetes-locust/master-deployment.yaml#L28) - - [`slave-deployment.yaml`](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/benchmarks/online-inference-pipeline/locust/kubernetes-locust/slave-deployment.yaml#L28) + - [`master-deployment.yaml`](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/benchmarks/online-inference-pipeline/locust/kubernetes-locust/master-deployment.yaml#L28) + - [`slave-deployment.yaml`](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/benchmarks/online-inference-pipeline/locust/kubernetes-locust/slave-deployment.yaml#L28) 7. **Deploy Locust** - - Run the [deployment script](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/benchmarks/online-inference-pipeline/locust/kubernetes-locust/deploy.sh) to deploy Locust master and worker nodes. + - Run the [deployment script](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/benchmarks/online-inference-pipeline/locust/kubernetes-locust/deploy.sh) to deploy Locust master and worker nodes. - This will deploy into a Kubernetes namespace named `locust`. - **Note:** Ensure you have `kubectl` access to the cluster. @@ -57,4 +57,4 @@ One benchmark that has been performed targets **5000 RPS** with a **P99 latency The high number of replicas for predictors is necessary to mitigate the effects of Python's [Global Interpreter Lock (GIL)](https://wiki.python.org/moin/GlobalInterpreterLock). This allows for greater parallelism and lower latency, especially at high RPS. -You can view the full benchmark report generated by Locust [here](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/locust_reports/locust_report_5k_rps_25_batch_size.pdf). +You can view the full benchmark report generated by Locust [here](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/locust_reports/locust_report_5k_rps_25_batch_size.pdf). diff --git a/integrations/great_expectations/Great_Expectations_Hopsworks_Concepts.ipynb b/integrations/great_expectations/Great_Expectations_Hopsworks_Concepts.ipynb index 66c44762..498eeebd 100644 --- a/integrations/great_expectations/Great_Expectations_Hopsworks_Concepts.ipynb +++ b/integrations/great_expectations/Great_Expectations_Hopsworks_Concepts.ipynb @@ -9,7 +9,7 @@ "# Short Introduction to Great Expectations Concepts on Hopsworks\n", "\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/great_expectations/Great_Expectations_Hopsworks_Concepts.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/great_expectations/Great_Expectations_Hopsworks_Concepts.ipynb)" ] }, { @@ -348,7 +348,7 @@ "source": [ "### Next Step : Data Validation with Great Expectation applied to the Fraud tutorial\n", "\n", - "Check it out here : [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/great_expectations/fraud_batch_data_validation.ipynb)" + "Check it out here : [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/great_expectations/fraud_batch_data_validation.ipynb)" ] }, { diff --git a/integrations/great_expectations/fraud_batch_data_validation.ipynb b/integrations/great_expectations/fraud_batch_data_validation.ipynb index dc987d80..9858efe1 100644 --- a/integrations/great_expectations/fraud_batch_data_validation.ipynb +++ b/integrations/great_expectations/fraud_batch_data_validation.ipynb @@ -19,7 +19,7 @@ "source": [ "# Data Validation using Hopsworks integration with Great Expectations \n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/great_expectations/fraud_batch_data_validation.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/great_expectations/fraud_batch_data_validation.ipynb)\n", "\n", "**Note**: you may get an error when installing hopsworks on Colab, and it is safe to ignore it.\n", "\n", @@ -47,7 +47,7 @@ "source": [ "\n", "Check the step 1 in the fraud batch tutorial to learn more about Feature Group : \n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/fraud_batch/1_feature_groups.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/fraud_batch/1_feature_groups.ipynb)\n", "\n", "\n", "## 🗒️ This notebook is divided in 5 sections:\n", diff --git a/integrations/langchain/news-search-langchain.ipynb b/integrations/langchain/news-search-langchain.ipynb index 0e8440a3..8f9a2890 100644 --- a/integrations/langchain/news-search-langchain.ipynb +++ b/integrations/langchain/news-search-langchain.ipynb @@ -15,7 +15,7 @@ "source": [ "In this tutorial, you will learn how to create a news search bot which can answer users' question about news using Opensearch in Hopsworks with Langchain. Concretely, you will create a RAG (Retrieval-Augmented Generation) application which searches news matching users' questions, and answers the question using a LLM with the retrieved news as the context.\n", "The steps include:\n", - "1. [Ingest news data to Hopsworks](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/api_examples/hsfs/knn_search/news-search-knn.ipynb)\n", + "1. [Ingest news data to Hopsworks](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/api_examples/hsfs/knn_search/news-search-knn.ipynb)\n", "2. Setup a `vectorstores` in Langchain using Opensearch in Hopsworks\n", "3. Create a LLM using model from huggingface\n", "4. Create a RAG application using `RetrievalQA` chain in Langchain" @@ -34,7 +34,7 @@ "id": "ba83a905-3944-4bf1-b4d7-43d4336f0beb", "metadata": {}, "source": [ - "You need to run this [notebook](https://github.com/logicalclocks/hopsworks-tutorials/blob/master/api_examples/hsfs/knn_search/news-search-knn.ipynb) to ingest news data to Hopsworks." + "You need to run this [notebook](https://github.com/logicalclocks/hopsworks-tutorials/blob/branch-4.5/api_examples/hsfs/knn_search/news-search-knn.ipynb) to ingest news data to Hopsworks." ] }, { diff --git a/integrations/polars/quickstart_polars.ipynb b/integrations/polars/quickstart_polars.ipynb index 0ce839b5..cf1ff0b1 100644 --- a/integrations/polars/quickstart_polars.ipynb +++ b/integrations/polars/quickstart_polars.ipynb @@ -13,7 +13,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/quickstart.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/quickstart.ipynb)" ] }, { diff --git a/integrations/wandb/1_feature_groups.ipynb b/integrations/wandb/1_feature_groups.ipynb index 48d07b0d..0a4b75bc 100755 --- a/integrations/wandb/1_feature_groups.ipynb +++ b/integrations/wandb/1_feature_groups.ipynb @@ -15,7 +15,7 @@ "source": [ "# Part 01: Load, Engineer & Connect\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/wandb/1_feature_groups.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/wandb/1_feature_groups.ipynb)\n", "\n", "**Note**: you may get an error when installing hopsworks on Colab, and it is safe to ignore it.\n", "\n", @@ -433,7 +433,7 @@ "\n", "In the following notebook you will use your feature groups to create a dataset you can train a model on.\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/wandb/2_feature_view_creation.ipynb)\n" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/wandb/2_feature_view_creation.ipynb)\n" ] } ], diff --git a/integrations/wandb/2_feature_view_creation.ipynb b/integrations/wandb/2_feature_view_creation.ipynb index 97e00e5b..c37ccc71 100755 --- a/integrations/wandb/2_feature_view_creation.ipynb +++ b/integrations/wandb/2_feature_view_creation.ipynb @@ -13,7 +13,7 @@ "source": [ "# Part 02: Training Data & Feature views\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/wandb/2_feature_view_creation.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/wandb/2_feature_view_creation.ipynb)\n", "\n", "**Note**: you may get an error when installing hopsworks on Colab, and it is safe to ignore it.\n", "\n", @@ -195,7 +195,7 @@ "\n", "In the following notebook, you will train a model on the dataset you created in this notebook.\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/wandb/3_model_training.ipynb)\n" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/wandb/3_model_training.ipynb)\n" ] } ], diff --git a/integrations/wandb/3_model_training.ipynb b/integrations/wandb/3_model_training.ipynb index f37d2481..6803c822 100644 --- a/integrations/wandb/3_model_training.ipynb +++ b/integrations/wandb/3_model_training.ipynb @@ -15,7 +15,7 @@ "source": [ "# Part 03: Model training with Weights & Biases & UI Exploration\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/master/integrations/wandb/3_model_training.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/logicalclocks/hopsworks-tutorials/blob/branch-4.5/integrations/wandb/3_model_training.ipynb)\n", "\n", "**Note**: you may get an error when installing hopsworks on Colab, and it is safe to ignore it.\n", "\n", diff --git a/quickstart.ipynb b/quickstart.ipynb index f8113b6b..06048aae 100644 --- a/quickstart.ipynb +++ b/quickstart.ipynb @@ -1102,7 +1102,7 @@ "provenance": [] }, "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "tutorial", "language": "python", "name": "python3" }, @@ -1116,7 +1116,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.5" + "version": "3.11.13" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/real-time-ai-systems/fraud_online/1_fraud_online_feature_pipeline.ipynb b/real-time-ai-systems/fraud_online/1_fraud_online_feature_pipeline.ipynb index c82af3e0..ef906efb 100644 --- a/real-time-ai-systems/fraud_online/1_fraud_online_feature_pipeline.ipynb +++ b/real-time-ai-systems/fraud_online/1_fraud_online_feature_pipeline.ipynb @@ -45,7 +45,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "49806257", "metadata": {}, "outputs": [], @@ -89,10 +89,67 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "27f2b52e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
cc_numgender
04796807885357879F
14529266636192966F
24922690008243953F
\n", + "
" + ], + "text/plain": [ + " cc_num gender\n", + "0 4796807885357879 F\n", + "1 4529266636192966 F\n", + "2 4922690008243953 F" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Read the profiles data from a CSV file\n", "profiles_df = pd.read_csv(\n", @@ -112,10 +169,109 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "713a9568", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
tiddatetimecc_numcategoryamountlatitudelongitudecitycountryfraud_label
011df919988c134d97bbff2678eb68e222022-01-01 00:00:244473593503484549Health/Beauty62.9542.30865-83.48216CantonUS0
1dd0b2d6d4266ccd3bf05bc2ea91cf1802022-01-01 00:00:564272465718946864Grocery85.4533.52253-117.70755Laguna NiguelUS0
2e627f5d9a9739833bd52d2da51761fc32022-01-01 00:02:324104216579248948Domestic Transport21.6337.60876-77.37331MechanicsvilleUS0
\n", + "
" + ], + "text/plain": [ + " tid datetime cc_num \\\n", + "0 11df919988c134d97bbff2678eb68e22 2022-01-01 00:00:24 4473593503484549 \n", + "1 dd0b2d6d4266ccd3bf05bc2ea91cf180 2022-01-01 00:00:56 4272465718946864 \n", + "2 e627f5d9a9739833bd52d2da51761fc3 2022-01-01 00:02:32 4104216579248948 \n", + "\n", + " category amount latitude longitude city country \\\n", + "0 Health/Beauty 62.95 42.30865 -83.48216 Canton US \n", + "1 Grocery 85.45 33.52253 -117.70755 Laguna Niguel US \n", + "2 Domestic Transport 21.63 37.60876 -77.37331 Mechanicsville US \n", + "\n", + " fraud_label \n", + "0 0 \n", + "1 0 \n", + "2 0 " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Read the transactions data from a CSV file\n", "trans_df = pd.read_csv(\n", @@ -129,7 +285,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "4ad0edf3", "metadata": {}, "outputs": [], @@ -146,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "8efc0deb", "metadata": {}, "outputs": [], @@ -188,10 +344,105 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "6f7d5009", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
tiddatetimecc_numamountcountryfraud_labelloc_delta_t_plus_1loc_delta_t_minus_1time_delta_t_minus_1
04c51b54665c7ddb466ea5936f4f3a4282022-01-01 08:11:01446736074068208977.77US00.00.0001480.333333
14c30185aea2e28e7d9797004710e13c62022-01-01 10:03:424700702588013561781.27US00.00.0000700.416667
21a109febabc5c36409f2caf729e110d32022-01-01 10:08:59420509487725610536.25US00.00.0001080.333333
\n", + "
" + ], + "text/plain": [ + " tid datetime cc_num \\\n", + "0 4c51b54665c7ddb466ea5936f4f3a428 2022-01-01 08:11:01 4467360740682089 \n", + "1 4c30185aea2e28e7d9797004710e13c6 2022-01-01 10:03:42 4700702588013561 \n", + "2 1a109febabc5c36409f2caf729e110d3 2022-01-01 10:08:59 4205094877256105 \n", + "\n", + " amount country fraud_label loc_delta_t_plus_1 loc_delta_t_minus_1 \\\n", + "0 77.77 US 0 0.0 0.000148 \n", + "1 781.27 US 0 0.0 0.000070 \n", + "2 36.25 US 0 0.0 0.000108 \n", + "\n", + " time_delta_t_minus_1 \n", + "0 0.333333 \n", + "1 0.416667 \n", + "2 0.333333 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Use the prepare_transactions_fraud function to process the trans_df DataFrame\n", "trans_df = transactions_fraud.prepare_transactions_fraud(trans_df)\n", @@ -210,7 +461,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "5b97f5f2", "metadata": {}, "outputs": [], @@ -230,7 +481,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "bbc8e914", "metadata": {}, "outputs": [], @@ -272,7 +523,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "d2331129", "metadata": {}, "outputs": [], @@ -289,10 +540,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "51383029", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{\"expectation_type\": \"expect_column_values_to_be_null\", \"kwargs\": {\"column\": \"cc_num\", \"mostly\": 0.0}, \"meta\": {}}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Check binary gender column to be in set ['M', 'F']\n", "expectation_suite_profiles.add_expectation(\n", @@ -348,10 +610,36 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "35f1e17e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-08-08 20:24:16,171 INFO: Initializing external client\n", + "2025-08-08 20:24:16,172 INFO: Base URL: https://10.87.45.79:28181\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-08-08 20:24:17,307 INFO: Python Engine initialized.\n", + "\n", + "Logged in to project, explore it here https://10.87.45.79:28181/p/119\n" + ] + } + ], "source": [ "import hopsworks\n", "\n", @@ -370,7 +658,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "3e926dc7", "metadata": {}, "outputs": [], @@ -399,10 +687,39 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "9a366430", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Feature Group created successfully, explore it at \n", + "https://10.87.45.79:28181/p/119/fs/67/fg/1037\n", + "2025-08-08 20:24:20,011 INFO: \t5 expectation(s) included in expectation_suite.\n", + "Validation succeeded.\n", + "Validation Report saved successfully, explore a summary at https://10.87.45.79:28181/p/119/fs/67/fg/1037\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Uploading Dataframe: 100.00% |█████████████| Rows 365112/365112 | Elapsed Time: 02:28 | Remaining Time: 00:00\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Launching job: transactions_fraud_online_fg_1_offline_fg_materialization\n", + "Job started successfully, you can follow the progress at \n", + "https://10.87.45.79:28181/p/119/jobs/named/transactions_fraud_online_fg_1_offline_fg_materialization/executions\n", + "✅ Done!\n" + ] + } + ], "source": [ "# Insert data into feature group\n", "trans_fg.insert(trans_df)\n", @@ -411,7 +728,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "3d7de1db", "metadata": {}, "outputs": [], @@ -442,10 +759,39 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "e8027f2d", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Feature Group created successfully, explore it at \n", + "https://10.87.45.79:28181/p/119/fs/67/fg/2061\n", + "2025-08-08 20:27:04,696 INFO: \t2 expectation(s) included in expectation_suite.\n", + "Validation succeeded.\n", + "Validation Report saved successfully, explore a summary at https://10.87.45.79:28181/p/119/fs/67/fg/2061\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Uploading Dataframe: 100.00% |█████████████████| Rows 1000/1000 | Elapsed Time: 00:00 | Remaining Time: 00:00\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Launching job: profile_fraud_online_fg_1_offline_fg_materialization\n", + "Job started successfully, you can follow the progress at \n", + "https://10.87.45.79:28181/p/119/jobs/named/profile_fraud_online_fg_1_offline_fg_materialization/executions\n", + "✅ Done!\n" + ] + } + ], "source": [ "# Get or create the 'profile_fraud_online_fg' feature group\n", "profile_fg = fs.get_or_create_feature_group(\n", @@ -463,7 +809,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "ef348581", "metadata": {}, "outputs": [], @@ -492,7 +838,6 @@ "cell_type": "markdown", "id": "36294255", "metadata": { - "jp-MarkdownHeadingCollapsed": true, "tags": [] }, "source": [ @@ -556,7 +901,7 @@ "hash": "e1ddeae6eefc765c17da80d38ea59b893ab18c0c0904077a035ef84cfe367f83" }, "kernelspec": { - "display_name": "Python", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -570,7 +915,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.11.13" } }, "nbformat": 4, diff --git a/real-time-ai-systems/fraud_online/2_fraud_online_training_pipeline.ipynb b/real-time-ai-systems/fraud_online/2_fraud_online_training_pipeline.ipynb index 805ca851..bf7c082b 100644 --- a/real-time-ai-systems/fraud_online/2_fraud_online_training_pipeline.ipynb +++ b/real-time-ai-systems/fraud_online/2_fraud_online_training_pipeline.ipynb @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -73,9 +73,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-08-08 20:27:34,164 INFO: Initializing external client\n", + "2025-08-08 20:27:34,165 INFO: Base URL: https://10.87.45.79:28181\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-08-08 20:27:34,758 INFO: Python Engine initialized.\n", + "\n", + "Logged in to project, explore it here https://10.87.45.79:28181/p/119\n" + ] + } + ], "source": [ "import hopsworks\n", "\n", @@ -102,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -122,7 +148,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -156,7 +182,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -182,9 +208,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Feature view created successfully, explore it at \n", + "https://10.87.45.79:28181/p/119/fs/67/fv/transactions_fraud_online_fv/version/1\n" + ] + } + ], "source": [ "# Get or create the 'transactions_fraud_online_fv' feature view\n", "feature_view = fs.get_or_create_feature_view(\n", @@ -206,9 +241,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finished: Reading data from Hopsworks, using Hopsworks Feature Query Service (35.19s) \n", + "2025-08-08 20:29:55,880 WARNING: VersionWarning: Incremented version to `1`.\n", + "\n" + ] + } + ], "source": [ "# Training/Test splits, datasets creation. Using timerange arguments.\n", "train_start = \"2022/01/01\"\n", @@ -235,7 +280,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -248,7 +293,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -261,7 +306,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -271,7 +316,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -284,9 +329,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "fraud_label\n", + "0 0.996458\n", + "1 0.003542\n", + "Name: proportion, dtype: float64" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Display the normalized value counts of the training labels (y_train)\n", "y_train.value_counts(normalize=True)" @@ -317,12 +376,975 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": { "scrolled": true, "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
XGBClassifier(base_score=None, booster=None, callbacks=None,\n",
+       "              colsample_bylevel=None, colsample_bynode=None,\n",
+       "              colsample_bytree=None, device=None, early_stopping_rounds=None,\n",
+       "              enable_categorical=False, eval_metric=None, feature_types=None,\n",
+       "              feature_weights=None, gamma=None, grow_policy=None,\n",
+       "              importance_type=None, interaction_constraints=None,\n",
+       "              learning_rate=None, max_bin=None, max_cat_threshold=None,\n",
+       "              max_cat_to_onehot=None, max_delta_step=None, max_depth=None,\n",
+       "              max_leaves=None, min_child_weight=None, missing=nan,\n",
+       "              monotone_constraints=None, multi_strategy=None, n_estimators=None,\n",
+       "              n_jobs=None, num_parallel_tree=None, ...)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "XGBClassifier(base_score=None, booster=None, callbacks=None,\n", + " colsample_bylevel=None, colsample_bynode=None,\n", + " colsample_bytree=None, device=None, early_stopping_rounds=None,\n", + " enable_categorical=False, eval_metric=None, feature_types=None,\n", + " feature_weights=None, gamma=None, grow_policy=None,\n", + " importance_type=None, interaction_constraints=None,\n", + " learning_rate=None, max_bin=None, max_cat_threshold=None,\n", + " max_cat_to_onehot=None, max_delta_step=None, max_depth=None,\n", + " max_leaves=None, min_child_weight=None, missing=nan,\n", + " monotone_constraints=None, multi_strategy=None, n_estimators=None,\n", + " n_jobs=None, num_parallel_tree=None, ...)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Initialize an XGBoost classifier\n", "model = xgb.XGBClassifier()\n", @@ -333,7 +1355,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -346,27 +1368,127 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 0, 0, ..., 0, 0, 0])" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "y_pred_test" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
amountloc_delta_t_plus_1loc_delta_t_minus_1time_delta_t_minus_1label_encoder_country_label_encoder_gender_
693641.150.5914180.2678640.0420491110
4120910.940.2724490.3121190.0123961110
30369660.350.6165260.4925780.1680211111
\n", + "
" + ], + "text/plain": [ + " amount loc_delta_t_plus_1 loc_delta_t_minus_1 time_delta_t_minus_1 \\\n", + "6936 41.15 0.591418 0.267864 0.042049 \n", + "41209 10.94 0.272449 0.312119 0.012396 \n", + "303696 60.35 0.616526 0.492578 0.168021 \n", + "\n", + " label_encoder_country_ label_encoder_gender_ \n", + "6936 111 0 \n", + "41209 111 0 \n", + "303696 111 1 " + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "X_test.head(3)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'f1_score': 1.0}" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Compute f1 score\n", "metrics = {\n", @@ -377,9 +1499,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[40229 0]\n", + " [ 0 0]]\n" + ] + } + ], "source": [ "# Calculate the confusion matrix for the test set predictions\n", "results = confusion_matrix(\n", @@ -410,7 +1541,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -424,9 +1555,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "['fraud_online_model/xgboost_fraud_online_model.pkl']" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Save the trained XGBoost model\n", "joblib.dump(model, os.path.join(model_dir, \"xgboost_fraud_online_model.pkl\"))" @@ -434,7 +1576,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -469,9 +1611,97 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "da9394e111414814bf717fb19dcc5f74", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/6 [00:00*}4KqTiNAi-A2C@N7hN{$|cLy*t{2jmbX zCqc4+ghS4}x#;lqcmH}-_tw3C)&JLfyQ;fS8xDK#HP@VDjxpv6xT|=Z^aRZbA|fJE znLEEK6A>NzK}2+9{MZrrl_ythd*MGKPSToADz*=u9vM2A5Gfcs*;&~-S(zJMa4~Uk zG`F?k<>bD`$;*Dh%*n~lQG|=j`tLu%Y3pFh^^mbJ5WeNO-5o7QA|lSq=$`|wDUvQk z2Z)Gde!ZdU8vFCx;aJ-79m$1BqvywtKR5Wjageh}2YymXyvuCkbAeww zTZsITE#dy>r&1U0kUV9)a$R?R&wXO8awbRD=8@y$Pmk8TmijboCS4xJ_r}H1*+^{d zsH>{0tD}e6zY>|X$xShT{onN~iHH9BEd75!@4t%w_ItQq@3}QwH2g-6^SJ0-vGI?g zO79;X87gL9-$|##RWVz()SM*T=dskJ67T2ApjqJ9EX*|SNoLzrA0oL+%~`p<_@UqC zlTWz1HVga5MxG>-h6t9HI&I4wwQ0kfTIVD@Tx;zwD?Xc;esbvK9XHA4neMy}se?pB zX3N^)?(hubnfduV9*gx%!yj8Dhz&dl(oq7|+EKiwia&ZV#+vR^DWSyi&yJp2t((Su zup6$j+ufM-GpzPyY~dgZnw-wcJIR#b;jsAjFnS+hrw-5&J)wEn79>47%Ah+_F;rwbbj@a<7^i=*Wmfyg_K|(fB)@h{t)jhZ%=xh>r^?i}bRr^GM89=*G(+IoN}x;!o3o%Q&*$4aM0 zP=Nds{=p&7$)pw`uKvEgpL#ce5XBJ!2B9(3G&ZJsGx@qg^N)pI3cSls!ZB z;5f4{-iX@PzJ@rBJNeMcRG#;aWt+fz4GE>Rw(zotHEDm`*V(bm{L!6faL8I=l-s;5`I=FUf0#K_tldqA zXAY~24P1Uok~@6nEXvH*-_mWlqn5s04oQ~pw=Vitfna|D7&n;96uZvQwGEjQ# zvbs|vWwy?T`!^g;(qxI%CYoAL=G`NSuUl%h%qtfd@Y8gpg^@RDVv$r``aOk!ioiEUoScR?BSk_a333-crRS|ZS%+bkT+OC ze&N)vyo8;frZ4OGqL!LPxiaR8nYpu<2z3lhld1$t9Y46kBCU{91dmm0zYTHa?n+*e zkVSNKj7=#^3EcBtZ|#oJd?F&67c;q^I4y-tOU5IJTO_uu)?2)H$pm^Igo;!P-}~e; z`-V7)t9)76b>gjQ)%VZOg7~IWjjNs?Ki95Gm8d&g*sag9W($9Jtjf$&wWZgk-?}=# zf%B8*=%G4QGp`Zh>BJk)s(w34Pn}yjojE3Sx<5O&VsrXjNSyd%7l+)<9^;Vk2iXt= zzwezvVHmfrFk&1cE--^@>~S#<;=nSy_UhP_n7A%A30BK6#T7)ox9ZDRBMz9AN~E2I zpCm&O7i}%IyEU{oQ65V@9YCdIDe@!n*|bS8pY6+x>oEe>RT5i2$p&3VU)VKr*FD(w z+TGqToG)KHXN1^e6bjgHx9{nuR_iBf7juIuZ*AS0!RjeD;F89zRnzqERl;#fYt`;3` z5p|qsToMC`X!)ZnM@RGVY@t-1e)&zQ_2qV@(7@|tOo}A%hh+{P zCb$L8R$8c&Uwn=>?kUObYZeOtG5#zJXlZ2)?Q9+N@Uen>x5XqB69!m=A zQ)vmAQvMK<34C2$x9$6Hy`(*ND}+8yY>g1rfibRoke~-YN>04&YEG;~=%DA?w*|OI zU&y0O^9hh~pYN>pbMdJKeHs!RH@>OI%ElJz7*#w}<0M}!C>~;OO)WkR&qqr{%D+Sm27ItZ~Ezb{0@=+3YC~Sv3*Yp-8 zf-a^N)0ID7QyetK^=XVq(6rEvN^I_|4KdBU9LA?aXtQdqK?Lo#Vn4t& zT1lX6SsO8xS)2P7I;uUeYNS(aXXrYUQ&hDzUzremQKFLI!70&w-n!s5mnN(_PVG2R zYp_-MN>@06Jbl8tfuks!oeJglae;m-m6DpXau0$(PFS}l-Uv5M@N^l6n9^)@Z;Pag zR5SVf^l-S~fSuGhY(Gmq?}i8twxT|ZEWC!k8)MpvHMaL*&_H~t4S!u^gTR^bo93cX%D$BVCg;#W)K(smbOzHn_`m ze0?aBhCo3vLrTabj>5Wl_y(q%^x_*qLx0gvpv*g}(unFloo@9sSSmE#$#+~Bud5(TZ_1pxR z_B5qDS^+D~@e^#>cV{+dbO!eFXHTtUP;Yrk(UUFqMr`&`IZ&*k&sR#2D710( z&_5-y{vFS4T+6;FM3qLNgB?}#P@Ji5e9O!B$$2IMvu&_5t#O#ccCgGXyj%aVwZq&% zszQwPoCe|JY=??c&NjUG^JBW)LzC9aKfZ>e0=n3%+U@og(xb*k4VjW^S0n(M3I}-9 z#?8Luq0&e5`Xy_Fk2NG#dyMOa-K8XKgC4)sb(AcFy2}bs=NmpSFi^YhOiQT1H>Y;| zOkQP0V#`qJU^RhvI{Ra@Y<#;k-T9M3-=E-*lU*e*$bp!1{yK7k$V{MOeI#h1k+8qoO9jGb&3l%%xi%FO3d|x{A3D`%q1b zx%`T0yFt5!Dov6feRR86%GtGE&47y%-WU-n?(UCMsWGYYj{$L3_nIcebvyW;VxVsp zo{SAM$2{$VciWj>@!EhhK?WmIXOWddsVKhEfxua-E_vyYpdaL%krJ5!Q&ic<97_cV zZsoh1Gu5yetD6LeXrnVV&v5m-*<>>do8ZIBB38aXJ9RZ-*q$MP!qvz*ReEE4sipdZ zVf7nXk={P*aM^5C$1fNCu58tJY8cSfhA65<3G``qKwMt4tJSCn(sSSYvRM z^&6vcj|ShW+GaEq4SAin`813D0ITN*zbMQnKv=R(30FUXaI^B)={*P6Uffc$=F~F$ ze8uM1Biz{)HRHA2+mPnv_M^k5bSiaBa}xk*cmF0JfXsc+a?464bocgf4~;SGZIYDb zp}k$fl55*g*~Y|e*?qig&fI=J`cjmWrMp6C1lOWjyBDsZUvF7K%_9ssI923|apaZ+-7Vd2xz4yXC2@VsKKn%Q8g5lW4+W9Z=+hhscA-&sQrlo8E!cs2ND z8c?bnr@;6n@y1a4RhD0^*dRa>$i=+H==C3cb2j|Zn$&4+i-sIumHvh}J(pfa^T-IeZ#u_D?)r^8 zn*>Y}Ztb?!d-7^6pJ2u#g55f{p6NQz=j-SUD-nz=fhdXkk$F1clG7+aAuRN-Ep>Uo&CAM&V(Rw7Bv-HQi2ie@;Lz@EGa-+^R`cs{0 zuPiT3x46W8M@`YF_>%eq{Ax6g7?0}ukAV_Lvu~x&ivzWG^4V6W57Aw|{nF|Kv8o@P zi)Jx^Sf11(>wbal?MX=`&MK~=0f%~Ne`*o;A4z_1xKj;C0=I6FOj(r?Y7f5{mE7A< za3z0jSZlJ;R1+kbUSv&iP#8{!T79l}A`?5Nkf3KNG;iCrt9R>!Ydbfm~derQ(| zWu$nOSu+U>->o<1fjM!v2`^397y`6C?dgG0mMgQPW}~f-X%mn6)DNU*JYJh~u26amt;P~o7ogKHJg2;tciCO4wa>-5 z#ay_x`(nPeb_$-FUK``pIYoofKj+nk-xS9K01?b!Ci6CkSzo9A9$1HsRume3{vaX4 zmQbv_wBjFrtt~{xUNSw$LMgT~Rx)&Ci8{-*b2~eL!gIv_z3os%l5yS3`pwa;%!w@w zCTo7DBC77W1KFVzf|0wsTBNH4*`agX#qFOS&5t==QNJFrWli#%nkRi-OS*5SMR#ii zgyER$YkFonZlhln6Qpm`yFKW_Yc{Zdl+X9BZTWp&YY^(?m@e0x26x9b_vIg9bq0_Y z>r1{u_0^p$F{+ph;IC7SaV_&t8nyau+|?ss{-Y*8V}DTibD2AQ@uIcdwckC&OoO*vHr*bfazxfF3@Ml=wQlWx z_9gN?hRZgOx61WyHGOiXdrnJMVgad)?Hw3( zlcQDA^%kQ{)zE%n9Ml;xB0<-p2U%!ZI!@sO5mB^1YJPWdg5#@`l6xf&M&F9LIgCTw zmcX4V;kl{03%{D-Tmgvroddw}mME@4dXI(Av0ZTz2GEvfc_X;W26(ecdy-Dn#cEt+ zCOZ@p48g}aX)OswiFcw!V{_a7txu2l-i}H{JJ0~q61(uq8Z{pc6zKy&5!)pQ2^jk zJBPztKYDjFyx8Np5>x#AQ4;D`-4dGPC6n=4Kfk`eozGCd+GiO~Ov#?^81F+&rH(*C zI9pDEa;?iC)a`K6o!u}*%Zx+YZxP4T zzd3?&nj0tyC%xG3NFCDoVud$jVy3c|#!~eqHTSzNw;A_6ic1oYri}3`Jv#WE3e~#j zn>~A!lRak>f_^)6^kL&CXuLHlAR84nug!g$SI_-4SK1k3;84%V8*wI~-KfE3+rP!! zQ)D8_n5PQ^nWIa12$-2y+(SGK>?#|G7pCju3jdXq!3u-+$s9MulKF}) zbuUDD#7fQ$O*K#hPE_nCB_eu7jCw*X#9y5~d1vq0%))TBoIvVw&?=#;(tB^r$W(8H zVdj`-i#-m%d1I6~?q$v^y z&yKIZCFi=cw>}yYPMV{7AP)C!Ud~yb+4=h9fMZKjlx?S)MouiBxw7Kt7sYXf<&+AH ziH^{qn`U2*uj7#QAnh--j6CCxACJ0gf~bOox6h7{W-jR_S3J9l(pl{yC!o@@h}YKg z+^V|Wd)_s#jIpEj@2c>H88e=0ClAD}_vPl}Sp!&|Ei~>jMX)Tacb0A_hz(2i7K2i>JIJC)CI?0FhWM?M54`(3eRr zPj_ZDZx}cDp0_RIX7Mvnv62d}7|_LG88PK@R+XW0EZe|dioVA+0BuDPUE1T2nA+3E zilt)y)#~Uy62WgyDz}LJdM3tzuT)%(D@CoO`T5Y@p5hi0OVZatJ=09s6L@K>k~94Z z9ppMw?3p+zDRbapQO@CwkL;_Ni{PFrW{c+NbJtC;Gp7;XP zWyb>3>&7t|>)0%#?VeKPsu#Ad^=j}YC-#QtPfXp3zR8gi_mZIgh`~Yj_^p~zH*0IE z_C1EtVLt{%?R%Yt@X8|sP-;>Pd12iOJyB7Zd4lJv);d6CVr@YpqU$32+b^9%^=>9H z541mew|c%qidEtq8)wyjz6HhLE%y#o2QD*tZaXqA{|O7|q~tljcP&rE*!rgCKJ2+P z-IgN9EZDpH8(VJirQ?xf7QhcT9(xB|fbna?u)Z4NpHsi(mi~kVXIYzUNnjX!JYRnI zz8)Q*024DQhRk_VmQVDv=0<^#a9#E}Vmf~GU0t^wrejy|5brtst5BJW1mloSC zhXpwNEmbLDka=~>LE>u11klv-PaXIX1T}y{Q{HzDmTmL_wZ;21gPXkbE)YnSJ|&Kmsj;CQ6xkU~TBdQ~4ttOsPmb1wMc?tdSQ>{KWRYAw4oLUYv!#S$pnX`Ll^jQ&OTh?1hMKIA$ z8nUvMZ#yKMSH7x!mY#(Qi2}7V3m+49*fSDC^Q^WWTvU7G0&!WEbw637l_hP*^7khP z_EG3D2qT272(#Fn_^H!&QNzYT##5iop2hN8-J1N4qabV0 zR90Cq93tZ5>Fv{zE-Bc`u*FQ@BysNGv-|#ga8Ptfph?fD^pj|PK%>dAk8?+GPj7O{ z<0M_Zw+VHfYv|vt9XTL;u*}lbzNCxMB?%sy=1_0Q^r-2U_-5<16!SEKLQ0&V(`bL* z5-S!kpXMp9oz0r;5L>l^7E`)%ou2DRE%7v_ z@9y>nR3=#)pcL!fY31{_Rdg5=eStZ2IIUw;bK(*|*9m|Kmoi>00fF(LVBDWW%;<4k zZ8EK7s8Rp#9(_OfC~%N@@aPR6cvn0>p(36(u_cJjMzWBUO4y)po~(7k_|(Oqo_t$- zR_zpx!E=H(y6cb?0`=>8)Qe>D?8w+Q$ywEn16rhMA2+dzOi>)QB%T}VoBoJFSd}Le zgM_TZg@Zr|kH5LD(lp8K`@ra~K*DsmPwcbWOl?8~F~&h#IQ|((Q!iqkoFr;xXD=Ch zqm@=C`Jqm-N+5Z?Zamt1At%#LIo&G!-U!2&r4s7s-4`3WDH<0nPI{NU?BqofM3`+v zon0+ZnDHKmBqinv{48qc->>(j>BR1^HS6#uk{OJ;z82KK&NZQtOJZr$c>;$Rr zDpl$Gby{4|9W|;R;~&EjIyN$I(=;h=jQDgzPbab8hrAFR)XX;NEc>A;J7w)6%UBU& z3qA1%fOvj+6`Pq|nRJ9pkF0ds(v%Wfd%{Ldl}pNYr>NY5^j6+iU_Y_YljXk!9Yywz zkTxJoVtxJCqKqR>7LVyHMI{waZ7If!3Fk#Tn#wP93u<6@xz40xwxQ&`sj?0Xx&8u= zv2)q-?3b%YqT4wXh0?jx=N((u-ISM#&rA7mpDqMiQ1Hv%C?W=ka2G(QudA zTEFVIeY0X#&!n(Zy}_K$M@-ULCXbvO2(Z=;ob9qc@G!&A$rSIr-Tn4x$VZN|IGSj7PGp8Q&d-yUgk4 z#>p~o#LAIh@)Uiyj8$2|(Df!4tp=$*0JPgj9X2`{uvZ5i3SPmyqKlZQ^>-t!T`QCA z_B~hq%%CelOQa)EHARg3!dO%mwQm_))+<}oF?f3vFSp5Aa`ZAhGr+uS2EK_7aUWy+ zfhL!fft(o9Q5%zvReD{}|%ZLd^U(({N3<4y-GGEyQ= zj6Z;0WEjD&%}-E`u92JaI*;pL0Q^c&?gZzM4Tp=SZW@Tz=eq?Gtxw z%cSPT4-dnCH!}Bk?x2h}_n*BZIfvWHBF}M-*_xD55#P^AT>Ev_3{*~}oz*VCzI2<{xut<}R^T>WLeT{m__rjkS71SMgVZ$Ps}wb8S&H0c+q@6KW#&;gCqf}iB} z#rG-lo-*FEqqs!P*_6Ahd40I)JN%X3=(yUxxlP8&9Gd|c!D8_^%k`@VP=89Scar-b zgep`44M#@rx%@SG+2D>dI?4LxVIV$k36cjpZDHZ}I;PL#@9sW;>; zXrf2ClP<1)OC3RsTNJigWafdV71qKQV=|eQS3yG)W7WS1QY;f9Wo#D$HKBhxr4ZLb zh6KqBw0Y0R*jBjJ7EtNCUS!w&ka(l+b&l?1tN>{VUSJ+IV+)JXF`H&XXZk6Bf{r%~ zqLTvWtJ;VY80F6_X|ZS34Lnz}mr(XL1Q4Ti+@a&>mPU0Sgsn=Zei`H|nXQk@rLX;ykydVLd#XS~YnbfZ?}a zoQR%09M!1YRBM|{OUJKkLo#HvBF0OfO462i1vFW|)E|KOT8_Q1>(0iq@(_d-Bst|{g}BWFx~L`Jx4&SJ zthmwj4K&}dEy6~Fi-(o02a4Pdh3iLw!Uvt74Dl$zQy}@6JbP(89ZXTLFi$ zFHa*8@9@BSva=(l=%hbY8!-XWj*qWv)2NpV^cD}c=K$tsbvv6_YTZlN(%63JX%$no zN9)vjqj1*-_`NUgpDpZ>@NYO=<-^jB1cs-XtjB&l7hxMsX1Hu5o!oP2oT_qN8ow+! z0_sY69V9maX|_umfj4*L0M$GSDBS=qb6H=2^>)J=OA z25{uGMYdBXBrMOSXU6xQ78tOr4mO8;=wOBq^gKyZZCqIR5L`iK?EA|*4vl9^6s6Af zjz@Ql?io>pUzGBF;9)7MuEJ6S+E6IB_4^kBClvJDErV`yvew!@_Xci1DXB(4(=w+} z9bcenm?mNJcovp>5I_-E?F;Y>nDhZCe02|#SQ6CvCf`%8GIv2S30xVvTpSGO{XDIE zrB9lfq;pu{IcgY2*G0@)6O(DAJxQkq z%j5XB=+3zuph3goZY*V zuuJ-F`NToz5|HL@O3CeEy{%rTB)G55(W&b>cLq+sbmjip|llu2Tq+v9AOi)vx7f8EA1 z@jJ}4qH&h|7;lC>+Y0x^oh2c6;Bc0-ZdvJ>F3Qn00`S8sb{1_bMx^ilwZ8rZH<>L zI!Q(|1svF(OD25~prOy57Aac3w)bc5B#sK)hffLIkym~CEH=)m!iUAsb+ zN2Be2I=iBuLHag>r&Dc#@k`;W;CP!T3wE_%NsY0=7aybT-+DELaMxmASJ?|1e%8^0 zrk?(v6P%R5m80Jvv^L;ajd-91YD5BtZN1=lmCwx0R|M^#pRnUZvV7%qW)@GCE%$;O z#P%egxA4;2j$+twr`f`iH9`YBT^BQ=&txg3XjnDiKv|7FLlmTzi)8gB9X7B-=@*$CuSk37Am54`s4F`5l3-s%52=q3`Z&y8<(>pC{w9aq zFeEv8|BF49dpq;O38HaO5D#no6q-(dubhgra~gQ$Xl_g{b*K^?Hla&TQ%^SrP&j(6 zeoSEeEzJp-N|{Wrm8o{7wDCBg4(d&H6fkG;AjzxS5!nOZR$p?*Jte19!uTB&#Ii)^ zlhoHPT-K#sdmZP}#}W*TY@$i+LN5q@=a8F7T7r{)4`3Gp22V$>bA?dty5CIC z%Zoa~>&vXi6gqF6w+_44bm_?b5g#mzN@|D55U|sczhIF0X3Ptg!s;_OWYrHyBeUi+ z8jsf%1EgPnxN+GtlbR|InuJ%XTX(3qdLR$k3`5P$aUOKN$fSN)Chuxzo19Xl@(+Ibq`X^D)0z$tlko1`lSJrfS z5M0BQf)aOkwwUq^N|C|J3d}Y2ZVH^8P5`bcZ4j~7_Z8`Kv`V9vmCS_Vmq7%{8kuoq~6%bSD|0V7<@t_1S4^@<6JVc=zdy1wdA&?3aB@jnb1esVqlV2koj!S=vVr zm)6lgW-qxw)6KwEUbDk8_Xy(H&UG^GQNI5DQiy9QP-X&o)E6IB9cQ{iC2Z++NK*g) zfKd>~n;=(UBlR49`cvUYmR(~vkr@uyIsKjWo-VETyQ(AgCmbQ zv9U6h7a(FTW*vfTvZwY3C{?;JieVHrp_e%HLRSN6jd?rz`AGA7rQm>nIt3(?7f54o*4DK zduaJ}z2toLa{^_SJF=J}hv4yW9OZrPGSyCBHs9mTV0TMCYas03X_>EfA#n7?Bp<=~ z<>yG>l4`QplM;zqX z9c`V8uB-8nYKWGg19^K^HQ+$FtL}?Dle(95q1_)-sjmz|QT})&a#UE6gtC)_rS3vM zW2EjIv2N}%x|SfL9v=Erqfw(SHEVJMgT)*rs^oH1&x8o&=h#kldX!6ma6YEXk;_#f zY5auQE273l*S?(4bN<;R!i6h)rpRJKx>-iSS}{4Y#WGhnb#cQrmo|hS*#RBO0=Aq; zeCkGTY&-nADM1&a#myXHInD&FPre3Ntr~ZLM9%@clj*jlUHP{NU59!+DsthlPPvXd zA>yl=CR?ksRF<9)DOTf84--2KPAqRge*5_}{u6YZk|x5aRGL>5S1farPyc@C6#EZh z9p_FpHYPCTtqC{**3(~o4UDjcVyqCRdA1+;Q00lBHAjAMtKflNbi&k@bSTTINkCg9 zoVNrJy-~9knol<3dj?zP68W<+h}yi@Eet*3)4|(>p0D{P4Nuo9A#sv#)4xRil(H_Z z{`?uo1eRrLUU2i_A)lBwF3o%IiRP`G1RlWn9HbT-#e~B_?-A7)q3!;DiA}EXvQS^TpoID5ISEkiA zLcF)%JAkh#NW2zue%bd)f&N)wtiG6gPeU)P4+g9(>9^MmZ;pdW!N9g31AdgrZQ4O- z@$2^(*qb*Ugim%(E@6f0p8bFhS}%Ln8cAMAUSHarUJ|D*Dx7c_aYP1GhJxaQ;5%N= z>4Y!#1N)VDhDW~8h@jJ`;Dga3Ut^h#R%!GX` zA*MMj!S$zqr{1Eq%tz1|p93k4N%VY|iVWKszf!Up@wptObGoaX1H#WL(-5T-Mol(b zQB5Vg)NUkz$+x!3RC!cw+W6+SM{vS9jIv_x2w{}VdqYkc_rM-E>#tU36#hyVH?4`G zQ?feKNnVp_0P3h^n>)X{yBLVJp@Hl{Z0a?ETX(X>iiJj&%E1XTp-Rd)r-Yohnm|uT zc?N9V605+qf3oRTHLc2I8(=!`ibJoF;m#po~|dYn(p z#?U)4Q+RV9B3j@394)ysr8EqSC*OC)3JQ&|!S`vfk1A72V2=ctEE%6$)y$P(z&L*a zMv{IGs#9``y58sb_1iP+x92tKD_^;sR*+FU#<+V%GNL2oKvZ81B?(4N#mf3jKNa~U zEci$UTEK62{v2LLk4yeo5xnuu4v=LZ>|hR$gEUa|<-VfW!y0TT zf^6uG&ZOUa|E6{2-BS@l37CO;yjYGb(**TYG5gvX9lK1QHZtiuu2Mj4n~>9I9Qj z;GBGFFZuGu@;Yseu|r0D(M{ZSbt~TUQmz=fpYNL6M`unSl1Cbt18qiD3ZS^_#;Ru; zeuTN72C>r&T6P(exb@rPkWDVhCrF5c7|nz%;~Nof+2p6WUM|n}7Y%lcgRr~+vgK7O z9;EqQ1A=Po-J`O^Gke8xEk)HaF*2c78Het^Z21Nz^ibjk!bTuf#E4I|(NCt)+81)1 z@^wMtNNJA(>Ny`k*%kdnUDU`T*HwxCs0Y;6HknG>IJ`xKFo`0RW)Kxs_I!OIY zphWe&h5Sh5xe-S?Nx2a>GZGT#**^Z`aX80TUy={7y~3V?n(*gE{u+&X{Bhm=6`Jz@ z-+j&rEV29Ru6J9YPFDM$xm*P;2yLoj98F(=IT;iKiY^21oexzf*g9rPQMF!xlbOwgd^AU$KDK$?KQ0G|;jcuqb-v;t@ z2tlt{4Wl0s?V}K+G-$}^8X)Iad8x$;xT}B9r!DO5025^hrfkEJnt&j>R=25?$SUYf z&OdC54(BO{ftw`nyf z+}4JmCDvm*JHW=Z08V~C)Glk;sz{lAy6-h@ukd%3EayFUA&;-u&fc9q^NOik zY-!1LoFYjVmw64?Zg=Dj2;GA9)$IYSQBA31_4-jz7Gy0SWE_LUSMIKD^{Jp!U8hq- zDcIHu2EV`?te+n5(r2D#@{3wqoCBT(4aj9wNWxNIF)K({S8fqol1HRFOm0BN%+A0W73weyPWnk(it8&^%pIp-yht&5y9-M)W)_7Y$mD9 zmlOeFDozqaFyPU6o#|`%?UyF)3W7NmlE4Gl0f{8|$=c%1-}&PD$5lXu&ry-;T+4$| zEf`cArpTcOQ)So|5O{WqOE?R0jJ0aGZNq-HFj7{kz0d_Sh(qYR!xUhJGnTt--Qyp&@0Q8 zR6636nnr9{8U~*H8zl-c0>hx=9H*e5Sjm6EzcSs)mbU>mFM91l z3yO)B1OvJ&o8bJa2BJS6nJ8f;UvJYs69(U+b~%Tr_~|!;wJcQJ zjrr*&0%Pvc)Bko$y~W7d4p=;Hn>OoXO_w?br{?(^t6ddd zojGEq_b)zwWDEKUsqt_Vn7_Jl`g_oM&?$m$IKAaZ>u{&Xsa-+JQ#Nmt2arcuB)4gE ztTgAxi{1O*gGj0U>-ul3H>F`1F5PLSi=!kQ4`d>p7Z?P_Cea)a$jZrs-Ctp9L|Waa zUfR7ga{2E^;K=>+E1q1PM`H-c1ii9@h6|9}aj-j-{@7cnHu_5%>gtr_4^bNAv6X16 zi?F<&#$u%+MFz&~Kd$I9$?f4&os!4`VhDSd3-8{>_KPPd$8Jr?O5+RKr^b*ixVkif z(cq=0N>fwa&U@Mp{>mzl#v;2*uPVkyIaFe&Zz~!%WB{5uA_M^tablGF&jiQ!K`OSw=*fN(6yOS zsDJ+mqbKff=#QNP)k}ZL8+J{Qu0BgwC;7hxc1P%qXGeUX2hbQ|K#mKj_j{D1z0;KS8BL%7_|UneQ;q$Hwo|;sFPOQ`t!Y*T~fuFiJ|0l4f0Fbgd4mY zG?Az3SqVFBxJ$=Y!L1Q7S`L-qe?~SQM44yAufmXt4|Iun%`0l^>b^jFiL8~k60Hk7%R z!f7whKMTWpO*Sz!Z>}-?`Iy(m`+&5R0&*a0z7pnGhCxfy8xzCGHo_Oj@XP`eK*FKj zK|kwXDf<1%A)tUwYD_p}N&3oQ&W44;0U4Se7C{LCud*~{5eH1EkFFEK$`L{KbPkE-CrxSg-Sc5;2|zNUs%uX ztNiU3=sgZuY*hxH;X5g+maI;0_?14hOpNa;xUNniQ|ZbML~ZIXH>S^K)mZ`jGF<)H z%hC>$$F!Sk^DyBv>~{^q`vX*_u%UT)^}{e2dJOWZa1PTEwGpupiA^*7as-FYRpXc8 zmPS!mbMsPxP~s6==}<9*!eZ0a-*FmEEAL-V4yvtlp%JgJrl1pd)1j%!QYAw?i1(0V;V8t*u}-%oi3) zJfCG zu<69*TfxT|JeTh&@1#JrF@&6R3Yv5;O>nu+IO`#cq$^C^R@DTMRYMI9rBnrbr-icK z)guPeaw-FXii_>EJz#$Pn18v>Ea@_SZf2Ulc>t_mC zC#wGZokSKDibG#~jQ`5(MC7mk&R_rg&-b#7IBEY_6r$+-K;=_JX5@b_6SxP@{zMod(cO`cyi*u(Ut$BJ2u0!%8C;u-n)C_uXO&OyQzOZ z#DBQKK-eG$%zpW=ZjgW56zcb;4-mb0`akxNsyHHtVa>nN1JQr^Jd%#gDB?d~(38~c z|LGmeqsIM2S4jP@2>JKy_0LT9zx(`e87}J>1$3RQ$I}1q^Z%RAE3Za!f$Am2U$^&P zXN3OWjko{LZ;1ct8~mRQ=64;3h-eP~SF_!}Ww^rs>4?kzZ{HNrxCQh@`-e0Dze|j4 z0AK5%RHN-Wu- zt3horG`c@IK?}$iF^s?;0DmO&D)}xTINcs_4K2mbgY)VX%%3Py!|5qqLq+E8SCQgB zjE0?SHPg`Sldmfp0 z*O5y|)gyx~mJCsUh~`qvoT_*!t{r2FDj=HU1wkD*cFB%+(G-!V}uv zh#C=NK%`jCaI6_n5W|VjPM)Y-$8o)x#DGb==kF@{BigP=C1e=yG za0aRJAsnejW~OK+95@HXjc+hj{`$Go!nD)*bm;J?Fmt4Hq4Bx1Q5lOs=01m)vxbf#lAGfKUi4?>-mW1)*X9g<)oh^xa76o{ zV2L~?Kpa^lZ#jl-cn;qJ#|UKxYSrzQQBN(^;JFA#l* zR8|wW252vLQ*{kjriMv&+_57+8zs5 zo0c!HFK4B}JM$Wb0h(M#J+LAv2!R7HYaUL=X&VMF+6kD?)9$JpLPrCE^)R<$C{ry9 zouCr-L?HwE@qG{9{sQow$B?Sz4##Gmmd*B3ag0YU)-qs6lW28eik0O?1hrQHg|$V0 zEi{LWpTj}0RaFAot{)Nfx~68ph;6Fi3t-H;rO?;$9Sn!E?T;~0xFXRwzcqL$z>;>ZQPzey~NVdwPS=JPalduAqPNqD0P!uEXy% z$BFtW16@+5JZB50qa^67fVpJo2X1b93%s%?6TCLfn`io9fXtB|bZV#g(SVzyc8IaRMNN>P@SvBI)hufx)2&U0aE&KiPQ ztKLly<^=5(XUxC+lZ7A(0=8H1IU3d2+(nMprx!~6tuaRT$AE6Wv%51A#Pw!DDMkjfQI;%~XOAu?9nek|TP`D*#mN3sf z3mi;Qq8svK*g;T}o5+KZ-+PIHENdLO-A}<8S;UsPB@dY+K#CSSzPCNj<$WB9193?S zhlweZeQca`~Dd;dT6&fl{QJ&tOe5+ zRp3U9SH+zZc6hK3Om652aX8<`Sot24;t&)%EicnA;_ z`OPK|W8&xC9rQG$K->5im#Fz17-iApZ79#8Ly?4=96>?y4IpFkMa%@UU>kzwLW*b_ zv(8MlvjgYgI3u1N@coiN4fXy33%61h9v(gnRdxR~rJRNY*c-J4Jx|#f(g5=Ud0{Rh z!|`ozYTe41rQ*pMi+|fH*Po$ya$qyXNGn4Wyy*uYJ;UyLo#a)+7?>_)+SmbK)@c_o zkD&NURxQ`kJ#Y2lhS0`XXf>2V9dQodXCNTux4x5p_$(P5?+KC3Ohi`M)0ggz9iacMi8UY_`xqVK~?+H=a^Q14pU8G7D#i5QFhQyFwBKv}<6U z*M9aZKg0PZI36l*B}G!A`-$Se!sPER^!^!iBtsZp;H!e*H`VzWEjTNn8XYgdgAVr* zF520I*j~~$Aff40)p35mJhhR_1X@krP0ksM^nY$PBIh1Bz3?VTgRCj}CW# z2O1VIuxjdBV21%{$VS~cg$jqL2@iIeZKj(*h)tyFjd1CM<~xMI5;!b zRtjvsiHz@}Ie$+p_w@(BUAg7VybqDk9$U1C0T*$CALureW%1|~FgTJa-07d}^mXy) z=F?K{KbXqLRTTDZ^gCxR-x@26N25My^7nN^RvRSUF?AC-Vrdw-twuLJYl(2TO|?+*ePBaWe5of8r= z90#K;pEh@4-&X@ViEA%KhJmmx5SQC8F;v2+O6ou*6gZXmkiHrhO-LUevYkoDJZpma zC&haI3~0O*$V?YzzCULMgnu~}D*|pVl~g36CBeQ5ik*jzsaWC-erO*sn{heB1Bb~x z;M}d(xi;#8ux3!KDg0sLa`%P#m?5YBLj78ie z?9P33hhK7;E18S{{{Nr1g-&m(hGJO->H)*X2l?{)v0;K1 zRiyhd`24H2XqfviV^gt|X%X(nUf_j^{lcAgdY@k7Cdmhk<_D;RNm40BfWw1;qpGj$ zMbH*bxv)tuELY3+Z3i1t@RVMu({Zr9?YB~GJZ$QHZ{e_nE)%4W4TILxnB~)T?=~F; zIua4ipgN&6KUgl+ovT-k97ALEp()pgL;T>0Fto|2u?->V7zhFa2C^iz4P=#^l^jGQXA0YhASgK$L6Mv!XHcSm6i5z2NwOqMjzxXz zDJ{GEjMMkN-?`_{-DC9V5~y9Z_kO}!bImzdz=OLMPhWofGl_#0-j$jF9)Ueh?+W6GB9+(Auw=848`s( z0AYVCs34}hYNhp@0epTW1R^ZQLBv=*_dXkR2pp=GsEW>ZMA;amNDPCC+z>`P6asl| zZYM!tL`d-0pXIlkr}>IYMm>4*kSIk5ZjsDQw(WPu=`NnJOv}2Aw2^_1D#!yO>RmXj z4v$fVZW6M#`sSjqeFfI7;}%e$A)9%g-x8}K%8@^nVzH_cslk3Ce_##fT0qG4hZ@MF z6|yZ>WYqNrPT93qh$QDoT`OamrSUQeWm~z0uU>x#+iA(bcMvsi24mEGZec^CCi@*5 z)izWCfw>X!tF?h(2!xVc=8edXum$qZ79g9WK{&F1L^G(n!2;I#dNG4@3E^i=Iqn4< zy6w%I3H7eB7GRTJ!y_*gq3u*I zx&*2}5(2ekri`pC+GJwI$$Hx$>Mc>1uSh4(Znuv;8hmjZ*Y^hrqNwbj!4cIzq1JzZ z45~q{RMK((Ybg2uF6L&r7hu$1WB2~w!l?g*PA?_zpc2^0O{o?`>Eho2zkmDrKY+mh zNvY#ss|x?S_bc_pB9+(W^FL`Q{{^V$&3Jknj8)=UE_?;iCwU1YkCv9()L-{&B2M0= zOJl`*Y8{$mq$-RiDA2Xp^sn}e2+H{D9n#d~!H3>0eNXE_tAolGM%pb=$NFHAmXm;j z-}giIs&wM}JCK9{bp7K;UUU}M8-jn7X`oKS`7dzlKcV6OXW%NgklTD8$QuRbRbY z_qK2y2k)fc&<;R0wERVAmcZR}LD+%FLb;aK-{FrC0EDd00%F?kiwa&87OWCc9bcm) zXN#HNho2w4@eo3v)OBnw(dBvM&q+~G?w(H|mHWh^KaUV?zV z!(S81>XjgCVLrg86Q(jsxs$O_qHCx|^{nGFcQKyBJZG05we%mjOcwx zD1C|P9OVo?9I?tA`JZeX$75jT%=+Rp0iZ+Lw#;%8xF|e=P*iwhK8Ca#2@h>ah_$6* z8s$)lgpw-F-bbSVsZlFv`SB!UJ;^oo*rJrAwDJ3Y$ROS7pm4>i0$qyty$B8JL=$@K zSCpNSHDh)ijUk(1DK`~23J^I8@Q>fXqlQz<42U# zb4B9XFPf?N>8&KfY9f1KThtk(BH-+>Rs+5lGHXY>sc+qFAeEC%`#k-QVpEPNcBEUl zZWIBR0}$cQAOIfuw2Tet5%vG_Y>cC3(K5zRvJ z2C~s6AJ9OO<6I8c{tZI!$B3n~T87QR ziKU(xrYy0scVMkE0W)AS`4XjM&X6BY7>EBP$WX-}k5#^m1Sj7BMim3BhCA+tBIKI` zjXsShj3j@2Bipv`t1!%22x0Xf0P*jnxuxn^4Q2XvuGe`#3t91;Ftq%@TIU;Ndu;Ep z&OZ0BC{3M&k%&eZEedmi8Nsu7igG932g1^^0yCfzoS|n#ckH4h+ z*_$vlT_AQ%)z+wajspN#c#uLM@Zz{2ohZ-!Rb>o@f(Tf7%ZgE!X;oHxTj1<>)l(9Xk}6=4%Sy0O&M|(^ zOEJ6~1Z0)Saw)d}DW0pqz=Rp0@8h?Ae2Z&!mz)5MZPDo=HNHo@B3&7Xp{J=4l2r(h zUO@g&TDgECeQ=i1E@NX@{}A}T8)2Pvo!$kY0eXzci4e4Z$x9QNf&Fk_LkB;PAKxPA z0jPdEDlR!OMBb&nlB^!z@b?ne?HcmELHqK$sL@~y1Ysw<)#5dI^T@We07o1EbB4eN zmIj_6W?K;9Wwj|U+)cSESqMjc#UY`*1xXk6SyNovp03}zDZdp!<8cUfIT`@D+`@#d z^=vCrTq&~>+3zTZivS85cvn&NR3cg+&~>2p6sTxIt}uaVpqzyKj}uxX{Z{;nio7C{ z5h|F3z`F&|mj*3e8s_oAvJwK64?m#nQ=?1eGXswd_Lo8Iq1GY@wjV;TlkZ2wZ!o%% zb@_MainOka&1X$MoESsi-Q*)l zKYiTAW&cew+r2lJ8mQkiz%rwOkbe(gth|eFD1UfA<&ZSrVV;K9H64)4hHV_K5=BDO z|2mqGiJb;qe_)IfWHH|WiVJbQ`r{l_?OWm7XQ7f6?937f*Fk0xh>=ZLL0NURLLCTL5p2HA86}rF0n|uE%gHT<1{U^jJ&?n>&ICd@)0qmBvA8U<&ZTRzf zc>V@c6Qz=eK_}F)tnV^PPeDq&F2{af#&|Ot-a?#s5#m}~ZJ=Z#kmB!C(;Jo~1f?|5?1nK%91KAK?lzp(CD;H7bu6aGgJ0+szo`70?55MDza)i0G6wSSV8V@6CRxv zFgbcuKM06{;e7yRa?uT1FexD)UarT`>M8mqY&-@!PuQzcb_m9M2VPe4`a=){kIn#1 zEI>HrTEC0~M)n)9$efTsA^93d6DHW zt}#6+={K~W?7c#H_-|jqjrZ2z;(TPDqCz~JI`$z(N(xS}1C75Amf<#fl*d5j)e2wG zPVnN-h(tgVE{sjB^v9wXJ8Slxtb)b@Jm!K>ZeO`AXT%-b0M)iwfQ--l=a^eTM}W_(OPu=xs3Q zy457uwHq;B>538O8S!3Gq)-JMb0#FFI|ZQq_^QEsQ>dr!_7|VKnk-Bi_pzD~knTe~ zOw@CG&E$#K8mP||@~_?%_IgFvDMjL8r<@&Gv=K?Rg{(Dy9vH)LhxaFqZsAT3hY zc0(0Z?#-ni_een>wrn5;qC5wp#IOzRj^1&^rAO3o+&oaiMRkAxAaj$@a*;ND&sg>7 z&aYH0?I(p}Ej4bX>%Fv`!M(IvXRpL+&Q}Iydj4jJU+0!TwCHfXvnRo7qQ$L)|L2SexXihR@oV%?&!UR$=5qz`v-S&*sXWGdg>%qfTPMGJOZb%k zCxMN*CkmBwJWiR5Hi6|~jM1f@e>i`eX@1Bch0{t~OfPGC`{qjs&tt&?9rgu=TphC> z+mHDBVJl-!w^pW(S79<;xZFt{{-UUI{rFTLvMj!D+F<%zz}h7d$0CBMqfC%L_RM~)?+k|zTp zeG~$J!9lg+^CbKR^JdFt20E??gbCROc8-i+j; z3d+l8mX-Kr&R*RoM;6Ek#{LfpKt{Mm*E<+|4@r4eg*TgTL4ooh&`fGp*P+u4vFSx- z1)pLnEmaa-A0P9nG?7~^&m1++?sl6lEUgs1PPoHd6;8_%qS9Yqm%qb$zfj-2>9)sq zDO%^JY^|O(_=_T|`CBVxNCQ&2X4kt}W7PYcr3-J5 zaJf9Tdu8m#%Zyu}`8as+vDJ;1VtAI>4Cn5+cRS2@%nr~zEoY_H z{{|y6io1VdLMS=$Bp@)mGo{wfA6jfB$jGH0`cv~((`c1R1yQp>L@U>9uV1PA@~P2@ zm`Wbn@)8Zha6u2nm6eXHTMMa%RYBGX4`VnKcBO}y1wXj1hQD3&rn7VFS*@GtZ%i^z zO@`wi1`4mcg-JSmF-XUZ7f3y%)!z{Jbs;o<8-6`@t2c<@W&aJUi_O8s4;}KE52@)S zD%FZ>PoHI5U2a|nXfhm04v;TXB%XQ;h3F_-W3Q9eIWt zR&krR-tW18u_S(Td1XcCutx{WVB(DFgU}hbl2|L~qm^`K@iA-V`74L%!G8xkTXH@% zoiV@HJ*UMI?B$`-RnogR zt)>od^1+T+Py1-!olnBI3)>jEjz%3ivbGYOCG(!0HOCZ3lPUh~j`Mh}dZk;H6wR)K zCqJwemOPf&>zelFw#Zt8L}hVbddOUfXDUDI9^ypCp!JT;b0a){%SGDw-<0qh7KfHM z)$=5$6O>tZrdsFU5ScD6yVy)$V)DR#z{S-*+gB&oyiY7yJctDsK0BOtcXhAR1H#OQ zjfC+yb#c}Zt^A838#5Ku+LL8^wRWWXiQ#}FkZVf6#EH%P`K_air0!iR3%ZM{_=C|g z5A0fro856$6&V$$H|A3?@$wa;`R6J*n5+&7^%)JLmTP8`x#?SgwR(L!bWe%iVA8%( zlOn9jo4J#ozzT7OgVbos=vHYDZO`~SxA0O#U~BjXCO%HZI4PB#JeU7uywfQ>$b6p^ zkq=w=Q9m1qG#c?={?ymsr&lbTXCU_&H&wai;8f6jaiT&L!=^`P+%0@`;zPORBCgW; z4_~#aL{-Zf&Wd7J=XokInI59UPTEt#Y-tNQA9f1og|E~X$aF+{gi1;1IJFXeb`w-w zpeaJtOlR2%_odDUzIbNmS;br4?AnCf>2A8T<+m(cZ6=D5%$tQZ#+qB!vosA_XF65EMv$(tH8OBtixzw`u|!w$1YSl`W|^R8mYjKAp6o4dXBoF*I7fBNv# z02z9@j{%#`S}U`W)G8oJAXs=Y#2sh1n=FOXPfr;qHfk{Ek5 z`3)1Z=-@)kq75+KZ!s4t^?)-26Cb-s+w1)Bjl6$*XBb`~j4ry6FNgb@)t6@N-={Z} zzpxe;NLjRDL*b!a8*OWrq&<$8O%EtuXpO2mzdjQ=f5sE@2^`6$3)cr+89=Ngoq>WW z2)AIW7`!n7A{E(R8hObD%8C>{@m~j1wY^CC`C8nvBOA4_c;(OR^(Arn?ewjfdB>!3 zR-UGf`6uTltX3~B>j)e6f~jIq*BJwLY*7v&UcPU#Kx!AwU=zttmV2{(f1iWuHZvcq zkzrO7`H8H`YzJJy7qwI)dAC8F@cf?L&zwK^zvfj_h@380H0x9Lxv^@eSt1>K>%2M5#SWI_elqyN*K@eZVO>1=b3&jM#i7xUZVEx#uG@4l!2JP4yblOba~7 z*X)#V>ii@|XgqLa?T))iH|IeZf)S^!7Ms-h%pirAo)@tDl6Go5&R;o!wZ#R zi1qs&HfI`N2$qF+ia~}Di%`4wKz3n`2E)ZuM(>}Gzfn;AoG_jbxHxJxH-e97vy^m% zP?werM*t%tS(w_3Bca)%@nKmr5K4)fZG;Dy!>(>+8HP0uZe4Qpp+@C<4YN;HD4ocU zFX5*TnSAp{LvGL6FAp@$tzw=U7s2)-A33+E4l~_2b=I&~R#Ojh zMXz3W0atsC!c%!3-#(vDeF)5!(XzCT9ke^s+rF&dRIykA=z{#!<)JWE3E<9Q)YNa> zLQFN~f{38C>d-JULIeUx1>5}cOiVB1uT%%reL{FEy@h9(wD@N$I@N?@mhPhHf3Y}v zV5^&v{Q6ps46Gp&&77w46M3=)*KRYBIOZe6V_xUEb0;Wdr%9EKstEOPZ=B2;A?eN3 zq#tQ_>-f-h0KSNjVXa?dSZAWst#>4$bnVB}{mR-bnAy?LThN(dn?G>Vj zo*Mcq7trt!)2tVX*ZDClEJYiuuKG#^o{jC=k?}K}o4+6Up#Qw}OlsZE#zzM0t1p?1 z<=qxvE?>C#q{FR~)bZJOEOX?|ra8X(ee#qlpP17`*y(i37~_E0uF7r6crOSF-l@2Y6zL4UBZF{?i)^eM+Fg>8}lqU{PPlnr7*%g%S zWvsk+a3<)+=!rG^k(EoMP75W^AfgTk|aX&z$Ut_dq+R3qL$X zGfxgojAoWY5zE6J_O7p0TUIL^JFSCA z@ZBP2c+~H21olaNc|GXzT8UhFQIoeIKe5%Pm!uKh_mum}x3mgtAn!vfO^HgNCWhLt za;YFS;Eg-ms5D-Oq#IbK5+MjDH7*W#P7mK2;>6P%doYKZhn}|TTo471XODqHdggte z-q&{bubtethuD|F(8jvbcy_Q(rtM;u)th0)TN6a#_hFvHtgH>@o3#3C;)cD?nbn%z z4xf(Ay`4#V_{u`dqGwJq6TpqpVF1t@!a6hh1IYH#VE1$JUE@}&Bo|b>(-sxkTp6xH zE*YwF%?W)qDEqQlQw z>b+}>JZE{QHkZ9OUM(z{HoZ4@XJWtG)vqn0f;Sf+f__lK=YDLz_U(R$N`-Y3dCZrP zuF7-GzBqG+8OcaZE>p-HtDa75%q%w7WGa11>BQ$*;W;vmVb^Zz`k)Ezk@9q3edE{3 z)qQ1Eo2!{)dax8)l0<|67{`$wfH)(s1(hN5JIh^O-Kh@X{~eK$=NE1D?(9w?>rKHK zgsaW)T*9Hdnt*p-nCMLI;ze_xt>w(Q=J^ghaoTARfGmaZ0|hUE?2Bm1)ClQmm_ags zl_(dWbWg4sAF9$3bvx%Qcbpsw=GOE$U;R;htv16CqRaJhy-Cgpc`x$%KA8}ci z4c8g8<*PLew<;JM_f80_J`uvkDEKPVJ6PjbmYmA|ciW#FkqsSK#zf6;I(_P?oz6a+ zGdf?CQRIjVw~Ho7Uj3~YUpCWCs_wVg5NEAef<3EIrC;exfj*R+9;Ynx>6fp?glwmZ zHSWE6pJoWvol7#@p=fRX<~VgUGS8R=;#%EnnLvZiG))_*hSxp{u6?zKRZ<-MQT@b- zQAN(V7!Ax;!S1|H zq&%gX*9B6%?xz=T(-#$McipTu&r>Yh{MNl(sGEW%5sQ}V9+QY{bIDfgt%Y-#<|of~ zq=njf)>p>QwjJNx??dR~_x8j$M<>~4%s7?}Cn{|oDPEe?nJ9BRT<)P`OEPy^Hw8%g z+A3$*go|CU@*=YWx^v%Pv8aLCd+WC*w7mcv+)N!1Gyy^r#vz8YCi1=M@vqq2vn@JO zw3cQ56t6##^mGEb*p@Gs&5YWI$^62HOvc*YT)TwaONNnQ@zgxqa1`q6gS9h_b}>N} zZ*$?ur)ary2J)$4sBZa2c`ekmhe3*p%bV!_mUVSXOl@i-+dvbVc8q_OqxGB7vA-ESzpO*l z5O?YGd7)$_D>sx~L8|C>jQLdYgipU%+|zNy>a@*=cGpbNB`MaLixsX3z1P(XJVe+O zOV^mPdia>Ggx+Cp`=-`R)9IJVQe@csj_~wxIV-am5igZ`X+E@iXq#MWTHwr`3+TpO z*V4+{YU4@&GxBn_@v2+uJ$l0hm&2iWqEde!^3|9;?Jqv__r= zvt6xabMI!At0uemzu$Z1@bw^%Ks*+iB|g7l|V)Qu*~e3CBmQj_5+suO-qjsqf` z_Sv>I4feSmPc16XKC>EoWvp7u#eOvM^>O)&xtScXIYxuoX1noXPI!@30zSod^QFEd zGp1<#)VlblB=fnL>s5-ubioVDJiLa{O%`&1}w_DS3iHMxaKt0 z$>movG%=PcR6i3s;m5#oaC7waNQ^Yjbk`TYJ9l;Ac1^TqU3|<<2svrhUBT38Z(Zl7 zx(pH9IDmae5bfK3L$wHEf*NR~t~#}d%6wpJ5URT*4DF{ypev19i4vWj%%AL|{Vaig zr|XwT0;yybP#a9_FiPdbROXk*o(5cNI$d`-aO!TL??c&IN7PJMaG;GYCK(heJ4b-r zp_j9iO7nc<3*SOCl>jDwO4jgO6z3P;eAlm`&DA?HmH6Io&YST1=DCje-5BqV_xKQ5 z6nlQc;cIKTtW)@-M;_da_O;PAUo!pSPQ?_6vtrg}pS({TA>o@VCfu^RJ{C<0sAuTV zSBQ^IJI}r>=rkj$@~GFSKHikXm9CzfeP*|m+*RiDh1U;{wXrVUTKyRCj+xROwnv(lUl5Oc0^Y zZXaD=u4h4A(Zs<>dTe%Xw6X1l4$?p5b#cZOlg(HCOTgOz`BO(l6QKCe$Z$TmT}L}C z9#)~F3^pBqvVow7pUUh>6(-mEc6QVDp5f#fO!V@dQ}j(lch6wEiQC=j?{rOE(_+?H z?FlM5o*vB34JCUdh0@cuI@lay-t0`pGsSZURd>6vn_z_aL->S{Xdx*QL}_ZG z%RPh`VdpYv2cjbQJfs+5B^%(zGN1JbmvIV47a4bpL6s!!!`1uC}e8SGZKd0}fk0A}bFR&vy>m0Tp zXHl&btytYGn_cM76j<||5hvZ!DV!5M*g(&BnbBzTQUqa<0dX;9 zvjj3V70%Vobx0OM?D!a5RHIP&Pcrq}ax+ws>J-n7I@ajxzJL7+C_pf%crZfHfk4*Iq*cCVs&m2xs0KX;t&&jYL^J3;5CPB`C?mThJ1s!AqM%RnQL+cx{}z+~33fGuZ_J#R zBZZ0vv8J1I7QJO0_`&WRjFY@Oaoz0;R_?-?0<4F=xXZeaOTSz1m9wo~hL{bdsEnBs z-5GAefQO9%3pcs}Ddmw1$+maqIF%W5JF`a{hjW^3Ngj9OXoTfS!!TyrYk*!d4ARmj z7Sx{J{P)*lAardM9RMSuIG{H>3|Io6u0a%aZZxcUO5d?b%$w5-o=I=iqpvy{++|tc ztYWq~6(=3wVzK(hd3LA}dh@p3RitZ8K8#E5E~ci6xKDdUDZ}#Cy5 zA2CwqEn04@Id&T>(=)2n1h(Vxo-WV<1;nb0k#WOJrZc*u8>~2wSlr9-3%x@;s-*C; z#Ra!Z$&gE#Md$b0Hs-pS&)ugU9bj0$^Nh5&La5%OQ9bRwXu+ZvFH<^b;I;CRbSUbi zGT*A(xHV?Ik1^7e%`lj|*DUVYc%DA!Y+hY_yr<`d7W4`CLCS!>Z6*rKDefMli&}GM z=_9-p&*r(W&aFVNWmeR?8hJ*pE4&+_^&0UAP&o(Wp<$o|@;D|_UE?3ti#Wuip5`kQ zukV+@_~r`!1Xq1^it6I7cPcEw!On!$wPuF?`E%!LA7Vtg5)-PE)=5lNiiJ4?4jo+# z!XBLEtM^j?d)@eUZ-v0yd_}n}A(w4w1Gh(%V4@h+7kk)X$IU zg|5{#v(qcf1|?Sc8C_a&_@rJ*OlXwT?I5u&R7o)->wQ4XFl#B3c^~((P1j-`h(&c! z{kJP8GY*l*^AHBs$zdpyH+hD*^KU}X{cP(Vk=RbtG_xw22_fUyS3m96X4H!ce<@lX4j|siXd~{b&^SD-A04zWW2`;TaMHfT%bDoS zPx_@)Ot;wS>C{pqQLcZ%-?YoEy5p_2HS58CJK5m7xLvg6d%IXMR|cv?N!HzaZMyxJ zQf)Uo-(mc(c+MNU)h3MaJho!hb#ts2UCunGk8=w>3W&mJ&qV50yI;h-Ij>0S&Uh)> zDh225*rPMN2F1sE+OQiyIkJ@l1zPU;We)rf)-y&|^(^01k9rP0iSZD-j^fN>WlDg|VhDZOW?}qDDa^3r zM^d`aOhD)?yU)FT@*L2(B62V!EBjzOeJR6c*Yw%EKu7;2<~~)1E2^v|7`wj073;*y z=6#>;;(Tb!7`m9xiL0e=WH-4=Vr~0x*;M`Oa z2l;(48(;93QX!l*E8ior63b1vyfJe;>-OrLSmx|>;q2^Eca|t2AMY61ZC3RuCS@C7 zLu`0|C64YiKCH6t5jKV?6@5e%b!dvfbvR`fPqy9o{=n~nB7u#_x{6|9)No$$6sL<6 z0K+Tbwaf&ibA8k*t%mO)RVOS}E`kS|w6S(~3asx$`W2^GE@O}LZ)o9)jBa=C!??c0 z2~N%}rVn${Vh>pLjS3xhUwv`fX6j=>T?73p4FT)CC_I$ple+o%>p4Cbk;@ye#+5vU z=o9-_rZg(>Vxk0=(pmk}gn5@A*C~X*-PY|=_i1l&4&j6MInqWKT&s^hDZim;*l$xY zu6+m0ARBO2tpoZA1EP|)@wnfoG?>m5Pb}yk-izOOCn-wcs5r+vU9psW zISt4tW>z>cV(tpYG$zt+TDXE_=#c8^g-sr5s$uhq^gfpfn+bJoO`<%5_3VDH8oU$wgKLQ(M6WH%g>&oUKGf(9Z_)d zCRC?avTyffyLROQAgk4tHl~xU?jeS2H;xHelYht&+x9B}hjQkYX@IhmE}su_`oKl| zg^gsW^rxu)y4D?2_`WimOF5i|75f~0^q3r)qx-{c8TuMJZTGpKUsV!c+ zPM|ucZm>_CdgGH#J*m?iZ?!ZakgV!3+^6=&7=2}9UQ6?+%y#uqw**pCOG#ytxTK)A0(M(>ZF4uBiO!aVTHY9Y>M znJJxDw4VrB-&*C(r(2ws&+}niWqR#~3rsh5w+_zdH$pwM#>rdIzJLvX(Xp_3!14*d z+BH;(f_=II@)WRvf(3nv3f21(_FbX1G;V^BmqXQ8QDg$C9}@JmqZK ze&w{~_@}SMilJW%JZ8EHg}Sc{LqFv|^o~?qxBL*jbSY|y_|XdgY)@25zU)R!xx}z~ zrcUAI=7kB;a@Weug%L)nnVu4Dv12LOZ2JYiy=QLgiV$(Cvz)Nkjcg4N7zrv3!0eeR zKXsI&R8UZI-RyF2*@>|?IZMTsHEYB!Z`~bM%oEF*Yx{P|9pLq`EGH(0E#ML@RnEee zHk7EYx7QHQ%o^jdIZA5@#;~VwYac&%n@pq+d3h{*)3{kb-+Vp!+F!~WBfjM94(5)* z4vHd6VckwMWjl2sdTHv1HZl>8A9~CaqtFf{)2t9BCWzv%+@j0I!CQ}l)o9vsG%VA6 zMghNiBZVle)E|m6i(6VEelBZXuvYVDfUzjjQ{>Aj*4$K_lUf3TbuCbbBBQr@bs9`! zBxNs(q5VhkDkp=JD$9}NCg8FRb2ik9_DjLPt zySj!ZtuP!|xE6upP2yVZR!rGB#{rwiuKiv4xdNVdzbp@jc9F!1GCDBjlTZ*QiZ4+5 zg|mK{$kPWbU|T;XT9s(=+O!PLZKvzyOr}?6m302s6^BlW~aV&*~$cSLCvtzGvAV#RqfK%&aj1wWVJ-F{;+v}Pv3{+)Uf;tgD>BfNl)b! z!pV@+4!k<#`GQ*}tuKrzTOJt!QSnpIsdTKalVtY!KSkz#@$nsX=K{($AuKTU$9p&} z_On~|r6!z`lXEAh5wTDVssk~?K@^Rg)GIBNJ;k}oI=}6p&hHSj>8&>&$p0owzey~?< z-U)}q@*ctd=32$}=DsWvV&LoVFP6Rvc+be;nKu`kblG??OW*?0nKk!>9^C#x>Z+Ak zrZq%f`0njyIKzghn@FI##3|NrUlbxPKh&SW21LX)Knl}EdvAT@+Pj5?OPlFhss~v8 zqe9A|oE;GXDRQ;v`lRmU85no$0W-^-&qTS`Vo0ko7MT*Tv$Z9Qjly=ONjN2EYjp{= zP_ir)>PfT>GX_Hy^F1&5UWQ-tIbiG$ncw_^YM&nORQS&1*Rdse1|WNI5W*@;1|s)D z^8zf05vrR#m(KpA<(s`iT^g}_Q$fl66j;|4y_OY=>DHr%@ldo7^fOHt7fTs(PJDf_ z8B{P9rO0*jDI#*Yl|pf_5HS5XKJG_WbkJ^2BfYDj+LW7(*?wnQsioqa17NX!AcC)9 zFvY&;BCHqoE}tc!Na4Xx4LpU+*xY7~+bo-G`a3-?-tS9|WkQ2-=XsKx^)b zHQE{=-3SJk#l2(V^cVRdq8qV@^uU5}b@o2%#t}Dn$XIJofdw4JlV+n4>;w@{sDxtl zFccLs9AZLN&WE08c7BurU82eZ3@XdJ-;`lcVY(o4_2m~Dk5|0fL&kGyH^3Fb4aNH zJ?pR%s3CMMY7KA2cMD4YHgC7=s0OFVXbIHOrW*A|0@N_MS>?oK9eRy9ruYp=FUiM# z9BOTj^888Y1279**E47j*C=;)$)h=qYKzF&28Fw#gV<_lnq)~s2y(h7!!Z#Q3A|*~ zC(;H2;<~5wJhs0`BMlsC2PnGC+2*Wv&`2iNvWsu(l(OhmJ5Z+8p(b>8o8ST~W0`cw z2$l0`v=zOmcF%YWWz_4NB{NMgMF0<7;f&B>Yz@_7$;gXCZl0rM{2) z+!Q-Zgp|j?qydDGbF6C|Yu_q!8fsr+qf`M|azMQ{Wz%1fZ*r>|!Dl(-kxTd+z$)NJUGqA3xMwYjeMXyce&Tym*B_-KH(eEiR+v5h#hoF%MUQw6g z??cEd-3Bk=fBRnESImjKuQ)#*8)%O~E%NprzvAbM^hr=WRi9Xt`JI!&IM~-hR*b-= ztIc?9tkw=3mJ$pj05jPHQ5 z@%9;jO1WZwwLig)DBWHO=gFF6Z*UHEWSKeBOaSOl6rfCEuZsM7Kg`dw)NB-`c@aO9d{1WZ+A zlslHgtC`K_k->H<&^K{T{vl`V#p`6vt^f^L&7|>$p~TJ_?haS5t;Vd6Le{M0y8Y-uL5Q-}s-MFoErVbVBs46o(_w5C#k_ z3u-GGkdeHC*r0~*r)r=B?t5jl%a?FxfG!Qe!UI(RG>UAEmvX@GpccufMx&u$P94Y* z+Li$+_5ok0j95XKJm|>fU}q0E2&EF(`iq!C3*tIJaHR%z8ypgRJUor7;(^Jlv@76< z!0!oJnnP}lr^?DzLl+;;}1B9ir-)Grrz3FtBVaZhf$@TR51m=qz?=pi(sTXl1`Cw zQmH9PCwhm0F9PZ{U>=H{>jGx@A<#o;mO}J1_BEy0@uMj8bZEquIu}H)UX2Bul{9Ih#umW)Uf$7rrbXcO?jPcFT1_gOAeZ^N`;yQkcPW!393T%-% zWeT#Yo@o(eQpR)`Cxf~Ld<5OU1~6q^#qWF)p$sf9@uaHX_fs9ui1-sRkZ9W2jwjRA zo23fAu>Y(HqdKP!q9)y8M#>stc4$i!$4zJ!(X$M^Ge6KaWXwjuQ)(=a5w;#+%!Evg zd`MUQ2Hyjatg96q$1}Jc%gs-M_SIrRkaD6VCOA!hRze^fNj!;K;A>9)E{k7qQA}XPrFd>mMdydBwO6g`jX4ZHbX(H1Jz@%P zQEt(;Y62ECmbnASaoaCX_7^E{$nqSXSyDuAv9|yT^8)7^c97D(m~sXeE28?E;N%rPhhCZ@@=Xn5$0!l%h;ZTklZ3wmj$D~>i*Fj#LnF}~rV?F~v@@lP3AeHs&t zcY=~;9Z0rcB{KzyCU&#KxA}dqKW}+C`Q`JkE%Dv-*OZzU3iG?!1$fq*%X7QgrLV)z zR=pc4&L}72eutfVqqv&7p;OaLSKkF_eZJ;{pZYZ|P=IkHynfC0Xb&?ZQX?X-C{bNI zZf@G~Ah`@k7Kp9usM`9A8bz)rZ6#|-40?qeq`=5~A6ht^fm}geM1X9DB6wulB<3)_ zAMxd&FOV?Jm&3O2k7j{6IDN_OAGef>dvA;R5K4}`!|dD( zIrPw+9MlcR3StRnWsO~ zI!?KkMfFxMH!}n1Xu^OTCy~LpP0RTmH4x4WgACpm*%#LEQLZ7jV_%E;dFV&Y2xi_- zKvnvOrl#g@7I6_hSS}1gDdWB+h5BpmDHH>M_>;q<0(OH!aGUqhrTg)*(y3}%lfRg` z1NYAiwR1uNFd7HQW&jZAn4hO=845Cbs=KA-Fbu9=0PK?lkyE6ECu}i3T0f#_Drrjq zuP!)%%w~NP<~x@KFXiIW!@U%DEyD6@f;T`+eZY3oy%TK|+=D4hyYIVUSaP zn+vjO-VXuna!~8X$>2kSgX8rRc9DjAO2{4xjz15KctGiCCli{svz%jij#UA-N#2X zOa~BW!ABR^c;rJmHSxaUz8eGE5|5&$M_?4Cyqi(rMEQFE4D{2LV9XQx@rP85AiMGo z6R|P4U;G6WWJgigaZk!6$Bv;30Dl|^4t~7BOxkdpR|l8o)K5vZ^!+hF5fPbW7DUn} z{srKRfJ{`MYEc$-s%ICa6R8nkgM}@vXBa2~B}t2vZ_wiGTWIkMTH#@qj9Hc~cKV=v z;jc&2auu!GkS8YSpADCXz`3M*^yizOpf@A^+vGa5O*xQ9xdY=FuPGr7>|PMK`2_?7 z*jIM|k?Sz*K0J;xlv4=M!E|dqZwgHg6!83dr6MRPGzg{a8PdnmJpgqg)&ql!V`Hd%Y_3)QieF**}Y=8iz6?kV&D zuLj|^4`tv`QPHUMwlF2fKz=Q@zUhWv*F#RFqA@_%+3Z;cQ75bfCe_f}Y8aGeGWy8V z3_gn!O-)ejrq7qlbfdW9&g;YMV*rFf4m>TbXrc>{oo)96}Uzz-`shO+_B zc|4O2#4$doqY@#aLuNX?`RB6{klYgWi^bKOm)}x$r&t%LpT>4Pv}!2%>mk^}&c7En zdu~Tc&_^|LUPkf&WgJm$`u|)PNxAqKRe?}s$Y-@I-9^)<{sugclz@k7?zr-hTlU*=hJUQ}ihJ3jtSE0a zV+LZyNd{4eciRqDgJ*;47usK(#2 z{#<7N8FKu6)co7)e~hO8>5%;U$^UV}|898x{g3~X*Z(--fAT1LW0o^*1QIQ@%Xp4*82D& z;kTai;6rDa(1rjteFTd%WIDAQS`neG#LTa`y*uKL@sFWK_3Sp-L(T%Jq7F9Vy{N(3 zckUAC(V0*zg9U^?3)BJamN9HAZ2&+B?)hDCBpPY_kx`?-Ddi}(cH2%((;BES(E&Dk z=dK61ll!6+VH#F;jr`l1!OgI@txX8Q9^4HjPo`3(qcL3iXN+l96RDpWUHgKC2?Yqs zAL98dYu{hKx)^GpE-m3v*ajQ_g`Dg$Of&cRCJN_1fd_2IBN;UU+CTs>t?&kXN2te3 zv31K=uu|}Yth;k;&6BuR1CAziKnHawvy{6anuxWPJqfzSye^Y7;e}sc`3wPatAH2o zFl|T-F!;I3Ui%H1rsoY}!6m91`OknEjUDQYlLb{*^`b9eIO~UmF9%L){H_4Z2cKc6 zUS!Z2nS&c3N=maJyH^`Kc@|vxx*_Br0`+}bmmX?vh1}7BQ-si11iTRQ(hgN~@$kr% z;ISaiP*@6|YkEQnmCR!0Wr)T1TI^9C*yExw`? zHTn*8O}KDs=;^C7hz=HOev14*?^8o5J*#G+Qu|9Dn8cGNOb^KxhPg=#EpTm zae5c$c`~i*4n#!Qc{J)255@Bx&p_1MLV)&BsfiCEaQXmC0 zdPvbC934f$U{s9>C&SjS4&J080^Q0VQJWxzdQdh5?=upGl+1E>HXCvxK|blh`jMlJ z&@4=e?$08yJF&g20CQ5e)tPf*d)b)Hx&87{u>!F- z@&?+V3|Kf4AU95bmwV(a{a=kBrFr22W&(FWz7xW?S6t43doGO7sDwx?5}r_9wO00OuaZiEiFy9 zzXn)o7PS>b%H5c=20JoU+~Q=<*@!=(u5p%7nDH{a8eBFHL*v_K7EWC&pvGTM^gv$E zCwO_&=k*X-&{8+^5RhvBT6psgK?^mU4LGMFM2<#l0##kFh_hSz0b}G@<#DKwH;-8a zc3Y&TkGo^&jK*Lz;1GFL)y-;jez7O9c*3$b@%eu_CyXoHEEGh>%W4tsj@*rJLlM_} zIUG5mBI5_tAR!JY8a54FiPb=ARml|Oln?uzM=k9=3u;OUD`AvtdKUPVt1=j?Ml}$4 z@Ld|~@+Ur@rN7bdMnky`Y4^a{Ot{cc9{69ar{rFOYm?zDq)-IxIWUl&D8s=5Z?SP3 z5@Q>P?-Mdni*rPA5_qp1s^9z+6i~V|#oTyhRI9fJ6Fke@W?x26*8Oupp8V;AeBueX zL9@ef?~QfK1n<&Az$Mz>iXv=qfOs$J1mhu*`R?2(ApYK{_Jf+m!Rq$h4jfnr zt~2VZ(G8YDoqAC`x-q`lsDvgKkaBuCx%n)8z+UwqBfLdA+!qa$dQ^#iuut&`*ew_} zr-L|f7JRLW^(NS&Ou&F54PopC(7x<6=!*ofXTa71wZ|go^RQs6Qg3Gl{Uez|-IZ}z z9QjGLtDm`$k}t?)nduk2lx&`P0m!iA0tES!uhk<6(qC%R8CQh5#iP64F}BI}VIH8% zYH%53WM@}hPh5bp<}n2%IU5850k#!b9&q#8G3q(CEic)s1Cx*T=aShs;K^~xLv{LQ z4y3cClO5%)l7dsJ@1gSbb4J7-K?cJj<4JLh_ST5GcP)TVv;s)vb~s)8kC$vdoUUtk zc?L;b9eA)ciRtzMm~QW`ZrhAxJ`z#@%%Mb)?2R*^UR3e{I>^5&4>+xym)@h{#+#n2 zq~J#XU!EWW3q1slfi|q9c$LEjmJU>v4P7q7EW`mlo>pQ%64m4okWpIKp%zrMN$)ou ze`V_wPJVDcsbdDp{dy0fs+-b#k&?p1(yzh9W#EjmzY!E1Y~0LjQ@z&)CR~SzNlF$G z9KUg*o=~y^w1>V!Pg~SE`|e;Rc`y`jw;wO*s+8>t4GB@}>q+>>1d>i&hLV56Z~^zD zTYErIPzYN`+g_E2*-%a-Jh%r2`m6vXr7rb0^gjVfoYo8xMzHeZxCTO^e0cWd^@WsM zMw&-~2=0rTuA#$gdqdFvgw&m@i(orlT~NN5j(xD6dlRWqi*S*h1ML0l!s*HJbjoonMp6!fnk97fdA4!we!1KRDb`i4_&nO?8} z{4Egc1Q*637_+Xx0XgD!2N>GE$iteZasNdKi0u4A`StaN9gzG&bQxlSPL~+a!_|Uy z&G>$kWcPB(bvAHVI0#IK|8idzV4I3JOhdMq62ee}LgvhdF!uRm6=Vou(BpIzo0{(S zVb8hA=XM*Y7VVM=QC{M`5L?tw?%8lG_sl%3@}s_6wW9#oe%ZPBbXNZ1Uu`3D#aO|H z(|yWP-v>D+HZKBLhziS@HnPvA0BXfim$MB=BRvyiCaG4q{DmF1U0*#o6Im9+@-1^P z;%Sr>xLj%wKBG>9!_Xe1fyMMpIJd*W;lHmaW~wlv51{_esC|H22GB5P0mMm!j|Km# zX3+uAw7mjYTTl2?cSbn>;I=6qgihR~33Od}PUbi475g$3H{$v~Q@O!vns@W`=t$T%=_F&dhU zj_3qFP$BQ6WshiCS)e~c1Isd?2U90hB4H&Lobptj2HuA@a7!{e-9k=k(xHYLxFUd3 z)9b~*>wL%~PI@a;yvFCb^T9{G4(OJ25OFs}PjsehBO&@}cy^cjIH}hGJ!2M)OHm`B zwG(x4RLjr_mneX4tab2{qFy)XvIDD+{^l>7M`Ix7EvTIU45l9}`FGjkbiwQXBZRG5 z%dnwX4MEt7W8v4%KZ&9rgE=Yj3O5CmLqSnW?Rn-o1Q%8IN#hK0$h2fa1muC9n0uLWBar z4$lx@O)*exwAyH*z5`;67GO&D6iPAY2*qr^^GJFH0 z9@R~B#fc3276P%tZn^>tNe#Yze%B)gbAts#oX{VJZOmWP?u$`oKS)0ekmtT(yu}<4 z&T3(dutJ-RhG;oIYv5FL15%$u7y!>oWHfCEH6Bq=Xpx9xnKH@{TtaumB=T5@Aa^Mz z8x8gVV5PSu>gjNJXFKQuBKKWQ=8ILxJUVnWr=4rs5Gxc9H5V0O+ZOUW;>`T>Pul+j`DMl^JUe;!Y2+aI3_G!TNTgajw z(XA1+X>=Y?XkkydKH+-PhJu8>`zRQ-*&wWC5#<)Z8-OshWC-G-w60zR6+`!-GgFFz zzKtm&kH#T@NL`9n5b9wAro#)@9h%6gQNY~}jJj(ek;oyKf+Day#SVcq&|$pAD259k z@%Di*c~rtm7=j=ge~1DTm>p3aw?i++_m1r4o5Vp{%+)JbJA|>yd}Fw?U`c9!vK=Z$ zS`!XZtMy7l;3wvX$P4G)V2ihzMXhV2?0fUAw1*uK{ZveC61=i*U)vD}d#faa&86#P zB&kJ!Ts&y`Yzj#Dt4Y(k7#%q3J!6*(d2N`1)?WnOBfIpEuqiiYtJQiGQ&WC>~}1%_ZP7y+=#?B`*FizQf|4|9=4x*#D56l{2N^IDwqt% z@BRxhqH_6bVg1iut3)){{D|yg*&8YFJ_XbM&(87p&-~~4%zU=A-;?70F%YPZ`2SoW z|HId^^F@??khw(F{)Ywse?K8G5_Woh`QKwCT-M$lyJzD%B^mhtIzWv67XZX(#}HRwzcNRl zH;~}==TR{6hzj5QL((MqYaa$h=v3CQ$>RW)U^Gel9Dh}0+ne9 zcXiKOQm!+0(L!iT6sk9)Oyv-0YF%~I)I#Q6I0BPr2map}7*9@kn|y}yn;&o^IB5v~ zhr9QHi}KvoN5>vb)QBBLVnk5v0!mT9iin_sh#g}z4QC+z0H;kGvD`q@4MF1R*t}3rRFPAeTUJ- z@h6-H45v4N9g2HEDJ?oEp?=^(UO?IeGb&b)cH;nSK__$Z;rp<`Y@~_Lg6BQzY0{gR zPbeK1@wq>;uS+_=w~$`;Ls2L_KCBe03Z>Z)Xi6f^fWXOquWa7*z?8hrTF%m$G#9?B zkiE)&*<0vhJ2wPOJz;V^i6E<~I-|(fd69X*mu_2A2smx=4{Qeq?DwX z`|xTg3Bp@2C%a(Dh#&c>HVAgCI%ary_+2pT+@X|pL5Yinzx$QLf%EN^B{)X>{)t#U zg_`@V6{(@`&ym$tJWp_9A0I)nA|Xh@`Sx-%lGZ^$-Zhk<+7hLGb-QiVd^*5c-my-C zN+A_>R8oq_@F&D$D z$KBy6q%R@}@^>$`guyQFSEc=)C-nYQz}TA@JR~3NUaxcSOTu}xE6yw3&=@^qn{%_b(g}=x zN1?2;Z(_B}74tV*RW*V(cXlLVp+@~F#=ibD)FY6>hHDD2paL=fju)qhFYz-_*jBa4 z!z9g|=IER&Luj`8^Xu&A;NzgncA>TKP(lbPK)XaMTn(wTV-Pffpb{FqL&-h_<C9pu7$$8uXu|CE0ifBUssafw4KD@JCv2Q`Hv#;ZAyD*vIx5hzk5} zWrLNF0^RQYQrDop3$mALu?c~^t-h}UC0FSNyEUF1#8!}s4*K?GeJ3V7SJNm>0vk>u z@GYi5*WxbM2}l;5>pb3?a}{#On~}HMSL^K-5sbil3;S7UWn$$(eI8p_X19NC);)1^ z{Z4DZN#)D5y5DdIyP9RG0%eRy#esa#hr?HUt4=Tzv`8v|7MA;1@bw0l)79)HP47|% zvZ%8k&HKD#>Cn|E$4Rn3Mx~sd@LtONe)=Ju@-*NZd=J=SeCT@Hfxf$|4{z=~BQJA& z=)I)B)jHm37Fh(Du6r1PTNi`ACQmn^azNWdTRRV39klix1`l_#f4}$K@n&3PPUU_q ztBy$LHPA1O4;I4Mqb~#6!IkC^6bhSgo{p@a#)xCDIH`^=S$8+^YuQJ z6R}I8n$JssX&UZ90Lrc4ecC)^{jMq9B zD2K4sharU_$v^dW2<#ouO^d?LDi^v;Y5<2Mcb(pW>Ho>5R(GSNbZ;Y<+wG{qb34Q2 z>>eyxk>U$rV&|5%9ckkiGhMHka8-4M90>cBHs8d6I6sr;0?$Ir&klV!-N(DGwMdPT z7sgg7e_`!rj;uv^!nTRE1WS+R{9loiw#^&fae_C=hH>fzyF0r$fG|U?aF}G82qOF6 z)hgWHE+mA-!h&l_4UgEB6l^a?0XKH2EpkqKg@@QPnj@?9E*NJ9Ee;RX{0&T{dj<75 zcf5kC>b>EeaNGxMC=^Ok0bz{;I4$2%_qTF|FSXUOQQw=KsUOQO%41H;oRZ)Wi?o@@G@sY5pSx^3V(xg^$(X zhz!m(<2<-YzuFim&NfJD!*-{K z+#P6&8f=#uoKA>A&j23Ton%^A1NP?Yi>Wq0Q=;EdoFvAis82#B$eh)Oq&Io74`RE# zKs;KisKELvcn4owWQqa#H~6V0M;p7NJ$79wn>J^!hnu{pcLo!21TkZ01Lv_9tlKu} z8btWp4*i4G9VXRK^M&Y24o5U_VQL8 zZ$@e?>m=Rx5A;J-abF`^1a^DI7m5@`>N#6W;TZmxD(m9pb*~e>;Ax0r8iI%^=mPaw zug`R+{mWWUuWne{%1B@59RYC4Ps@4QekI>s*8o%Odv+YPK&GY;ZZh}94c7>$67vd0 zEqK;{PO{!rx^e|J0%ih`dr@tiEc3-<4(e)L@8UXxmw? z4B*bYi}CEW!jvJfJC|LrNDKW~o0BRl;4uU0G%Jcfp=IN@$%F511gOe2;mY%D$xh_VO)9e+fMKu7NWLqf=3}a25Fgnu}1p}t!=_^TbMN| z(#q!o%bB0W*z-g1iC4j&Pd@@G=x#yqHzwkjBDq|Oh_T% z`>`dZYSX5PQ~w4zm@+*#9!0LlHEIcW%FSD~HX9i^VIn1O*!PsX0MA!Ba8(CH9sEir zguAHS?U}f4>!!e7CJKKq9i^&1)&dIdb5Q6Vqgjq&uuKbj0o*quG%*D&d&2fVeEt(b z$7f9#fZ7%--Py2h8Rg)mVwCya{>ir7MCz4Z7FUTJy^rP`l%g*QRJd;Mx)S z^=2HasBd*FTlSxbH}ZN*`JS5)or702@(Y5kA2U5Ul8g(>*^-Qs=p(v|>mr=Yu8ZNp zXo$-Ex8xm$9hojCIkdEN;0NUVQg2>?$=33zn${qCSV3kKOcrrG{W_(yun2oglE>2Q z(1!R_#QwNg+*3v2(pik9Im^Aj~|19)Cnavtnd%t5a+ApWxOy=WyWn5V=I60+y7jerjnlF zBxD~G;5fy0N%Pe}(rHXCqhNnyh!A9`zD|%#o5i=#6CYlr#)@mZY;0tZLU<~m?E4!_ zaUNEXM=t7UxYO4Zupaj!R3OtoijE4YTWSrGX(A-xP?$xXifml*J{>eNVekX^R`|S` z3|0N)(#RAY!Lb|&$ZkK)M$@agjSj{L8l1NGDr7$8ZuGAOvV5>9LH5u$y6CMLj$-9` z+DlKaGr|1rRP+>gJ_PgEO^FaE&!NJts#nu@2d#PSl#eM&`z5oLv9%A}P0{67>bJ+H#vJ1sQtT+Wzz^{(#u&nuv+O6An>7KV z>jWnz!xgF>)v?%v=4(1{L`s&x#Rb-0GHq&kjT_>r%8q732(uw3i`ylFc^$yJ8IFlG8(OP4P7Upr8T2mFLNtbv?G z#)zW*1%Vw_t=sIG0!Jeyk+j=r%%wn90*}rBDyVt^{R!V(K58>4@#S<_tu`0Zg0Qp`r1b&j5$jm__-wXv*Gt1T3l*2W@qu zvIZ*+E3nt+*NGW3%Gnj#9<7&Gre=yQK;S5JMW9NDl69&YmW8UEz}Q7HEi@WqIUl7d z@o#B2Q-hEDk^{SZ&JrhAN-Nu}LOOSF)ZP3~GP*P@iXb(>ee) za|aRfl#YBWy6i(b2a+6$)vzEXq&n8eZ+Bg(q2DwqLOj>p3NbfF8=+@FJPNX4Zta{D z%dXy}r+3h>bK+2!z&htCfg!r(*f(zM@@=koCpcj<(c7Wof?8VGoQ!7Q6)EwHwXEmP z(wjI^1H_Rq01|=P4T6;v>~`}Imh8_|yB#-H9^(`=yMSXxDO4l~wkUNcUT#!|gn=xf z!wgQOKFJ8J8V;7dnd>J^P!qaZ(kXRlp1iT*>1#1hAdU∓rA^CaO*5=%(c)-tU{= zB#Z$;ke=Y6M;PAFx#sC&g4=9G-+!fNEC1S{(bHC*Ws(lr5;F!_0IAHWdy-kor1ctMdj* z!RA(7-oLoA&!< zhRWaHm$kSD$fg`~1cBJ6VJNNEJOAGNl#A-`|qZY5`^H6b`B5q#=W174~ zvJ1>d1GCV`1a^3$rFexU>uTDJtUmlR@ZVS1s6D4E;$(QO-#`jQQuJEVE*I&u$H4M? zN#NhB3rY;A=))%vw~R-nMuS6-pxFZ5dub0ocRVBkQ^RYZ>vO$)WWNp$%)N?j$HrD- z?VA>(loQb$#Aca-oQ7Mb&DWQOT!pHMzNO19)waM<$>Ei6XYK#Cu(ImSYZV(OJ@#8H z)gwoe0r6t1%|ns_V_BAD0K1@Nj%6?nDgw!?*&sq@V#B2V&M>rXjn68-f@i+YYIB-F zusBl2BdJ79!Bm&=lCHp`UJM&vIfN(a3j{vh!EISiZj)fs?zXB{BpQiiNDcPU0IkkK zw5Kak{(1=Q4-na?wD5=8J?iWe@AKkO9>+1;?(8@-K?fZ*CE9BhetE>7Hs>)mFUcl; zrM+wS8^F5gfF^|M@Bk|tP#Y=qtcWw6#~z1&$BTx|nq~;_Xgj^vfYQ*B5)&?rdsgPB z1z58jLb9vCx8pM`qh9?By?>rX)IT=)7oy(zrhY7n%=)O^h%YS1DX}Z=1MyE{M;}v3 zt>1!7O!;z%E&Wg5q7cVKYq20UC}X#s>{rRSo6Z&p z?(tozn~k88h|=sL0WPV-^C64$S%`wJ${7WnD|<)(ycm{O9{h)SHPH7E_2xM31xP+Z zxfSGn;`y6-fRU9rrQ?G=XRc?w{&}j-Icr!DLkWK4zjR+Db;RP%|6dQj~SEx`2P;s zW~adQZZ33!PbD&dZ1<19V#-BW6E*Ebsx3gdq`Dy=GszmAQpV=@Pwqj@jxKo!w9^`x}6?F zzc}*9;z1_gXm3@EEI|1h4>0kyz+t^@&j&A{(}!PPK30ZtP1RvImqNe)%50zWqL6>n z=WKU;R+g+fxHZyg3Ga)bofsrgIS4ra_I`lNH)*p&S?ARBiq-M~*l0f>!oEPl2)Twd zxg1c(Zi!mHQTNH(hBoM)#q+cE{(cm+P-mW2Q+3{*kW}*h6QDxpwK3{U?YPf;2E|Y> zg+b^uvpbFbuDUDM9;>Rm!{3N=+5v#R#N>RJ2}W;}mnVI1a-MWREHFxY6J!~zmz}TA zH#^0OgpZySdlJcOVZ{QZ?T;S<$)>m7!>hkhryxr{`Lq19H>y6@&wD=f$spG3I%=HoH3O5#-#Kh+2TxAD^Fx zn0`080V@#T;kegldoqd?x(K8Mu0`X4>?HB`Fqa75HGRGC$}0tlrhSd!?AAm#dF?Tg zD4+A24}^OI31+>dKMX~=+|e{UNrzWq*(r7UPP(%eg?#%Fm=d1-t>{PZ?=>~vQ4TTs z0Ac>Z@XP@DuC#yuevV@lX^!K~-o%yU2t0OH*Wx)mkUiLG^!W03mvyGJyRPx|8t@buf!@4b|q|Rjv^wrvJg|%%HCz zd>HJw{>A2l@n?*!ARni*iRzU?5rK&{F188Csh^iHR8TS0{+LxZYm9E|C=Z%Z3y}W1YW%GdTd!cs~LEm9eJh8__tLVqm6?;Oso^~(Movde}j;qXzc;eG^2 zHMXL_m`yzEUXE(QZr1cHI*aVv^a92srs}| z0ykUyp}f+0r*A)y{|4fgT#@7pYA8at7Bac_q6*W2kMH z7HGX+*2ll{S*6o!?X|4U4&K?B2!g)`B6xP3@X%#s90=Z}n(gVkBLsXzV{eOfi)9#Q zoK#vRLiMgw<_J`mL@SBA{}jI#m8Z50zh^o+L3^OId1uQi2k=10PV=-AdHR)spXf!OEcWuXXNNM6oHwHRue(r6telZ!cGh_(>EL5=`Vx`CyS{r1^cn7?8_^uh-J z6aD1hN*~o&a)5s>jL8jRLY}Wa?3>MK+rh468UNm&9}9ck7`o%qH-?T6Rsh$xEAgGc zW~A8W@BX}3PjdFqO}CpBj97(9|NZEx@6@MizWL)A>E}l}+c`@ZtN-8m^GNG(ofkRci>#%T=EjRj1!1Xe zQiIrxVF8GL|G69SFGrycYEqMMecPmHM1ors>ocv(ARVm093y)O|HBJ1VIbilEf{%# zVE4>#?jZpg@s@a-HF+T#MUC#H!$YiIXfi2|R9x!sWxE81hK6Rw{vaMU*&v)8z2kmpl0*ZS;7+FPIjy|Kui%@WPm)MUmv;QxDDh~g(c<&gpSVeY^9i(g>(Y4v z_mdZV4Uz*N7~BW(>5f2J68x@9r*E`?{5^?r3Tt+D@6!8V$UfL>g$HVE2LzDqr_}_= zKo+O)EZ&~1D{f;|iQFcEFqC22F!JfX*>f8IQUL^KseUM|@JnbWBmXCN@|F_=I(6pa z-sW0Dz$j^L@2;u$6eX3GigF0!kAu9oBah3cdW~&|X=Yb{1g0RxmOOQfFS{)xh%Tn=Qzh|5ARqdqsV{f~Q)Da|&U+mmrR zaXRjV$rh$J!1+{k!m~(x^)jAF+{FH-lc}>4KmQhf&R5a+)yx7D_Sq1hhGsr>n#yf} z1xbUwNvW^^c2hP0WmOp?+$A**1FQNnD-Za?J0JeiF-9E3QhVy=M~6W;bUUm)8Q;47 zW&TVR^=Z(lsknJ0!8S?xFh6=WKtON}ij^j_P#S}l)M=5HC@bU9bpBonDNac_F)zgV z`K~sD;w0O_@UyH>HgxVrs&M`JS-&^%Cssrx6v#9Vz5-YHDlsRL5Y<$_6urH~-!}D^ zw~$FzT=;RNuJHiX%#@e%;y`u*|HR}-elf{JMAQWi-_^pf!QfKU zw=1G&%pbgc>9~`*nsd&YV{cq8grReiRFi1*cgfth^Y&m8n^Ce;&;=9Qow2US9$9`4 zqUBfDl~@9%nyro_Jt4Q7vqd3SI6{q-kL#3+cDgVubQZpnj;vEV$9WTD$` zoGn9oB1;pDHoh|MJ;|KkXti92a&M+O3f!lUd*@}IjMwzI;!75ziR4NzP!o^Y+hAbFhUkpM0oWZvh;MA>y!-FYP;9T!pNnPZNEFd0;|uQ zwM7tT%95B9C%ioBKsP&|+H=R!XbHVFXHo3oLT+Jc-Fx{Jb8!#$ISFNm`lOpmwcpFE z^jg&&g}I~$Z4ALCvgA^UUz1m@v)op9c+^PFv~vft*|D(aK}n~DmD2$PU-x! z{GXzfDY5+E)1ZQ9_7??U-@MV&)AOM7V4Q_fputn&(yxgg{-L2KRip*dzk6ENVx>%& zVK!&ypGyb31_#QH*Gxfhiiw2TslxcPrbx?-f86hMG}0nR@w87$1=QOs^}RrECS%QT zhZyMVw{_@r?|Q_~>5}&F!{MQMDghMq@fw;_2YzJl>ZhPj9(3w&u!Seo$f6Wp*t{tv zgh%XEQE{ysd5NX(QA_{AJbRn(V*8f@Fx{=t55;pA6+85_4VNT)`0uRLP&>5|!sl5v z4)D2tL+n`suTmw^

{IcoHaqM>_VeOly2!WLDoZX#YF_m?WQ7Z=Ii2>%$KIJiA z<91LNd!>Cvv~vYyGKOmtK|EGo6i9n7hZ~f3+FCW? zoOy(Sz$Yi1&df7>_Mruz8!L z76G;@nd&H`AXQiW&E%P0s530X;Ds5N5*Nlk042}-?9X4_&JGm39$Dx|4CZ^~@(V3Kz5 zpgT*wK!Q`mte8@+T#(mNBZrt@4>}CRE7+|UD-S3aEe}}h7$f+oa!D{nS#%%ua#|Fe z7pmno)sxj{0FWm2bYi=lOfPOfx8ugn0|QHfA+oD-^e%+KyshMzQ@_N{whahjaKPSF z6<16C6>>q@hL9mnu=QT{FWy_8vMZa9@=ywc&lUAp<2`1gj5@nqkiM?}ffEuJpeDbv zVUpdX5zYO-)r$?+2za}Ql|$z?Lva7fhS7FA|5xue^27bZ@8)VgPI??tO4%RxZ(Gfg zUypq~k#WB*N{?1l)NW%lPNsZND>p zxY7S-ez<@3tKNBgOk>oZQ6qx=X8g0)UaVa37(}ISVlR<#A#X8z^_U^>btws;esz7@ zob^JxWyQg#+E1m}HLQ++x6-d29UZE!?0-ReJVtQ~yMIt;dQ|7>HL$b7756xskCI;e zbCzrYR2A3Vye=8gEQ|v&rw+d~H!?DcZ!$W01H;;W8UNFqpGilH!a9XQfG8CM5fGJ* zVTB9wNG@arz5DJewzUOwPwf=ujx(c-T~XA|?VsHF?h2uBp+*rKZN}>frfN=O%~|8E zi1k9e(p8i&uON%zrcKHRZ4}QJ4&xIk6H^wW znjP)IC#{#k?@$;LMP>Mt{ao44z#JXLq02LDvrV3swR8-kPIQCd2^vfU8Qkp?VzJGH z1U;U%Wbr%$-<$YDg`vB~omLfp{p==z#okKeJ+aUBWvZ{74b@0-RR?!ygf+*8wiC(i zKT$yU&MKsg$hO9;}sPhY)f1b}%S3)RBZq=mt*f!RVM&c+WLETZ5B|*(F?%8(L!YBLWj}WJmN1&fdFO7yrtFMO0 zR}`RN9;htsP<>m~v$;6(X;p6%wDw z__OYkDfi$kqp6>S7ZgdJM)ry^iJ&1M<~g>Tji#6Kc`F%@HM_pZdy7uh)xfo;^jJXS zK?%^;Vq;@3|DwS4ykJ1T`J5%wJ=3aMsJnOBuQWi3^~#zE-adt#wqFs`v4E1;A?n*e z3d$@o5TC?9O#y8NTw_uBOWuJ~l-ezLo!xVO<_CA_FhxB1{iZ7!xWX6sl9UlXe-7;` zKSee}5@>{95XL^gSoMI;+mrabdo3^6P4Q4N${UwuAv;F^o$IQh&%_YiGNQ$k7Maco z*t7fFT@{C-0t2zG1u%rQqTQIP+=eW{Q}tl2ddii$;; zB9ysd`h*2%4h5-239^*2s1d_gvOkpfhqt**CR0aaERPN%ySkrepg;$fxuBII^$(V| z-C1&QUY@qbP@(1G7+b2UBK`Z*zJbIbxyMVQS~iL|aQ!k}ymsHcO&7-}BekHdWj$aQ z0=^5Cv?YpsN0XgJGD?N#e`9Y2Ih7l;ry(>22)~$g7@pY9+ zcLOXl$Jx2-(&jxyT5m^3?wsHusHCOEOQUq=l?rqsM(AcBn`Nqv$Gg9#FCz*eN<~Oq zvtX^K5b&BHIrfULjht_+%%2FmUUWkuVAfPbvz5~(h1ux7hd0!DI>RJ@Zt3!qp$&w= zc;p&bW;2EBUVy)U!J}W&JIhj<4EUI`>pdXVs9~=B%L`s?3Uqs)OW{$+jM%ZSx6Tz< zzhpaxm1-7A)L1o}dfkB0A}anRha&f|!>;l=L|AC&X0elJ-yURz>k$S{jr<=`L$@>Q zriSR!akU$8gujB+KPzO9f`*0$rSXJe+uepPLD8SB7qIK3<%_oR%y6}E>w=r`h&b!* z>^+3R_^t{KkmrOJKh}SSR!MLq$@MFb*_&eO;CFBA*cYV!Y%Qq?@?(sTzBf=eNn7ln z!*(S;�SP ztJKSU;><^#cYVTFj#`UhJNLQ^GHqrNR(3SqP2|0rg`jJ1(Wc8Q(q$!gOBPA8pA9=h z1O)ucBS}Xj;)ivnd zY-so;g&X(;{Z*B6uB>0xbGy42 zf6U^-nv*t(&$XZ(#e#6bdE%eqQCJ0sFo#fqmmPRqiVjDyw}L(M@fEXB&|L1qDQ#$> zkUdQ&Znl<{v7bR+ZmvlD?pRK8`>o0N(bd|oZ(Z&RP3TmMFon7Q!{-4wfb;9zfLQBy zp5ge&cgo>1+=SF)P)O`g!HIdeuFulkPsdmAOg259;XW6Isey3!?gj{?L?l{R zN_1ciA;>vBkYr=+oauavCBZb%>Q%FhweTRjcmB+e zaJog_O7lnYh%e(H8{cs4Z^g!HAX;C$YzMn8H>?7BXN;%v`0z9d9_&Ov^<@tB?IueS z$O1ZQ$=Vtu$6mtT7jw*VHb<60w%aa+os`6URK6Z#pNR|x&sMw(rMq-{K_Z2+L`Q>j zZ1G@X!%rA$6aT3nmvC(`8#T9cpgw|7Gq}&R2ClO5IZ&Lto68Pex5#Z7fsbdKDMqQz zmfjkDqNE(fg+27SD{ZY2WyzX60s)ooi)Eof4pQm&rr*$1pKp*CVvmJb%ZxtR7}0h2 zOi_OS1H?9|xC(=p(a98O0>Z}_Q({u2`d&(H%`IXz z0^aF1>nloQ>Z-j`op%qOOl;RMrD`&j*K(ItTaeJvFOPp;9ExNfZ=uiqKiLKBAG%qp zLewyr-8-|D8V0Q29b9jK6Du7-mIJjm-=>Cp^=a=T)=cm@PW}qH+14Tp1}ml;B?@$_ zxYgLyH{J#5wSA?ZjsS!aGW7lmo2329f2c);$EtC65pO%#4$S%{CGX%dzcUyvdgRsB zuUS_lXOM93D0m;bPcuvIh^YD)0twhtUA^kS9NykaKLj@4Zi*dzRZ%ui#kwM!9*QeW zT$`+WxJ1%^3x|t&+$4T|eSNpS2K%NYk+`-{aVN`Y!vd~ja?%s*);{e4bvnk+Yuc)W z_{cP!UdNJE4wB`c=SUhJFf5K)SiMSp#_9g*(_vQ0*ZEDQY*MaNl&`Y&-%2q-`*_PT z8MXVyqH?!WK7xqjoS#*zE-Xr@e3Xx6jUSv}}$`Iut}h`O@F6 zN+0}m@+eqD2LoG^Emkcu%yr$3QnsH3?IS<%dA->2Ha}*+OoZ*%xc>G`kM_El+P7MUZ3XK?`i`GH zO}pk2r9>}m3Ax_u8b*J;kJ?zMfs0|s)Aw1*wpe?4_sfzooxE`$EHgJ$ENZ=?IyL#F zqh#Ldr>EZEFlL^#o$cptcH{~Zjpd$nD=EBN!1+ZXukS!twq%QKcSE~zxs>Pr)p<#_ zNq0pmPQMQ}V6F>pZ7e?9>c~5w`yxLkn)m0Vuc7lKA|d;<)35HG%kXf*OfZK3!8Mc*pz8Ej&W8v(at--U%JjBDc`oyX57);Qtf_cj9Zwdml+ z-Jd*f9cyk19{3xWqAi1G^9Xd^#7I&dexg? zDcNVbj34E}HFy09^-7axeZ3@D-z&p&8&AKq5Vjg#)0&MctMhz9R;DyZpL}n?^Ce@< z{hJ$GE0qQqb8vqr)1?&Lo?bSv;O-mBQ?~k80c!f~Ab_92IV|Ns4F$0Q#c6DlBs>Qk zYBI7!=3qN8vdDmtdiKB=f%zoLC0{Qbwc96#x>iy_iqq4quuR6O6uxz6Rs@ z{p$Yi%rFd%xeylJB(+Z8;6|nh@8_reJ_bI$o*oCkUU`d)C!E zG7I$1MH=c@`YQg6ZaV*k)A50$R<)j63KKLdEU}8XSFO@0Y$nO5_>b5f7_T>*>{FB> zGDh-{lW=W*>+7J(7+P9+XJ(^qy5+ju2%aH`!E;hx!UwnY`U zEodfRmMWbb(uJg@rBMVlLcti~(jB@6Q`@*N8MG--S{D__x5If!= zHBcc{KCkTS_80v`a<`wq>p`x2XUkAbc>9VQIRj>Iqd9^8{%O23Z|B@Hdsc9Q5~hIP z1%3ciP2mxZAMq(quFZ?kakL0!7AFu57stdtQoEdW5}bOK$CLen(vsI1O?z8N11hD0J3PflF9bF(Pdyrs0cqdt}?GWTH8;VmORis1Ha z;a7o8p=+NYh&vVY6WU8uvP~dkc}4n!(fm-Hm<4w6@#s>(?P?1c#7 zt+bB)-Hzy7YxF2C)_xqdkFrZwwDBL?+OO2MTJnN@@RtYjRs zpFS@Z0iww08fv8m_~ zKNm_web8y3CVt_&pRpt_Pu|)fA0`E$Ord#;%Z*hx{k0N?-kTYE(m4#>LbPQLpaO0t4^SQ z8OH?wAlTE=Z3#}Ej++|>>(=8L&Fo7^)>S`f+PklTbQKEM7R!b{O7D}i_b7>|=+;eM z(rNp+X$>-?iH9gMZ}FDgC=tJakw4Q%#@=x4)7s?ZCN+Z()#+hodY_V-iw(Yhw%Uw2 zR(5%;HU3C{{tpGJV;rj^1>R+sT0?(Bm(!|uNtoi;<4=pinv~p}EEJngEPDFKhJmT0 z7*FTHwguSF`DAcRNrY*2^}&_1dM8AuS>GSD4NPh3EIU~?B@j|>Z?~bhw>qMLefYX2 zg?5i)BBd~6upC>%OR+bqPS}Vm+Xx0nwyfiV6CY63$7SbIBkEW{$ZbkQfuxS#hauT7+ zap7EVl6w)+6#2fl)Y7mo)m&=noVbb%t<c^IOUn(7ErBAgzU#GM4_;v$IP?yk}B#E*QE8X2EBv)^O$*gb_yaJl>BpDtYjE= zYrF0w6$wlcKc)U6e_5bPs{Nq3>3wEQbzIrWwVj_nNaio>z=bfIMFr%ZYuu-QP=m8) zZx_X{KQ%1KKBWOn76S$#p_p5=CMFzoHqF}JYl-gU;m6oV&D(Roe$&ci-87Xlw{Q+? z>8~xkD-`-9>YS-fXS>#`EfKs+=g18ltG<@X%lw0~Kh4IoewkirPhRva^;Kj=fnJUm zngwWgMeZ?yfY4ZoE6q``uUoi%(<9j(pKtN{l4#HCKNktEH!7ULJ6NexK5#F>EkQum zc<^IkLwUAsTtJ$RR>H@=+QO9120R+^X;jh(%2!CgMkc_GSX)h>)#nUGIV(0P+e5?_ zg;Sd0y;)axaLS*__4`H(T&=hX7*ae8x4jTf0=eA_4DB^M^V*;9r|>mwJfRI zXt{xJdb!N*!9DZWv_9cd!T!EyBWw3^Y6Owiz2^+q{M|GyK@+(#`DlHL%m$&}RX?@y zg!-qjPaxit` zjq*6JjS@O12BL&gd3!yn{@)e06Hi@z4b?=qY4#f(nlZRw+$1=fM(5+{b0=|pkqLg> zQ96Tfw4+>mMQR*u>6XqSCeMl$x?&MPGbzLoxLqwAu(WrZLV_W^taE{o_=$vWU(wp< zcbtpU>(ma;^OC6f&>FmKmE%f{*oXT$3x3a2rz|^_A}x36PsY9O_{*&G5>U_gGJy-RybyHcQ(nUv!EaYy#T zYE*#f6XYQOR{PwIg;Gu+TVQHz^OA`Hd|kzmHjk?SFO)Vx*C|5;^8Cz}XdjB6AOQK+ zmMMB4By)RqJ^GlXf!%;!b~ARli>_(LMdp_gU;Kv?Mg8)w)|;=*(jg^5l36%kTQ(;r z=M@NCZlX6|oC&uudAKiJrQ(;jh(9XFARGDjo9^i2+Eb74?Oi)}wvE>`vNhjK84r}2 zX}dw`uO&yd*7f!EmWr_6x4$i89@NOCTz(ebMNFe4YHy(ij?p6L}7(Y^lmno&rN!PG7C> z(k}4nW!JC`Z{W>X%6~VaB&qLh(pJRiv~?xI9N#@W3|^ZEN{U%+MX@LeC1vsIJC}@T z_!{wi&d(K0S^Cv4xs;`mQ_ltq57iJ3jO?N#AS=38iWO;5(lhyvDXqNky|Pi$gLcQk zuZ{p9y=$=i%odeQi83Wbi93U-O8565;uv=r~!_&fG!C!;B9HI-u+T~>mpm@F#$_}WIF3NdoykKcP z)YC6}@fFQqyTMAWe?|(0_7wJ1c)RUXRWk%(qday9Tz7GS-6|Dqb1f)mt_wX&a=>^;MQa8~^pIly-!iP6n1;n$=p3Mycpg z2RT(QcM+jT^Ky%;xQCu=hAuCNh&`x)&@sO`jf8S3xG#tX)n}^F4oq{(ySrpR))0*} zL4|FqkzPZqf#yHb193$kIn*;SI!S#46dy*3wxr6Tpeab@=ZUwHRA2z0hFEd{``6~r z#jjn1$t7R0)w{KLJC8LaLlKRiOdOMePdFXd?M@t5T1LJ9Z&9keevWO@r45y6a-b0Hnwh36~!F&au5Y?CKx`QVDz-8HCjKl zl}D<`ri}bDF(8h78vlxA>v`q#jb;1NMR|mCNj!e--@uJ1;4k$zVtY@pA+B9()n;-QS0T_rn zs!P2Owjq1UR6$cZn@sp%AVp}bIqK7!pjc`yiqTb$Z}(`qNzmiBWqHPVBp5l0C!FIG zJqhw%bhJ1Qq(36)*&BL2R%em9(*HAcg*k$~S?UTBLEL6j(hL@r2%$JQ+o4}GV;N_A z{QY5({F+T3+BT+_I3lGtS0I)m72A#T_wPVwy^>Y{q{y$ePA1u6&%M6pXv#xQU;-mK zE!QAgv6UK4l2ouT7R=4s4k>yCn5+|4^-5mX!=n$&!`jDsNq0YP@Gu`YL$ATvQHRKz zwv5v{8l$xz@ZUG8+kyDhtC>)nwUTI>@_vZ9rucwXRIgGE*+bufA-<8vEMS*OV=nl} z`7~G>rQs`R;JI4PG2az@jg%--Zz4vEWr9^mL^S^jIWDX|hm25q4EqB(RrY_>+a z;|)i~^&2r}+c3oZS3L(|ROTeK%j1l3Csm*FV>E)aCtaqbJ{``mZfPAPQ#>L~`{x1D z7e&q*`p9+}yQKYgCSbnO&31wgy`VQeYE!_b>ahYQc}qZ=L)!=+DycL z7}7vb>l(3tGSZQjAF`6-Vyn@WC6kP2aA@gDU+W+IpukYWxxkMU#Cx12&3Nd|Z@-Pb zi`gZ;-{kib>kPyQsml!%woCGXU7R2V@1w-al1Yy?z-OO&cH8zS`4YU!dSw|ygEF3U z6M#%Ilc>Nzla$UbFaW|{r9%Qb^KP$a>MKcKZO)d8L@KWwk`RO3;}MVi)Q{>y=wHb_ zN`oY5(>Mz18#0m(FRrXj*ux)Q%$O!K|94wdvpqD*JG<26q%Y z{k=T)YLZU<8Fv`CW^Xy!iU#fmvXtZvG}T0dy%HI5U>LU zxCc9h*ZVC_OGxWuQ(mmwW4C@{?LNo{K>B5=<{sCO)(PLZTIuGI8unG#qe)1=&f$6K zH;3*hBY>7wKB|HdYMasQ?tq~i>f!!y^sUFO#7BI4)eX;${ZD0WY&w^;-p4dX?-@1L zZ0+adRW@rdu|vx#!D&$E6!6g7+N;=a>;@HK7#`y@U@mq(b&cd7`U~l&`fE$k+kSfm zz&AE`hjfUVh@;9=C~$)c|>l>yhrT6+B15z_}nwNoAam%$U8Rl|33-*R?qzR zj}tb9j*N(Zk)QvkDr!74o4?mGUxPsPbzgV;ywa(2*YXW($(A4ugKI8emk~E()QFp* z&hi_OSTY{TYvYRMt-1Sw;5yU_j<5@)XQYZ{d4Mq93Tc3j zDBjF!HUEnk1LaB~<+Yp#%liWz3%l;G3ohyMg6PZ``Hr*9Goe{&v*hmz)`)vrAVy(V ztf8mm1`D~1%xv{@b&n{ki`Ew8Z^9z@`whLD0P9&NL zddhj!>!@MkH;s!Bg+8Qhl=s65`LJ~-is$h5*UD6&612VX$(^{fa&%laiM}Z4w~0w9 zi45=AJ6^zgFoS9gNFc)x#iMOB29!?o$R0q}KF+HIV|Q9Hu~8Hn$fEd6#1E8j;qBQ& zVQ=Fq(h3PI`-}X6w>kmq*rqL%0m0;FFKu-=9eH*D(!e6&lc@W;v)&;TbY2Q7l(}Wy z8K=uBB(8X{Wl1EBF2Iydns{$?_cU6DS5%sO{d0C=mTy8Lsf!_kf=vy>(al_X&U|LnTZ2dQAocfNbV~5Ln5xYLwGTAh}7cZR$dek z5CXatjHk1FwzcmgyG*ipkO=RkIoI8LD8k@7OUkuIaerH@7ZL&8IdYx-Z&VVq8~Z4C zl?FSrOp0-shd#+hKHSDvzmnblFzlwd%;)V$Xt54G)QEQ1AgVWmrbHw3&@jdZ_+DFy zwMBJ6V2I!kHGBr@r(TR(k--bS1j!DI1y{8bwdbkdPl1 zo#O+$a(8l6zTOv!tS0E^l!uukDd6t6NFnQRQivCT=2oez1@<1MT8O5JqJqQpa|6h>{L`G(zwjQa!xwpP@Ly1!2fh5&hP_c9dDM1T ziRyqv99Iz)6=e)-Tr#o)d0{wrfJ8k?FM(15#xbEdRN31h>`8KRD#c&9u?*71R|R+ys?R%RXXkFh{>ln;01b_6$I#!os68e#Wox{S*jjUk7Q}QaVng6~ z6YM~0hKG1R7!ex4H($uSKIir3?BHrjRH6QU0Oo!Pa#5ETix}k1Q13$8$K!+%NddE% zh9{PbIV^h?J;&a_;Di@~MnnSBI32v5;?B{Frt|N*N?J^8wKop{&!!&QwlUjjv0U3i zmcv8d;gB1}q~N9!XFQ8}V*Uob%ry$KcOONG$UWQb-!)>9O8XfDO--RV8M2704^20( zbX40pnh(SYYjgy3pmPRcshUP(f(d_K8Z!u>DdY5LeOa!yg1y9ChOF`TF`nQR)QM<_ z+&ApJT86YN2~ZUfoM2HRf!=J2|H3-=5BqVg9l_tMrxA!`cH&N11r;&PQ)o}!DUufx z_YQK^>mk@8?Sn&9-&pTVP>C8oEq7AjTer$$*$aH@OU#rgjxr49{J#9Qs<#z5maZu& zh&@W{10EM$Xzja#`?RZGJV?*)KN%x9A2S+g_r!L9sk3_XH>e^tV6P<{7SYSRNviUV zQa(6FTyNuud(FtF)aKZaJruKM;u)!NY~E#S!8|n8+8@wpStnOoO0$5ns#$<$gg8!81Vc+J%oNcW2t9i+Pw zGmy61CfnDVrr1%({;~tl?le>xCyfU%)A(mWZ&j`G$@>@@~Yog24IzuK}JyFE{9CWt@C?W&T1b2u|y4y@>{09{z(y0tN)HvPi z>(-o{$oWk>BqabPPD*hSvB=v-@b(Qn#!tMrWa#;MoUv2kI7j9#5c@dQB7VfLZFj-5qL4*tGBpjQi>#IHm8gT2lx`s4hJaHvnQnR z#ru6UFNlodhQD8MZb4*&su&UYqcaI2dsYZU$L`eSMCy( zq=>}rw|u_~fGWAKvHI~(MVeMZwa+th8$|>^^gE(du)1=O2g;_6;2%6&NC4tWgWV*b zJB?N2miu$5tjyh{-nb-#4>oDV0L>D@5dV9jm9m+jK5tJwz5;UG?`gEk!ZXVcC>oe# zvSI@~H)MjUY|p%NNzY<~?=s*=j!+_Pmwb}5pKc#~bN|7v-Znbvyl-z`Isw-vvpgPZ_$tSwo%;H_Mg8!c%bS5PWoNxTzYn-hv}o2UfF+_X_MK6e~H}v zv#7Esf4_M4f1uw?$-B~B5#+RbH?q$cPyY+r_Sq<&C5t%{d4}}$;VtQJd+ohOZk+T# zWdI+HJrn<%?&^_U?#OpDXT971(yRTS0u}$Sem#-5+g3Jk{V&rLzRN%OCld?)J3Z&$ zT*iNajQ+vIf_hdcz&{vR@PFf!zeVlKc+6mLKTwSQJJ0yY@Wt=(wEwHU z?~IBv+qT8jt#GhK0To1BZB&XRN=8AEG9bYM3MATsNCwFu6532MQh;QU9F-_RF#!sK zWF)s@iBKYv6$IYg*r&VC9pl{b-Wzwk`+i*i=npCC+uz=6uQk_PbLNP#r7ZqG{ItKH z@&Dp4RLmq_=)d>z{{3QopKkvD2QK_8P5<|x)}J*p+rLF4|Gpz4m_RWP&-c#%S%d!D zpZjk>F$;|#N%mi-D@s0%@n5D=`hSP8{)=wvfBq4FLu4*{py4?${ZHg7QNa;^`Fr5* zRUluFhpG{4B<0sX-~FCsX$VmfH}hT)>})4SQLk0PikpX6d7@7lTB=AnmW5$`Mb@Sd z&!jV66#p?9w{3&#gWN1X(@fvZEOyG_tBN!Y0^XsTb0hegU4#-*7=h1>DFo9~Tlc&E zM>1IzI(+vQL}PHIoJ@q=C;at_63Bw0%DPNs?9o){1^Hk2ODU1u|L1&K$bD0a{@=Ri zA?+Yd)j%l}{o}3B-Qp(2$S%-}NuKx&woTW6`e=*Vcz)y|VzGOilsq_N|2(O`t78m* zJp>eT&ecLn#S~LxkP2{^oi&CT`Y(*orm7{*(EL&0 zkCPCmT?FMQ_9t=xzJmVa9073_7~Q+UlH)IEq>*%~I!`_2Twx{ePLza%&lFujQ_lU{WQ#wj}mjWi!6W+@F-kYpX5d8{%2W$s@y%ib&~&5)bb0+ zznLW*68rBkDdG<$pSK@ggc$vp|Fb7~_&-ywywq~Q^{<#B9}K4M-^D1`OCIg#owHX5wbsip?=#{6%56Sq?Y*7ds!< z{$(R7I1B}h?by{VSzgI%w^m~Lx)NtvGxW7jb%B2l$pUI7p=;{4o_`N=U?$OCU}GB$ z{pIaV@s%CwumTIaIgPKtO$L&F)}F1+jh9^Bh$b}80~qJ*HZWclu^D=TbsC2_Y6Y}s z!OKchVnd_!xgQ~O=QR`dU{;AMOLvS9z>N5P6LfFITB+lIf>3O#oBG|t7pHZY)y3^C zDfR8WBKJ4e_m;mc)HeTw#^a$~%fT_|vOW-9VA&~=JRDIM6>;*wKT*CkwchPD5V48J z0Zfr1;}ruDb(nVjV-4f9iIyKG$S$&g_*DXU2{a%SgC(u{Zj0lc{#vzYWjq_kBe|BC zkr!hfTro> zC20!VW(6^24IB*<(O((+?cG)mVp&>xR%2Hk-{1cKp|euoNvH(JCIBd?SgFT>Lw;B6 zyLWUR4BsH{Vo87n5vv*<>A(H7TfH1uTO1WWP>&(@p{591SKJ?tMiT=N4~&MBgJB)s zhywTv#QxU8i>^A(nnH&J3uX(`h#m;EpV+go0k-WXLtvIyAzE-B{>r@s59!1_5oa&a z9Kc%LZu7eFsMfBqYhHtAnQ$WCJEHZFEB4PL_Omd`LN7wKC1+b^+7qY#EtF1Q{`mCA zpVk|VOr@Op^QFO3wiOOx1eiSvp$LdM3i$>#!via9C|nylnXaBX+wq)!bFJOpdk)cK zXBN$E47fm{yxzN)OXtWvDwW3NEqagN@LBkp!)if%)jdDBEqc9jmD#>kQjgc0HNC#K zI)F1{oAtBh+cxoDn~CjvXfYYxCZAjKRIDWSm7H^@f>%eU*QIyvlU}h~zwiD%{UW+g zT>shb-;=)ePnl0P!N2+i|L^}Tc^LE}HJEw5Fd5z{$-w;d!?y-OdTRKlK z9U_sE;~439r2H3P_x-C1`s}!yq=)UK4a`s*Z=?YVDG`J}J_h_vdOWL;(i|NIf^hL; z)&PK*J42wF)wu)>^K!aI-%&Op%s_2lFi_(zARzl~$$^{>VK0qgGbf-KxK((t6eC(m z(0fXN$3c3kdv^0_oW}-2U2iJE+KQM44I_Z01VeOFawh?t+kZXB*#H(tYg0*sbu=Jr z^#>fNA2^N&p=G+~Xy4y;X@;+8!1OL>!ON zJ8Xn*Li$V>QT--{buM6yC&J4vOl zghtl(it-iUlr-TkNa}_Ti(gQg>;ib+WEms1NDNCN8b3obgd&f-pyP@yk_HYB z#wkg-Pj1Y@sZFL|5z7>~#f&JkQ4rbvPQH7>A)cXikBKj3nKNUh4o#7p=-@?_P7(_~ z!e2cxjo&ZQKEJfvlFND6BsW7*+nOxdY49;A-Cj(FUVzCfK(q~DQAa95?RyNw(nP34 zCq%oo3uOW(%om%)YqF>&a9^7fBF#bJjJwJPaCvWdAL$U&USJo=H$YhHibNznx1X-7 zFKFnFO{AsWVRm(Q!nl=*LoTWQT2@V2K*G zBmZFSav!Jluzs05?Me0sfU#Ib0JBZVzw!XCB_kjumaop9nLAn0=LL7=M&hD}1uyyN zTyYP5L*H|XjUci|?7qIPPwa}t;tb+5uWRI3o(G3J^SLE#eizEuWHTcnogaY)GpM9j z8xWKt!Ku5RFPC0}Oe!ZWQzCQ4xI-u$r0 zJ(X_xx30uh_!0Q0L#vmS1C6G-zadc#U)NyLHViRTiBUOa5I;Q@r6y5cpxqNmMDJ==?G6m?rn|{T+so(^lxp2cw_OQ z0_e^@xMH~O66xMl+Vp$x~fEUy#-C?UOF@y7-fVGPMr6V9&|0k$s*m_~8=wN1_ ze9UuV)zFCF5oQ9y_-Th+#nwYvcrQECKRDx!CqTnpS3y&!Q4Z=X4P*+Xbh5X&jRZW= z!cMJTi{NDcUitetE*$N#P*0JtgWzmi{#O(MO>({&b8y|NDd1`bpFXZBXgIl8taiqiadH)NMvO>;0ht`7*Srx@c-5-W2dd+?iFYI6 zrBsvIk3qchHqN&Dk0XTJ*{Ih{?*vO%8T`Zqn>xDzErOV?kD~KR0z{dGGUN_OpA(YW z9j`|CTkg$~Ijv%Rb8T&TxYds&iNm|cz`?flKxaxPd<_}^wI5-+3uoqOxjmJcd>!J2 zDes)fk|Mzo4tgx>7rhY5RW|LA%AX{BLo%;2(Vo&bpCtr?k_7_sonIkH!!TAljzw2P zzeZz1PWYIaS>s(>Zu*LI!$gG*4jDn5zH4{BMw3`btY$q z%RgF`b#9#^I>8onW9yxuS5jo`ag?VeA+-H5_3;;nj}|&R{I^%`s1~_dvr2Or=Jvu; zQ=e(4A&@$kkD*)+CDKj%yDC8Zk5~_%tkOv$-H3u*++0SySN^ius!E}lC}U7g2sWM<^w$7W2Nm3f=7w3pXX?W4@pPzv*w7u@`P+f_$oFwgH=2(Y;m{dnaRog!biQgO_PIdqSKRNtr*gnYkmcllQc!0OzBr;MBx zYI>eV03@2ob<7@jJkN}82#JqB{7Joj>9c3*A1)GNbOREpV-ftVVn@wSG^#n?xdX!yl;-p7%c2;1Mx@SB%>#~e*AnBEekYRGe>b#v(3 zClF)jag!dDH(@fydP1KE(#qadd7r)~k$p3w{Sc2e&Puvg`$NU*nwZ@szuS?_*$R`<_u8eWxoIY0{Jert!b?n(oc-_8@A1l(mU3I)G7n8{ z&0&O1qheu~Poh=KpJiWP6)l8s?5h`#?= zK&jPLfZI8`Y2v`e3lD6Ke~a`u>kQsqv&p3kRd#atvKO{j80J-ajMIA@n_)(Li!h~5 z?>=%Q>)tD8Z^<$FZEikDg{^>Q{krjT%br9E)5x-H@<>4B?dGmuqo697NvE-MCALLY z*K{y+4TQWJ9O1;!HU?Xa+l6nF=a#Km#FfzQnif>qD`}IQp*wWb8OABG77K27QuYzb>w{P$Kt^VzRIebtW8-V}cB8hUKau0=4v;<$PkViG zM-HIMlRlZ+*)yzl zDdDR$ch`bn9JwA9zB~xJ0b=z6%mMx>Ibrctn(+nuVX0-p+aOd3)8s^=C`zo;UBwJz zmA0XncQy-Z{Z{^TLD8EiW4AWJ89-f!NWqkw9j^V{^~g()ynGYjB}Yg|HMqVlf4$}a zbxHUN(MXKpXx$imH-#%U(7*>p4v%P5MBuh^Z^WKtk{C3SqSzwJasL#?ob{VdhvM@~ z`#b#(-F6BS62H^K$DDn$F-Cd@r}}0Kw5HF7MuUd8Y!ZM8?{c%!_k)mUQX?&b@-b2~ zKy36%6Kb)OQYVEZPtm*Pznfz3F}&47yfy;>T6tqEhgUIktIKOTkk;R=FVvR%g(#TB zhsN$OSDgZ5OUs$4J(};0b|MnJd>E6av7YaqeUlhwLb3h|_+#r(#RT&6nAYOdd=Dg) zqgh=j_)~vBrC~G)7*qI7H-Mri$Ael+cY}N9*+?eE+1dL-HsqQ<+s9GVx>thGRxdaS zSnm7>+a4PNgk`=YWs=Mc{5vG4q``Cx=QaE|H|tP=;z7Tu<~6h|MWhZy%ez(?7&GPJ zZ5CiqiSyVU*L%!!wu)sVN{Zdmeqg5Dr5{oDT;Kf|h>z%R5+>E_3c0=3o0S?gy4kF1 zKE72wLZ_~jSk#`YwXu1CNtn1O;6`~nXjZ?pf=S%{p#vvopWC9sPJ#mvx%abSs9#yy z&huv*yFS%)3iX@Fz!tN`)=shELHr#^mqb0jqK)xRon$1bQWRm~>d09ns~jd1BpxrD zfMQ|9MSL6n!ZSds=@mY%FESIjc@A*cU3W$Go`@7~wd4?xqR}LzZHCYlqwSsmSxUsx z1n({tCo+yfvM()^;=)O>0LEO9ibzhmEm!z>x3phdb9a8+RuKnm+LPXm_CSU-$W6_7 zrJF+ZCkW4G=;9zL7te-1K+)WQTJNU&&2N5-mLJTlhahOUjKJPxWkR#lx7mT;C=@ho z(0!i?HT`XA>>95v?*xxj+yee)6J4!x|0EK^B@TDMPq)J3riaG>F(ZKVk-8i>&6qy! zSit5NN;tv>w?OAgBAu+E2l(>R#u!hdX7-UiOJ=-s`JI~OAU@haF6PO+PE;etEP0Xx z%lBC=Uyj@$uY%7;M@?yFtdT0}i(ScNT)O(`IpB9g)(*#g)2qwAP`kY(q75rbrS$RH zjYtEuBz8l(&IM7RwfN=eE2UV%QoC^19k=9X+(pL>ZSPUL$B3S@yl@Ipb+AHIraIUl z0Mtg3joFAIMi+)U5J;$?wA!8{q3EzvuwX8&hcp3dk=kFA%M07j&=Q z?Uq_C^S%I;(io~{1#ie<(L?<`-~IppSeUQ4Nw)FV(2O>?bo0g7CUjnP#yB6o6X-@Ujw+8v9k#Ld|a z3^6s5WS37vz$k5JQ)2`1U!%`g!W3qk5|ZhRfqJBy(E_g$Lk%e7AKRQa4vDg{g8%#~;Uolw zIIC~NSAAbTVJB0!<@1oo1WC+tXUAb`Tw~u|UeoLmFoIl2+?s#*ym^KLAU~d=^591v z)YKump0QNPUOCh+rLK5prheR}f(|39U}z7rmVR_r(_2SK6n9%R0g`{3#}MorUYWo_ z$7v$K3?}a4=?2tJ5!p@K$ zCR>SwR^Q|>b4@nzfPz$XIP`fMmNq_<-BwF00u&|Kkt}O*p&001j^8UBx-j+1qVT-(HO)>)9nLV!g#)*4{|P~XGK4|WN|L$uV+tbWs5{zrIgw$3%KY)$Hg6Do%CaHI z1_mcJ^JArATtt+l8BgUL7fqsVE;6v+?l6z9(HFE(7%){ZVNONka%gFwWOa?4(&+qh zCE4Q+6(> zy8|^hm$B%rFxo}a3-h4tu+QoS*r9(dA&cLZE8spTsg0m!GFnhx@bz`r=GNpq94C2& zfzGpmKaeaGr zA(n67X#j-jdAy4d$|{iJtp$)s%Fx^iT`7OvQm!)rmpJ3!YIswp-=S<$$b=-HRP9c5 z#axh8Cc*gByf<;`vcv#hN)*xD7tzsaJSQ~lr=oM!7!Nv-kGt6NPxKURncYQzgTOCht!pu4%#wT_e*t#OvyStfMU^ zJgzgOu15Cq+uq&VlH=b_Mv_YU19sU7_2z1>N{}ti% zRfQ>SMrz?90pw9lcDU%{)&6Xiv#A~oM0e-=pKTle>?5-M&%fCW5}?eK1aXTK*~UNEuCl!geFTGR#NK7Pu{hh@8=&?5rx(k^zMWo z;IsQN)bOHRlI&co=hrs&qeo{w&~;~ho-b**`i0K>oyJ}81X|ws1HbJxz_AaLy1y$T zXsHIuj3aqsJ5q?eJQgDhayggX%=m4|3cAj+TG z_(_QTBpbtu@Sy7!=GnB05j3$M1D{o1a|r#aXmi`=%*?lq_mIsQM=io&QKM-f^QUn_ z{NK{?eP$VuKMo;H*VwgL5+`>IO);I)N$_mAAHZ0#V#`KIF)q+o8S0^S- zAigGGY(dN$@vxs9s3%&a6uiP{;4t;0Lsjkb2^)YnWf`%|ECx5B!584MBuo`c%}I{Kw@AKz zKMR8c8d$1h5hx+^M^$8*0MRu_)k7+}`dAijaCef`&v#7*hc+jy;yGVH`ZukF<>7+V ziLR!Eg3t~E@(;IFr|+b)l4Xa`1adZLA{iQ8(b*^v z9e-P0&K(pJ7c3Y}PFP`m?_`@%z?FfQtix)yF)xctDl9Q|j9!Qh$AV~a~=z0BWDVs6o`*`f` z5D)1wl+a09{$}fVbex{b&DZOTsM|L$R41BHioSVS0cPt&&&B7{7Yk5I`ja9X;H(-> z7kt69Al#!YN$>orw2~h-ExpNK>u>Y(Sw3jPJnz0bb2UJTHY3=6gIQn_5Lc<`Gjh}V z+<~HaYYltZt;!U~&dt}f9|nJMF!~91p2{h~cFX+5wB1{>CM^YcHzqGVP92X(N#hpn zd7E4D3Q^m)+o_&6ZNxoK%u)0}Vp<*EzDt{Vn0f0rAh9vg=f23L?WEqP=$&zAU#3#7 zh;%fby%i1dNa!5jm@!-tD?A}LD|&gzHtLmZ4|9_&cO?mseg+}iBt(+Uo^Dp z^g;0)c~8*sYFsZzdUs%ZiLlTFV!YywEtDo>rC&UJ@h;2>mK<4zTHJi7Y|cl2BdXnV zIWP%*(B4D#y2Asar7PS*MuZEm01eOlh>I!B_0|_PfCv`dPGodCcvJ~War3RAa2cP6 zZ!Yp%cU7R?qBBdICU>?7)*h4SD^p~NSCCPi(wR2+b`BUN6%T#LsTMPwz=hd#p9Ixv zVvAmCPAUf0S6}3rRlKOS1HJ+f{hBk}Te*77Vb3S0nd!-F*IB!Qwk-~2SUENWel%wA z*JYANr+>IEOi8xQZ`O4c6;JF)0}}Fyq9?Fd@2nG88C*s@o+rg{X_e1D(Yuh0b(KdFby{lnA(#mCS~H!y0Yh4L&CVo2m{}*=8VkUA%`}u|r zo9U;%S+7m6t7oq@UMA+xbwMsICKX?YyyDuUYalCWcl#Q1|uqO|%GD)il zNb6|Q0c^4SA8eMBCUYX3>h#pELie4chkMgmU1nyja^Cv@UQ>oQFbK~++tkERRf}j> z-Mt-`osSv^X1Dw-6St}_<+ZdP!OloY$BGiKJ#gOMdRmNujHq>_y{Wux5dl{oPrIN` zoec1*)N~3pxVVQtp3kpk-WuvxuDgJrPBP4#jtyTL=FvU65!b#(KNMIGm{4!dw`3ki zE3bo*_L8A$bh{5w7jz)An#sJ*P zkr22{}>e28vW_nx!|W%_RYz1eA7xh-a%!r&m=Ljd%)|okWemZYeAkyuDY)Pe)zo=kjkIz9s{U}1__~MAi`R{d!TBSncq?s83Ur7wHPTz zO8Wtkgq~2~+$}?3fcX|FdbjDWazXaZ=#Tvi2Jsa>88{7O#-lmnO zQ8oUrt%Zvjnu=df(0+>~odw2|xV`7n&P)(?TYXFH^sRmR`udNn2|Ak!$`MZ^L8hjF z>qhc*fRfk{XJ(w{oV)##@^_=>bck?{nVXcPgjN~M*F{J@>eGJuryv%EEf zowoPNc~b$9@-&j<|M6ht+g*gSWksXQZ661}a`n28MX=1^%Nr!+%#v4LNDGpoE3Bhd z>7F{0PwyIcf+(?hW*%7)%kLa%ZgW67&(oVmV9=dNsA%JEs0_Irf@ik+?c?} z2J|CdNE40!ICD)@&S&?D*8I_GU_`SIL!7PnF(Z`FJC`^NXmx!}`DR?j`RSc*y??-A z(!4k~0oDdoyT9++kfqniNDN(B7zLRtjpYd-tWg}=5t4WX*1o>b_(;Tca@%wYfV(Q$ z7MZLwAgT^TCExa$YK%=Hv(5B+qEJF8T`=-tTL{?vDT?sCyc8PHHg#hTL(|3`GQ%OCQr>ukY?hGfm^L zSkZ}cFXBi-_`?m*s176hj8gq(t5wxvl3#JZyS z@v`=?eVm{V!Im2l4X+umx(%Mvc=z1>&^yHqV5jC>JEF5cfVE4{NZWCy2X@1rLzR=F z4-ZTE&xrR_I?;Z!xT!9Eh$tl%=<6GW5q;(fZz2rb2u|H~jL;YlP7-H4zq#h5jPL{T z_~+4Wo1R_cAM=2y1fg~x&Y4}hPUUj^QLYR*q~48$hRJlVKPlAG0ks^F6)P7oQr2l3 zd)-Nypcz_jKKnD0wA6yGI;aRm%rg!IidZD$&Z!c-;8Q_*!W+X)uehK2v~RgwLVF99 zU`Ip+Sl9eLV5sV|>x!NZ5#KZcWVBMcyo8X=OG!Pf&hih~LiiM?fv~52%R=<9LKa=E ztOmAL&B8T~_LQFWDXehno?ouebVRFoV&xX{V+D=!EmdvK>n?tHht|I(tI&yS zMc^PoVA{sVLEF)t?h?wgu7~>0F~Jq9clxPaHAW97i}TUdmFd$XW{5%tkFO1Fx6(6Nk-#)pQrSEK_nS9-{P}05A z@xT=B%G`lX`Tp$d31Gte=_qn8n<&z4uBLcIVTQo!IO)eV;8-%mia*3RK|@8}(2Nrr zh8#UADaq1kY-;Kr>I202(qJj|`R0Y|x%NLgD}k_(EvBr=_m?xJ&Z=`ZE^Yv@7oGx}zi zhmIQcM%ou!1&=;+e@BQix^i0|DwJPl-_7;R-+DI}clu!+&H(3Wh=_@DR8t=TokfVB zhjRDaA(VPvcK;R)8vU1cW2!>p(jn4+e9WUmYVR%T5g`}6syYtYA!8!bMy=??Zt(ge$KDo z&!31$SXf`c#S$xvu|Vr(iO(hv?w+DAL5^MA{NQe33;#_XC+_^N_au_f|M_nn^ZB3e g@V_-$mFhC9l=wp9+c9~!cCuuLs2VC+%D