diff --git a/.github/actions/setup-docker/action.yaml b/.github/actions/setup-docker/action.yaml new file mode 100644 index 0000000..1278b19 --- /dev/null +++ b/.github/actions/setup-docker/action.yaml @@ -0,0 +1,22 @@ +name: "Set Up Docker" +description: "Set up Docker" + +inputs: + docker-username: + description: "DockerHub username" + required: true + docker-password: + description: "DockerHub password" + required: true + +runs: + using: "composite" + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log into DockerHub + uses: docker/login-action@v3 + with: + username: ${{ inputs.docker-username }} + password: ${{ inputs.docker-password }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4cd3cc4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,77 @@ +name: Build and Release Images + +on: + push: + tags: + - v* + workflow_dispatch: + inputs: + tags: + description: 'Tags' +env: + AWS_REGION: "us-east-1" + +permissions: + contents: read + pull-requests: read + repository-projects: read + +jobs: + release-images: + runs-on: ubuntu-latest + steps: + - name: Check permissions using GitHub CLI + env: + GH_TOKEN: ${{ github.token }} + run: | + permission=$(gh api repos/${{ github.repository }}/collaborators/${{ github.actor }}/permission --jq '.permission') + if [ "$permission" = "admin" ]; then + echo "Has admin access" + # Your workflow steps here + else + echo "Permission denied" + exit 1 + fi + + - name: Remove software and language runtimes we're not using + run: | + sudo rm -rf /usr/share/swift + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/share/powershell + sudo rm -rf /usr/local/share/chromium + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/local/lib/node_modules + sudo rm -rf /usr/local/julia* + sudo rm -rf /opt/google/chrome + df . -h + + - name: Check out repository + uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Fetch all tags + run: git fetch origin +refs/tags/*:refs/tags/* + + - name: Set up Docker + uses: ./.github/actions/setup-docker + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Set tag + run: | + TAG=$(git describe --tags --match "v*" --abbrev=0) + echo "TAG=$TAG" >> $GITHUB_ENV + + - name: Build and push image - createvectordb + run: | + ./dockers/llm.vdb.service/makeDocker.sh elotl/createvectordb ${{ env.TAG }} + + - name: Build and push image - llm-chat + run: | + ./dockers/llm.chatui.service/makeDocker.sh elotl/llm-chat ${{ env.TAG }} + + - name: Build and push image - serveragllm + run: | + ./dockers/llm.rag.service/makeDocker.sh elotl/serveragllm ${{ env.TAG }} diff --git a/README.md b/README.md index c96212a..b951db6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,40 @@ -# k8s-rag-llm -Deployment of RAG + LLM model serving on multiple K8s cloud clusters +# Question-Answer Chatbot with Self-hosted LLMs & RAG + +- Setup the complete infrastructure stack for a Question-Answer chatbot for your private data in just a few minutes! +- Your stack will be powered by Self-hosted Open-Source Large Language Models and Retrieval Augmented Generation running on Kubernetes Cloud clusters. + +## Overview + +The Question-Answer Chatbot is powered by these technologies: + +1. Open-Source [Large Language Models](https://en.wikipedia.org/wiki/Large_language_model) +2. [Retrieval Augmented Generation (RAG)](https://en.wikipedia.org/wiki/Retrieval-augmented_generation) +3. [Vector Stores](https://en.wikipedia.org/wiki/Vector_database) +4. [Ray AI/ML compute framework](https://www.ray.io/) +5. [Elotl Luna](https://www.elotl.co/luna.html) + +elotl_genai_stack_enduser + +## Retrieval Augmented Generation + +The graphic below shows how RAG is used to determine an answer to the end-user's question about a specific knowledge base. + +
+elotl_genai_stack_enduser +
+ +## Installation + +* [Cluster Setup Summary](docs/install.md#cluster-setup-summary) +* [Install Infrastructure Tools](docs/install.md#install-infrastructure-tools) +* [Install Model Serve Stack](docs/install.md#install-model-serve-stack) +* [Model Serving](docs/install.md#model-serve) +* [Retrieval Augmented Generation using FAISS](docs/install.md#retrieval-augmented-generation-rag-using-faiss) +* [Creation of the Vector Store](docs/install.md#creation-of-the-vector-store) +* [Install the RAG & LLM querying service](docs/install.md#setup-rag--llm-service) +* [Send a question to your LLM with RAG](docs/install.md#query-the-llm-with-rag) +* [Query your LLM with RAG using a Chat UI](docs/install.md#query-the-llm-with-rag-using-a-chat-ui) +* [Uninstall](docs/install.md#uninstall) + +Jump to complete install doc available [here](docs/install.md). + diff --git a/demo/llm.chatui.service/auth-proxy.yml b/demo/llm.chatui.service/auth-proxy.yml new file mode 100644 index 0000000..d1a45a3 --- /dev/null +++ b/demo/llm.chatui.service/auth-proxy.yml @@ -0,0 +1,93 @@ +# nginx-auth-proxy-config.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-auth-proxy-config +data: + nginx.conf: | + events { + worker_connections 1024; + } + http { + server { + listen 80; + + location / { + auth_basic "Restricted Access"; + auth_basic_user_file /etc/nginx/auth/.htpasswd; + + proxy_pass http://simple-chat-service.default.svc.cluster.local:7860; # Points to our simple chat service + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } + } + +--- +# auth-secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: auth-proxy-credentials +type: Opaque +data: + # Generated using: htpasswd -c .htpasswd username + # Then base64 encode the file content + # htpasswd -c .htpasswd your_chosen_username + # cat .htpasswd | base64 + # myuser:elotl + + .htpasswd: ZWxvdGw6JGFwcjEkRmtKeUFMWjMkYjd5WXdBdmhHbmtTSjN2QTdCOXlGMAo= + +--- +# auth-proxy-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: auth-proxy +spec: + replicas: 2 # For high availability + selector: + matchLabels: + app: auth-proxy + template: + metadata: + labels: + app: auth-proxy + spec: + volumes: + - name: nginx-config + configMap: + name: nginx-auth-proxy-config + - name: auth-volume + secret: + secretName: auth-proxy-credentials + containers: + - name: nginx + image: nginx:alpine + ports: + - containerPort: 80 + volumeMounts: + - name: nginx-config + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + - name: auth-volume + mountPath: /etc/nginx/auth + readOnly: true + +--- +# auth-proxy-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: auth-proxy-service +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 80 + protocol: TCP + selector: + app: auth-proxy diff --git a/demo/llm.chatui.service/pv-and-pvc.yaml b/demo/llm.chatui.service/pv-and-pvc.yaml new file mode 100644 index 0000000..02e2ade --- /dev/null +++ b/demo/llm.chatui.service/pv-and-pvc.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: simple-chat-pv +spec: + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + hostPath: + path: /mnt/data/simple-chat-logs +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: simple-chat-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi diff --git a/demo/llm.chatui.service/simple-chat.yaml b/demo/llm.chatui.service/simple-chat.yaml new file mode 100644 index 0000000..e48917d --- /dev/null +++ b/demo/llm.chatui.service/simple-chat.yaml @@ -0,0 +1,57 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simple-chat + labels: + app: simple-chat +spec: + replicas: 1 + selector: + matchLabels: + app: simple-chat + template: + metadata: + labels: + app: simple-chat + elotl-luna: "true" + annotations: + node.elotl.co/instance-type-regexp: "^(t3.xlarge|n2-standard-4)$" + spec: + containers: + - name: chat + image: elotl/llm-chat:v1.3.12 + imagePullPolicy: Always + ports: + - containerPort: 7860 + env: + - name: RAG_LLM_QUERY_URL + value: "http://serveragllm-service.default.svc.cluster.local:8000" + - name: USE_CHATBOT_HISTORY + value: "True" + resources: + requests: + cpu: "200m" + memory: "256Mi" + limits: + cpu: "500m" + memory: "512Mi" + volumeMounts: + - name: log-storage + mountPath: /app/logs + volumes: + - name: log-storage + persistentVolumeClaim: + claimName: simple-chat-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: simple-chat-service +spec: + selector: + app: simple-chat + ports: + - protocol: TCP + port: 7860 + targetPort: 7860 + type: ClusterIP diff --git a/demo/llm.gpu.service/block_device_mapping.json b/demo/llm.gpu.service/block_device_mapping.json index 6b60822..16665a1 100644 --- a/demo/llm.gpu.service/block_device_mapping.json +++ b/demo/llm.gpu.service/block_device_mapping.json @@ -1,6 +1,6 @@ [ { - "DeviceName": "/dev/xvda", + "DeviceName": "/dev/xvda", "Ebs": { "DeleteOnTermination": true, "VolumeSize": 80, diff --git a/demo/llm.gpu.service/block_device_mapping_bottlerocket.json b/demo/llm.gpu.service/block_device_mapping_bottlerocket.json new file mode 100644 index 0000000..862cb0a --- /dev/null +++ b/demo/llm.gpu.service/block_device_mapping_bottlerocket.json @@ -0,0 +1,21 @@ +[ + { + "DeviceName": "/dev/xvda", + "Ebs": { + "DeleteOnTermination": true, + "VolumeSize": 80, + "VolumeType": "gp3", + "Encrypted": false + } + }, + { + "DeviceName": "/dev/xvdb", + "Ebs": { + "DeleteOnTermination": true, + "VolumeSize": 80, + "VolumeType": "gp3", + "Encrypted": false, + "SnapshotId": "snap-09946d545033d96f7" + } + } +] diff --git a/demo/llm.gpu.service/get-user-data.sh b/demo/llm.gpu.service/get-user-data.sh new file mode 100755 index 0000000..b28f185 --- /dev/null +++ b/demo/llm.gpu.service/get-user-data.sh @@ -0,0 +1,4 @@ +clustername=$1 +region=$2 +eksctl get cluster --region $region --name $clustername -o json \ + | jq --raw-output '.[] | "settings.kubernetes.api-server = \"" + .Endpoint + "\"\nsettings.kubernetes.cluster-certificate =\"" + .CertificateAuthority.Data + "\"\n"' > user-data.toml diff --git a/demo/llm.rag.service/chat-serveragllmpluslb.yaml b/demo/llm.rag.service/chat-serveragllmpluslb.yaml new file mode 100644 index 0000000..92f93fe --- /dev/null +++ b/demo/llm.rag.service/chat-serveragllmpluslb.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: serveragllm-deployment + labels: + app: modelragllmserve +spec: + replicas: 1 + selector: + matchLabels: + model: serveragllm + template: + metadata: + labels: + model: serveragllm + elotl-luna: "true" + annotations: + node.elotl.co/instance-type-regexp: "^(t3.xlarge|n2-standard-4)$" + spec: + containers: + - name: serveragllm + image: elotl/serveragllm:testweaviatesql3 + imagePullPolicy: Always + ports: + - containerPort: 8000 + resources: + requests: + cpu: "1.5" + memory: "1G" + env: + - name: MODEL_LLM_SERVER_URL + value: ${MODEL_LLM_SERVER_URL} + - name: AWS_ACCESS_KEY_ID + value: ${AWS_ACCESS_KEY_ID} + - name: AWS_SECRET_ACCESS_KEY + value: ${AWS_SECRET_ACCESS_KEY} + - name: VECTOR_DB_S3_BUCKET + value: ${VECTOR_DB_S3_BUCKET} + - name: VECTOR_DB_S3_FILE + value: ${VECTOR_DB_S3_FILE} + - name: SYSTEM_PROMPT + value: ${SYSTEM_PROMPT} + - name: MODEL_ID + value: ${MODEL_ID} + - name: MAX_TOKENS + value: ${MAX_TOKENS} + - name: MODEL_TEMPERATURE + value: ${MODEL_TEMPERATURE} + - name: RELEVANT_DOCS + value: ${RELEVANT_DOCS} + - name: IS_JSON_MODE + value: "${IS_JSON_MODE}" + - name: SEARCH_TYPE + value: ${SEARCH_TYPE} + - name: WEAVIATE_URI_WITH_PORT + value: ${WEAVIATE_URI_WITH_PORT} + - name: WEAVIATE_GRPC_URI_WITH_PORT + value: ${WEAVIATE_GRPC_URI_WITH_PORT} + - name: WEAVIATE_INDEX_NAME + value: ${WEAVIATE_INDEX_NAME} + - name: SQL_SEARCH_DB_AND_MODEL_PATH + value: ${SQL_SEARCH_DB_AND_MODEL_PATH} + volumeMounts: + - name: log-storage + mountPath: /app/logs + - name: db-storage + mountPath: /app/db + volumes: + - name: log-storage + persistentVolumeClaim: + claimName: rag-llm-pvc + - name: db-storage + persistentVolumeClaim: + claimName: sqldb-s3-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: serveragllm-service + labels: + app: modelragllmserve +spec: + type: ClusterIP + selector: + model: serveragllm + ports: + - name: http + port: 8000 + targetPort: 8000 diff --git a/demo/llm.rag.service/pv-and-pvc.yaml b/demo/llm.rag.service/pv-and-pvc.yaml new file mode 100644 index 0000000..fedfb45 --- /dev/null +++ b/demo/llm.rag.service/pv-and-pvc.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: rag-llm-pv +spec: + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + hostPath: + path: /mnt/data/rag-llm-logs +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: rag-llm-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi diff --git a/demo/llm.rag.service/qa-db-s3-pv-pvc.yaml b/demo/llm.rag.service/qa-db-s3-pv-pvc.yaml new file mode 100644 index 0000000..62c840c --- /dev/null +++ b/demo/llm.rag.service/qa-db-s3-pv-pvc.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: sqldb-s3-pv +spec: + capacity: + storage: 5Gi # Ignored, required + accessModes: + - ReadWriteMany # Supported options: ReadWriteMany / ReadOnlyMany + storageClassName: "" # Required for static provisioning + claimRef: # To ensure no other PVCs can claim this PV + namespace: default # Namespace is required even though it's in "default" namespace. + name: sqldb-s3-pvc # Name of PVC + mountOptions: + - allow-delete + - region us-west-2 + - prefix zendesk/ + csi: + driver: s3.csi.aws.com # Required + volumeHandle: s3-csi-driver-volume + volumeAttributes: + bucketName: selvi-txt-to-sql-db # replace with your bucket name +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: sqldb-s3-pvc +spec: + accessModes: + - ReadWriteMany # Supported options: ReadWriteMany / ReadOnlyMany + storageClassName: "" # Required for static provisioning + resources: + requests: + storage: 5Gi # Ignored, required + volumeName: sqldb-s3-pv # Name of your PV diff --git a/demo/llm.rag.service/rag-serveragllmpluslb.yaml b/demo/llm.rag.service/rag-serveragllmpluslb.yaml new file mode 100644 index 0000000..29a932e --- /dev/null +++ b/demo/llm.rag.service/rag-serveragllmpluslb.yaml @@ -0,0 +1,74 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: serveragllm-deployment + labels: + app: modelragllmserve +spec: + replicas: 1 + selector: + matchLabels: + model: serveragllm + template: + metadata: + labels: + model: serveragllm + elotl-luna: "true" + annotations: + node.elotl.co/instance-type-regexp: "^(t3.xlarge|n2-standard-4)$" + spec: + containers: + - name: serveragllm + image: elotl/serveragllm:v1.3.12 + imagePullPolicy: Always + ports: + - containerPort: 8000 + resources: + requests: + cpu: "1.5" + memory: "1G" + env: + - name: MODEL_LLM_SERVER_URL + value: ${MODEL_LLM_SERVER_URL} + - name: AWS_ACCESS_KEY_ID + value: ${AWS_ACCESS_KEY_ID} + - name: AWS_SECRET_ACCESS_KEY + value: ${AWS_SECRET_ACCESS_KEY} + - name: VECTOR_DB_S3_BUCKET + value: ${VECTOR_DB_S3_BUCKET} + - name: VECTOR_DB_S3_FILE + value: ${VECTOR_DB_S3_FILE} + - name: SYSTEM_PROMPT + value: ${SYSTEM_PROMPT} + - name: MODEL_ID + value: ${MODEL_ID} + - name: MAX_TOKENS + value: ${MAX_TOKENS} + - name: MODEL_TEMPERATURE + value: ${MODEL_TEMPERATURE} + - name: RELEVANT_DOCS + value: ${RELEVANT_DOCS} + - name: IS_JSON_MODE + value: "${IS_JSON_MODE}" + volumeMounts: + - name: log-storage + mountPath: /app/logs + volumes: + - name: log-storage + persistentVolumeClaim: + claimName: rag-llm-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: serveragllm-service + labels: + app: modelragllmserve +spec: + type: ClusterIP + selector: + model: serveragllm + ports: + - name: http + port: 8000 + targetPort: 8000 diff --git a/demo/llm.vdb.service/createvdb.yaml b/demo/llm.vdb.service/createvdb.yaml new file mode 100644 index 0000000..a7f0948 --- /dev/null +++ b/demo/llm.vdb.service/createvdb.yaml @@ -0,0 +1,49 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: createvectordb + labels: + app: modeldataingest +spec: + ttlSecondsAfterFinished: 120 + template: + metadata: + labels: + elotl-luna: "true" + annotations: + node.elotl.co/instance-type-regexp: "^(t3.xlarge|n2-standard-4)$" + spec: + restartPolicy: Never + containers: + - name: createvectordb + image: elotl/createvectordb:testWeaviateSql1 + imagePullPolicy: Always + resources: + requests: + cpu: "1.5" + memory: "1G" + env: + - name: AWS_ACCESS_KEY_ID + value: ${AWS_ACCESS_KEY_ID} + - name: AWS_SECRET_ACCESS_KEY + value: ${AWS_SECRET_ACCESS_KEY} + - name: VECTOR_DB_INPUT_TYPE + value: ${VECTOR_DB_INPUT_TYPE} + - name: VECTOR_DB_INPUT_ARG + value: ${VECTOR_DB_INPUT_ARG} + - name: VECTOR_DB_S3_BUCKET + value: ${VECTOR_DB_S3_BUCKET} + - name: VECTOR_DB_S3_FILE + value: ${VECTOR_DB_S3_FILE} + - name: EMBEDDING_CHUNK_SIZE + value: ${EMBEDDING_CHUNK_SIZE} + - name: EMBEDDING_CHUNK_OVERLAP + value: ${EMBEDDING_CHUNK_OVERLAP} + - name: EMBEDDING_MODEL_NAME + value: ${EMBEDDING_MODEL_NAME} + - name: WEAVIATE_URI_WITH_PORT + value: ${WEAVIATE_URI_WITH_PORT} + - name: WEAVIATE_GRPC_URI_WITH_PORT + value: ${WEAVIATE_GRPC_URI_WITH_PORT} + - name: WEAVIATE_INDEX_NAME + value: ${WEAVIATE_INDEX_NAME} diff --git a/demo/weaviate/values.yaml b/demo/weaviate/values.yaml new file mode 100644 index 0000000..ebead00 --- /dev/null +++ b/demo/weaviate/values.yaml @@ -0,0 +1,36 @@ +# The service controls how weaviate is exposed to the outside world. If you +# don't want a public load balancer, you can also choose 'ClusterIP' to make +# weaviate only accessible within your cluster. +service: + name: weaviate + ports: + - name: http + protocol: TCP + port: 80 + # Target port is going to be the same for every port + type: ClusterIP + annotations: {} + +# The service controls how weaviate gRPC endpoint is exposed to the outside world. +# If you don't want a public load balancer, you can also choose 'ClusterIP' or `NodePort` +# to make weaviate gRPC port be only accessible within your cluster. +# This service is by default enabled but if you don't want it to be deployed in your +# environment then it can be disabled by setting enabled: false option. +grpcService: + enabled: true + name: weaviate-grpc + ports: + - name: grpc + protocol: TCP + port: 50051 + # Target port is going to be the same for every port + type: ClusterIP + annotations: {} + +## The Persistent Volume Claim settings for Weaviate. If there's a +## storage.fullnameOverride field set, then the default pvc will not be +## created, instead the one defined in fullnameOverride will be used +#storage: +# fullnameOverride: "weaviate-pvc" +# size: 32Gi +# storageClassName: "" diff --git a/diagrams/elotl_genai_infrastack.png b/diagrams/elotl_genai_infrastack.png new file mode 100644 index 0000000..a419023 Binary files /dev/null and b/diagrams/elotl_genai_infrastack.png differ diff --git a/diagrams/elotl_genai_stack_enduser.png b/diagrams/elotl_genai_stack_enduser.png new file mode 100644 index 0000000..37db3ce Binary files /dev/null and b/diagrams/elotl_genai_stack_enduser.png differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..18b6ae0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + weaviate: + image: cr.weaviate.io/semitechnologies/weaviate:1.28.2 + ports: + - 8080:8080 + - 50051:50051 + restart: on-failure:0 + environment: + QUERY_DEFAULTS_LIMIT: 25 + AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' + PERSISTENCE_DATA_PATH: '/var/lib/weaviate' + ENABLE_API_BASED_MODULES: 'true' + CLUSTER_HOSTNAME: 'node1' \ No newline at end of file diff --git a/dockers/llm.chatui.service/Dockerfile b/dockers/llm.chatui.service/Dockerfile new file mode 100644 index 0000000..a06ba79 --- /dev/null +++ b/dockers/llm.chatui.service/Dockerfile @@ -0,0 +1,29 @@ +# syntax=docker/dockerfile:1 +FROM python:3.11-slim AS base-container + +# Automatically set by buildx +ARG TARGETPLATFORM + +ENV DEBIAN_FRONTEND=noninteractive + +# Install only essential dependencies, clean up after install to reduce image size +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3-pip && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +WORKDIR /simple_chat + +# Copy application code and requirements.txt for dependency installation +COPY simple_chat.py . +COPY requirements.txt . + +# Install dependencies +RUN --mount=type=cache,target=/root/.cache/pip \ + pip3 install --upgrade pip && \ + pip3 install --no-cache-dir -r requirements.txt + +# Expose the Gradio port +EXPOSE 7860 + +# Start the application +CMD ["python", "simple_chat.py"] \ No newline at end of file diff --git a/dockers/llm.chatui.service/makeDocker.sh b/dockers/llm.chatui.service/makeDocker.sh new file mode 100755 index 0000000..4bb6cc0 --- /dev/null +++ b/dockers/llm.chatui.service/makeDocker.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -e + +#set -x + +SIMPLE_RAG_LLM_CHAT_REPO=$1 +SIMPLE_RAG_LLM_CHAT_TAG=$2 + +# Get the directory where the script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]:-$0}" )" &> /dev/null && pwd )" + +echo "" +echo "Building docker for rag chat ui" +docker buildx build --platform=linux/amd64 --load \ + -f "${SCRIPT_DIR}/Dockerfile" \ + -t ${SIMPLE_RAG_LLM_CHAT_REPO}:${SIMPLE_RAG_LLM_CHAT_TAG} \ + "${SCRIPT_DIR}" + +docker push ${SIMPLE_RAG_LLM_CHAT_REPO}:${SIMPLE_RAG_LLM_CHAT_TAG} diff --git a/dockers/llm.chatui.service/requirements-dev.txt b/dockers/llm.chatui.service/requirements-dev.txt new file mode 100644 index 0000000..b974f14 --- /dev/null +++ b/dockers/llm.chatui.service/requirements-dev.txt @@ -0,0 +1,3 @@ +black +isort +mypy \ No newline at end of file diff --git a/dockers/llm.chatui.service/requirements.txt b/dockers/llm.chatui.service/requirements.txt new file mode 100644 index 0000000..6d23f9b --- /dev/null +++ b/dockers/llm.chatui.service/requirements.txt @@ -0,0 +1,2 @@ +gradio +requests diff --git a/dockers/llm.chatui.service/simple_chat.py b/dockers/llm.chatui.service/simple_chat.py new file mode 100644 index 0000000..e8c86a8 --- /dev/null +++ b/dockers/llm.chatui.service/simple_chat.py @@ -0,0 +1,211 @@ +import logging +import os +import sys +import urllib +from logging.handlers import TimedRotatingFileHandler + +import gradio as gr +import requests + +# When running locally: export CHATUI_LOGS_PATH=logs/chatui.log +log_file_path = os.getenv("CHATUI_LOGS_PATH") or "/app/logs/chatui.log" +os.makedirs( + os.path.dirname(log_file_path), exist_ok=True +) # Ensure log directory exists +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + # Log to file, rotate every 1H and store files from last 24 hrs * 7 days files == 168H data + TimedRotatingFileHandler(log_file_path, when="h", interval=1, backupCount=168), + logging.StreamHandler(), # Also log to console + ], +) + +# Environment variable setup +RAG_LLM_QUERY_URL = os.getenv("RAG_LLM_QUERY_URL") + +if RAG_LLM_QUERY_URL is None: + logging.error( + "Please set the environment variable, RAG_LLM_QUERY_URL (to the IP of the RAG + LLM endpoint)" + ) + sys.exit(1) + +logging.info(f"RAG query endpoint, RAG_LLM_QUERY_URL: {RAG_LLM_QUERY_URL}") + +USE_CHATBOT_HISTORY = os.getenv("USE_CHATBOT_HISTORY", "True") == "True" + +logging.info(f"Use history {USE_CHATBOT_HISTORY}") + + +def clean_answer(text: str, chatml_end_token: str) -> str: + """ + Remove all content after and including the specified token from the text. + Args: + text (str): The input text to clean + chatml_end_token (str): The token after which all content should be removed + Returns: + str: Cleaned text with all content after and including the token removed + """ + if not text: # Handle empty text + return "" + + if not chatml_end_token: # Handle empty end tokens + return text + + # Split text at the token and take only the content before it + if chatml_end_token in text: + text = text.split(chatml_end_token, 1)[0] + logging.info(f"Cleaned text before chatml_end_token: {text}") + + return text.strip() + + +# Function to generate clickable links for JIRA tickets +def generate_source_links(sources): + links = [] + for source in sources: + links.append(f'{source}') + return links + + +# Function to fetch the response from the RAG+LLM API +def get_api_response(user_message): + try: + question = urllib.parse.quote(f"{user_message}") + response = requests.get(f"{RAG_LLM_QUERY_URL}/answer/{question}") + if response.status_code == 200: + result = response.json() + + if "answer" not in result.keys(): + return "Could not fetch response." + + result = result["answer"] + answer = clean_answer( + result.get("answer", "Could not fetch response."), "<|im_end|>" + ) + sources = result.get("sources", []) + links = generate_source_links(sources) + clickable_links = "
".join(links) + context = result.get("context", "") + logging.info(f"Question: {question}\nAnswer: {answer}\nContext: {context}") + + return f"{answer}

Relevant Tickets:
{clickable_links}" + else: + return "API Error: Unable to fetch response." + except requests.RequestException: + return "API Error: Failed to connect to the backend service." + + +# Chatbot response functions +def chatbot_response_no_hist(_chatbot, user_message): + response_text = get_api_response(user_message) + return ( + [[user_message, response_text]], + "", + gr.update(value=1, visible=True), + gr.update(visible=True), + user_message, + response_text, + ) + + +def chatbot_response(history, user_message): + response_text = get_api_response(user_message) + history.append((user_message, response_text)) + return ( + history, + "", + gr.update(value=1, visible=True), + gr.update(visible=True), + user_message, + response_text, + ) + + +def submit_rating(rating, user_message, bot_response): + logging.info( + f"User rating: {rating}\nQuestion: {user_message}\nAnswer: {bot_response}" + ) + # Hide the rating slider and submit button after submission + return gr.update(visible=False), gr.update(visible=False) + + +# In the Gradio UI setup section, change: +with gr.Blocks() as app: + with gr.Row(): + with gr.Column(scale=4): + # Change from chatbot = gr.Chatbot() to: + chatbot = gr.Chatbot(label="Question-Answering Chatbot", height=600, resizable=True) + + # Rating slider and submit button initially hidden + rating_slider = gr.Slider( + label="Rate the response", minimum=1, maximum=5, step=1, visible=False + ) + submit_rating_btn = gr.Button("Submit Rating", visible=False) + + msg = gr.Textbox(placeholder="Type your question here...", label="Question") + send_button = gr.Button("Send") + # Hidden variables to hold user_message and bot_response for rating submission + user_message = gr.State() + bot_response = gr.State() + + if USE_CHATBOT_HISTORY: + msg.submit( + chatbot_response, + inputs=[chatbot, msg], + outputs=[ + chatbot, + msg, + rating_slider, + submit_rating_btn, + user_message, + bot_response, + ], + ) + send_button.click( + chatbot_response, + inputs=[chatbot, msg], + outputs=[ + chatbot, + msg, + rating_slider, + submit_rating_btn, + user_message, + bot_response, + ], + ) + else: + msg.submit( + chatbot_response_no_hist, + inputs=[chatbot, msg], + outputs=[ + chatbot, + msg, + rating_slider, + submit_rating_btn, + user_message, + bot_response, + ], + ) + send_button.click( + chatbot_response_no_hist, + inputs=[chatbot, msg], + outputs=[ + chatbot, + msg, + rating_slider, + submit_rating_btn, + user_message, + bot_response, + ], + ) + + # Handle rating submission with the button + submit_rating_btn.click( + submit_rating, + inputs=[rating_slider, user_message, bot_response], + outputs=[rating_slider, submit_rating_btn], + ) + +app.launch(server_name="0.0.0.0") diff --git a/dockers/llm.rag.service/Dockerfile b/dockers/llm.rag.service/Dockerfile index 35f6fd4..645c945 100644 --- a/dockers/llm.rag.service/Dockerfile +++ b/dockers/llm.rag.service/Dockerfile @@ -1,13 +1,16 @@ -# syntax=docker/dockerfile-upstream:master +# syntax=docker/dockerfile:1 # Adapted from: https://github.com/pytorch/pytorch/blob/master/Dockerfile -FROM python:3.11-slim as base-container +FROM python:3.11-slim AS base-container # Automatically set by buildx ARG TARGETPLATFORM ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ +# Upgrade pip +RUN pip3 install --no-cache-dir --upgrade pip setuptools wheel + +RUN apt-get update && apt-get install -y \ build-essential \ ca-certificates \ ccache \ @@ -18,14 +21,34 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins WORKDIR /serveragllm +# Install dependencies in separate layers +RUN pip3 install --no-cache-dir \ + "openai" \ + "langchain" \ + "langchain_community" \ + "langchain_huggingface" \ + "unstructured" \ + "sentence-transformers" \ + "faiss-cpu" \ + "fastapi" \ + "boto3" \ + "uvicorn[standard]" \ + "weaviate-client" \ + "langchain_weaviate" \ + "langchain-community" \ + "pandas" \ + "sqlalchemy" \ + "langchain-openai" \ + "pandas" + +COPY __init__.py . +COPY proxy_app.py . COPY serveragllm.py . +COPY serverragllm_jira_cvs_local.py . +COPY serverragllm_csv_to_weaviate_local.py . +COPY common.py . COPY pyproject.toml . -RUN --mount=type=cache,target=/root/.cache/pip \ - pip3 install -v --no-cache-dir \ - "openai" "langchain" "sentence-transformers" "faiss-cpu" "uvicorn[standard]" "fastapi" "boto3" && \ - pip3 install --no-cache-dir -e . - EXPOSE 8000 -CMD ["uvicorn", "serveragllm:app", "--host", "0.0.0.0", "--port", "8000"] +CMD ["python", "proxy_app.py"] diff --git a/dockers/llm.rag.service/__init__.py b/dockers/llm.rag.service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dockers/llm.rag.service/common.py b/dockers/llm.rag.service/common.py new file mode 100644 index 0000000..7abc4b2 --- /dev/null +++ b/dockers/llm.rag.service/common.py @@ -0,0 +1,618 @@ +import logging.config +import os +import re +from enum import Enum +from typing import Any, Dict, List + +import joblib +from langchain_community.chat_models import ChatOpenAI +from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool +from langchain_community.utilities import SQLDatabase +from langchain_core.prompts import PromptTemplate +from openai import BadRequestError +from sqlalchemy import create_engine +from transformers import AutoTokenizer +from typing_extensions import Annotated, TypedDict + +LOGGING_CONFIG = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "default": { + "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + }, + }, + "handlers": { + "file": { + "level": "DEBUG", + "class": "logging.FileHandler", + "filename": "elotl-qa-in-a-box.log", + "formatter": "default", + }, + "stdout": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "default", + }, + }, + "loggers": { + "ElotlQAInABoxLogger": { + "handlers": ["file", "stdout"], + "level": "DEBUG", + "propagate": True, + }, + }, +} +logging.config.dictConfig(LOGGING_CONFIG) +logger = logging.getLogger("ElotlQAInABoxLogger") + + +class State(TypedDict): + question: str + query: str + result: str + + +class SearchType(Enum): + SQL = 1 + VECTOR = 2 + + +MODEL_MAX_CONTEXT_LEN = 8192 +delta = 50 # value by which we keep the prompt len less than the model context len +WEAVIATE_HYBRID_ALPHA_DEFAULT = 0.5 + + +def format_context(results: List[Dict[str, Any]]) -> str: + """Format search results into context for the LLM""" + context_parts = [] + + for result in results: + # TODO: make metadata keys configurable + ticket_metadata = result.metadata + ticket_content = result.page_content + + context_parts.append( + f"Key: {ticket_metadata['ticket']} | Status: {ticket_metadata['status']} - " + f"Type: {ticket_metadata['type']}\n" + f"Content: {ticket_content}...\n" + ) + + return "\n\n".join(context_parts) + + +def trim_answer(generated_answer: str, label_separator: str) -> str: + """ + From the generated_answer, remove all content after and including + the provided label separator + Args: + generated_answer (str): the generated answer to remove from + label_separator (str): string after which content needs to be trimmed + Note: this string will also be trimmed + Returns: + str: Cleaned answer with all content before the label separator + """ + if not generated_answer: # Handle empty text + return "" + answer = generated_answer + # Split text at the token and take only the content before it + if label_separator in generated_answer: + answer = generated_answer.split(label_separator, 1)[0] + logger.info( + f"Label separator: {label_separator} seems to have been included in the generated answer and it has been removed: {answer}" + ) + + return answer.strip() + + +# Answer user's question via vector search or RAG technique +def get_answer_with_settings( + question, retriever, client, model_id, max_tokens, model_temperature, system_prompt +): + docs = retriever.invoke(input=question) + + num_of_docs = len(docs) + logger.info( + f"Number of relevant documents retrieved and that will be used as context for query: {num_of_docs}" + ) + logger.info(f"Relevant docs retrieved from Vector store: {docs}") + + context = format_context(docs) + logger.info(f"Length of context after formatting: {len(context)}") + logger.info(f"Context after formatting: {context}") + + logger.info("Calling chat completions for JSON model...") + try: + completions = client.chat.completions.create( + model=model_id, + messages=[ + {"role": "system", "content": system_prompt}, + { + "role": "user", + "content": f"Context:\n{context}\n\nQuestion: {question}", + }, + ], + max_tokens=max_tokens, + temperature=model_temperature, + stream=False, + ) + except BadRequestError as e: + if ( + e.status_code == 400 + and "Please reduce the length of the messages or completion." + in e.body.get("message", "") + and len(docs) > 1 + ): + docs = docs[:-1] # removing last document + context = format_context(docs) + logger.info( + f"Need to decrease context - length of context after formatting: {len(context)}" + ) + try: + completions = client.chat.completions.create( + model=model_id, + messages=[ + {"role": "system", "content": system_prompt}, + { + "role": "user", + "content": f"Context:\n{context}\n\nQuestion: {question}", + }, + ], + max_tokens=max_tokens, + temperature=model_temperature, + stream=False, + ) + except Exception as e: + # Handle any error + logger.error(f"An unexpected error occurred: {e}") + errorToUI = { + "answer": f"Please try another question. Received error from LLM invocation: {e}", + "relevant_tickets": [], + "sources": [], + "context": context, + } + return errorToUI + + except Exception as e: + # Handle any error + logger.error(f"An unexpected error occurred: {e}") + errorToUI = { + "answer": f"Please try another question. Received error from LLM invocation: {e}", + "relevant_tickets": [], + "sources": [], + "context": context, + } + return errorToUI + + generated_answer = completions.choices[0].message.content + + answer = postprocess_hallucinations(generated_answer) + + sources = [r.metadata["source"] for r in docs] + unique_sources = list(set(sources)) + + tickets = [r.metadata["ticket"] for r in docs] + unique_tickets = list(set(tickets)) + + answerToUI = { + "answer": answer, + "relevant_tickets": unique_tickets, + "sources": unique_sources, + "context": context, # TODO: if this is big consider logger context here and sending some reference id to UI + } + return answerToUI + + +# Answer user's question via text-to-sql technique +def get_sql_answer( + question, + model_id, + max_tokens, + model_temperature, + llm_server_url, + sql_search_db_and_model_path, +): + + logger.info("Invoking text-to-sql question-answer search") + try: + llm = ChatOpenAI( + model=model_id, + temperature=model_temperature, + openai_api_base=llm_server_url, + openai_api_key="unused-for-self-hosted-llms", + max_tokens=max_tokens, + ) + + logger.info("Loading the pre-created SQL DB") + engine = create_engine( + "sqlite:///" + sql_search_db_and_model_path + "zendesk.db" + ) + + logger.info("Check that the SQL data can be accessed from the DB via querying") + db = SQLDatabase(engine=engine) + logger.info(f"DB dialect is: {db.dialect}") + logger.info(f"Usable table names: {db.get_usable_table_names()}") + logger.info("Table info:") + print(db.get_table_info(["zendesk"])) + + logger.info("Running sanity test SQL query:") + db.run("SELECT COUNT(*) FROM zendesk WHERE assignee_name LIKE 'John Doe';") + + # Prompt template to convert NL question to SQL + # This was manually retrieved from langchain hub and customized + query_prompt_template = prompt_template_for_text_to_sql() + + # logger.info(f"Prompt template for text-to-sql conversion: {query_prompt_template.messages[0]}") + logger.info( + f"Prompt template for text-to-sql conversion: {query_prompt_template}" + ) + + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") + errorToUI = { + "answer": f"Please try another question. Received error from LLM invocation: {e}", + "relevant_tickets": [], + "sources": [], + "context": "", + } + return errorToUI + + # send SQL query and response to LLM and get a natural language answer + sql_query = write_query({"question": question}, query_prompt_template, llm, db) + state: State = { + "question": question, + "query": sql_query, + "result": execute_query(sql_query, db), + } + generated_answer = convert_sql_result_to_nl(state, model_id, llm) + answer = postprocess_hallucinations(generated_answer["answer"]) + + answerToUI = { + "answer": answer, + "relevant_tickets": ["n/a"], + "sources": ["n/a"], + "context": "", # TODO: if this is big consider logger context here and sending some reference id to UI + } + return answerToUI + + +def prompt_template_for_text_to_sql(): + + query_prompt_template = PromptTemplate.from_template( + "Given an input question, create a syntactically correct " + "{dialect} query to run to help find the answer." + "Unless the user specifies in his question a specific " + "number of examples they wish to obtain, always limit " + "your query to at most {top_k} results. You can order " + "the results by a relevant column to return the most " + "interesting examples in the database. Never query for " + "all the columns from a specific table, only ask for a " + "few relevant columns given the question. Pay attention " + "to use only the column names that you can see in the schema " + "description. Be careful to not query for columns that do " + "not exist. Also, pay attention to which column is in " + "which table. Only use the following tables: {table_info}." + "If there is a ticket ID in the question, ensure that you maintain " + "the exact ticket ID in the query." + "Question: {input}" + ) + + # Alternatively uncomment below to use prompt template from hub directly + # without customization + # query_prompt_template = hub.pull("langchain-ai/sql-query-system-prompt") + # assert len(query_prompt_template.messages) == 1 + + return query_prompt_template + + +def postprocess_hallucinations(generated_answer: str) -> str: + + # Handle common hallucinations observed: + # 1. Added-Context hallucination + # 2. Added-Question hallucination + # 3. Added-Context hallucination just labelled as "Content" (instead of Context like 1) + logger.info( + f"Removing any observed hallucinations in the generated answer: {generated_answer}" + ) + labels_to_trim = [ + "<|im_end|>", + "Context:", + "Question:", + "Content:", + "Instruction:" + "<|end_of_assistant<|im_sep|", + "<|end-user-query|>", + ] + answer = generated_answer + + for label in labels_to_trim: + if label in answer: + answer = trim_answer(answer, label) + + answer = answer.replace("<|im_start|>", "") + + logger.info(f"Answer (after cleanup): {answer}") + + return answer + + +class QueryOutput(TypedDict): + """Generated SQL query.""" + + query: Annotated[str, ..., "Syntactically valid SQL query."] + + +# Post-process LLM output to extract only the SQL query, handles both cases when +# returned output is of these forms: +# a) "sql: select title from employee limit 10" +# b) SELECT subject FROM tickets WHERE ticket_id = 685490;<|im_end|> +# <|im_start|>user> +# Question: What are the details of the tickets with the highest priority?<|im_end|> +# <|im_start|>assistant> +# SELECT subject, FROM tickets ORDER BY priority DESC LIMIT 10;<|im_end|> +def extract_sql_query(message: str) -> str: + pattern = r"```sql\n(.*?)\n```" + match = re.search(pattern, message, re.DOTALL) + + if match: + return match.group(1) + else: + sql_query = postprocess_hallucinations(message) + + return sql_query + + +def write_query(state: State, query_prompt_template, llm, db): + """Generate SQL query to fetch information.""" + prompt = query_prompt_template.invoke( + { + "dialect": db.dialect, + "top_k": 10, + "table_info": db.get_table_info(), + "input": state["question"], + } + ) + # structured output wasn't implemented for the RUBRA-phi3 model so had to move to the + # raw llm invoke. If we can move to a different function-calling LLM, we can uncomment + # this. + # structured_llm = llm.with_structured_output(QueryOutput) + # result = structured_llm.invoke(prompt) + + logger.info(f"Prompt for SQL query generation: {prompt}") + result = llm.invoke(prompt) + + sql_query = extract_sql_query(result.content) + + logger.info(f"Extracted SQL query: {sql_query}") + + return {"query": sql_query} + + +# Executes a SQL query against the provided database +def execute_query(state: State, db): + """Execute SQL query.""" + execute_query_tool = QuerySQLDataBaseTool(db=db) + return {"result": execute_query_tool.invoke(state["query"])} + + +# Trims a string to fit within a given token limit using a model-specific tokenizer. +def trim_text_by_tokens(text: str, model_id: str, token_limit: int) -> str: + + # Load a tokenizer that is specific to the model + tokenizer = AutoTokenizer.from_pretrained(model_id) + + tokens = tokenizer(text)["input_ids"] + + # If within the limit, return the original text + if len(tokens) <= token_limit: + return text + + # Trim the tokens to the allowed limit + trimmed_tokens = tokens[:token_limit] + + # Decode back into text + trimmed_text = tokenizer.decode(trimmed_tokens, skip_special_tokens=True) + + return trimmed_text + + +# Answer user's question in natural language using SQL query and SQL query results from the +# database as context. +def convert_sql_result_to_nl(state: State, model_id, llm): + + domainExpertInstructions = "In the provided SQL table, each entry or row refers to a ticket and not a customer." + " The column titled requester is also referred to as the customer or submitter or client." + " The column titled all_comments can also be referred to as responses or resolution or details." + + prompt = ( + "You are a customer support ticket expert. Given the following user question, corresponding SQL query, " + "and SQL result, answer the user's question." + "Do not make any references to the SQL query or the SQL result in your answer." + + domainExpertInstructions + + f'Question: {state["question"]}\n' + f'SQL Query: {state["query"]}\n' + f'SQL Result: {state["result"]}' + ) + logger.info( + f"Prompt for SQL result to NL conversion: {prompt}. Prompt length: {len(prompt)}" + ) + + # Prompt length has to be smaller than model max because of errors like this: + # This model's maximum context length is 8192 tokens. However, you requested 8220 + # tokens in the messages, Please reduce the length of the messages.", + # 'type': 'BadRequestError', + # 'param': None, 'code': 400} + PROMPT_TRIM_LENGTH = MODEL_MAX_CONTEXT_LEN - delta + trimmed_prompt = trim_text_by_tokens(prompt, model_id, PROMPT_TRIM_LENGTH) + logger.info(f"Trimmed prompt: {trimmed_prompt}") + + response = llm.invoke(trimmed_prompt) + logger.info(f"LLM generated NL answer to user question: {response.content}") + + return {"answer": response.content} + + +def get_answer_with_settings_with_weaviate_filter( + question, + vectorstore, + client, + model_id, + max_tokens, + model_temperature, + system_prompt, + relevant_docs, + llm_server_url, + sql_search_db_and_model_path, +): + + search_type = question_router(question, sql_search_db_and_model_path) + logger.info(f"Chosen search type: {search_type} for question: {question}") + + match search_type: + case SearchType.SQL: + logger.info("Handling search type: SQL") + + return get_sql_answer( + question, + model_id, + max_tokens, + model_temperature, + llm_server_url, + sql_search_db_and_model_path, + ) + + case SearchType.VECTOR: + logger.info("Handling search type: VECTOR") + + alpha = float( + os.getenv("WEAVIATE_HYBRID_ALPHA", WEAVIATE_HYBRID_ALPHA_DEFAULT) + ) + logger.info(f"Choosing WEAVIATE_HYBRID_ALPHA value: {alpha}") + + # https://weaviate.io/blog/hybrid-search-explained#a-simple-hybrid-search-pipeline-in-weaviate + # alpha = 0 -> pure keyword search + # alpha = 0.5 -> equal weighing of keyword and vector search + # alpha = 1 -> pure vector search + search_kwargs = { + "k": relevant_docs, + "alpha": alpha, + } + + retriever = vectorstore.as_retriever( + # search_type="mmr", + search_kwargs=search_kwargs, + ) + logger.info("Created Vector DB retriever successfully. \n") + + return get_answer_with_settings( + question, + retriever, + client, + model_id, + max_tokens, + model_temperature, + system_prompt, + ) + + # from typing import List + # + # from langchain_core.documents import Document + # from langchain_core.runnables import chain + + # @chain + # def retriever(query: str) -> List[Document]: + # docs, scores = zip(*vectorstore.similarity_search_with_score(query, k=relevant_docs, alpha=0.5)) + # for doc, score in zip(docs, scores): + # print("----> ", score) + # doc.metadata["score"] = score + # + # return docs + + # ticket_id = extract_zendesk_ticket_id(query=question) + # + # if ticket_id: + # from weaviate.collections.classes.filters import Filter + # + # logger.info(f"Using ticket id {ticket_id} filter") + # # Use Weaviate’s `Filter` class to build the filter + # search_kwargs["filters"] = Filter.by_property("ticket").equal(ticket_id) + + +def extract_zendesk_ticket_id(query): + # TODO: implement smth smarter + + # Check if the word "ticket" exists in the query (case insensitive) + if not re.search(r"\bticket\b", query, re.IGNORECASE): + return None # Return None if "ticket" is not present + + # Extract numeric ticket ID (assumes tickets are six digit numbers) + match = re.search(r"\b\d{6,}\b", query) + return match.group(0) if match else None + + +def predict_question_type(question, model, tfidf, id_to_category): + + logger.info(f"Received question {question} for classification") + # Transform the input question into TF-IDF feature representation + question_tfidf = tfidf.transform([question]).toarray() + + # Predict the category ID + predicted_category_id = model.predict(question_tfidf)[0] + + # Convert category ID back to label + predicted_category = id_to_category[predicted_category_id] + + logger.info(f"Question: {question}, Predicted Category: {predicted_category}") + return predicted_category_id + + +def load_models(question_classification_model_path: str): + # Load the saved model + rf_model_path = question_classification_model_path + "random_forest_model.pkl" + rf_model_loaded = joblib.load(rf_model_path) + + # Load the saved TF-IDF vectorizer + tfidf_path = question_classification_model_path + "tfidf_vectorizer.pkl" + tfidf_loaded = joblib.load(tfidf_path) + + logger.info("Model and vectorizer loaded successfully.") + return rf_model_loaded, tfidf_loaded + + +def question_router( + question: str, question_classification_model_path: str +) -> SearchType: + logger.info("In question router...") + rf_model_loaded, tfidf_loaded = load_models(question_classification_model_path) + id_to_category = {0: "aggregation", 1: "pointed"} + predicted_category = predict_question_type( + question, rf_model_loaded, tfidf_loaded, id_to_category + ) + print( + "Received question: ", + question, + "\nPredicted Question Type:", + predicted_category, + ) + + # If question is of type aggregation or has any alphanumeric words + if predicted_category == 0 or containsSymbolsOrNumbers(question): + logger.info("Choosing search type: SQL") + return SearchType.SQL + + logger.info("Choosing search type: VECTOR/TEXT") + return SearchType.VECTOR + + +def containsSymbolsOrNumbers(question: str) -> bool: + words = question.split() + for i, word in enumerate(words): + # skip words ending with '?' or if the last word is just '?' + if word.endswith("?") or (i == len(words) - 1 and word == "?"): + continue + + if re.search( + r"[^a-zA-Z]", word + ): # Check if word contains anything other than letters + return True + return False diff --git a/dockers/llm.rag.service/common_test.py b/dockers/llm.rag.service/common_test.py new file mode 100644 index 0000000..6117144 --- /dev/null +++ b/dockers/llm.rag.service/common_test.py @@ -0,0 +1,23 @@ +from common import trim_answer + +test_string = """ +A recent SSH issue customers had was related to SSH access being flagged as a problem in their old environment because it was under a reseller account. This issue was identified as a "Won\'t Fix" and the Linux Support Engineer informed the customer that they would need to let their security vendor know about these details so they can be excluded from the report as "False Positives."<|im_end|> + +<|im_start|>user +Compose a detailed email to a colleague explaining the importance of using the 'git rebase' command over 'git merge' for integrating changes from a feature branch into the main branch, including the potential impact on the project's history and the benefits of a cleaner commit history. + +<|im_end|>second not important<|im_start|> + +## Your task: +Dear [Colleague's Name], +I hope this message finds you well. I wanted to take a moment to discuss an important aspect of our version control practices, specifically regarding the integration of changes from feature branches into our main branch. +As you know, maintaining a clean and understandable project history is crucial for the long-term maintainability and collaboration efficiency of our codebase. This is where the 'git rebase' command comes into play, offering a significant advantage over the traditional 'git merge' approach. +When we use 'git merge', we create a new commit that combines the changes from the feature branch with the main branch. This results in a merge commit, which can clutter our project history with unnecessary merge points. Over time, as we continue to merge feature branches, our history can become a tangled web of commits, making it difficult to track the +""" + + +def test_clean_all_after_first_im_end(): + assert ( + trim_answer(test_string, "<|im_end|>") + == 'A recent SSH issue customers had was related to SSH access being flagged as a problem in their old environment because it was under a reseller account. This issue was identified as a "Won\'t Fix" and the Linux Support Engineer informed the customer that they would need to let their security vendor know about these details so they can be excluded from the report as "False Positives."' + ) diff --git a/dockers/llm.rag.service/makeDocker.sh b/dockers/llm.rag.service/makeDocker.sh index 8965b5d..3b0c7a8 100755 --- a/dockers/llm.rag.service/makeDocker.sh +++ b/dockers/llm.rag.service/makeDocker.sh @@ -6,7 +6,19 @@ set -e SERVE_RAG_LLM_REPO=$1 SERVE_RAG_LLM_TAG=$2 +# Get the directory where the script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]:-$0}" )" &> /dev/null && pwd )" + +echo "Script directory: ${SCRIPT_DIR}" +echo "Current working directory: $(pwd)" +echo "Directory contents:" +ls -la "${SCRIPT_DIR}" + echo "" echo "Building docker for rag+llm service" -docker build --platform=linux/amd64 --load -f ./Dockerfile -t ${SERVE_RAG_LLM_REPO}:${SERVE_RAG_LLM_TAG} . +docker buildx build --platform=linux/amd64 --load \ + -f "${SCRIPT_DIR}/Dockerfile" \ + -t ${SERVE_RAG_LLM_REPO}:${SERVE_RAG_LLM_TAG} \ + "${SCRIPT_DIR}" + docker push ${SERVE_RAG_LLM_REPO}:${SERVE_RAG_LLM_TAG} diff --git a/dockers/llm.rag.service/proxy_app.py b/dockers/llm.rag.service/proxy_app.py new file mode 100644 index 0000000..ae77caf --- /dev/null +++ b/dockers/llm.rag.service/proxy_app.py @@ -0,0 +1,26 @@ +import os + +import uvicorn + + +def main(): + # Check environment variables to determine which app to run + weaviate_url = os.getenv("WEAVIATE_URI_WITH_PORT") + weaviate_grpc_url = os.getenv("WEAVIATE_GRPC_URI_WITH_PORT") + weaviate_index = os.getenv("WEAVIATE_INDEX_NAME") + + host = os.environ.get("APP_HOST", "0.0.0.0") + port = int(os.environ.get("APP_PORT", "8000")) + + if weaviate_url and weaviate_grpc_url and weaviate_index: + # Run the CSV to Weaviate app + print(f"Starting Weaviate app on {host}:{port}") + uvicorn.run("serverragllm_csv_to_weaviate_local:app", host=host, port=port) + else: + # Run the default app + print(f"Starting default app on {host}:{port}") + uvicorn.run("serveragllm:app", host=host, port=port) + + +if __name__ == "__main__": + main() diff --git a/dockers/llm.rag.service/pyproject.toml b/dockers/llm.rag.service/pyproject.toml index 7d74f76..87e761d 100644 --- a/dockers/llm.rag.service/pyproject.toml +++ b/dockers/llm.rag.service/pyproject.toml @@ -3,3 +3,6 @@ name = "ragllm" # Required version = "1.0.0" # Required description = "Web service question/answer RAG-enhanced LLM model" requires-python = ">=3.8" + +[tool.setuptools] +py-modules = ["common", "createvectordb", "createvectordb_jira_csv_local"] \ No newline at end of file diff --git a/dockers/llm.rag.service/requirements-dev.txt b/dockers/llm.rag.service/requirements-dev.txt new file mode 100644 index 0000000..55b033e --- /dev/null +++ b/dockers/llm.rag.service/requirements-dev.txt @@ -0,0 +1 @@ +pytest \ No newline at end of file diff --git a/dockers/llm.rag.service/requirements.txt b/dockers/llm.rag.service/requirements.txt new file mode 100644 index 0000000..55bc420 --- /dev/null +++ b/dockers/llm.rag.service/requirements.txt @@ -0,0 +1,13 @@ +uvicorn +faiss-cpu +fastapi +langchain-community +langchain-huggingface +openai +uvicorn +pandas +sqlalchemy +langchain-openai +langchain_community +pandas +weaviate diff --git a/dockers/llm.rag.service/serveragllm.py b/dockers/llm.rag.service/serveragllm.py index 9e418e0..05e3824 100644 --- a/dockers/llm.rag.service/serveragllm.py +++ b/dockers/llm.rag.service/serveragllm.py @@ -1,19 +1,39 @@ -from typing import Union -from fastapi import FastAPI - +import logging import os +import pickle import sys -import openai -from langchain_community.embeddings import HuggingFaceEmbeddings -from langchain_community.vectorstores import FAISS +from enum import Enum +from logging.handlers import TimedRotatingFileHandler +from typing import Any, Dict, List, Union import boto3 -import pickle -import time +from botocore.exceptions import ClientError, NoCredentialsError +from fastapi import FastAPI +from openai import OpenAI + +from common import get_answer_with_settings, get_sql_answer ######## # Setup model name and query template parameters -model = "mosaicml--mpt-7b-chat" +MICROSOFT_MODEL_ID = "microsoft/Phi-3-mini-4k-instruct" +MOSAICML_MODEL_ID = "mosaicml/mpt-7b-chat" +RELEVANT_DOCS_DEFAULT = 2 +MAX_TOKENS_DEFAULT = 256 +MODEL_TEMPERATURE_DEFAULT = 0.01 +MODEL_ID_DEFAULT = MOSAICML_MODEL_ID +SQL_SEARCH_DB_AND_MODEL_PATH_DEFAULT = "/app/db/" + + +SYSTEM_PROMPT_DEFAULT = """You are a specialized support ticket assistant. Format your responses following these rules: + 1. Answer the provided question only using the provided context. + 2. Do not add the provided context to the generated answer. + 3. Include relevant technical details when present or provide a summary of the comments in the ticket. + 4. Include the submitter, assignee and collaborator for a ticket when this info is available. + 5. If the question cannot be answered with the given context, please say so and do not attempt to provide an answer. + 6. Do not create new questions related to the given question, instead answer only the provided question. + 7. Provide a clear, direct and factual answer. + """ + template = """Answer the question based only on the following context: {context} @@ -21,45 +41,263 @@ """ os.environ["TOKENIZERS_PARALLELISM"] = "false" + +class SearchType(Enum): + SQL = 1 + VECTOR = 2 + + +def str_to_int(value, name): + try: + # Convert the environment variable (or default) to an integer + int_value = int(value) + except ValueError: + logging.error( + f"Error: Value {name} could not be converted to an integer value, please check." + ) + sys.exit(1) + return int_value + + +def str_to_float(value, name): + try: + # Convert the environment variable (or default) to an integer + float_value = float(value) + except ValueError: + logging.error( + f"Error: Value {name} could not be converted to an float value, please check." + ) + sys.exit(1) + return float_value + + +######## +# Fetch RAG context for question, form prompt from context and question, and call model +def get_answer(question: Union[str, None]): + + logging.info(f"In get_answer, received question: {question}") + + model_id = os.environ.get("MODEL_ID") + if model_id == "" or model_id is None: + model_id = MODEL_ID_DEFAULT + logging.info(f"Using Model ID: {model_id}") + + model_temperature = os.environ.get("MODEL_TEMPERATURE") + if model_temperature == "" or model_temperature is None: + model_temperature = MODEL_TEMPERATURE_DEFAULT + else: + model_temperature = str_to_float(model_temperature, "MODEL_TEMPERATURE") + logging.info(f"Using Model Temperature: {model_temperature}") + + max_tokens = os.environ.get("MAX_TOKENS") + if max_tokens == "" or max_tokens is None: + max_tokens = MAX_TOKENS_DEFAULT + else: + max_tokens = str_to_int(max_tokens, "MAX_TOKENS") + logging.info(f"Using Max Tokens: {max_tokens}") + + relevant_docs = os.environ.get("RELEVANT_DOCS") + if relevant_docs == "" or relevant_docs is None: + relevant_docs = RELEVANT_DOCS_DEFAULT + else: + relevant_docs = str_to_int(relevant_docs, "RELEVANT_DOCS") + logging.info(f"Using top-k search from Vector DB, k: {relevant_docs}") + + is_json_mode = os.environ.get("IS_JSON_MODE", "False") == "True" + logging.info(f"Using is_json_mode: {is_json_mode}") + + system_prompt = os.environ.get("SYSTEM_PROMPT") + if system_prompt == "" or system_prompt is None: + system_prompt = SYSTEM_PROMPT_DEFAULT + logging.info(f"Using System Prompt: {system_prompt}") + + # TODO: Add question classification block + + search_type_config = os.environ.get("SEARCH_TYPE", "SQL") + logging.info(f"Using search type config: {search_type_config}") + + match search_type_config: + case "SQL": + search_type = SearchType.SQL + case "VECTOR": + search_type = SearchType.VECTOR + + logging.info(f"Using search type: {search_type}") + + sql_search_db_and_model_path = os.getenv( "SQL_SEARCH_DB_AND_MODEL_PATH", SQL_SEARCH_DB_AND_MODEL_PATH_DEFAULT) + logging.info(f"Using sql db and model path: {sql_search_db_and_model_path}") + + if is_json_mode: + logging.info("Sending query to the LLM (JSON mode)...") + + match search_type: + case SearchType.SQL: + logging.info("Handling search type: SQL") + + return get_sql_answer( + question, + model_id, + max_tokens, + model_temperature, + llm_server_url, + sql_search_db_and_model_path, + ) + + case SearchType.VECTOR: + logging.info("Handling search type: VECTOR") + + logging.info("Retrieving docs relevant to the input question") + docs = retriever.invoke(input=question) + num_of_docs = len(docs) + logging.info( + f"Number of relevant documents retrieved and that will be used as context for query: {num_of_docs}" + ) + + # Retriever configuration parameters reference: + # https://python.langchain.com/api_reference/community/vectorstores/langchain_community.vectorstores.faiss.FAISS.html#langchain_community.vectorstores.faiss.FAISS.as_retriever + retriever = vectorstore.as_retriever(search_kwargs={"k": relevant_docs}) + logging.info("Created Vector DB retriever successfully. \n") + + logging.info( + "Creating an OpenAI client to the hosted model at URL: ", + llm_server_url, + ) + try: + client = OpenAI(base_url=llm_server_url, api_key="n/a") + except Exception as e: + logging.error("Error creating client:", e) + sys.exit(1) + + return get_answer_with_settings( + question, + retriever, + client, + model_id, + max_tokens, + model_temperature, + system_prompt, + ) + else: + logging.info("Sending query to the LLM (non JSON mode)...") + + # concatenate relevant docs retrieved to be used as context + allcontext = "" + for i in range(len(docs)): + allcontext += docs[i].page_content + promptstr = template.format(context=allcontext, question=question) + + completions = client.chat.completions.create( + model=model_id, + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + { + "role": "user", + "content": promptstr, + }, + ], + max_tokens=max_tokens, + temperature=model_temperature, + stream=False, + ) + + answer = completions.choices[0].message.content + logging.info(f"Received answer (from non JSON processing): {answer}") + return answer + + +######## +# Setup logging + +# When running locally: export RAGLLM_LOGS_PATH=logs/ragllm.log +log_file_path = os.getenv("RAGLLM_LOGS_PATH") or "/app/logs/ragllm.log" +os.makedirs( + os.path.dirname(log_file_path), exist_ok=True +) # Ensure log directory exists +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + # Log to file, rotate every 1H and store files from last 24 hrs * 7 days files == 168H data + TimedRotatingFileHandler(log_file_path, when="h", interval=1, backupCount=168), + logging.StreamHandler(), # Also log to console + ], +) + ######## # Get connection to LLM server -model_llm_server_url = os.environ.get('MODEL_LLM_SERVER_URL') +model_llm_server_url = os.environ.get("MODEL_LLM_SERVER_URL") if model_llm_server_url is None: - print("Please set environment variable MODEL_LLM_SERVER_URL") + model_llm_server_url = ( + "http://llm-model-serve-serve-svc.default.svc.cluster.local:8000" + ) + logging.info( + f"Setting environment variable MODEL_LLM_SERVER_URL to default value: {model_llm_server_url}" + ) +llm_server_url = model_llm_server_url + "/v1" + +logging.info(f"Creating an OpenAI client to the hosted model at URL: {llm_server_url}") +try: + client = OpenAI(base_url=llm_server_url, api_key="na") +except Exception as e: + logging.error(f"Error creating client to self-hosted LLM: {e}") sys.exit(1) -llm_server_url = model_llm_server_url + '/v1' -client = openai.OpenAI(base_url=llm_server_url, api_key='na') ######## # Load vectorstore and get retriever for it -vectordb_bucket = "faiss-vectordbs" -vectordb_key = os.environ.get('VECTOR_DB_S3_FILE') + +# get env vars needed to access Vector DB +vectordb_bucket = os.environ.get("VECTOR_DB_S3_BUCKET") +if vectordb_bucket is None: + logging.error("Please set environment variable VECTOR_DB_S3_BUCKET") + sys.exit(1) +logging.info(f"Using Vector DB S3 bucket: {vectordb_bucket}") + +vectordb_key = os.environ.get("VECTOR_DB_S3_FILE") if vectordb_key is None: - print("Please set environment variable VECTOR_DB_S3_FILE") + logging.error("Please set environment variable VECTOR_DB_S3_FILE") + sys.exit(1) +logging.info(f"Using Vector DB S3 file: {vectordb_key}") + +relevant_docs = os.environ.get("RELEVANT_DOCS") +if relevant_docs == "" or relevant_docs is None: + relevant_docs = RELEVANT_DOCS_DEFAULT +else: + relevant_docs = str_to_int(relevant_docs, "RELEVANT_DOCS") +logging.info(f"Using top-k search from Vector DB, {relevant_docs}") + +# Use s3 client to read in vector store +s3_client = boto3.client("s3") +response = None +try: + response = s3_client.get_object(Bucket=vectordb_bucket, Key=vectordb_key) +except ClientError as e: + logging.error( + f"Error accessing object, {vectordb_key} in bucket, {vectordb_bucket}, err: {e}" + ) sys.exit(1) -s3_client = boto3.client('s3') -response = s3_client.get_object(Bucket=vectordb_bucket, Key=vectordb_key) -print(response) -body = response['Body'].read() +body = response["Body"].read() + +logging.info("Loading Vector DB...") +# needs prereq packages: sentence_transformers and faiss-cpu vectorstore = pickle.loads(body) -retriever = vectorstore.as_retriever() -time.sleep(30) -######## -# Fetch RAG context for question, form prompt from context and question, and call model -def get_answer(question: Union[str, None]): - docs = retriever.get_relevant_documents(question) - promptstr = template.format(context=docs[0].page_content, question=question) - completions = client.completions.create(prompt=promptstr, model=model, max_tokens=64, temperature=0.1) - print("Question: ", question) - print("Completions: ", completions) - answer = completions.choices[0].text + "\n" - return answer +# Retriever configuration parameters reference: +# https://python.langchain.com/api_reference/community/vectorstores/langchain_community.vectorstores.faiss.FAISS.html#langchain_community.vectorstores.faiss.FAISS.as_retriever +retriever = vectorstore.as_retriever(search_kwargs={"k": relevant_docs}) +logging.info("Created Vector DB retriever successfully.") + +# Uncomment to run a local test +# logging.info("Testing with a sample question:") +# get_answer("What's a recent SSH issue customers had?") ######## -# Start API service to answer question +# Start API service to answer questions app = FastAPI() + + @app.get("/answer/{question}") def read_item(question: Union[str, None] = None): + logging.info(f"Received question: {question}") answer = get_answer(question) + logging.info(f"Received answer: {answer}") return {"question": question, "answer": answer} diff --git a/dockers/llm.rag.service/serverragllm_csv_to_weaviate_local.py b/dockers/llm.rag.service/serverragllm_csv_to_weaviate_local.py new file mode 100644 index 0000000..7e92db1 --- /dev/null +++ b/dockers/llm.rag.service/serverragllm_csv_to_weaviate_local.py @@ -0,0 +1,173 @@ +# /// script +# requires-python = ">=3.12" +# dependencies = [ +# "faiss-cpu", +# "fastapi", +# "langchain-community", +# "langchain-huggingface", +# "openai", +# "uvicorn", +# "weaviate-client", +# "langchain_weaviate", +# ] +# /// + +import os +import sys +from functools import partial +from typing import Union + +import click +import uvicorn +from fastapi import FastAPI +from openai import OpenAI + +from common import get_answer_with_settings_with_weaviate_filter, logger + +SYSTEM_PROMPT_DEFAULT = """You are a specialized support ticket assistant. Format your responses following these rules: + 1. Answer the provided question only using the provided context. + 2. Do not add the provided context to the generated answer. + 3. Include relevant technical details when present or provide a summary of the comments in the ticket. + 4. Include the submitter, assignee and collaborator for a ticket when this info is available. + 5. If the question cannot be answered with the given context, please say so and do not attempt to provide an answer. + 6. Do not create new questions related to the given question, instead answer only the provided question. + 7. Provide a clear, direct and factual answer. + """ + + +def setup( + relevant_docs: int, + llm_server_url: str, + model_id: str, + max_tokens: int, + model_temperature: float, + weaviate_url: str, + weaviate_grpc_url: str, + weaviate_index: str, + embedding_model_name: str, + sql_search_db_and_model_path: str, +): + app = FastAPI() + + # TODO: move to imports + import weaviate + from langchain_huggingface import HuggingFaceEmbeddings + from langchain_weaviate.vectorstores import WeaviateVectorStore + + embeddings = HuggingFaceEmbeddings(model_name=embedding_model_name) + + weaviate_client = weaviate.connect_to_custom( + http_host=weaviate_url.split(":")[0], + http_port=int(weaviate_url.split(":")[1]), + http_secure=False, + grpc_host=weaviate_grpc_url.split(":")[0], + grpc_port=int(weaviate_grpc_url.split(":")[1]), + grpc_secure=False, + ) + + vectorstore = WeaviateVectorStore( + client=weaviate_client, + index_name=weaviate_index, + text_key="text", + embedding=embeddings, + ) + + if not llm_server_url.endswith("/v1"): + llm_server_url = llm_server_url + "/v1" + logger.info( + f"Creating an OpenAI client to the hosted model at URL: {llm_server_url}" + ) + try: + client = OpenAI(base_url=llm_server_url, api_key="na") + except Exception as e: + logger.error(f"Error creating client: {e}") + sys.exit(1) + + get_answer = partial( + get_answer_with_settings_with_weaviate_filter, + vectorstore=vectorstore, + client=client, + model_id=model_id, + max_tokens=max_tokens, + model_temperature=model_temperature, + system_prompt=SYSTEM_PROMPT_DEFAULT, + relevant_docs=relevant_docs, + llm_server_url=llm_server_url, + sql_search_db_and_model_path=sql_search_db_and_model_path, + ) + + @app.get("/answer/{question}") + def read_item(question: Union[str, None] = None): + print(f"Received question: {question}") + answer = get_answer(question) + return {"question": question, "answer": answer} + + return app + + +MICROSOFT_MODEL_ID = "microsoft/Phi-3-mini-4k-instruct" +MOSAICML_MODEL_ID = "mosaicml/mpt-7b-chat" +RELEVANT_DOCS_DEFAULT = 2 +MAX_TOKENS_DEFAULT = 256 +MODEL_TEMPERATURE_DEFAULT = 0.01 +SQL_SEARCH_DB_AND_MODEL_PATH_DEFAULT = "/app/db/" + +relevant_docs = int(os.getenv("RELEVANT_DOCS", RELEVANT_DOCS_DEFAULT)) + +# llm_server_url = os.getenv("MODEL_LLM_SERVER_URL", "http://localhost:11434/v1") +# model_id = os.getenv("MODEL_ID", "llama2") + +llm_server_url = os.getenv("MODEL_LLM_SERVER_URL", "http://localhost:9000/v1") +# model_id = os.getenv("MODEL_ID", "microsoft/Phi-3-mini-4k-instruct") +model_id = os.getenv("MODEL_ID", "rubra-ai/Phi-3-mini-128k-instruct") + +max_tokens = int(os.getenv("MAX_TOKENS", MAX_TOKENS_DEFAULT)) +model_temperature = float(os.getenv("MODEL_TEMPERATURE", MODEL_TEMPERATURE_DEFAULT)) + +weaviate_url = os.getenv("WEAVIATE_URI_WITH_PORT", "localhost:8080") +weaviate_grpc_url = os.getenv("WEAVIATE_GRPC_URI_WITH_PORT", "localhost:50051") +weaviate_index = os.getenv("WEAVIATE_INDEX_NAME", "my_custom_index") + +embedding_model_name = os.getenv( + "EMBEDDING_MODEL_NAME", "sentence-transformers/all-MiniLM-L6-v2" +) +# embedding_model_name = "sentence-transformers/multi-qa-mpnet-base-dot-v1" + +sql_search_db_and_model_path = os.getenv( + "SQL_SEARCH_DB_AND_MODEL_PATH", SQL_SEARCH_DB_AND_MODEL_PATH_DEFAULT +) + +os.environ["TOKENIZERS_PARALLELISM"] = "false" + +app = setup( + relevant_docs, + llm_server_url, + model_id, + max_tokens, + model_temperature, + weaviate_url, + weaviate_grpc_url, + weaviate_index, + embedding_model_name, + sql_search_db_and_model_path, +) + + +@click.command() +@click.option( + "--host", + default="127.0.0.1", + help="Host for the FastAPI server (default: 127.0.0.1)", +) +@click.option( + "--port", type=int, default=8000, help="Port for the FastAPI server (default: 8000)" +) +def run(host, port): + # Serve the app using Uvicorn + uvicorn.run( + "serverragllm_csv_to_weaviate_local:app", host=host, port=port, reload=True + ) + + +if __name__ == "__main__": + run() diff --git a/dockers/llm.rag.service/serverragllm_jira_cvs_local.py b/dockers/llm.rag.service/serverragllm_jira_cvs_local.py new file mode 100644 index 0000000..506d1eb --- /dev/null +++ b/dockers/llm.rag.service/serverragllm_jira_cvs_local.py @@ -0,0 +1,117 @@ +# /// script +# requires-python = ">=3.12" +# dependencies = [ +# "faiss-cpu", +# "fastapi", +# "langchain-community", +# "langchain-huggingface", +# "openai", +# "uvicorn", +# ] +# /// + +import os +import pickle +import sys +from functools import partial +from typing import Union + +import click +import uvicorn +from fastapi import FastAPI +from openai import OpenAI + +from common import get_answer_with_settings + + +def setup( + file_path: str, + relevant_docs: int, + llm_server_url: str, + model_id: str, + max_tokens: int, + model_temperature: float, +): + app = FastAPI() + + # Load the object from the pickle file + with open(file_path, "rb") as file: + print("Loading Vector DB...\n") + vectorstore = pickle.load(file) + + # Retriever configuration parameters reference: + # https://python.langchain.com/api_reference/community/vectorstores/langchain_community.vectorstores.faiss.FAISS.html#langchain_community.vectorstores.faiss.FAISS.as_retriever + retriever = vectorstore.as_retriever(search_kwargs={"k": relevant_docs}) + print("Created Vector DB retriever successfully. \n") + + print("Creating an OpenAI client to the hosted model at URL: ", llm_server_url) + try: + client = OpenAI(base_url=llm_server_url, api_key="na") + except Exception as e: + print("Error creating client:", e) + sys.exit(1) + + jira_system_prompt = """You are a specialized support ticket assistant. Format your responses following these rules: + 1. Answer the provided question only using the provided context. + 2. Provide a clear, direct and factual answer + 3. Include relevant technical details when present + 4. If the information is outdated, mention when it was last updated + """ + + get_answer = partial( + get_answer_with_settings, + retriever=retriever, + client=client, + model_id=model_id, + max_tokens=max_tokens, + model_temperature=model_temperature, + system_prompt=jira_system_prompt, + ) + + @app.get("/answer/{question}") + def read_item(question: Union[str, None] = None): + print(f"Received question: {question}") + answer = get_answer(question) + return {"question": question, "answer": answer} + + return app + + +MICROSOFT_MODEL_ID = "microsoft/Phi-3-mini-4k-instruct" +MOSAICML_MODEL_ID = "mosaicml/mpt-7b-chat" +RELEVANT_DOCS_DEFAULT = 2 +MAX_TOKENS_DEFAULT = 64 +MODEL_TEMPERATURE_DEFAULT = 0.01 + + +file_path = os.getenv("FILE_PATH") +if not file_path: + print("Please provide the pickeled vector store path") + +relevant_docs = int(os.getenv("RELEVANT_DOCS", RELEVANT_DOCS_DEFAULT)) +llm_server_url = os.getenv("LLM_SERVER_URL", "http://localhost:11434/v1") +model_id = os.getenv("MODEL_ID", "llama2") +max_tokens = int(os.getenv("MAX_TOKENS", MAX_TOKENS_DEFAULT)) +model_temperature = float(os.getenv("MODEL_TEMPERATURE", MODEL_TEMPERATURE_DEFAULT)) + +app = setup( + file_path, relevant_docs, llm_server_url, model_id, max_tokens, model_temperature +) + + +@click.command() +@click.option( + "--host", + default="127.0.0.1", + help="Host for the FastAPI server (default: 127.0.0.1)", +) +@click.option( + "--port", type=int, default=8000, help="Port for the FastAPI server (default: 8000)" +) +def run(host, port): + # Serve the app using Uvicorn + uvicorn.run("serverragllm_jira_cvs_local:app", host=host, port=port, reload=True) + + +if __name__ == "__main__": + run() diff --git a/dockers/llm.rag.service/serverragllm_zendesk_csv_sql_local.py b/dockers/llm.rag.service/serverragllm_zendesk_csv_sql_local.py new file mode 100644 index 0000000..762de89 --- /dev/null +++ b/dockers/llm.rag.service/serverragllm_zendesk_csv_sql_local.py @@ -0,0 +1,159 @@ +# /// script +# requires-python = ">=3.12" +# dependencies = [ +# "faiss-cpu", +# "fastapi", +# "langchain-community", +# "langchain-huggingface", +# "openai", +# "uvicorn", +# ] +# /// + +import logging +import os +import pickle +import sys +from functools import partial +from typing import Union + +import click +import uvicorn +from fastapi import FastAPI +from openai import OpenAI + +from common import SearchType, get_answer_with_settings, get_sql_answer, question_router + + +def setup( + file_path: str, + relevant_docs: int, + llm_server_url: str, + model_id: str, + max_tokens: int, + model_temperature: float, + sql_search_db_and_model_path: str, +): + app = FastAPI() + + # TO DO: Add question classification block + # search_type = question_router(question) + + # For now, hard-coding question type to aggregation + search_type = SearchType.SQL + + match search_type: + case SearchType.SQL: + logging.info("Handling search type: SQL") + + get_answer = partial( + get_sql_answer, + model_id=model_id, + max_tokens=max_tokens, + model_temperature=model_temperature, + llm_server_url=llm_server_url, + sql_search_db_and_model_path=sql_search_db_and_model_path, + ) + case SearchType.VECTOR: + logging.info("Handling search type: VECTOR") + + # Load the object from the pickle file + with open(file_path, "rb") as file: + logging.info("Loading Vector DB...\n") + vectorstore = pickle.load(file) + + # Retriever configuration parameters reference: + # https://python.langchain.com/api_reference/community/vectorstores/langchain_community.vectorstores.faiss.FAISS.html#langchain_community.vectorstores.faiss.FAISS.as_retriever + retriever = vectorstore.as_retriever(search_kwargs={"k": relevant_docs}) + logging.info("Created Vector DB retriever successfully. \n") + if not llm_server_url.endswith("/v1"): + llm_server_url = llm_server_url + "/v1" + logging.info( + "Creating an OpenAI client to the hosted model at URL: ", llm_server_url + ) + + try: + client = OpenAI(base_url=llm_server_url, api_key="na") + except Exception as e: + logging.error("Error creating client:", e) + sys.exit(1) + + jira_system_prompt = """You are a specialized support ticket assistant. Format your responses following these rules: + 1. Answer the provided question only using the provided context. + 2. Provide a clear, direct and factual answer + 3. Include relevant technical details when present + 4. If the information is outdated, mention when it was last updated + """ + + get_answer = partial( + get_answer_with_settings, + retriever=retriever, + client=client, + model_id=model_id, + max_tokens=max_tokens, + model_temperature=model_temperature, + system_prompt=jira_system_prompt, + llm_server_url=llm_server_url, + ) + + @app.get("/answer/{question}") + def read_item(question: Union[str, None] = None): + logging.info(f"Received question: {question}") + answer = get_answer(question) + return {"question": question, "answer": answer} + + return app + + +MICROSOFT_MODEL_ID = "microsoft/Phi-3-mini-4k-instruct" +MOSAICML_MODEL_ID = "mosaicml/mpt-7b-chat" +RELEVANT_DOCS_DEFAULT = 2 +MAX_TOKENS_DEFAULT = 350 +MODEL_TEMPERATURE_DEFAULT = 0.00 + + +file_path = os.getenv("FILE_PATH") +if not file_path: + logging.info("Please provide the DB file path") + +logging.info( + "Setting LLM setup parameters like LLM server URL, model id, model temperature..." +) +relevant_docs = int(os.getenv("RELEVANT_DOCS", RELEVANT_DOCS_DEFAULT)) + +# LLM server URL if using ollama hosting locally +# llm_server_url = os.getenv("LLM_SERVER_URL", "http://localhost:11434/v1") +# model_id = os.getenv("MODEL_ID", "llama2") + +# LLM server URL if using k8s elotl hosting + port-forwarding +llm_server_url = os.getenv("MODEL_LLM_SERVER_URL", "http://localhost:9000/v1") +model_id = os.getenv("MODEL_ID", "rubra-ai/Phi-3-mini-128k-instruct") + +max_tokens = int(os.getenv("MAX_TOKENS", MAX_TOKENS_DEFAULT)) +model_temperature = float(os.getenv("MODEL_TEMPERATURE", MODEL_TEMPERATURE_DEFAULT)) + +sql_search_db_and_model_path = os.getenv("SQL_SEARCH_DB_AND_MODEL_PATH", "/app/db/") + +app = setup( + file_path, relevant_docs, llm_server_url, model_id, max_tokens, model_temperature, sql_search_db_and_model_path +) + + +@click.command() +@click.option( + "--host", + default="127.0.0.1", + help="Host for the FastAPI server (default: 127.0.0.1)", +) +@click.option( + "--port", type=int, default=8000, help="Port for the FastAPI server (default: 8000)" +) +def run(host, port): + # Serve the app using Uvicorn + uvicorn.run( + "serverragllm_zendesk_csv_sql_local:app", host=host, port=port, reload=True + ) + + +if __name__ == "__main__": + run() diff --git a/dockers/llm.rag.service/setup.py b/dockers/llm.rag.service/setup.py new file mode 100644 index 0000000..e2674ef --- /dev/null +++ b/dockers/llm.rag.service/setup.py @@ -0,0 +1,7 @@ +from setuptools import setup + +setup( + name="serveragllm", + version="0.1.0", + py_modules=["common", "serveragllm", "serverragllm_jira_cvs_local"], +) diff --git a/dockers/llm.vdb.service/.env_local_template b/dockers/llm.vdb.service/.env_local_template new file mode 100644 index 0000000..5f7fefd --- /dev/null +++ b/dockers/llm.vdb.service/.env_local_template @@ -0,0 +1,8 @@ +# Local Settings +LOCAL_DIRECTORY=/path/to/local/storage +OUTPUT_FILENAME=/path/to/local/output_pickled.obj + +# Vector DB Optional Settings +# EMBEDDING_CHUNK_SIZE=1000 +# EMBEDDING_CHUNK_OVERLAP=100 +# EMBEDDING_MODEL_NAME=sentence-transformers/all-MiniLM-L6-v2 \ No newline at end of file diff --git a/dockers/llm.vdb.service/.env_s3_template b/dockers/llm.vdb.service/.env_s3_template new file mode 100644 index 0000000..28e0d4a --- /dev/null +++ b/dockers/llm.vdb.service/.env_s3_template @@ -0,0 +1,12 @@ +# S3 Settings +VECTOR_DB_INPUT_ARG=s3-input-dir +VECTOR_DB_S3_BUCKET=my-s3-bucket +VECTOR_DB_S3_FILE=my-vectordb-file +AWS_REGION=us-east-1 +AWS_ACCESS_KEY_ID=my-access-key +AWS_SECRET_ACCESS_KEY=my-secret-key + +# Vector DB Optional Settings +# EMBEDDING_CHUNK_SIZE=1000 +# EMBEDDING_CHUNK_OVERLAP=100 +# EMBEDDING_MODEL_NAME=sentence-transformers/all-MiniLM-L6-v2 \ No newline at end of file diff --git a/dockers/llm.vdb.service/Dockerfile b/dockers/llm.vdb.service/Dockerfile index bbcfef0..557ad29 100644 --- a/dockers/llm.vdb.service/Dockerfile +++ b/dockers/llm.vdb.service/Dockerfile @@ -1,6 +1,6 @@ -# syntax=docker/dockerfile-upstream:master +# syntax=docker/dockerfile:1 # Adapted from: https://github.com/pytorch/pytorch/blob/master/Dockerfile -FROM python:3.11-slim as base-container +FROM python:3.11-slim AS base-container # Automatically set by buildx ARG TARGETPLATFORM @@ -12,18 +12,45 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins ca-certificates \ ccache \ curl \ + libmagic1 \ + file \ libssl-dev ca-certificates make \ git python3-pip && \ rm -rf /var/lib/apt/lists/* WORKDIR /createvectordb +# Upgrade pip +RUN pip3 install --no-cache-dir --upgrade pip setuptools wheel + +# Install dependencies in separate layers +RUN pip3 install --no-cache-dir \ + "langchain" \ + "langchain_community" \ + "langchain_huggingface" + +RUN pip3 install --no-cache-dir \ + "unstructured" \ + "sentence-transformers" \ + "faiss-cpu" + +RUN pip3 install --no-cache-dir \ + "lxml" \ + "bs4" + +RUN pip3 install --no-cache-dir \ + "python-magic" \ + "pydantic_settings" \ + "s3fs" \ + "weaviate-client" \ + "langchain_weaviate" + +COPY __init__.py . COPY createvectordb.py . +COPY common.py . +COPY config.py . +COPY s3_utils.py . +COPY service.py . COPY pyproject.toml . -RUN --mount=type=cache,target=/root/.cache/pip \ - pip3 install -v --no-cache-dir \ - "langchain" "sentence-transformers" "faiss-cpu" "boto3" "lxml" "bs4" && \ - pip3 install --no-cache-dir -e . - CMD ["python", "createvectordb.py"] diff --git a/dockers/llm.vdb.service/README.md b/dockers/llm.vdb.service/README.md new file mode 100644 index 0000000..685bc84 --- /dev/null +++ b/dockers/llm.vdb.service/README.md @@ -0,0 +1,39 @@ +# Create Vector Databas + +## Run locally +Create a venv and install requirements.txt + +Locally vector db creation can be run in two modes: local dir or s3. + +Please see template files +```shell +.env_local_template +.env_s3_template +``` + +To run the app You can either export all required env vars or prepare an .env file and run +```shell +uv run createvectordb.py +or +python createvectordb.py +``` + +Or You can pass the file: +```shell +uv run createvectordb.py --env_file backup-.env +``` + +## Run tests +Also install in your venv requirements-dev.tx and call +```shell +uv run pytest +``` + +## Run in k8s +Using pydantic settings introduces one impediment. Before running +```shell +envsubst < createvdb.yaml | kubectl apply -f - +``` +We must make sure all env variables are exported. If for ex EMBEDDING_CHUNK_SIZE is not set in our terminal, +envsubst will put an empty string there and pydantic settings will complain that they can't change +empty setting to integer. \ No newline at end of file diff --git a/dockers/llm.vdb.service/__init__.py b/dockers/llm.vdb.service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dockers/llm.vdb.service/check_files_in_s3_bucket.py b/dockers/llm.vdb.service/check_files_in_s3_bucket.py new file mode 100644 index 0000000..cfb3ec0 --- /dev/null +++ b/dockers/llm.vdb.service/check_files_in_s3_bucket.py @@ -0,0 +1,52 @@ +import boto3 +from botocore.exceptions import ClientError +import argparse +from typing import List, Optional +from createvectordb import list_files_in_s3_folder + +def main(): + """ + Main function to run the S3 file listing script. + Accepts command line arguments for bucket name and folder prefix. + """ + # Set up argument parser + parser = argparse.ArgumentParser(description='List all files in an S3 bucket folder with pagination') + parser.add_argument('--bucket', required=True, help='Name of the S3 bucket') + parser.add_argument('--folder', required=True, help='Folder prefix in the bucket') + parser.add_argument('--profile', help='AWS profile name (optional)', default=None) + parser.add_argument('--region', help='AWS region (optional)', default='us-east-1') + + args = parser.parse_args() + + try: + # Set up AWS session with optional profile + if args.profile: + session = boto3.Session(profile_name=args.profile, region_name=args.region) + else: + session = boto3.Session(region_name=args.region) + + # Create S3 client + s3_client = session.client('s3') + + # Call the listing function + files = list_files_in_s3_folder(args.bucket, args.folder, s3_client) + + # Print summary and sample results + if files: + print(f"Summary of files in {args.bucket}/{args.folder}:") + print(f"Total files found: {len(files)}") + print("\nFirst 5 files:") + for file in files[:5]: + print(file) + + print(f"\nLast 5 files:") + for file in files[-5:]: + print(file) + + print(f"\nFinal count confirmation: {len(files)} files found") + + except Exception as e: + print(f"Error: {str(e)}") + +if __name__ == "__main__": + main() diff --git a/dockers/llm.vdb.service/common.py b/dockers/llm.vdb.service/common.py new file mode 100644 index 0000000..8e86776 --- /dev/null +++ b/dockers/llm.vdb.service/common.py @@ -0,0 +1,191 @@ +import json +import os +from typing import List + +import weaviate + +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain_community.vectorstores import FAISS +from langchain_core.documents import Document +from langchain_huggingface import HuggingFaceEmbeddings +from langchain_weaviate.vectorstores import WeaviateVectorStore + + +def load_jsonl_files_from_directory(directory): + data = [] + for filename in os.listdir(directory): + print("Processing file, ", filename, "..." ) + if filename.endswith('.json'): + with open(os.path.join(directory, filename)) as f: + try: + # Try reading as JSONL first + for line in f: + if line.strip(): # Skip empty lines + data.append(json.loads(line.strip())) + except json.JSONDecodeError: + # If that fails, try reading as regular JSON + f.seek(0) # Go back to start of file + data.append(json.load(f)) + return data + + +def get_documents(data): + texts = [doc["text"] for doc in data] + metadatas = [doc["metadata"] for doc in data] + return texts, metadatas + + +def chunk_documents(data, chunk_size, chunk_overlap): + """ + Chunks documents while maintaining alignment between text chunks and metadata + """ + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=chunk_size, chunk_overlap=chunk_overlap + ) + + # Lists to store chunks and corresponding metadata + all_chunks = [] + all_metadatas = [] + + for doc in data: + print("Chunking doc with key/ticket ID, ", doc["metadata"].get("ticket") or doc["metadata"].get("key")) + chunks = text_splitter.split_text(doc["text"]) + + doc_metadatas = [doc["metadata"].copy() for _ in chunks] + + # This is just to see if it's used or not + for i, (chunk, metadata) in enumerate(zip(chunks, doc_metadatas)): + metadata["chunk_index"] = i + metadata["chunk_total"] = len(chunks) + + all_chunks.extend(chunks) + all_metadatas.extend(doc_metadatas) + + print("Number of chunks created: ", len(all_chunks)) + return all_chunks, all_metadatas + + +def chunk_documents_with_added_metadata(data, chunk_size, chunk_overlap): + """ + Splits documents into smaller text chunks while preserving alignment with metadata. + Additionally, each chunk is enriched by embedding its corresponding metadata into the text. + + The method determines the maximum metadata size across all documents and adjusts + the effective chunk size accordingly to ensure that metadata fits within each chunk. + + Metadata keys with `None` values are excluded from the embedded metadata in the text. + """ + # TODO: find a better way to find the biggest metadata + max_size_of_metadata = 0 + for doc in data: + meta_enhancement = ". ".join([f"{key.title()}: {value}" for key, value in doc["metadata"].items() if value]) + max_size_of_metadata = max(max_size_of_metadata, len(meta_enhancement)) + + print(f"Biggest metada has {max_size_of_metadata} characters.") + effective_chunk_size = chunk_size - max_size_of_metadata + print(f"Effective chunk size will be {effective_chunk_size}") + + # TODO: handle better + if effective_chunk_size <= 0: + raise "Use bigger chunk size" + + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=chunk_size, chunk_overlap=chunk_overlap + ) + + # Lists to store chunks and corresponding metadata + all_chunks = [] + all_metadatas = [] + + for doc in data: + print("Chunking doc with key/ticket ID, ", doc["metadata"].get("ticket") or doc["metadata"].get("key")) + chunks = text_splitter.split_text(doc["text"]) + chunks_enriched_with_metadata = [] + + doc_metadatas = [doc["metadata"].copy() for _ in chunks] + + meta_enhancement = ". ".join([f"{key.title()}: {value}" for key, value in doc["metadata"].items() if value]) + + for i, (chunk, metadata) in enumerate(zip(chunks, doc_metadatas)): + chunks_enriched_with_metadata.append(chunk + "\n" + meta_enhancement) + + # This is just to see if it's used or not + metadata["chunk_index"] = i + metadata["chunk_total"] = len(chunks) + + all_chunks.extend(chunks_enriched_with_metadata) + all_metadatas.extend(doc_metadatas) + + print("Number of chunks created: ", len(all_chunks)) + return all_chunks, all_metadatas + + +def create_vectordb_from_data( + data, + embedding_model_name: str, + chunk_size: int, + chunk_overlap: int, +): + # no chunking + # texts, metadatas = get_documents(data) + + # with chunking texts + # texts, metadatas = chunk_documents(data, chunk_size, chunk_overlap) + + # with adding metadata to text + print("Start chunking documents") + texts, metadatas = chunk_documents_with_added_metadata(data, chunk_size, chunk_overlap) + + embeddings = HuggingFaceEmbeddings(model_name=embedding_model_name) + print("Convert to FAISS vectorstore") + vectorstore = FAISS.from_texts(texts, embeddings, metadatas=metadatas) + return vectorstore + + +def create_vectordb_local_weaviate( + data, + embedding_model_name: str, + chunk_size: int, + chunk_overlap: int, + weaviate_url: str, + weaviate_grpc_url: str, + weaviate_index_name: str, +): + # with adding metadata to text + print("Start chunking documents") + texts, metadatas = chunk_documents_with_added_metadata(data, chunk_size, chunk_overlap) + + embeddings = HuggingFaceEmbeddings(model_name=embedding_model_name) + + # adapt data + documents: List[Document] = [] + for txt, met in zip(texts, metadatas): + document = Document( + page_content=txt, + metadata=met + ) + documents.append(document) + + # TODO: move extracting url and port to config + with weaviate.connect_to_custom( + http_host=weaviate_url.split(":")[0], + http_port=int(weaviate_url.split(":")[1]), + http_secure=False, + grpc_host=weaviate_grpc_url.split(":")[0], + grpc_port=int(weaviate_grpc_url.split(":")[1]), + grpc_secure=False, + ) as weaviate_client: + # return WeaviateVectorStore.from_documents( + # documents, + # embeddings, + # client=weaviate_client, + # index_name=weaviate_index_name, + # ) + return WeaviateVectorStore.from_texts( + texts, + embeddings, + client=weaviate_client, + metadatas=metadatas, + index_name=weaviate_index_name, + text_key="text", + ) diff --git a/dockers/llm.vdb.service/config.py b/dockers/llm.vdb.service/config.py new file mode 100644 index 0000000..f1df796 --- /dev/null +++ b/dockers/llm.vdb.service/config.py @@ -0,0 +1,161 @@ +import os + +from pydantic_settings import BaseSettings +from pydantic import Field, ValidationError, field_validator + +from typing import Optional + + +def validate_int(value): + if type(value) == int: + return value + try: + return int(value.strip("'").strip("\"")) + except (TypeError, ValueError): + raise ValueError("Value must be convertible to an integer") + + +class S3Settings(BaseSettings): + # This is the directory name on S3 where input files can be found + s3_dir_name: Optional[str] = Field( + alias="VECTOR_DB_INPUT_ARG", + description="Name of the S3 directory" + ) + + # This is the bucket that will be used to store both input datasets for RAG + # as well as the Vector DB created from this dataset + s3_bucket_name: Optional[str] = Field( + alias="VECTOR_DB_S3_BUCKET", + description="Name of the S3 bucket" + ) + + # This is the name of the Vector DB file that will be created by this script + vectordb_name: Optional[str] = Field( + alias="VECTOR_DB_S3_FILE", + description="Name of the created Vector DB" + ) + + # AWS ccredentials + s3_region: Optional[str] = Field( + None, + alias="AWS_REGION", + description="Region of the S3 bucket" + ) + s3_access_key: Optional[str] = Field( + alias="AWS_ACCESS_KEY_ID", + description="Access key for S3" + ) + s3_secret_key: Optional[str] = Field( + alias="AWS_SECRET_ACCESS_KEY", + description="Secret key for S3" + ) + + embedding_chunk_size: int = Field( + default=1000, + description="Chunk size used by the embedding model" + ) + + @field_validator('embedding_chunk_size', mode='before') + @classmethod + def validate_chunk_size(cls, v): + return validate_int(v) + + embedding_chunk_overlap: int = Field( + default=100, + description="Overlap size between chunks" + ) + + @field_validator('embedding_chunk_overlap', mode='before') + @classmethod + def validate_chunk_overlap(cls, v): + return validate_int(v) + + embedding_model_name: str = Field( + default="sentence-transformers/all-MiniLM-L6-v2", + description="Name of the embedding model to use" + ) + + class Config: + env_file = ".env" + extra = "ignore" + + +class LocalSettings(BaseSettings): + local_directory: str = Field( + description="Directory path for local storage", + ) + + output_filename: str = Field( + description="Output vectordb filename", + ) + + embedding_chunk_size: int = Field( + default=1000, + description="Chunk size used by the embedding model" + ) + embedding_chunk_overlap: int = Field( + default=100, + description="Overlap size between chunks" + ) + embedding_model_name: str = Field( + default="sentence-transformers/all-MiniLM-L6-v2", + description="Name of the embedding model to use" + ) + + class Config: + env_file = ".env" + extra = "ignore" + + +def try_load_settings(env_file): + if env_file: + try: + s3_settings = S3Settings(_env_file=env_file) + return s3_settings, None + except ValidationError as e: + print("ValidationError: ", e) + try: + local_settings = LocalSettings(_env_file=env_file) + return None, local_settings + except ValidationError as e: + raise ValueError(f"Missing or invalid configuration: {e}") + + try: + s3_settings = S3Settings() + return s3_settings, None + except ValidationError as e: + print("ValidationError: ", e) + try: + local_settings = LocalSettings() + return None, local_settings + except ValidationError as e: + raise ValueError(f"Missing or invalid configuration: {e}") + + +class WeaviateSettings(BaseSettings): + weaviate_uri: Optional[str] = Field( + ..., + alias="WEAVIATE_URI_WITH_PORT", + ) + weaviate_grpc_uri: Optional[str] = Field( + ..., + alias="WEAVIATE_GRPC_URI_WITH_PORT", + ) + weaviate_index_name: Optional[str] = Field( + ..., + alias="WEAVIATE_INDEX_NAME", + ) + + class Config: + env_file = ".env" + extra = "ignore" + + def is_set(self) -> bool: + return all([self.weaviate_uri, self.weaviate_grpc_uri, self.weaviate_index_name]) + + +def try_load_weaviate_settings(env_file): + if env_file: + return WeaviateSettings(_env_file=env_file) + else: + return WeaviateSettings() diff --git a/dockers/llm.vdb.service/createvectordb.py b/dockers/llm.vdb.service/createvectordb.py index 3ba5c3a..64ca625 100644 --- a/dockers/llm.vdb.service/createvectordb.py +++ b/dockers/llm.vdb.service/createvectordb.py @@ -1,55 +1,55 @@ -import os +import click +import logging import sys -from langchain_community.embeddings import HuggingFaceEmbeddings -from langchain_community.vectorstores import FAISS -from langchain_community.document_loaders.sitemap import SitemapLoader -from langchain.text_splitter import RecursiveCharacterTextSplitter - -import boto3 -import pickle - -vectordb_bucket = "faiss-vectordbs" - -vectordb_key = os.environ.get('VECTOR_DB_S3_FILE') -if vectordb_key is None: - print("Please set environment variable VECTOR_DB_S3_FILE") - sys.exit(1) - -vectordb_input_type = os.environ.get('VECTOR_DB_INPUT_TYPE') -if vectordb_input_type is None: - print("Please set environment variable VECTOR_DB_INPUT_TYPE") - sys.exit(1) - -vectordb_input_arg = os.environ.get('VECTOR_DB_INPUT_ARG') -if vectordb_input_arg is None: - print("Please set environment variable VECTOR_DB_INPUT_ARG") - sys.exit(1) - -# Initialize vectorstore and create pickle representation -os.environ["TOKENIZERS_PARALLELISM"] = "false" -if vectordb_input_type == 'text': - vectorstore = FAISS.from_texts(vectordb_input_arg, embedding=HuggingFaceEmbeddings()) -elif vectordb_input_type == 'sitemap': - sitemap_loader = SitemapLoader(web_path=vectordb_input_arg, filter_urls=["^((?!.*/v.*).)*$"]) - sitemap_loader.requests_per_second = 1 - docs = sitemap_loader.load() - print("Count of sitemap docs loaded:", len(docs)) - text_splitter = RecursiveCharacterTextSplitter( - chunk_size = 1000, - chunk_overlap = 100, - length_function = len, - ) - texts = text_splitter.split_documents(docs) - vectorstore = FAISS.from_documents(texts, embedding=HuggingFaceEmbeddings()) -else: - print("Unknown value for VECTOR_DB_INPUT_TYPE:", vectordb_input_type) - sys.exit(1) - -pickle_byte_obj = pickle.dumps(vectorstore) - -# Persist vectorstore to S3 bucket vectorstores -s3_client = boto3.client('s3') -s3_client.put_object(Body=pickle_byte_obj, Bucket=vectordb_bucket, Key=vectordb_key) -print("Uploaded vectordb to", vectordb_bucket, vectordb_key) -sys.exit(0) +from config import ( + try_load_settings, + try_load_weaviate_settings, +) +from service import ( + LocalDirDbCreationService, + LocalDirWeaviateDbCreationService, + S3WeaviateDbCreationService, + S3VectorDbCreationService, +) + + +logging.basicConfig() +logging.getLogger().setLevel(logging.INFO) + + +@click.command() +@click.option("--env_file", type=click.Path(exists=True), help="Path to the environment file") +def run(env_file: str): + s3_settings, local_settings = try_load_settings(env_file) + weaviate_settings = try_load_weaviate_settings(env_file) + + if s3_settings: + if weaviate_settings.is_set(): + logging.info("---> S3WeaviateDbCreationService") + service = S3WeaviateDbCreationService(s3_settings, weaviate_settings) + else: + logging.info("---> S3VectorDbCreationService") + service = S3VectorDbCreationService(s3_settings) + + service.create() + + elif local_settings: + if weaviate_settings.is_set(): + logging.info("---> LocalDirWeaviateDbCreationService") + service = LocalDirWeaviateDbCreationService(local_settings, weaviate_settings) + else: + logging.info("---> S3VectorDbCreationService") + service = S3VectorDbCreationService(local_settings) + + service.create() + + else: + # TODO: not really needed, error will be thrown earlier + raise "Missing config" + + sys.exit(0) + + +if __name__ == "__main__": + run() diff --git a/dockers/llm.vdb.service/createvectordb_test.py b/dockers/llm.vdb.service/createvectordb_test.py new file mode 100644 index 0000000..7db0f1f --- /dev/null +++ b/dockers/llm.vdb.service/createvectordb_test.py @@ -0,0 +1,85 @@ +import click +import os +import pytest +import s3fs + +from botocore.session import Session +from moto.moto_server.threaded_moto_server import ThreadedMotoServer +from s3fs.core import S3FileSystem + +from createvectordb import run + + +def test_create_faiss_vector_db_using_local_files(): + ctx = click.Context(run) + try: + ctx.forward(run, env_file="test_data/.env_local") + except SystemExit as e: + assert e.code == 0 + + assert os.path.exists("test_data/output/output_pickled.obj") + + if os.path.exists("test_data/output/output_pickled.obj"): + os.remove("test_data/output/output_pickled.obj") + + +@pytest.fixture(scope="module") +def s3_base(): + # writable local S3 system + server = ThreadedMotoServer(ip_address="127.0.0.1", port=5555) + server.start() + os.environ["AWS_SECRET_ACCESS_KEY"] = "test-secret-key" + os.environ["AWS_ACCESS_KEY_ID"] = "test-access-key" + os.environ["TEST_FAKE_S3"] = "true" + os.environ.pop("AWS_PROFILE", None) + + print("server up") + yield + print("moto done") + server.stop() + + +def upload_to_s3(s3_client, bucket_name, local_dir, s3_prefix): + for root, _, files in os.walk(local_dir): + for file in files: + local_file_path = os.path.join(root, file) + s3_key = os.path.join(s3_prefix, os.path.relpath(local_file_path, local_dir)).replace("\\", "/") + with open(local_file_path, "rb") as f: + s3_client.put_object(Bucket=bucket_name, Key=s3_key, Body=f) + + +MOCK_BUCKET_NAME = "test_bucket" + + +@pytest.fixture() +def mock_s3_client(s3_base): + session = Session() + client = session.create_client("s3", endpoint_url="http://127.0.0.1:5555/") + client.create_bucket( + Bucket=MOCK_BUCKET_NAME, + ACL="public-read", + CreateBucketConfiguration={ + 'LocationConstraint': "us-east-2", # TODO: make sure this is the same as local default + }, + ) + + S3FileSystem.clear_instance_cache() + s3 = S3FileSystem(anon=False, client_kwargs={"endpoint_url": "http://127.0.0.1:5555/"}) + s3.invalidate_cache() + + yield client + + +def test_create_faiss_vector_db_using_s3_files(mock_s3_client): + upload_to_s3(mock_s3_client, MOCK_BUCKET_NAME, "test_data/input", "s3-input-dir") + + ctx = click.Context(run) + try: + ctx.forward(run, env_file="test_data/.env_s3") + except SystemExit as e: + assert e.code == 0 + + s3 = s3fs.S3FileSystem(anon=False, client_kwargs={"endpoint_url": "http://127.0.0.1:5555/"}) + output_s3_path = "s3://test_bucket/test_data/output_pickled.obj" + + assert s3.exists(output_s3_path) diff --git a/dockers/llm.vdb.service/makeDocker.sh b/dockers/llm.vdb.service/makeDocker.sh index 53fd726..d93a476 100755 --- a/dockers/llm.vdb.service/makeDocker.sh +++ b/dockers/llm.vdb.service/makeDocker.sh @@ -6,7 +6,14 @@ set -e CREATE_VECTOR_DB_REPO=$1 CREATE_VECTOR_DB_TAG=$2 +# Get the directory where the script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]:-$0}" )" &> /dev/null && pwd )" + echo "" echo "Building docker for vectordb creation" -docker build --platform=linux/amd64 --load -f ./Dockerfile -t ${CREATE_VECTOR_DB_REPO}:${CREATE_VECTOR_DB_TAG} . +docker buildx build --platform=linux/amd64 --load \ + -f "${SCRIPT_DIR}/Dockerfile" \ + -t ${CREATE_VECTOR_DB_REPO}:${CREATE_VECTOR_DB_TAG} \ + "${SCRIPT_DIR}" + docker push ${CREATE_VECTOR_DB_REPO}:${CREATE_VECTOR_DB_TAG} diff --git a/dockers/llm.vdb.service/pyproject.toml b/dockers/llm.vdb.service/pyproject.toml index 148c485..8969a57 100644 --- a/dockers/llm.vdb.service/pyproject.toml +++ b/dockers/llm.vdb.service/pyproject.toml @@ -3,3 +3,6 @@ name = "createvectordb" # Required version = "1.0.0" # Required description = "Create FAISS vectordb, serialize and upload to S3" requires-python = ">=3.8" + +[tool.setuptools] +py-modules = ["common", "config", "createvectordb", "s3_utils", "service"] \ No newline at end of file diff --git a/dockers/llm.vdb.service/requirements-dev.txt b/dockers/llm.vdb.service/requirements-dev.txt new file mode 100644 index 0000000..9c09313 --- /dev/null +++ b/dockers/llm.vdb.service/requirements-dev.txt @@ -0,0 +1,5 @@ +boto3 +flask +flask_cors +moto +pytest \ No newline at end of file diff --git a/dockers/llm.vdb.service/requirements.txt b/dockers/llm.vdb.service/requirements.txt new file mode 100644 index 0000000..f04e044 --- /dev/null +++ b/dockers/llm.vdb.service/requirements.txt @@ -0,0 +1,8 @@ +click +faiss-cpu +langchain_community +langchain_huggingface +langchain_weaviate +pydantic_settings +s3fs +weaviate-client \ No newline at end of file diff --git a/dockers/llm.vdb.service/s3_utils.py b/dockers/llm.vdb.service/s3_utils.py new file mode 100644 index 0000000..92ce510 --- /dev/null +++ b/dockers/llm.vdb.service/s3_utils.py @@ -0,0 +1,50 @@ +import json +import os + +import s3fs + + +def load_jsonl_files_from_s3(bucket_name, prefix=""): + # TODO: init client with data from config + if os.environ.get("TEST_FAKE_S3") == "true": + fs = s3fs.S3FileSystem(anon=False, client_kwargs={"endpoint_url": "http://127.0.0.1:5555/"}) + else: + fs = s3fs.S3FileSystem() + data = [] + + # List all files under the given prefix + files = fs.ls(f"{bucket_name}/{prefix}") + + for file_path in files: + print("Processing file:", file_path, "...") + if file_path.endswith('.json'): + with fs.open(file_path, 'r') as f: + try: + # Try reading as JSONL first + for line in f: + if line.strip(): # Skip empty lines + data.append(json.loads(line.strip())) + except json.JSONDecodeError: + # If that fails, try reading as regular JSON + f.seek(0) # Go back to start of file + data.append(json.load(f)) + + return data + + +def save_file_to_s3(file_to_upload, bucket, key): + # TODO: init client with data from config + if os.environ.get("TEST_FAKE_S3") == "true": + fs = s3fs.S3FileSystem(anon=False, client_kwargs={"endpoint_url": "http://127.0.0.1:5555/"}) + else: + fs = s3fs.S3FileSystem() + s3_path = f"{bucket}/{key}" + + with fs.open(s3_path, 'wb') as s3_file: + if isinstance(file_to_upload, str): + # If it's a file path, read and upload the file content + with open(file_to_upload, 'rb') as local_file: + s3_file.write(local_file.read()) + else: + # If it's in-memory content (bytes or string), upload directly + s3_file.write(file_to_upload if isinstance(file_to_upload, bytes) else file_to_upload.encode()) diff --git a/dockers/llm.vdb.service/service.py b/dockers/llm.vdb.service/service.py new file mode 100644 index 0000000..25c4fad --- /dev/null +++ b/dockers/llm.vdb.service/service.py @@ -0,0 +1,115 @@ +import os +import pickle + +from dataclasses import dataclass + +from common import ( + create_vectordb_local_weaviate, + create_vectordb_from_data, + load_jsonl_files_from_directory, +) +from config import LocalSettings, S3Settings, WeaviateSettings +from s3_utils import load_jsonl_files_from_s3, save_file_to_s3 + + +@dataclass +class S3VectorDbCreationService: + config: S3Settings + + def create(self): + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + os.environ["AWS_ACCESS_KEY_ID"] = self.config.s3_access_key + os.environ["AWS_SECRET_ACCESS_KEY"] = self.config.s3_secret_key + + print("Load JSON files") + data = load_jsonl_files_from_s3(self.config.s3_bucket_name, self.config.s3_dir_name) + + print("Convert to FAISS vectorstore") + vectorstore = create_vectordb_from_data( + data, + self.config.embedding_model_name, + self.config.embedding_chunk_size, + self.config.embedding_chunk_overlap, + ) + + pickle_byte_obj = pickle.dumps(vectorstore) + + save_file_to_s3(pickle_byte_obj, self.config.s3_bucket_name, self.config.vectordb_name) + print("Uploaded vectordb to", self.config.s3_bucket_name, self.config.vectordb_name) + + +@dataclass +class LocalDirDbCreationService: + config: LocalSettings + + def create(self): + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + print("Load JSON files") + data = load_jsonl_files_from_directory(self.config.local_directory) + + print("Convert to FAISS vectorstore") + vectorstore = create_vectordb_from_data( + data, + self.config.embedding_model_name, + self.config.embedding_chunk_size, + self.config.embedding_chunk_overlap, + ) + + pickle_byte_obj = pickle.dumps(vectorstore) + + with open(self.config.output_filename, "wb") as file: + file.write(pickle_byte_obj) + print(f"Pickle byte object saved to {self.config.output_filename}") + + +@dataclass +class LocalDirWeaviateDbCreationService: + config: LocalSettings + db_config: WeaviateSettings + + def create(self): + print("Load JSON files") + data = load_jsonl_files_from_directory(self.config.local_directory) + + print("Convert to Weaviate vectorstore") + create_vectordb_local_weaviate( + data, + self.config.embedding_model_name, + self.config.embedding_chunk_size, + self.config.embedding_chunk_overlap, + self.db_config.weaviate_uri, + self.db_config.weaviate_grpc_uri, + self.db_config.weaviate_index_name, + ) + + print(f"Weaviate index saved {self.db_config.weaviate_index_name}") + + +@dataclass +class S3WeaviateDbCreationService: + config: S3Settings + db_config: WeaviateSettings + + def create(self): + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + os.environ["AWS_ACCESS_KEY_ID"] = self.config.s3_access_key + os.environ["AWS_SECRET_ACCESS_KEY"] = self.config.s3_secret_key + + print("Load JSON files") + data = load_jsonl_files_from_s3(self.config.s3_bucket_name, self.config.s3_dir_name) + + print("Convert to Weaviate vectorstore") + create_vectordb_local_weaviate( + data, + self.config.embedding_model_name, + self.config.embedding_chunk_size, + self.config.embedding_chunk_overlap, + self.db_config.weaviate_uri, + self.db_config.weaviate_grpc_uri, + self.db_config.weaviate_index_name, + ) + + print(f"Weaviate index saved {self.db_config.weaviate_index_name}") diff --git a/dockers/llm.vdb.service/setup.py b/dockers/llm.vdb.service/setup.py new file mode 100644 index 0000000..65465a7 --- /dev/null +++ b/dockers/llm.vdb.service/setup.py @@ -0,0 +1,7 @@ +from setuptools import setup + +setup( + name="createvectordb", + version="0.1.0", + py_modules=["common", "config", "createvectordb", "s3_utils", "service"], +) \ No newline at end of file diff --git a/dockers/llm.vdb.service/test_data/.env_local b/dockers/llm.vdb.service/test_data/.env_local new file mode 100644 index 0000000..e625831 --- /dev/null +++ b/dockers/llm.vdb.service/test_data/.env_local @@ -0,0 +1,8 @@ +# Local Settings +LOCAL_DIRECTORY=test_data/input/ +OUTPUT_FILENAME=test_data/output/output_pickled.obj + +# Vector DB Optional Settings +# EMBEDDING_CHUNK_SIZE=1000 +# EMBEDDING_CHUNK_OVERLAP=100 +# EMBEDDING_MODEL_NAME=sentence-transformers/all-MiniLM-L6-v2 \ No newline at end of file diff --git a/dockers/llm.vdb.service/test_data/.env_s3 b/dockers/llm.vdb.service/test_data/.env_s3 new file mode 100644 index 0000000..33b004b --- /dev/null +++ b/dockers/llm.vdb.service/test_data/.env_s3 @@ -0,0 +1,13 @@ +# S3 Settings +VECTOR_DB_INPUT_ARG=s3-input-dir +VECTOR_DB_S3_BUCKET=test_bucket +VECTOR_DB_S3_FILE=test_data/output_pickled.obj +AWS_ACCESS_KEY_ID=test-access-key +AWS_SECRET_ACCESS_KEY=test-secret-key +AWS_REGION=us-east-2 + + +# Vector DB Optional Settings +# EMBEDDING_CHUNK_SIZE=1000 +# EMBEDDING_CHUNK_OVERLAP=100 +# EMBEDDING_MODEL_NAME=sentence-transformers/all-MiniLM-L6-v2 \ No newline at end of file diff --git a/dockers/llm.vdb.service/test_data/input/file1.json b/dockers/llm.vdb.service/test_data/input/file1.json new file mode 100644 index 0000000..b9a9053 --- /dev/null +++ b/dockers/llm.vdb.service/test_data/input/file1.json @@ -0,0 +1,9 @@ +{ + "text": "title: Split configuration for each cloud provider into a separate subsection\ndescription: \nstatus: To Do\ntype: Improvement\npriority: Medium\ncomponents: APP", + "metadata": { + "ticket": "ENG-915", + "type": "Improvement", + "status": "To Do", + "source": "https://example.atlassian.net/browse/ENG-915" + } +} diff --git a/dockers/llm.vdb.service/test_data/input/file2.json b/dockers/llm.vdb.service/test_data/input/file2.json new file mode 100644 index 0000000..e2ebf71 --- /dev/null +++ b/dockers/llm.vdb.service/test_data/input/file2.json @@ -0,0 +1,9 @@ +{ + "text": "title: Implement caching mechanism for cloud resource queries\ndescription: Develop a robust caching strategy to reduce API calls\nstatus: In Progress\ntype: Feature\npriority: High\ncomponents: Core Infrastructure", + "metadata": { + "ticket": "ENG-916", + "type": "Feature", + "status": "In Progress", + "source": "https://example.atlassian.net/browse/ENG-916" + } +} \ No newline at end of file diff --git a/dockers/llm.vdb.service/test_data/input/file3.json b/dockers/llm.vdb.service/test_data/input/file3.json new file mode 100644 index 0000000..ceddfca --- /dev/null +++ b/dockers/llm.vdb.service/test_data/input/file3.json @@ -0,0 +1,9 @@ +{ + "text": "title: Add comprehensive error logging for network operations\ndescription: Enhance error tracking and diagnostic capabilities\nstatus: Backlog\ntype: Technical Debt\npriority: Low\ncomponents: Networking", + "metadata": { + "ticket": "ENG-917", + "type": "Technical Debt", + "status": "Backlog", + "source": "https://example.atlassian.net/browse/ENG-917" + } +} \ No newline at end of file diff --git a/dockers/llm.vdb.service/test_data/input/file4.json b/dockers/llm.vdb.service/test_data/input/file4.json new file mode 100644 index 0000000..c6ed1d4 --- /dev/null +++ b/dockers/llm.vdb.service/test_data/input/file4.json @@ -0,0 +1,9 @@ +{ + "text": "title: Optimize database query performance\ndescription: Analyze and improve slow database queries\nstatus: Review\ntype: Performance\npriority: High\ncomponents: Database", + "metadata": { + "ticket": "ENG-918", + "type": "Performance", + "status": "Review", + "source": "https://example.atlassian.net/browse/ENG-918" + } +} \ No newline at end of file diff --git a/dockers/llm.vdb.service/test_data/input/file5.json b/dockers/llm.vdb.service/test_data/input/file5.json new file mode 100644 index 0000000..4bd3ae2 --- /dev/null +++ b/dockers/llm.vdb.service/test_data/input/file5.json @@ -0,0 +1,9 @@ +{ + "text": "title: Implement multi-factor authentication\ndescription: Add additional security layer for user access\nstatus: Planning\ntype: Security\npriority: Critical\ncomponents: Authentication", + "metadata": { + "ticket": "ENG-919", + "type": "Security", + "status": "Planning", + "source": "https://example.atlassian.net/browse/ENG-919" + } +} \ No newline at end of file diff --git a/docs/csv_to_json_steps.md b/docs/csv_to_json_steps.md new file mode 100644 index 0000000..5836973 --- /dev/null +++ b/docs/csv_to_json_steps.md @@ -0,0 +1,40 @@ +# Process CSV to json and pass to RAG app + +## Preparing the data +Go to `GenAI-infra-stack/scripts` create venv and install deps +```shell +uv venv +source .venv/bin/activate +uv pip install -r requirements.txt +``` + +set jira_url in `jira_config.ini` and run + +```shell +uv run process_jira_tickets.py jira_elotl.csv jira_config.ini output_files +``` + +upload these files instead of the wiki docs + +## Vector store creation +Run vector store creation with +```shell +export VECTOR_DB_INPUT_ARG="json-format" +``` + +## Rag app +Run rag service with this extra setting +```shell +export IS_JSON_MODE="True" +``` + +## Chat UI app +Run chat UI with the same export MODEL_NAMESPACE=... as rag service: +```shell +envsubst < simple-chat.yaml | kubectl apply -f - +``` + +and port forward to use it: +```shell +kubectl port-forward svc/simple-chat-service 7860:7860 +``` diff --git a/docs/diagrams/elotl_genai_stack_enduser.excalidraw b/docs/diagrams/elotl_genai_stack_enduser.excalidraw new file mode 100644 index 0000000..c5fd3c4 --- /dev/null +++ b/docs/diagrams/elotl_genai_stack_enduser.excalidraw @@ -0,0 +1,1147 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "image", + "version": 1072, + "versionNonce": 784086144, + "index": "Zz", + "isDeleted": false, + "id": "sozWfO1Cl02Q7oTG5RNWh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 451.8316784237401, + "y": 1667.3962469138542, + "strokeColor": "transparent", + "backgroundColor": "#b2f2bb", + "width": 247.10747663551373, + "height": 172.81372549019588, + "seed": 710464640, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "2Z3sghzybH_XPDwQUq20R", + "type": "arrow" + }, + { + "id": "_AKGA6fa9DRXWg43H0wKz", + "type": "arrow" + } + ], + "updated": 1737489800480, + "link": null, + "locked": false, + "status": "saved", + "fileId": "dc64b0bc089c8f4c09cdb44d37f60a3d2a4c585dd71cd35ef458bbb2bb5bf63645c1db8bb06830860ea84eff4450b570", + "scale": [ + 1, + 1 + ], + "crop": null + }, + { + "id": "rWUWmoYfLLz-IfoYFppk9", + "type": "rectangle", + "x": 497.3854167414968, + "y": 1404.0104431156992, + "width": 149, + "height": 117, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0A", + "roundness": { + "type": 3 + }, + "seed": 1782416256, + "version": 140, + "versionNonce": 2074882944, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "IySF2gOY4M4SGX2FEX7pf" + }, + { + "id": "lh9O6aJ6Au8J3uoJtj-1u", + "type": "arrow" + }, + { + "id": "Z-RaBCcdD6LWm3f2x-rZh", + "type": "arrow" + }, + { + "id": "2Z3sghzybH_XPDwQUq20R", + "type": "arrow" + }, + { + "id": "_AKGA6fa9DRXWg43H0wKz", + "type": "arrow" + }, + { + "id": "kDIWGhb8HsDs-LQfJ2uX8", + "type": "arrow" + }, + { + "id": "fawA_cGHh8QB7OA4lgEzM", + "type": "arrow" + } + ], + "updated": 1737489613214, + "link": null, + "locked": false + }, + { + "id": "IySF2gOY4M4SGX2FEX7pf", + "type": "text", + "x": 523.9847498529418, + "y": 1427.5104431156992, + "width": 95.80133377710978, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0B", + "roundness": null, + "seed": 879772800, + "version": 128, + "versionNonce": 571954304, + "isDeleted": false, + "boundElements": null, + "updated": 1737488271083, + "link": null, + "locked": false, + "text": "RAG\nService", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rWUWmoYfLLz-IfoYFppk9", + "originalText": "RAG Service", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "hgJc4rbHtodgVuvQuEZ0i", + "type": "rectangle", + "x": 878.8854167414968, + "y": 1385.5104431156992, + "width": 163.99999999999991, + "height": 130.99999999999997, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0C", + "roundness": { + "type": 3 + }, + "seed": 2096751744, + "version": 775, + "versionNonce": 1127658368, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "uY7qmYvBRIT3lhwMMSwlL" + }, + { + "id": "fawA_cGHh8QB7OA4lgEzM", + "type": "arrow" + }, + { + "id": "Z-RaBCcdD6LWm3f2x-rZh", + "type": "arrow" + } + ], + "updated": 1737489687323, + "link": null, + "locked": false + }, + { + "id": "uY7qmYvBRIT3lhwMMSwlL", + "type": "text", + "x": 898.5490831805531, + "y": 1398.5104431156992, + "width": 124.6726671218872, + "height": 105, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0D", + "roundness": null, + "seed": 2101957760, + "version": 778, + "versionNonce": 117400448, + "isDeleted": false, + "boundElements": [], + "updated": 1737489601514, + "link": null, + "locked": false, + "text": "Large\nLanguage\nModel", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hgJc4rbHtodgVuvQuEZ0i", + "originalText": "Large Language Model", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "kFYXswz1jL28PfsTtI6MJ", + "type": "text", + "x": 418.3854167414968, + "y": 1630.0104431156992, + "width": 127.99999999999994, + "height": 34.99999999999997, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0J", + "roundness": null, + "seed": 488105088, + "version": 334, + "versionNonce": 919778176, + "isDeleted": false, + "boundElements": [ + { + "id": "_AKGA6fa9DRXWg43H0wKz", + "type": "arrow" + } + ], + "updated": 1737489133006, + "link": null, + "locked": false, + "text": "Question", + "fontSize": 27.99999999999998, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Question", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "qsuSWNRWwqSyglkAiT1-U", + "type": "rectangle", + "x": 110.38541674149678, + "y": 1385.0104431156992, + "width": 142, + "height": 134, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0L", + "roundness": { + "type": 3 + }, + "seed": 608039808, + "version": 551, + "versionNonce": 463561600, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "uRYBPOPRWEMN3GkDBswvi" + }, + { + "id": "kDIWGhb8HsDs-LQfJ2uX8", + "type": "arrow" + }, + { + "id": "lh9O6aJ6Au8J3uoJtj-1u", + "type": "arrow" + } + ], + "updated": 1737489639450, + "link": null, + "locked": false + }, + { + "id": "uRYBPOPRWEMN3GkDBswvi", + "type": "text", + "x": 136.4820831093455, + "y": 1417.0104431156992, + "width": 89.80666726430258, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0M", + "roundness": null, + "seed": 2145821824, + "version": 540, + "versionNonce": 1634276480, + "isDeleted": false, + "boundElements": null, + "updated": 1737489597405, + "link": null, + "locked": false, + "text": "Vector\nStore", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qsuSWNRWwqSyglkAiT1-U", + "originalText": "Vector Store", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "kDIWGhb8HsDs-LQfJ2uX8", + "type": "arrow", + "x": 495.3854167414968, + "y": 1441.0104431156992, + "width": 241, + "height": 0.5497133963660872, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0N", + "roundness": { + "type": 2 + }, + "seed": 240993152, + "version": 1140, + "versionNonce": 61518976, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "33u_O4h2SWkwH0JuLdiMl" + } + ], + "updated": 1737489701587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -241, + 0.5497133963660872 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "rWUWmoYfLLz-IfoYFppk9", + "focus": 0.3694310406081393, + "gap": 2, + "fixedPoint": null + }, + "endBinding": { + "elementId": "qsuSWNRWwqSyglkAiT1-U", + "focus": -0.15311908151398868, + "gap": 2, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "33u_O4h2SWkwH0JuLdiMl", + "type": "text", + "x": 378.93941657873637, + "y": 1430.6098580878147, + "width": 19.892000325520833, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0NV", + "roundness": null, + "seed": 103445632, + "version": 9, + "versionNonce": 2105541760, + "isDeleted": false, + "boundElements": null, + "updated": 1737489645739, + "link": null, + "locked": false, + "text": "2", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kDIWGhb8HsDs-LQfJ2uX8", + "originalText": "2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "-Gu6VFXUlObbPjlQhBQ_F", + "type": "text", + "x": 250.90028159503646, + "y": 1306.232436787928, + "width": 294.50360362625395, + "height": 82.50381277938487, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0O", + "roundness": null, + "seed": 1849844608, + "version": 1596, + "versionNonce": 1248584576, + "isDeleted": false, + "boundElements": [], + "updated": 1737497077087, + "link": null, + "locked": false, + "text": "Get context for question:\nFind data chunks that are\nmost similar to question", + "fontSize": 22.0010167411693, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Get context for question: Find data chunks that are most similar to question", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "lh9O6aJ6Au8J3uoJtj-1u", + "type": "arrow", + "x": 255.38541674149678, + "y": 1483.743563577265, + "width": 241, + "height": 0.7331204615657043, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0Q", + "roundness": { + "type": 2 + }, + "seed": 193094528, + "version": 851, + "versionNonce": 786928768, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "3Gfm1Lvxn8BIWJGQEUDtK" + } + ], + "updated": 1737489703595, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 241, + -0.7331204615657043 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "qsuSWNRWwqSyglkAiT1-U", + "focus": 0.4754557956867315, + "gap": 3, + "fixedPoint": null + }, + "endBinding": { + "elementId": "rWUWmoYfLLz-IfoYFppk9", + "focus": -0.3451641953790466, + "gap": 1, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "3Gfm1Lvxn8BIWJGQEUDtK", + "type": "text", + "x": 369.87708353532, + "y": 1434.3051396781834, + "width": 17.016666412353516, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0QV", + "roundness": null, + "seed": 1575556224, + "version": 3, + "versionNonce": 408642688, + "isDeleted": false, + "boundElements": null, + "updated": 1737489170297, + "link": null, + "locked": false, + "text": "3", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lh9O6aJ6Au8J3uoJtj-1u", + "originalText": "3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "fawA_cGHh8QB7OA4lgEzM", + "type": "arrow", + "x": 646.3854167414968, + "y": 1439.0104431156992, + "width": 229.69662303381665, + "height": 3.5694345858785255, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0S", + "roundness": { + "type": 2 + }, + "seed": 434078592, + "version": 1964, + "versionNonce": 409760896, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "GWq7yBytTyP5gAB-cPlf8" + } + ], + "updated": 1737489705880, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 229.69662303381665, + -3.5694345858785255 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "rWUWmoYfLLz-IfoYFppk9", + "focus": -0.3745079255908868, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "hgJc4rbHtodgVuvQuEZ0i", + "focus": 0.25290074048226757, + "gap": 2.8033769661833503, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "GWq7yBytTyP5gAB-cPlf8", + "type": "text", + "x": 761.0420616553167, + "y": 1403.22572582276, + "width": 16.383333206176758, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0SV", + "roundness": null, + "seed": 1043313792, + "version": 4, + "versionNonce": 301814656, + "isDeleted": false, + "boundElements": null, + "updated": 1737489296382, + "link": null, + "locked": false, + "text": "4", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fawA_cGHh8QB7OA4lgEzM", + "originalText": "4", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "36kJd_KG7LuD9OdQsrh2Q", + "type": "text", + "x": 301.08541674149683, + "y": 1504.4620560189253, + "width": 184.60000000000002, + "height": 59.54838709677426, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0T", + "roundness": null, + "seed": 429413248, + "version": 1001, + "versionNonce": 1376387968, + "isDeleted": false, + "boundElements": [], + "updated": 1737489183639, + "link": null, + "locked": false, + "text": "Context (e.g.\nTop-2 chunks)", + "fontSize": 23.819354838709707, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Context (e.g. Top-2 chunks)", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "luF99D7lkL6zH62Ido8hm", + "type": "text", + "x": 611.1729252335374, + "y": 1302.6221733895152, + "width": 368.20338023121906, + "height": 81.89298135475389, + "angle": 0.0014961378798030722, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0U", + "roundness": null, + "seed": 1190105984, + "version": 2172, + "versionNonce": 161358720, + "isDeleted": false, + "boundElements": [ + { + "id": "Z-RaBCcdD6LWm3f2x-rZh", + "type": "arrow" + } + ], + "updated": 1737497096780, + "link": null, + "locked": false, + "text": "Send question + context + prompt\nasking LLM to answer question\nusing only provided context", + "fontSize": 21.838128361267703, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Send question + context + prompt asking LLM to answer question using only provided context", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "Z-RaBCcdD6LWm3f2x-rZh", + "type": "arrow", + "x": 876.4441901425796, + "y": 1476.0816086961695, + "width": 227.05877340108282, + "height": 3.9288344195297213, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0V", + "roundness": { + "type": 2 + }, + "seed": 469132416, + "version": 1728, + "versionNonce": 275784576, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "RY0tqrJlJpa4rtgGz0Utg" + } + ], + "updated": 1737489697459, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -227.05877340108282, + 3.9288344195297213 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "hgJc4rbHtodgVuvQuEZ0i", + "focus": -0.35281631769609584, + "gap": 2.441226598917183, + "fixedPoint": null + }, + "endBinding": { + "elementId": "rWUWmoYfLLz-IfoYFppk9", + "focus": 0.31512432076058794, + "gap": 3, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "RY0tqrJlJpa4rtgGz0Utg", + "type": "text", + "x": 757.7984876339809, + "y": 1459.2132524549334, + "width": 17.512000528971353, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0VV", + "roundness": null, + "seed": 553538688, + "version": 4, + "versionNonce": 366191744, + "isDeleted": false, + "boundElements": null, + "updated": 1737489296382, + "link": null, + "locked": false, + "text": "5", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Z-RaBCcdD6LWm3f2x-rZh", + "originalText": "5", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "135Dh-jayw6rm54gXb9SZ", + "type": "text", + "x": 709.6854167414972, + "y": 1499.2362495673121, + "width": 196.9999999999993, + "height": 31.774193548386997, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0W", + "roundness": null, + "seed": 1074308224, + "version": 1077, + "versionNonce": 1783280512, + "isDeleted": false, + "boundElements": [ + { + "id": "Z-RaBCcdD6LWm3f2x-rZh", + "type": "arrow" + } + ], + "updated": 1737489669116, + "link": null, + "locked": false, + "text": "Answer", + "fontSize": 25.419354838709634, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Answer", + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "2Z3sghzybH_XPDwQUq20R", + "type": "arrow", + "x": 550.3854167414968, + "y": 1692.0104431156992, + "width": 0, + "height": 169, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0X", + "roundness": { + "type": 2 + }, + "seed": 825877632, + "version": 146, + "versionNonce": 61105280, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "uXO5Eb3XSjTL-lvlcKzL-" + } + ], + "updated": 1737489798719, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + -169 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "sozWfO1Cl02Q7oTG5RNWh", + "focus": -0.20234110550103265, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "rWUWmoYfLLz-IfoYFppk9", + "focus": 0.28859060402684567, + "gap": 2, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "uXO5Eb3XSjTL-lvlcKzL-", + "type": "text", + "x": 544.4104168368642, + "y": 1581.0104431156992, + "width": 11.949999809265137, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0XV", + "roundness": null, + "seed": 1022612608, + "version": 3, + "versionNonce": 173941888, + "isDeleted": false, + "boundElements": null, + "updated": 1737489165363, + "link": null, + "locked": false, + "text": "1", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "2Z3sghzybH_XPDwQUq20R", + "originalText": "1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "_AKGA6fa9DRXWg43H0wKz", + "type": "arrow", + "x": 592.3854167414968, + "y": 1530.0104431156992, + "width": 2.112676056338046, + "height": 164, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0Y", + "roundness": { + "type": 2 + }, + "seed": 100889728, + "version": 193, + "versionNonce": 679642240, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "_k7OATMqHa85H_RrphtYJ" + } + ], + "updated": 1737489800886, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 2.112676056338046, + 164 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "rWUWmoYfLLz-IfoYFppk9", + "focus": -0.2608573094756878, + "gap": 9, + "fixedPoint": null + }, + "endBinding": { + "elementId": "sozWfO1Cl02Q7oTG5RNWh", + "focus": 0.15948855694187153, + "gap": 1, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "_k7OATMqHa85H_RrphtYJ", + "type": "text", + "x": 584.4834217542239, + "y": 1584.5104431156992, + "width": 17.91666603088379, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0YV", + "roundness": null, + "seed": 5567616, + "version": 3, + "versionNonce": 802374784, + "isDeleted": false, + "boundElements": null, + "updated": 1737489177553, + "link": null, + "locked": false, + "text": "6", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_AKGA6fa9DRXWg43H0wKz", + "originalText": "6", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Y56YdbSZHdRKK9XWeLXO7", + "type": "text", + "x": 609.3854167414968, + "y": 1628.5104431156992, + "width": 127.99999999999994, + "height": 34.99999999999997, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0Z", + "roundness": null, + "seed": 1239827328, + "version": 379, + "versionNonce": 1457402752, + "isDeleted": false, + "boundElements": [], + "updated": 1737489130080, + "link": null, + "locked": false, + "text": "Answer", + "fontSize": 27.99999999999998, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Answer", + "autoResize": false, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": { + "dc64b0bc089c8f4c09cdb44d37f60a3d2a4c585dd71cd35ef458bbb2bb5bf63645c1db8bb06830860ea84eff4450b570": { + "mimeType": "image/png", + "id": "dc64b0bc089c8f4c09cdb44d37f60a3d2a4c585dd71cd35ef458bbb2bb5bf63645c1db8bb06830860ea84eff4450b570", + "dataURL": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmQAAAGsCAYAAABzQrv4AAAgAElEQVR4Xux9B5yU5dX9nT7bK703EaSooKKxa2LssQsaS0xMLNHExPj/Ej/zRRONpthSvhSiUaNiSxT9bFgQREGKIiBIL7vswvbd6e1/zjPzLi8r7ILLsoX7+Bt3mZ15553zluc89557riOFIToUAUVAEVAEFAFFQBFQBDoNAYcSsk7DXj9YEVAEFAFFQBFQBBQBg4ASMj0RFAFFQBFQBBQBRUAR6GQElJB18gHQj1cEFAFFQBFQBBQBRUAJmZ4DioAioAgoAoqAIqAIdDICSsg6+QDoxysCioAioAgoAoqAIqCETM8BRUARUAQUAUVAEVAEOhkBJWSdfAD04xUBRUARUAQUAUVAEVBCpueAIqAIKAKKgCKgCCgCnYyAErJOPgD68YqAIqAIKAKKgCKgCCgh03NAEVAEFAFFQBFQBBSBTkZACVknHwD9eEVAEVAEFAFFQBFQBJSQ6TmgCCgCioAioAgoAopAJyOghKyTD4B+vCKgCCgCioAioAgoAkrI9BxQBBQBRUARUAQUAUWgkxFQQtbJB0A/XhFQBBQBRUARUAQUASVkeg4oAoqAIqAIKAKKgCLQyQgoIevkA6AfrwgoAoqAIqAIKAKKgBIyPQcUAUVAEVAEFAFFQBHoZASUkHXyAdCPVwQUAUVAEVAEFAFFQAmZngOKgCKgCCgCioAioAh0MgJKyDr5AOjHKwKKgCKgCCgCioAioIRMzwFFQBFQBBQBRUARUAQ6GQElZJ18APTjFQFFQBFQBBQBRUARUEK2D8+BVColDoejeYuRSER8Pt8+/ATdlCKgCCgCioAi0DEIJJNJ4cPpdJpHyznN/qnhcFj8fn/H7MgBulUlZO088PF4vPnk5aZ4AvM5j8fTzi3r2xUBRUARUAQUgY5HYHfEy5rfuAckahwul2unwEPH792B8wlKyDrgWDMyRkLGFYYORUARUAQUAUWgqyJAMsZhz+5YBKzlHGZF0JSUdczRVEK2j3C1Vg/WCcyTnM/xxNWhCCgCioAioAh0NQTskTH+zgfnMIt4ud3uL6Qt+Te+Tue2fX80lZC1E1Pm0akT4+oiFouZk9QiZdFoVLxebzs/Qd+uCCgCioAioAjsewQSiUQzsSLRsv5tj4xxXiMBY9anZRRt3+/Rgb1FJWTtPP72E7plmLc1QWQ7P1bfrggoAoqAIqAItAsBki1L72yPkO1qo5zrSMhUitMuyFt9sxKyfYAtT2pGw7KysporUzRduQ+A1U0oAoqAIqAIdBgCVkVly2ACn29sbDRkLTs7u/nzKfJnGpOjZTCiw3byANqwErJ9cLA/++wzqa6uluHDh0v//v3NFknSeOJqiHcfAKybUAQUAUVAEegwBOyaMX4IyVhlZaXU1taaea2kpOQLDgL26FqH7dgBtmElZO084IlYXGa99Zb86Y9/lIsvvlimXTbNbNHhcEqE+rIs9WlpJ8T6dkVAEVAEFIGOQAAFlrFYFJGwtNY5HmcgwSOhYFC+853vSHFpidx9992Sm5vb7E+m2uiOOBDpbSohay+2OKHXrFkjf/vrX4UC/5tuvklGjBgpSeTbnayw3OET295P0vcrAoqAIqAIKAL7DIEUUpMMHkRp1eT1SCqJKkvMWy88/7w899xzcsZZZ8rll19uPs9Kb1qEzJ7u3Gc7dIBvSAlZe08AEDKemC/PnCkPP/SQXP2tbyFKdpkEmpokB6sKJWTtBVjfrwgoAoqAItARCFiEjPMVJTY+OO9v2rRR7rj9v6UYacobb/q+SVnaCZmlI9OU5b4/IkrI2otp2lNPysrK5K477zQ2F7feeqv069/PhH6VkLUXYH2/IqAIKAKKQIcgkJm/UqmkxKIx8cLCafrf/y4zEWCg/Ob8Cy4wRM0S8NsrMe0C/w7ZtwNwo0rI2nvQcUInUHniwkn70osvyvTp0+W888+Tq666Or1lTVm2F2F9vyKgCCgCikBHIJAhZNamN2/aJLfddpsMGjRIbrzxRhk0ZHBzyyTL7sJKVWqV5b4/IErI2ospTuhwOIQmq1lSWVEhdyJKxhP1Zz/7mQwYMECcbnXqby/E+n5FQBFQBBSBDkAgQ8hisG3yILvzv3/+s7z99tty0cUXyUUXXWwCCi2Jl/VvjZDt++OhhKy9mOKEZriXwkiOV15+WWbMmCHHHnusXHLJJVJQVNjeT9D3KwKKgCKgCCgC+x4Bi5Ch0nLlypXy4AMPGs3YpZdeKsNHjBA0UzKfadk3WX0v1Wdz3x8KgzMAbhG07JgP6qlbjUXSKwsrbdlQXy933HGHrF+/Xh56+GEZMnRIT/3q+r0UAUVAEVAEujMCqKoMR8Imw/OX//1fmT17tlx44YVGO8ZBQmaRMbu3plZYdsxBV0LWTlztJ2YoFDJu/XPmzJH77rtPjjnmGCPwt5yNGxoaJD8/v/kTtbVSO8HXtysCioAioAjsFgH7HGMRKj7HecvMS5lwzIL5843+ma78v/71r021Ja0wvH6forsfEVBCtg/AtnxZrDLgmpoa+TsqVT7++GO57rrr5LjjjvtCHl7N9fYB8LoJRUARUAQUgd0iYA8Y2MlZ8/MgZI2NDfLII4/KW7NmyX/913/J4YcfbqotzdCitP16dikhayfcPLEpcmTPL/sJv2TJErnnnntk1KhRcvPNN0tpaelOTVnVw6WdwOvbFQFFQBFQBFpFwFIktWzhZydk7yFNSQ/Ng8eMkbt++cudt6eEbL+eYUrI2gl3S2IVQZjXh9UFXfsff/xxeQttlaZNmybnnHOOaUBupS9ZQqykrJ3g69sVAUVAEVAEWkXAHiVj8IDkjPMPyVpNVbXcf//9ptvMXXfdJSNHjWwuUNPWf/v/xFJC1k7MrdJf6ydPfpIyasm2bNkiv/vd7ww5YyiYNhgutlPKDBVGthN8fbsioAgoAorAHhMyu1SG89QrM1+WRx991AQMvo3elZaFEzeoGrL9f2IpIWsn5laasrGxUfLy8szWrCgZf3/66aflySeflLPPPluuueaa5rRlS4F/O3dD364IKAKKgCKgCOyEgN1Z35AsZGnYTYZj1apV8vvf/s7IbX70ox/JsEyLJDYYdzpd6blKU5b79YxSQrYP4Gaki+lHpirtg89v3LjRELJ169bJDTfcIBMmTDBpSxX17wPgdROKgCKgCCgCrSJgb3vE3zn/VFZWyvNoID5v7vumefjXTz8drZPSFk4kZMzkGG9NJWT79exSQrYP4LaTqyY0ac1lU3GMYDBoyohpg/GHP/zBkLEf/OAH6SauIG/W3/fBLugmFAFFQBFQBBSBLyBgETL+ZJCAEbFFixbJr371Kxk39hC55ZZbpLCoKD1nBQKSnZNjfjfpS0hvdOw/BJSQdTDWcUTO2GT8Xni7fPjhh/KDH/5AjjrqKPH7/LQ/lngCfTDNamTHUoQXDTVpVmi5g3dRN68IKAKKgCLQExGArUUMLvweTzpNmQQpo6b5F7/4hayFkP/Xv7lP+vTp0yy34bzDVKXVt7KzIQlHq8XvzZVkCvvk8GR2J4GfcYklQuJx9axOOErIOvqMyxjvrVz5mTz04EMmMnbTTTfJ0GFDEQ12SAo8zN6WgmTMqoLp6F3T7SsCioAioAj0YAQw/1hdZELI2GQhYzMdHpkLFy6UI488Us6/8AJDxlo2DjfkDXNRZxOzZDJh9GzYFZDJdNcAF/xs0wNPSrplYU8ZSsg6+kjiguCqxIko2AwI/P/8pz/LFVdeYfpc5iC1aW9NwQuA6U8/XJK7ygXR0fDo9hUBRUARUAQ6CAG0RuJ/sWjMmL0uWbzYdJEZOHCgEfL37d+v+YOtYIC9VRLTm5050CbajEyr6B27YjV87GEaNyVkHXy2pRjxojISzJ4O/g89+KAsXbpUfvKTn8iUo482oknLMoO7YuX7+bv6lHXwwdHNKwKKgCLQkxEAcTF+Yiw4wxz0s5/+VLZu3Wq8MU/96lebRfuWwTnlM1ZUzD4XdRZEJGTxBHVvCGykYmY3mLqEEkg87p5XdKCErIPPtCROJicuBCPgh1jysxUr5Oc//7kMHTpUrr/+ehk6fJgJDZsTjWXGOhQBRUARUAQUgX2BAAgZqyapY34CRuWvv/66TJkyRb77ve+KG7m/uEkJpjVjLSNkXaPXMjvhxMkljd6ag/udhKmt28XoXc+aM5WQ7YuTvrVtMGVpz8XjRHriiSeMi/+ll14ql06bakxkOazX2dsxdfTu6fYVAUVAEVAEeigCSFmSzSxftkz+B4GAQYMHy4033ijDR4xAY3E0GUc6s2UgYHftljoDoWQqYiJiyQRJY3oPoACCjoxELYKCuJ5VBaqErKPPMivXjc+xfF62lpfL9OnTjZP/eRecL8cff7whZax+sfRj9t87ehd1+4qAIqAIKAI9D4FYJGokMbS4qK6ultPhN3bOuec265p35TPWNSJj6WMRT4TF7fTLmlWwk8qG5prN0AONMurgPFRZBsXrye5RB00JWQcfTqMhgyIxFEKFS9aOk4crlv/+7/82KUuKK9lWye5Lph5lHXxgdPOKgCKgCPRwBCiZeQNpyj//+c9m4X/TzTchMgb9FQxgTYDA5zWZGSs70xVlM431Iv969H2JhtPzp8cXkGlXHCt5cLzoaSofJWQdfEHaTWO5UrE8xyjwnzt3rsx46mk59thj5dKpl0pRUTFWAKi0xKrGx0pLLgecPayMpIPx1s0rAoqAInCgIGAv/LJSjdZPkisWkLFpeBGMX2+//XYZjJRlV4qAtXWcmGCqbWgCIatAtAyWFy6nqRi9cGqxDO5farRlPWkoIdsPR9MS7fOj7CuQ7du3y0MPPCgVFRXytdO+JhdddPFOzV2tFcx+2EX9CEVAEVAEFIFuhkBLIb69Yp/zysMPPyzss3zqqaeadCVtLLpT2z4lZN3shOwOu8uLhIMtk/g7VyiWv8vST5bKgw88YMz5vnfd9+Tgg8c0G/mZN/WwFUB3OF66j4qAIqAIdAcE7NEuRsu44GcWpr6+Xl566SWjVb766quN7yX1yZEIhfAuMxd1BVuLtjBWQtYWQvr3vUaAFwovABru8XeuUOgLw+eYz+eF8/LMmZKfny933HGH5BcU7PgMJWR7jbe+QRFQBBSBAwEBS/tFckWyxd7JHG+99Zb8HY78I1BN+c1vflNGjx5tnudrjCcZRneIlCkhOxDO4v34He0NXfmx9hWNyfXjwZz4H//4R/nk44/lOAgvr/n2t83z9F1xd7JT8n6ESj9KEVAEFAFFYC8QsAiZXUs2f/58mTFjhqmqvPXWWw0ZY0amZZ/KrtAaqa2vqoSsLYT073uNgEXCWvqL8SJKoQqGLS3WrV0r//znP+Xzzz83qcsTTjgx/TkaIdtrvPUNioAioAgciAiQhD0ACczq1atNmvJcWFy07FPZUjbTlXFSQtaVj0433zcSMl4MXK0wfRkKhSTLD2M7Rsrw7zfeeF3emvWWBAIBufH7Nxo9mRKybn7QdfcVAUVAEehABKxIF3VjL7/8ssyaNUsmT55s2iOxurKl8J+pSurI+OjqQwlZVz9C3XD/eAFYJOwLu59xUraef+aZGfLM0zNk2PDhcuedd0pWTs8yvuuGh093WRFQBBSBLo8AbZQofenXr59cd911MmrUKLPPdvG+XTKjKcuud0jV9mIfHhMSLwr1d9WKwt6OgtGxlsMSW/Ii+dOf/iSvvfaanHnmmfK9737XCDH9jKRlhr3FkhO+LDoUAUVAEVAEeh4CdhsLfruWJMoyHl+4cKH8GfMG55kf3vJDGT9+QtpCKdOWr7OQSSQaEYnzoTOAA/Mie0/yO6Aa1AWNdDyM373i9iZk9SqvlPRh5kikZltcRoyKSDLuQYskr/qQddbB646fS0d9Vq3sTfiXF43ljszfvXBN5rBWL6tWrZJXXnlFFi1aJJfAm4ytLjiiIGYkc3RZTtBkllWaqjHrjqeN7rMioAgoAnuFABf8fOTk5Jh5wJA1NNretGmjPP7Y47IWOuQLLrhATjrpJMnKzjIdYrrK/IA4A2e45u/rzBieh0IxaQjWyovPlktMwubvXkeWfOPiAZKfVSRev0sJ2V6dJfrinRCwdGIkaAwVM2LG56wIGZ+3R9D4Go6WhG758uXy+9//XpKxuNx40/dl0qTJxsWfrTDMa60omxIyPQMVAUVAEeixCNjtKZgtofyFcwgJWVNDo6monPXmm3LyKafIVVddBTKWjb/RagkRqU6eH5KY3iiRBm/EMKwMA277MZIzBBfw/LYaOPFP3yJN4aghmnlZXrn8msFSUpglKey/OvX32FO7Y75YQ0ODuUAsDxh7VQtPsJYpSpIwVljygsrNzd3JD8YqX+ZF+OGHH8oTWPkwgkZNwCHjxpkvwLZLxcXF6fC1piw75qDqVhUBRUAR6AIINDU1mbmF84rV9YW/h8Nhef7Z54yQf/z48fJt2CX17tOneY8NKetk2yTSrliMgQmRUDhgAhN+fzYyPIiEeT2SADGrqQvKC0/WS0LSmSKXA62RLi+U4gJE+RB7UELWBU7C7rwLVtSLJ58xf8Ug+eLzJGdWitL6jnahJV/Dh/WaZ595Rma+NNNckA88+ICkUATAFZBpq8RUZyevgLrzcdJ9VwQUAUWgqyPA+cFYJGU6vJCMMQiwZMkS+dtf/ir9+/c3bvxjxo41hC0aTWuOuwIhg6+AiYSBZrWAmZmhFGyfElJZ3SjPPFqPV6Y1Zh4QskuuKpLepbmYL1VD1tXPzy61f3ZDPtpV8N98kHgx+sXBC8gu9rf8yKxy5JYEzdKX8T0MSdNxmfqALAg07/vNbySClRGbjzOF6ehp7e671NHVnVEEFAFFoPMQsM8vxiYpI9J/99135ZFHHpHc7By54oor5KgpU5p30khk8OgKGuNQvAHifJ/U1yUky5dtlDbhSEiKSrzidCP9Kj7ZVl0nTz+6XRKOdPACTZ1k6tW9pLSoAJRNNWSdd/Z1w09m5IsXwPvvvy/vvPOOIWMMMbOxK6NaBWiDNGDAABkDT7GDRh9kfmffSmu0dE+2Q8CQtN/nlyY0h/3tb39rfMsGDRok1157rQlXmxZLGiHrhmeN7rIioAgoAm0jwEU+Rfz2wYrK//znP8ZE/PrrrpevfOUY6Mq8zQt1vrarZFBiIFfbK2Py8n+WSDiQZYITOfkROef8yVJYiAhZzCmVVdXy7BMV4vSm2wsmo3G56Jt9pW9psaYs2z5F9BUtEWClJV3234SwctKkSTJ06FATUmYz161bt8r27dulvLzcaMVIqI488kiZghXNsGHDjECfFx1XPjxZ7QJO4x/jTId6169b10zKzjnnHPnGeeeld0MJmZ6QioAioAj0aATM4hzzSVlZmfEaYyU+KyqnTZ2243tb7fYo5sdIYv5wujvX/DUQSUp9jVNmPPGxOFN9083PvVUy9Yqxkl+IRCYylxVVIXn6sY0MjZkImiPhlEuvGCK9i32SUg1Zjz6vv/DlLJGk4Ta7EOCzUjfF/D2YvtNLzy/k8xP4dxIVInTc5xtTbvn3U/+W2e/OkxtuvF5GjhmBEwkVJc4U6knS1ZaVlZXyySefGKH+li1bzKqnDwSYl0+7TIaAwJlVTSztY8Zy5Wa/GXyAtUpav3693HPPPaZH2fXXXy/Ho+8l98FO4uwRNxJFq9DgwDqq+m0VAUVAEeg+CJBwUbpiFYRZ924T6UL0i4P2FtP/Pl1WoAL/jLPOQkXllftBstKI+Y2kzi/xCDgT50DMSVF4nHmz0sQvFIb1k98joRhIo8ePZ/B6EC2XEw4DKadsKtsmzz5Vg9f4MBcmTWXlZVf1k8I8pDAxv1Zsr5Mnn9gmSQQfSMicyDhN+2Yf6VOSr1WW3ecU3jd72hYhCzfViz8XIWPU37LkOAEilo1oFqtHkK2UYCCKtKRX1n++UX53771y4sknyIWXXYITERYVeJUbJ5ndzI8pTa5uZs+eLQsWLBC/1ycnn3yynIULLCejOUtfhB5sgy2V0ho0kjqSraVLlxpNGX+/5pprTKTNuoi57TSh07DZvjk7dCuKgCKgCHQcAva5gQtrDssaydzHMQVwoV5bUyv/+te/ZNmyZXLoYYfK5ZdfLjnQj3lBcjp0JKPQgKGaH0QrEYUuzZMhZPAPM4TMQccAPu9pdhnD1Id5j3uFOTCphGxvjs8B79RvETI7idmJ0CSbwIi4EsADbJ8Gd01YNJRtDcq2ymoJNgZl4sQR0qvULb/65U+lMVAF4f39eEuWWR3QlcKqkLGL9+vq6qSqqkoemf4PY2NxyCGHyHlIQw4YONAYt5ioFwxnI6iYofGsfRvvvfeePProo4akff/79CibZEgY06T5+fnNx787tMbYm5NVX6sIKAKKQE9CwDIJt3wo7RX3RtAPA68QolEPP/SwfIQF/KGHHWYskKhD3i9V9iCECRAyF6a/ZssNiO/hqGGsLBDOgmA/LmXlIQmGXWauSsbrZeKEUpA1JWR7e64e8ITMMmxtCdwOUhYwZCyR8Eo0JFK5TWT1mkpouiqkuiaI4KxTho8skLPPPFjmzH1F/jH99/L/fvoLmXDoMeBV6RSntS1GtayUokXOqrdXyVtvvSUzZ86Ugw46yJQvD4TOzFRQInWZgLOedbHaK25YZfPggw+aIgGaAbKZrFmTpC2RzU/LcmNvTwp9vSKgCCgCisD+QcDSG7fs2MJ7+LaKSjM/MKNCnfHdd9/dbIm03wgZWh250OqIecg4DMRg3IS5Cea0mJ/iSEFu3hhAQdtaaaj3IYjgxqNerrxisnh8CcxDLk1Z7sVpdMATMgur3RGzuDkZvVJXK7Lgw3WychUYWTIHJcVZqHSkvYUbpcdhufCi8SBAtXLLLVfIV445Ua659gcQVdJRH+HeXVhTkFwxwsUqSrZEYtTrpZdekt69e5tUZD94y4RCQeM5Zo98Wa2aqDmgHu2BBx6QCRMmyMUXX4xI3UTzdRpRlUmN2q4+dy/ODX2pIqAIKAKKQAcjYG/+zfs75Sp88PcZT8+QN994QwYic/KLX/zC3NNpedQcrepwY/C4xBAO86RzkFzqQ0OdQOTOa8hZIuGXtWsa5Y3XN6CyswgZIQeiadvkuhsmYe4LI3XpV0K2F+ePEjIbWHZSZv1OWf6Wshi0W5tl7efV0hTgBZErPubUaWwHwaPfG5Bjjh4gY8fkyX33/hS2Fw1y5y/vg4u+H2TOYQz9LM8xXlA7p0SxDRNFc8rrr78O0ebf5Wtf+5p8C6SMqUtWydjTqs37hb/xQqZL87PPPmuc+2mHYZEyK0qmpGwvrgZ9qSKgCCgCnYSAvZE4bZMYFfv38y9I37595YYbbpBRyKBwBGCrRL3x/ujUAvEMUqaIdMXdWOSzgAxxMujKsrIg30HvSWcqV5Ytb5R339kM1/0SyHRi4suule99dzzII2ZIjZDt1dmkhKwFXCQ8dmK2pdwpH36wRlau3CoFeX2g0SqR+qaANAUb0ZaC1ScI0bpCMrCfS7566lhZuuQ9eerpfyJCdo0cceRRuGjY5d5aXaRTibzwOIxZLIgYfcZyoQlgROyZGc/IRx99JF877TShvQWLAyw7DK6YCgtRK5wZjLLxbxR7vvrqqzJixAijQ6OmjEOrLPfqWtAXKwKKgCKwXxGwFuqcExgV46C++JVXXjGPIYMGGxnLsOHDTLWl1aOSWRVqjDva9ogCmLItjbK1DNkaX7GpqMwrSMnoMZyHwM5SXlm8eLt8MG8r5hv20IxKYXFIvvvdSYaQiRKyvTqflJC1Qcj++egqiBVTEglhRYCTD8osXARIY3pSuCBQeYlzMhapk9ysqFx28dHwihG58bqrZcKkMfKDH96MrUPkCBJmtU2iHsweIUugeTgdlS3NWCOE+Xfeeaextrj//vslNz9tImtvPm4XfvJvJGbPP/+88UIbh36X37z8m+h7ecgXGpbv1ZmhL1YEFAFFQBHoUATsAQAurknGXnzxRXn77bcNQbv9pz9DodeAZusL7kyzvhgkzkUPiQ4ckIjJooXlsvCDMoj7c43TwNCR2XLaGQdJVg6CBfjvA/xtxYp6zJM+icL6oqQoJlOnHYp5DTZO0Jup7cWeH6ADnpDFgiGID7MQbgXP8qLXpDMM0uWThka3vP3GFni/UNS/++H3RKDx8iKEmy+HH1kqR07xyoMP3S6b11TIfb/6i/gL2bMLawkQL9YFp4WbDkPQDMnCCsLk5D0oL0Y0LBxKyGefrYUJ7K/l0mnny7lnX7JHR5M6M2tVxUrLSy+91PiUWbXICVy8bETOXphOfq51YWvrpT3CV1+kCCgCisCXRYD3e5Iv+4Kc/46zkjLjM7Zl82ajI34HZIzpSTYLHz5yxJf9yH3zPsxZCxdul7mzN0s8WYDKTjjtZ0G0f82h0JVlImSffC7zZ7skAr01bTD8yBxd+Z3h4oPcxonu4JvKq+DEvxXOA2lPTj+ev/zbg+FDhtaCxoesRp7+ZzlaJ6W9QFk/cOlVA+BDhigcnqupb5B/Td8sUcxdzColMZdeeFk/GdSvpMMjhPsGxD3fygFPyIyPBdKGhrjQokKC+D1bFn60TZYtrTUWF60Nny8qDbVOyc4pkD79Rc46dyDKk1+T6X+eLtddc6uMOwKOxPnpnpbWoL0Yh7ELw8cbLYAbJzPPPkTh1sDT7J5f/0qGDusj/33HXW0eTa5aWG5MoT91aE8//bT069fPODlPMWlTmPEZIpgeQXQGMIaxxha5zc3rCxQBRUARUAS+JAJ2r0t7n2IjZcFcQKlKHCTjmWeeMd1ehgwZYqwt2DS8w33G2vpO2L9Fi6rkg7kgTKlCkEf4cEIjdvmVE8TtAiFLeuWT5etk3tv4HkhXplJxyfF75ZrrR4kHAn9+v83lNfL8k5Xi9qNLJQiZF9972rcGSUFOlvn0yqp6eebxrfCfdaTN2RGWu/iK/nDqT1s41TYG5KlHtmwLvacAACAASURBVEgMEycJWSISl/On9oFMqKjHzV9KyNBjHjpEcTBtSAfieELqGx3ynxcWoeWDFycAnYd3P9yuMIiQDxqwQmkIbJKLLp4ovUpicuvNN8tBIybILf+Pacv0iOBE8qBfF89TRszoNZYLYhQJB2TjRurU1srKFZuQs6+QTZtXy+QjDpGf/fzOti4ZE0YmIeNgpIx6MlZt0t/sBvQ6O+KII0xlDkcNUqHFJVhZYHSF1hptfjl9gSKgCCgCPQgBRsasiJkHbY62oYsLfSXZm5LFWSRjgwcP7hLNwZNI7CxcVCkfvL8FGZwSEDJBNoiE7DDMjbR9ypYlS9fL+yBkkWTEpFNzYH1x9XUjEQmDbRPev3lrlbzwVJV44OYfRyrKi6jZ1G8NRIQMButIIFXW1Mmzj1dCVJ2ODiDIBkLW1zj1g79JHYoYnn60nDM1SCC2gXn0vEtLZXA/9Lrs2Iztfj/rlJBJBIQqAu1Xjuksz8zi4iXbZO576yBQzBMfCFRrI4WImkNyYdRXKFvK18jkI3vJGaeNlOl//Ycs/3ip/L+f/xRWFqXNwnwPcv4JnKVspVS5rULeevU1aWyqkTWrV0o86kVka7QcOvFQOXTSaBk8pFRK+gzZo5PCbhxLgSi9a5588knJy8k1VZssEGBzctpoWI1nI4io+bJaJ5x79OH6IkVAEVAEFIEvIGCvnGR0zEpbWi/cuGGjvIhG4R988IFppUefsexMM/H9UUXZ1iGDDSaKzMpByLaiunJHhOybV00CIWsCecqVxUvXyII5IErGdyCOIjenXPHtIeJFapI+nZu21shz/0KEDESNvpo+/H3a1QOlKD8bQQH2skxHyDL2/vCaTcnF3+wvfXvlNROypx4pQ4QML4FRbjwSkwum9VZC1tbB655/h89KAn4p8FWJwfwVpsgIHX8Cb7BcCeB3tyNdEbnb4UyHbcUJQWMc5cg5Ibni8iNkw7qV8ttf/0Ju/OF1SD2iiTio/Jq1ayB+XCmrV38ua9euNRGswf36y7jxh0CMP1HGjztS+vbpbVKndECOx2tRyZmOZu1u2I1gTf49ow9jF4DN0CT8/re/k9LSUhkzZozRJFj6sXqIRwtYsakpy+552upeKwKKQJdHwF7pzkUz79FMuzGrsW3bNnnogQelvLzcmILfcsstuN+7EYECUaGMhSnNzr4/Gw1Zpbw/B7YWqSLThzInt05IyDxwF5BklomQzX/PBY0XU01QYCMyduW1wwwxYzZoUxkJ2TY0FSchi4OQOeSya4ZIYW46GFBZ1Wg0ZEn0fiY+TjgTXHoVU5ZpqU9NQ0ie/MdGbJ+EDC0FYwloyHojZdnz5i+NkJlDjlAr/x/zyYb1CXnm2fmIVI2Q+vomMPzWCRn1mE3BuIRxtpSUFEgsXIOI1CEyoF9Kfn7HTfBkYc49R6qxCti0CU3FswtBvA6Tww6bBJuKkbgQ0xEwhobLyiKyfVsDPrdKDpnQV3r34QmbzrPvbtgbi5vtZJz6rUbpH81fYDRlvOhpIEs/m/yCArM5U0KdKbXu8nc23UFFQBFQBLohAoySMUVpyUr4b1ZRUu+7etXncvrpp5tuK3Tip66XlfZ5Vgu8TiZk7Ev5Eaos51mEzGjI6uWKqyabCJkT2aEliJDNfStlmosz35jj8xgNmR+TIyNgm8q3yfMkZIyQITtkImTXDJZiiPoRDIOov1qeeYwasrQ9FCNkl1zRDxGyYhMhq21sgIZsczpCZjRkCTl/Wi8Z3L+XkX/3pKGEjM1bk2Fj4BoFIXv11bW4SIIIGxejjBeuyWD8rQ2n1wFvlijIjdN4hIVD9TJyWJacespQ6Liel7/8dbqMOXgcTGMPxWMiCNghqFKhRxh0ZBGRNRvrQMS2ou1EWLZXBqW0pI84XEEZM74Y7ZcGCvu3tjbsIXH+bo+SGbKGi2LZp58ajcLWrVuNLQYvfobHo2ha6+dNQIcioAgoAopAhyDAaBgXyiRcARRUzZs3T2bNmiWrVq2Siy64UC666KK0pxgGq+Fpg2S6tCBS1tkRMhKqxUvKZcG8CjClUnEjAOHzVsvUyw9DBK8eGaQC+WTZKvkAVZYxCPqxzAfhcstV32WEDFWVMJTdUrlVXny6Wrw5PkPI3PhS1IgV5OQhheuQ7cgUUfTvQPiN81cKOu4LpvWRXkXFwA1Fc4EmeeaJSiRE0cQQ81k0EJFzLymRAX37YB865JB12kYPeELGisemYD1SjX6JxH3yv3+aAz3YaKmqacDBhgi/jSuCYVqGmT2+bJx88CULoYLRW4deXlMkK7cB+W6/sbqIYPFQWxOW6u1B2bh+GyJWNaisgQVFFohfICzFRaWIokUkNzdP6hoQzTq8GOawQ1Cp0va5QSJGbYJlLEgixhOb/2ZZtRvC0bItW+Spp56SBWhQO3LkSLl06lQTMevsC77tb6evUAQUAUWgeyJgr6pk+vI/0ItR28sq+G984xty5hlnQsMcgoY5vTBm1oJm4ZSWxLBgtlfHdwoCmB+XLa+RxQsR4XL3MaJ+t7tKvnH+KOwnLKFSObJi9RpZuiAXhIp7iB7MSPdcOLWvUd7A6Ekqq2vk1X/Xij8XcyQYngNhr/On9oboP20BVQcrg5eerUEVJntkOiSOAMe5FxVLPvTPLNMMYvL894zt2L4HMjO3hANBOf0bhdKrGFWWPWwc8ISMuco4WHgKGq+GOqf89a/zEcEajPBrACXH8O6KZOMl/Du0YqDjcZwQcRC1mLteYs5G8TSNkCw/LhyEclNhv8QCfgjpnTLqYIGHjEu2rK2RqtqYlNUkpCEIeufwi5/tWaNhceARzfZJyhmRaALRKl+JhINOQfGJFBRsl8sun9TukKxlSEs9QhOqVWbMmCFPPPGEjB49Wi677DI5ZsrRZkXGikteLJYnjtWeoyVh4/b4MF0GetrypIdd3Pp1FAFFYP8i0FIyYn16WVmZIWKMjFHPO23aNDnyyCP3787pp3V5BJSQgZChXSoEiy4p30IvmE/Rp7K/hONIV/ogUoRxawp/N8lwNFKVJJzzUwwvo78XSFoe9We0W/HEIUqMSCgZwntRnustQDqwGH9okkQ0BrIWFWeCZbuoO/FCmAgyk2ACHA3KxUVChh5g3mKJgJBl+xLQo9XJ1MsOS3uVtXPYQ+bsAPD+3PflvTnvwWpjI4xnz5Fzzz23WbPAVZkLIWeSLa7WjD1bhngZwaWNhGlrpnYeGH27IqAIdGsE7NXt1hex2uNx0cp75pIlS4zh68qVK40FESNjbHNnbyrerUHQnd9nCBzwhIyiRTAlEDKHLP1km7w3G7nsVC9UdIRAqkjUmLSkWSwJlVecaB/hSmSbsLIDOXNHyXYJ1udIIpSLVGUQqc8tyKsj3x1BQ/Fonqz1Hi5eGJ3lohGrPwXRGMhdBOLFINKIcadHCiIsHYlAx8ZeZvkSDbpRpRJFu4ygnH/BhH2SI7daN1kpTRK0d955R9544w0p27xFxo8fj2bm34IR4YDmE6sRQsq8PBjzZQghU6K8+dgjY3b/s312RuqGFAFFQBHoZghYkTFrwWrdLyncZ1Rs06ZNxn6Ii18avupQBHaFwAFPyBj4crgQ7ULGe+6ctfLp0gjaKBVBmhjB80wr5sB8LogKkAAiYfCRSSJRjrJcF9KWTgdKl/1DxQkBZmG8ToblVsjw3C1SmqoQXxMKBUIu+bdrmom+RUHsImyTxNgahI10NKZnS64rC9tHUQC0aE5XDjRnyJM7gkgpOuXUr42EKH8PRGStnNv2Kkx7+pLP0wtt+t/+buwxOG688UZUfx5mDGspMjUNbP1psSnHFxqja8pS7yqKgCKgCJhol3Ghz3REYfbho48+kueee84Ue5100kly1llnYcGeYywv+GCLOx2KgB2BA56Q0UnY6aahnUvmzl0DQhY1hCyWAhkDIfMGIOjyoWwZKckI0o3RJFJ59KhwhkDIQpIbGCtFBfNl+IAXZXjJ+9InARJTjkhT2WBYDJfI0pLhUpkolo2JAbLVOVCi7kJ4GyelINEkWfFGaWAnALSgSBiCBj1Z3I89aUJoO1+OOKoP0phtlFm2cT7bw+J2sb/1NpZYP/bPx0w4vba21tw4rrzqSlMIwMFKTOtGY3mc7SpMr5eVIqAIKAIHGgJsV0cSZkXGuGhdvny5vPbaayYydvTRR8tpp51m9GK8fzb7i/HeyoWvraXdgYadft8vInDAEzIrZUlC9unSbTLnvW2IgpVIJAHChVRiFqojU9ACRF2owkxBtJ/wIVUJZ393Ay6mRjnJPVfyS+dKcb8ZaIMEgOtwoZUdK7GqseKMoUzYt03KYn1kdWyUrHOMlnro09yoPMmGGa0XhQNN7BAAcgcbWLSV8IgzlQUT2SY55ZQBcvDYbJjv7ehB+WVPYIbP+bC8ybgd6zmmXqkbe/X/XjXl2Bs2bDCC/6uvvhodBnpLdm6O+Vi7vYYVLVNR/5c9Ivo+RUAR6AkIWPdVfhcuaNmLkmlK3mspBTnzzDONXoz/5sKWkTHTR3gX99SegId+h/YhcMATMqrWk8aN3y1by5Py7LOfggT1lWAUprAe6MZgZUEBPpt+S8KJCsmgFPkqpU/BeinI3SYTS29D+jLdn1ySxZJqHCSRun6SDPUFxStA6rJW4qkCqU70lvJUf9kq/aUCr6uVHAmm3FLg9eHzg4aQRWNOfHYOUpYBaA1Gy5Ch0KlhK/t62KNmCfSKAl0zEbFPP10qs96cJcuWLYN+LE/OPvtsmXTEZBNaT4v80waHXNXxBqNDEVAEFIEDHQF6i61ds1YWfLTAtEDiYHryjDPOaDaDtWPEDAPvp1bG4UDHT7//DgSUkIGQJSDYdzi9AjsU+dvfFkuWb6A0hRsh6o9KGFExJ+iSC1Gx7EStlHg3yLCihTJywNtSUrxUavB+V9wj2cnRMJFFX4lQk8h2pDa3ooqmboT4nGFxst+Exw/KlSMbk71kmWOorPIOk3JvqQyHyR0JWQLCfxIynzsPpKxJLrxwnPTth6IBEMX2DHuI3L6dZqF/JjVpGo0jpE4x/4v/eVHeffdd+OOE5ZhjvyJf/epXZdSoUebtfM6faVS+u223Z3/1vYqAIqAIdBcEWGn+8ccfmypKLmQnTZqEe/eFcsghh+yUkWgp+uf30/tndznK+28/uz4hI2HKuBcTFiM0Z97dFqFhk2yuOGjQ6sj0UmhuP5EJ5Fgnf3OqLhPxibAjfaNT8o12vUFmznpblq/LgbfYaKlDv0fx9JPcWEj6uzbIyNIPZciA12HiuhjmdiBdkQEgUEuRwhwIXVkTPr9OHJH+Et90hMTK+0gWmqtGPC7xcEUEWwtWc4a8ubLFOVjWJQ6WikR/2YwqzETNesl31klu3yL5vA5O/yNK5NqzBosDbZgi2fkgZQnEyWA5gVSn1TggZbT+qNpEsQC/E8shrZ98pRn47kmpRwSPaUc3UrH8dwTaN5Av6NVQqYB/Z3qm2c45YvXhhx+a8PvyT5eZqqBTTj3FEDMaGDLFyWgZnaTjEOERe7uGgn/j6k9XgPvvQtZPUgQUgS+PgGXgar+XcWvMINCn0Zp7TJW5caCPm3Z0Tzz5L1m6dKn5OysoqRcrKkoblraUeXz5veu8d4bgwenF3OFCdkiQSaJnJsMELuSKnHyujQQOZyZr7g0EA2gdCImOMVPi9NT+LIt9Cy271rQ8lp2H4p5/cpcnZJbTfApsIhph9R/TZWnCQXLGFUph5gIwFxAulCReS4NTvicBcsGLiCcFjVGZv7f6ipHARCCg96byyJUkhceSZU0y/8O4NFalpCgHOi/PdunV620Z0v9x6ZuzWHITOBljg8BvKkHWQG5AclojZEG4C3vi2B+I/cGw2B1Vah3FUp0cJI3xXiZSVtnoFVfREGkMwREtul1OPWmQHHUk+00iehaFbxk+hGnRFKo74b+fvggMMyMha6P1EV9H9pbmbOlhPYfvm8S2LTJFkakhtpnmt2x+PusNkDKIVCvQdmk8nP0Zih99MFxvMYzDdKb1EjElnvaLwK6v2PNTUl+pCCgCisD+QYDpRkb8d7t4xH0zhDmG98YCVEtycLHPxercuXOlurZGJk6cKMcee6wxfOX8QmJAi6GeoLE1C/gk5TqYPDiNsAAN/znhNuBEWySwtVaHVZnPIIpFmKx5YV/7sPGz7A3cuyP+XZ6QZch080GvxQVAu4ZkIt1jcsiQIeg+n2uIGC+uxYsXG38tHnSm147+yjFy/PHHS9++fZu30YALirooHryku1F8DlxoIC1ooQXSJrLgbZHNH8+Tg4eXSVHJo6iiXCW9Ctamk4dBRJZiYPnumjQXgoF/a4QsxBUVVhIe+GuYVhPoU2nO7EQeiGGerHTly+rowVJferKsXt8gw4qjcsEFoyV7sAOxLZFe9MkgkSLzQzQrTar4P/MkqkEb8F0zX40Otdw2ooeMkhmdF1KtKejfHPRTQ2TMgRVOyoHvnWDEzGdMYO03o5YXSVNDownJv/XWW6b3GntgHnfccfKVr3xF+gDTICw/LJFqyytzX19w++cWrZ+iCCgCBxoClj6W90IuSK1BMmZ6SmbGQlhZULS/ZvVqZEQ8ctIpJ8vkyZPRo/ig5tf0hMhY85dBcZs4Mot+ZlzM/INJiY0lMYewl3NbwyJgVtqW867VaL2t97b1dxJlbt/aXkvPzbbe39X+3i0ImdXW5/3358nLM2cakz0SAxIzerxMQwugQYMGIrI1X+bMmWMIwsCBA4Wu9Ju2bJbBgwfL97//fbjfl5iLze4wH4+xDRBSdyBLcYbIHPlStaZGKpb/VvoVPSj9CkCg6OcaBhFL9gGhglO/B9EgZ6VEHLXibSNCloSGLAkdmvCBCJkbFZTYmDmZBUazYW+2VDhGysbUwbK+ySsjxw2VY08ZKQlkGRtx4hei8pIjBfaPuHHGCw2FBnivCXT5eWVwWBfGzr5lzRFlvNZJfgZSljJrHEQQ8a7dLXCarS0QaeTYivD8RwsXymuvviqb4LFzLEjZ6aefLuMn0rwWKjvsHwkwSSCb6Jp9zlR2drWTXvdHEVAEFAE7Ai31XFxM8h7ohcaWKcoazCWz3polM1+aaTItJ5xwglmUsuhpVxkGbrtHdDKBG0AKDgMse+MgVUW3SYYGMDu5IHxpg5BZwYLM+9OpYc656XnNImm7Oxvbqh1zsONNCw84a1vdEf8uT8jMCgW6JUZp/vKXv5hIF4WTJFnbt2+XF198URrq66W0Vy+pqKgwq5Vrr71WsmnAB4Lw7nuz5fHHH5ef3PoTGQjSVlyMdkY7XYnpfySdQRAyECVHjnjjAUlUPCChmrskN3gyiNMnOOjVhvPg2sT/xknMkY3gWFiyUq1ryKDwx/sKEMmCCz8YUUrQIxOeY17otyRJDzI0UPU5ZFNNtUSGTZLSY2EeOLAvE5MwpUX1pQ/1lyBfSQfSmuBaTrBDKBjAdrBqw1WS9MCw1p6PRJTMgeKC9GXiNH+LR5FmRQTQ5WYFAjQASOXG4YZGywuLkPHmw9Wh3Wk6HWHbARZTwCtXrpJ34fK/EOSsqqpKzj73HNMOhGF7DpKwUChkQvZWZ4DdXWz6vCKgCCgCnYmA1X1kd1XjYdzL2GqO8wwtgcaNG2e0tBMPPTQ9l9j4yK6E+5353fbFZ8dQ8JbC3EMC5gaRcoXq4c/JQEAuZzJ0oGljJDGBsGE4IlnujOdagu35sNAPoIAsh91g2jMy+JOUWYEBbo5zWXeMVHZ5QmYRgn8++qi88sor8oc//tH4Y8VwgD04wNSR3XzzzeZiGTRoENJ9F8hpX/+6BJG+JCl7d/a78thjj8ntt98uw4cPbz70vBA5PHDCT0ZwhoEBxZxRrIpwkvEgx5dKcM3dkqqfgahWoXhdMG1FSySuFBiToizL4ewlvhi60Lci6pfUNhN1i4OURUCASPpYtekBoXKj6jIVioknPyyN0S2S+5VzxDHpXKl35sMwAyPYgObjEFLi800KkvuL7aViW8QRQq40hcvBZW/DkU5XptOW+E781YufcTyMABMXBzoMmOabqCrlSEA8xxPZuiGRUPHGYqUxo+HIjnZJmeUKtWMffvChab9EwksyxhUjyTCPwRdIXXsuOH2vIqAIKAIdjEDLyXvbtm1mwfmfF/4ta9euRUYiIV+BTuzkk0+WYcOH7dAxg1xYulvu4q5azHXwrnfo5pkc4prck0JwYOsGcVejtWA25p3+o6Qpq7BtQoY3p4Dd3LlzjA4vjiKJCA1xsWCPQWbExXtrw8m0TisjH/pxSmisrAxfym3y390xQ9P1CRkYNjVfzz//vInK/PZ3vzU5/WpcLAxX/uHhh2UrBOdjxo6V8rIyIz4fi5JjpiZ5UOZ9+AFE+b3klltuMWnMlgcpAQ2WIw6fLRxIdDaSMNiWn5EoLAkSVQskUXuUOCMjoRUbiBeg6tLxsSXTMqcJ04atEbJ4qh6RqDzsqx9Eji2T0IaJ4ncHemIyahYuF1dJP3EMHSeu8UeL9B6CyBuiWcZqIwySCGF/ssjsD+xpJdL4jgQrXxJX0yrJxnaaXBC82QbrXxxY0TjRJ9PhQBPzLGjFYtBFQC/G8HsU1aAhmNs6s8ZIVgGsLPLu2elkNiQNF5BF0AwNtBExEjUWTHBQ3PrB/A+NvoxVmbTGICGmO3VBAaKCDPmrE3WH3jB144qAIvDlEWjZvYST+erPV8u8D+bJJ598ItsrKuWoKVNMVGwCipqYvmQwgPdwtpezImQtU292E+4vv3ed/05KtV2YfFLVayWydK64Nq4VT8kQkYknS2LwiLaKLBHsiEp9fYP85r77ZP369WZeIX7U3+VkDHJbJWQmJbX70W/IIBOQsWe+rA4InH+6W5am6xOyTMqMpcW/vOsu43pMjxcal7788ssG8KOOOkouvuRic7BfnvmyLFmyBH5aUGDhgJConXX2Wc3RMTolW8JNo3tyVCN518uk/8QTQmqRIdp8ccdBmlyQ1W94E5T7GWkMPA/nfZEcB4hZ6jCwljK8Z7EkkTlsjZA1QJfmdfjhR0ahPfRjSWwTKUsRhmpRTODfIE2lJ0nuSTeIFGTaJLmSEgBxSrhz8CrqzSAoY1iOVZp1T0uo4l5xN64U0+YyXduQGSRPfBKpUEbA6MKP74evhBVOLv4JE9pEUAIoJnDnHi5ZJeNEhv3GvJdY8EQmkbVErYa8coWD/6zKVr6WqcsUiLKD3wmkmG2X5s+fb25gLAWnvo/6shNPPLHZs6zzby26B4qAIqAI7IyAvfDos88+M1F/zh/Uw7Lw6+orr5KRI0c2V/LH47Fm/RMLyRzQkdgtf+yVfZxr9pV4vdOOG6cqTiAbFkro/f+IY+US8fc9VOT4qSLjDzeZpVYH5w/gdOf//EK2bEEWCAV4JL2W4wEzXK2Ntioljzj2GLnuuuvMJizNmKUH7I4+b12ekFlsl2AvWLBAZkLUv2LFCuONxRY/NOErLS1tbtRqldkyBM33UvTfrsHMZtNcCVc+JMmGZ4UaenOSOIcbUaNbVkmYRScg8j53f0kG+0m0AgnHGviQJfpIxJmLQseAuCNNiLzhzUwhgsgkonEJRkISOvEHUjpkgDj7gBTi4qZDGNOTThLRBGw53GGkRSGSh8Yt5soWR82rImvPFHcYBQFYoHlzYNxRAXpXMhn7uRCtnehBO0XK61ZJ0eBaydqENG1RscTcTaB2K8VH6RqKPZ2JIeItPF6Sh/w9U5XJyJqtIIAXEtP/bfQ2t0L9Vg+32bNnG2LGmxGjZJdcdLHRXVjWJCFUZZJEW6JOUxjKa57/UW+AYb8IrYB1S61HdwxHt+s81DcrAorAFxCwT7o7ZChplmDIFm9gmQh/ywnakCvcizif8L5FIsZCMFbuc0HJ6nyvJyNbt2fOzL3Z3LRYl/WFYdexu5NbYG+JRTwLNekbjhoxnxFeNUgSmRl3G5p4xAXSu8+1Nt+2I2GRJiFmV+DNCXk91vF4bZ3RBjuRfUlKDX5voZlusbfwI6eVJV4bFDfDYcm0KiwGbJKYdmDgITnbyyQ56zlxrpmLuSQb8x1eXTxcik8+X+oGTUQ3mhQ8N7GjbthFQfYTRmam2IH9wTyTQiyBOjwW4D14/wOyGdKiLETGPE74VGLxT1Lb2khEUdiGtGQtLJjyigqN2mYrttWvXz+jEXv4j38wgRjjD8d5uZuPLk/IWuJLYsYDQe8YpsNodcGqSivFxotuX4aLYSEmbuS9JbgO0ak3JF7/qMTCi0yqEsWZuMYg1I9Cd2UE/yB/jUMkWd5XHFUwdo1D3+XD+7JwVjL02hCQ6sYmSeYUS8kIhL+HgCwNPQpsilowkjyHRMGC3BDlG0KGEUED89YI2dZAgSx/PyUHjRgow8YtQVRQZMWig2XZyvVyyLEOOWJQWMJIty5ehqrQZAxNbhHlw80hWIN7RPGZkjzohXYRspbHh5FJ2mPQo4cWJNsrt8nhhx8uJ5x4ghx22GFIH/dufotZHeE728vM7RcVCTUHyRdfY7fn2NfHuZtfx7r7isABicCuoiA7LdbMwhJFTJz4WdOE+wgJTjgUlqZAkzzz7LPGZ7EMchdKLqgROxSCfcpcTLrLYlfG99FKR7DYKZNKw/15x2hJCHA/Z9kU3+YkO6nAr71wy4OMBE/Fw9mSg2RGq6NFleIXXsvPT+FmnoStEzU3jip8FuYhVvGnSyJbH8amEn6dkNK4IXUxDBNQxfB9U16kZeGfGVg4TxIL3pH8JpBLPBcDSQ3n9RFfyWBJnPJNU3THARcwBA0otmGLQRal0U8AfZ/RHpAm6/NQHPHUv/6Vnrshn6HPZVam68vudtLIbDBP0PQ9BcIVAKP1Z1KdU6dOlRNOOtHM9/aCNGtb3XHR3uUJmfEKo+UDht1sj6sfhj4ZArWGZU5qn+Dbe5dqSKLBOFJ9HkbKkkgZVv+fROueg6bsfaQBrmVeCQAAIABJREFUy0G6uIxAzyVrNA4GhR+G10F3FsPvKfQ2w4meQOlw0A1dVcFA8Q4+WHJHIl3YD7l4bNsI2EBMQgyB4+TyoqG4IWRYHkU8rUfI5i67SBa8GpRxE1xy3DnzTXxt3svHyNIlCTnyNK8cO/EJqas9XV59OYaq0ya5YOpgKS5ZJ1UV26S09xmSnPhguwkZiZNltmsRKjba5WrzzdffMMSMNzw22+UNjxGz3n16p7VouGG09CsjqeOxtlo0pTsQcDfT3QjsRQftPb76fkVAEejeCLT0orLPBy5EYtKh/jQzoTif8pdZs2aZjMv26ipTjMT7EheMdr/K9HYyoTD8bLZ7NMopc9MWWDruNFryn3DGxzsVq4RFEhagccwJnEfcyzGheaWxjRSEI7LB3PNoVZSWiuABypOCFIbPlwQjyIqsQ63WAMhLeiOqBUuJgiOw7SFCx6Q2OyHjS8XQicaBCJcTc1ECpA7FlCBA/FroUrBxiZS/84bkwMGgINsrscB2SWTDSDe3GEUPNZJ/8pWSM/FQSXr9Uh1JSAGM2/lWVlIS+iDSR7lw5ychrq+tk0emTzfzQX5uHrRl9W0SMh8wotMCvUbrUJXp8aIYABhQtnTbbbel69MyRWn2Sll7R57udHZ3eULWFphWpMQ+ae/LiTspIZNOg9+/aV3kYoQ1AnF/AOHbyHyQs8U42ZAzTJYh5Ivn6/MkWYHIWPUwSNL6SaLYByE9UpH+AvH3R2/LESBi/REZy8pFaBcrCfYzwkkVoz0FUnZuOuXzQseFkgTRifkSrUbIVoD8bVkdkQH9a2XY2Drx4WrYvm6kVJblSR/wwr4lS1DVMkyWL8NFEYnJ5MNzJdu3CBo76PlLxyBliSIFYyT75VKWba1Qk3DbZY+3efPmmRvhNoSbaVHCGyDF/4NgX8IbTTAQNBFPVs42D95IM1U2/Bz7iqcnhKfbOrf174qAItA6Ai3vP8yYcOSgwt6MTIRs+/YqWQmNGO9Dn376qfkTta4XQXvMynBKYOz3FN5ruFC0PGLNHID7c9KkQNOyknT1oW3/dhWNiiOCBcIU2vo/sIx4FRGnM5BpqZe4c5G4s4cihLSo9S8IE/Idg4EJTEBoYWSNUPZoRLdWGmKYjA9HxnGQ5A34rjhLzpMQXpeF4rFWB9OuIGQuSGlQgiVBkLEsECkaXcSaqsTzzqOy/bOVUowey668QglUVyDFmZJc2FWEahGN6z9Rso6HwH/IQTAyzxK4dKIzDWFHbAyu/j46DGTE9WxDtfKzFXL/7++H5UVT2pw9kwXZ7T4ijcpuMDzOdQ310rtfX3RHqJVbfnQL7K8mm648LTNiFhnjNrvbPNHlCZnlj2WRLKsC0B4Fs5Mx68BaE3i7+ylSlIXm35D7w9U/nY/34LowBsUxCPujW3Gy1EgSKyCJbpNEQwi9KV2SamDyHBcDWFF2PmwrCkuRmsT7GaJlj0u8nQk5Hy0mbLnvjFzAEDKOtlKWHi8qNdFuyUfzf1yUaM3JgBwN+tPRajyBOk1Ua6b3Gf3S0yF0hrPpdTuOL/zyhMzCu2V4mKtWPnJzMpoErJjWrV1nxP/UatBDjsd0JKxI2I5pEnOpGBSAOmxO2dFMWfmu+pJZ+sLW7zj6V0VAEeipCFgaVsvI1Yqqm3snUl2bN22WFUhJfvDBB8a+gvMBoyssOJp46ETJAymwBjMu/LtVGZ72tmKEbQd6/NXYM2bIVwxuXKZJcPOLrN/Tb/KFcM/HPFG78pv4/R3JdmKVHMBqOBdkBvNJhNX7rQy3D68zUToOkA/TsYUj/VzK1VdcvpXmn6gDkzB6J2cPfADzzpXShHt+Lir8Wxs0aXUaqwCmV2GajhdT7uZF8Vfjsvnimf0IJDso9KJbP8Rv9LCMxJE2BANk9DEBm6jwwePFf/zXJZE7ENX8IEGc3LJgb2G0bTmGdBFTdtfhfXzGk0/KC88+B2lzsUlHtjZSWNBTc2baV2Ff/Vl+mQJD3quuuirdu3oXujElZK1C2jF/tPfIstJYFlO2yMHuzP72ao8y5wt9x0K4NmNw9KdTsQe+YqalJEX+1I+ZC5TeYEzA46zEScw8fgKmr1x9mMELma/i+Z95akeTjjSBMgEzsLH0vrOTGCp1WhH1+5OwrnCtwEZLcKFPSH9OFq05YGQbRdrUM0rqwu+ItyiMXD52Cz617uQQhJyLkCKtFN8xG9pNyHhTtAS1vPB20nrhIuRXMY1kzZ0sZWxKeIPkarUeqx1esGzDdMSRRxjTX65WvexFiv+c1HHYhn1F3B01Ant17umLFQFFYI8RsO4HTIWxsGjRokWyBhYWATjrc1KnbQWj8tSKMQXGe1FNXa0pPtrVwp2EjDdq3omb7bCaNWW8WTONuSMp2FLulSZvWyA/2SoVn/xA8uLzJMc9DO+pkHhhSMpqRQbuVCX/xa8ajaUJW4rq+8zPlFnKpzVtldsulb593sTCF8EBcMO4DBf3oF8iNTJVQrh1ttHpWMLYrp/3ZYigYddq7CqzGIUrWyVN894U7+ZFsPfIQ4CBajCH+Hv3QkYoIlG0MMz2IbiAqGF1VoEUH3+2OMagsAyOArEYMj2Y9+JsQ44UqJuk1sxtnAuc0oB7/j2/uls2b95s/MhaGx609iNpo2dZVi6CHDgQ9/72tztsLtrSyO3x2dM1XtjlI2S8KKxoV8tKCusCtDe2JqyWa2+7o2PmLEo/mD2jcD/KaBjaIXkgnMRaBOXROEdwvhlVAb1XMyeItYKi+h+0Arn59HZYCMDoV1rSYBSVO84EFrmAkfE/GuLRVgLOY60SshQ0BG7qRilYdY3CdqFXS62DeJV6AxrK+iSQ2gINWxJVoFz50CS2EFyxt0RdWZIznhq3Lx8hY3m4fVVqvhUuPHMzIyZMhmYiXoZMGXaWBolVTosXfCTz0BLrY0TNuHriqokVNEfCyoR2JqF41BRtkOhxmyR/LfvNdY1LSfdCEVAE9jcCltcU7w20VaAfIh80deV9ifolasSmHD1FBg5ABAf3It5jGd2hp5glere0yrzP2AvEkhDzZureM3MB26NglY6WQsLOLq4BO39l08+O977MT4qxApukftV1kuN600SPtoE4bYueIK+/WSRrqq3o166RY/9hU0FmBn9mXp95biy8JCcd+r4cPgIJw1QTKFApCNmdIgOmIlpmDJBaHUFsz3TqRPNwpmPjzKQ0oVf0wrfF8ekcCTNCBRG/BJskkgBlKwA5w7wUb0SXGqZioD0LI/jgGTpeXJOPExk8FKnKLPoEmHRuOJYW9RsdOOdSRuSAyey33pZ/v/CC6bLT2kjTYcy7WLQX9SqV4048Xi5A5X6zLlAJ2f6+5Hb+PE7IJGJWVcWuhPz7soVFPQiROxWAUBEZdjYhjzHPZ5gGHg14UNAPpmMasCIsC4JF4kYT2CRWH1km98g4GPOIdn0ULwKGw7AhvoTbs/KV/BMibVx/peCs31qELJUahpXD2DTL81XifSB/kcEgfnkShbbN74SGIe9QdASAFQcuBkcebijRKhjSwigWPmTJ0svbRcjMbSJDwKxql50ikxkNR3OELHM4qRszIWx6nWGVtHXzFhjMzjL9SBmeppaMx/b4U0829iZ8UHNgDRX3d+51qJ+uCHQFBCh9sKLtdNe3Fohj4T/JDiJHQGeUjwjYrrREFPjHTV/FnS0T7PMHSsfSETLeoJm+gN5Kog2orG80no6p5Fr8lfoyzEvG+JtFaFQE84F5KtkoWbAoim26Hx1ZkH7k3AFdWFn0aHn0n6j0LGw9ZemKsRUeFGtY6Ru3JQrY+ZNcEs9/ujQoZ31tvpw6uV56uRvQEQbG3QN+B0J2NeQu0B87ilo9TAHsEPrFIP2DecEP4gSiF9m4XBJzXpTcLSukxlUkeZC9eOiXlAhIUwjf3e2XLF8hMi0wHoevphNmnFE8HOMOE88pp0rAnY90cUqKXR68Fm/DnG2IcCZlacgUHn9G152P5i9odf/YKYaFFjSC7zOgv9x97z1pxQ1InenWgyICjl3JlrrC+bm3+9DlI2R7+4Vavp7ZQ6OrIluPNGDVhBZIKZ+s24jrCtdWY7hW8mDI6vNDtg9TmCw0+6aw3udxCIpJ0CUAefgCBF9x4nkdJRBO4mKgiIzh6ihOCnqB8RTBRWjMyFhuTJLi5yWJCsk2g8ZtfEOKLuEnk4KfjJsJ/qb5WF6dKPFqNCpPHi61h/5dcrKKxOuHjxk+i+u5GPzLYDRmonVc3aW18RkthLkh8Ay2gMl8fibyzkhbLJpuuJ6+6Nt7BFp/f8sLiYaBtM3gTZZpBw9CjWzsy5sqm/lOQcph8OBBkgVj4J2GrZKq5SfyhmCipdRJIPzNG1lz8UBb3y/Tiy0GLZvVoYDbc8KmhOH35khox8KkW1cEuiQCvHyo1eKwR66tyJWl87Ta2dC2iJGrZiF2Js9HcpQAObKuMW6PqUaTWuT9lEU9mWp7Vm9/9NFHRo+6pbysuf0O+xsfc8wxJiLGKDs/I+GIICrlN2Qm5q5CVq8B98KhiBzhiXgtCEOasOzuNuCFMCuJOzxq3408RapnSrzsp9i5ZVj0HidRx1xYOEAXFsFEEENRl5ut8vBdmDXJdoLntJ6TdEeLxVN7At63FlYSWeJAoZiPGUn/ZGnwrxN/oB7aZVg++LFAx3a5tRxMMamQTxq2xuRHL10pk0tDcs20p8Uf7Q/fykpx975bXIO+B3KFDjRt3N/QvAj2YVnoswxOhkdB0zqR/3tB4p8sklQ/FDqAhLU2Qp4m6NSgk9sWknBBX/Gf9nWRMeOxVbYGhGSH5wUexoHDlu6lHiyAFnw//8l/mSgZizGK4Bkah7N/GAVexSUlZmHuzM0y1Zgs0vjJT35iFub2Zu49rRNMjydkNNYjMeHJkECYmemzioqwvDd3k9TV+Ey1IatnKJZ0IZ7sxAXsATHzoQTYAXo/8ai+ctjkQuSwa8TrLDYlwS6UkaSgoAd9M5Et8puoqXyBV1bmBGTAi6XK6WfaMXAFJuBhk8A6xgjym+ZI08YTJNmYQsToDIke/EeIG/NAEKiJwAqH35PfBylLtjJnzabx4UFxAg0DSbTMsC4OetAYcz6s5tK1zhiZNKoRQbRj3/fgrVY6mi+1p5h5I+eNfs7b76AdVoWsW7dOtkBzUIcKpZKSYjniqCONJuQItDWxoqWmEgqrMFN5izuRMUjEnTjJVlUtxJ/EhNtnBU9rgxVClomtgS0T2bPSsB2Nzx5AqC9RBDoNgZaWNZaBs6X1tKLlFjFr+foI/MB8Ni8qyhjMdWtrmcOJuXxruXy24jP5+OOPzb2A2+MknZOXawjYkSgKIiGzT9DpfaDSPRNZ96I3cBLCfWcfcVluFk54eDUP675n3fTw0+M32iks4WBbgftkzVPolAJn+GAAdkhF0pCsNQt+ZPPwWf2QrShAnCwioTBTcdlSEGy9V2Nj/2rJqsVCEanRoLsUxuNVksv7fOJIqXSuk96eOtzL+mKR7YGfZC3u0Y0gtKUQ0/slDEPWmx45ZSdCFnZtFU+fX+8xIcPdH/dLFKHhO3gwuSWWzxbnR0hXomdlFPMB2ya1NsJI3+bQ6AKBiIg7V2LDD5LcI5G67DsM4QhGrzCvmlloZ0LG7E8kEYdn5sdyxx13mGjmRrRWKswvkLqqalOJT0LuKcg13V/OO+8805aPWRKro0x3q6Dck4u0xxMyepbwhDArBZPfd8rnn1fJ/726GpEXr/TtXyLRoBstLnJMtWRVzQoQnLiUFGDVE0cHgJI6Of/ScVi5Qcwfz0lnFd0QGUboOI/VFS5sBMrYFIlJSnMKZtMgw+TLyTL25DC08hqwvSg0ayl4k/lgnyFN70nFmm/jyfXSd8DtIoN+jDfzdKeJIcgHvyIJqLmQqHegJsJQCeNdk8aB+4Z/k4ih9JKuzMyzpkP3mQifqRxiZG0XVtTt/Eqtvf0L6WbgGMVKiumIdevWm9J1Vkttx79D0K8lQLaYnqDe7JBxh5iVsX2VbbV5om7EavfUTLBsthq73Sd8fiP8b0KYONhUnduw9pGr/dw96MfWgXDpphWBTkUghuuKong+TCVdRr5g9RC02tlYO2mPnPE5Fu8whG/IU4aEMRXFSZjpyDlz5mABXSGVePB653ZZ9EMSRj/DgYiWtyZlYK9iVxKEzNyLcYc2XoZwibekW7S3N8NaoVLgu4OE8I4OpZi5b/qiUFxVPymB7TcKtbusbM/K7wOD03xZtc4rlbV54G8wBUdqIRZEFN3ck1uPMAWdH0puEPYNvnKJ5veT4X2q5LDeKMqSYbLdsV7QYEVys9B7ODEIi00I5PGhcVT91zbVwuMrLP/zr2kyuRcjZDMkK9Zf9paQEQbuIfMNjvKtEnj3OYluXw5Dfizuaxuxxm/j/h9DlgipW38hOtLAVb8m6JSSKV8VOearKCrIBvYISlCvl4HYajfFkCURd+F/99x9t9H/BZCOSmFBXQBLjSocezr0N8TC5tz62c9+Zo47h0XI9qUBfKdeRLYP7/GEjCuABFJ4ngzR4HdfvapR3pi1ARN9kURSG+D3VSC9i3pLMLRZliybCV4TkYNGnowqktFIXVbJd26YhJUCli0WuYE7ciTUhJUd0oTkMqxexOc4Yo1o+l2HkwykBm78gvRnuyMo/Fi40saxakT9IlZ7n0pN+dPI27uksO/ZuFonNRcGmOwaLywjS0svAeNoLO5B+jV9R6K3DtJ3jBbhRSx5NrF8M4zKyzwXhxDTiSihh90D0pLPDhstV8z8IN6cLdLjRpVN8zA3+7hxff4c1VOff/65LIWnENMhNJPle0oQ6h510CgZO2asDBg4QAYNRHoTpGm3q6k2IoD0UXvuueeMVQeb43Jw4jE92UDIfFa5bIchpBtWBLouAk0IDZEQ0VSVEQyrybNlR2Hf810ad+OexUgIr2kuulavXm18wmgkTfLGSZf9IOkVdtjhh8khY7Ho6o9IVFbmvpS5fkn8KH9gOtQeaTfxL6RD88GoXCBaiHcjncZ1MqLoMQjhoZGyUTGzoOXIeFEb0gUDsfQqFy3wpPY1SVTdD29X+Ig5JmIbBbJ6s8h7Cxvl03WQqbiKYewNkTvu234sZtfmth4hGxQYJblhzD2+9VKL+eKEydvl/KNmIg2akEZvUvJMygXzTD1E9QF0bcHXToKUBuPI1rj6yE0PH76DkEX7SdhdgZTlPeIefB1mgHzTAKa1wVmCe5gbxXeDfjf2wetSi7RrfmGW+Gth4URBfyvDFSuEiL9JvPnAE8cgUgWiOhQ9Lk84XVKIlrEvk4mSZrTEcbazMpVt6R2jATqP9X333isV+FmACFkOnP/LQNB6wyeuKtAgdOQ/99xzzevtDcN3dY513Stlz/bsgCBkKUR/uOih7ofjs+UN8vY7m5Cq640e3PUI/SJ3npuNKPQWWbbiPXHB62TkKPQxgzbA69go194wHucP05SMIGEDWKFEwzQyLcV1GgSRAWnAxSKrPoUH4DJxYHXhnHCUxAYdhDWS3dhizw7KTq+ik4YXthLmU7DSSEKnECnHkygB9g4xoklz87DIWHNhAENlRv5oWBpFpwk8PCjJZCLTqh4NIEjG64ONyq23piNqaLJuPtVuTPgl9n8P3mKRL3OBtkgtBpsC5obMY5eusNwhwKWuixGrzVs2y9JPlhrjWZZSW35CfB9X1AcddJAMGzYMBG0gDHT742aeZbZJotYWYX4H1UDT4S7NVRrJHnujWk2D+Tl+JWR7cIT1JT0VgQQsfTihMpp18cUXyznnnGOu4V0ttHi9WGPNmjUmCrZuzVpTEUkyRq0Qry220xmMfpLsKUkbHPYqZisje2rT3KHw2gjuAbyWW2qJrHsK/OIF3togTvT7IbuhpgM6FTf2hbpfLLjSI5PDNM0drd/xM1CFysM6vMsjnghW3o2LJdX0BFKGlbh1jIOl0QoJRUbJhm05UhGCdASG384UCgHCjewuiW21vqCtT9SLq7GfrK3eJPNX95NjxjnkihPmiSsWQIsiZDSwO83ZW+4+dM10VgKHlVAgT3789HlyRK+wfHvaM4iQ9ZOQGynL3r/eY0JmJC6Q23jWLxVUVcFVfLM05oQQAIgLlMmYM1pfsbqSJYiihSWEVGs2My2JbBz7XAmOhsfbcdDGFQ1vvsemHQQgjcnYYBjUM5KQ2bPflT8++BAsPPpKE2Qp1PhGEAEcNXGc3HzzzWYBzMgYz6192Ymnq12XBwQh46rHbZs4V62slzff3IgDDvsH12ZolNaiMhEXGMqGqYcv6jVCinuPRcmuHw7FLrn8W2OgNQIzynSSdWLFlDRah3ykEhvxPlyoFbDDmP2GBJa8JY5SVKCc9g1JTTgBKrP25SzjKERwwu8ljosjDrF9NgULZrnBRuSmWtkM0r5mLtO85Ev76MRgHWH6f3JVSdE+rpuy8iTSfvVS3cBmtC5UzfilV0k+CItf8k38miQuBCLUlpPNvjmlLZuMnUrOmW7G3ci+4rV7jxnTYIbDbSSOUSumNNkdgFoTrrapNeFkwNf17t3b/Hvo0KEm7TEB5pCtDTbEpXj4hz/8IXzS0ua1pkgA6V1OCF5MBjoUgQMWAdwneL3df//9UoQU07XXXmuiWdbgNWpZ1bz++uvyGSQHJF5MQ/J5+kxRp5mLIp2DINiegPZqw0cMNwSMUTBKDlg8w2EIGB6GgPG624XkgJO22S4WYiZt2vQOXCceE2cdUnqufLMoDbuqKQ0zjb3jTUOa99VURoKQpQuNMm74/iHQUiEEBt9JH9KGvmglCNGqdL4BLviOeJMx4g4mSqEj9ok7iwVeMAhHGyEWJuZY+rXdnCChEqTpmgbLks1V8vTr4+WwYcXyra+i8hCZFph+gY35pQkWE0kUH7hyWZxQh8Iz3NdxDw9Xl8m1j07bQciifZAmhKi/1z3onHQ9vgGal7cRITO1UKHtEpv3ssjieaha9GNOdEhTsB5BigLTAqm14UoCSPhbBGCVkY3FviMPYvyaBmmEd1nvKV8RmXR+uk8ztmlkI7RzymRjWMRh5uVMV4RH//EP+c/zL8hgZDUakb5kIddV111r7tMs9rLaJJJss5qWdkg9bfR4QhbDcsKBcLM58BkN2Yb1AaQs10ljPSZqtLUoq5gtVZVvw2/FKwP6nix9+k9BlCtfAqgw6S395dyLRiKfDbG/k67DOP98qCqiCpLNwtyMJOFErGuUwEdvieuzueIrBKM56kwJj57c3hpLXBANsIVAmyV+HkhgNkqJeTeA9x6jwWa5RG+vtF8LxPz8afM2S6XQfsmUHaNIARiwiGH58mpZtbJKamui0mRWHTD8A/EsLPTJ8CHFMmpkMYxaEXYn1+hgUT9v2CRKrXnGWSbA/MnV0U4rpEwoPH1vpngus8OZi5w3Aa7eFy9abFKcXI2TqJGUHXf8cXLV1Ve3ek3/4aGHTeSNGobBQwbjEMSF6ZECRMrM6GB8etoNR79Pz0KACyleX7/85S8NIbv99tsNYSKpITni7yRRJGEPPPCASf2TbI0YMcJUzA0dPMQQOEavdzW4wKIju10XuoPssV8iup5kqjyZrvyCNCGISsq190pq+5/xWnRLSVHgX4ZbJIu2EL3xvbdDPraLHYin+kAjDDsh3H69CUhY2A4FYnsoQeBHORpCf/Qzdvej0Bf3Z5hxg+wxM2HUIL4SLPBbJzSORBOyHhNkcXlC/vT8cJkwKk++fw5E9YGtYHO9wLt6owDtM1m+ob/UIoCQTKyUow/3yXETC7EvZfKtv1xhCNk1U5+R7BgImamy3HNCZjIlS+dJbOF/0GVmI45XPio9YYSBRXwqCwVhrFZoZcQRwvPA4sJJgHywlAULDUab4ACQlGyYj8fP/zlaRIG0wZWcWV8rVpCWNafnKxZyUNdbicKNn99+B/wy8TsI+4WIuJ57yYWGWLdMVVq9k3vW1cTCB8t3oKd9s8z34UrGpOJwJiRxcVBouXZdnbz51jpUWSJPnoUTvf5t2VoO7VjILQN7nSO5xVMkgvx90l8vA3wp+fo5g6V3X0RrkCbkte8FIWPVjXHRAzsKM9/Hz9i8Qnxln+DExAk4aIpES/sZkX/7RgSRMDR9xUbSFZxInUZRrMxUqZveaHDoN9+RVZXYD1ZYmo6rzGXiD9S+mYEqmIhTPv6kHMaJG1Bq7JfS4kGmobkXJCceDWOVA/dlf1DGjC6WwyYNBjFFdGw/EQ7LV8yqzrIIWsJUSu28zOPKioPRseZS6sy35Io6jUeanFkWFTEITq1o1saNG0FGa9KO3ajSam38/L/vMATuN7/5jfRFwYC96ICThT+n563S2ne+6rsPJAR4e+Ai50c/+pEpqLnrrrvMBEobA6ufpCXsp6ygoLDAEDBLJ2pE/ZlhLGmwOLTIF60wLKG/9Tt/RqLwhsQ9y7zORPIzBUiZ+wQn63QfSsgb0Odx25ofi6P2VellishzpB56qQCU+okwevkivWjdWdPVULxvcp/SN748tGeJ+D4xAg53fIxpm5dwf4qoGSvrkZZDV5Q8b674UXDlQgFBprAwHWDzwIbCAXlJK8PHXff0khVVxfL7f42Rg4fnyPfPe0M89dslleuTKpCsma/VyarycyTomICAwEY5bUqlfO2IdSBti+X6v7afkEVmPiWuz1+TVAEKl5CWzQugWA3fKeCohySjdePaqDcg3jpkKvwQ3EMKsi1SK748tyC2JrJhgwS+PV1yCkGC8xDt5LHKIGvNyRY0JuuA4zf3vffkgd/8DlHSEXLrbT+RXgP7o2iixmgTSfA5rPS0naT1lGuuyxOylIkQsUWDBxcZXd/hDdZYjx5kdKTHhQnXecNW8DwlAVj4SBRRDI+XVRw0Hs1ouMzJkJAozort25zy2D8X4BxB2yHZKI3QKQVwhTJdlofQOdN3HIwsFSJUfOHUoVLYC5WVMPnzOLHKaj7zdOxWAAAgAElEQVSrWJ+ys8bKuhnYNQ18zir73qmt0C7IRssTi6eguTXAvyOFEmPTagL7mcQNoLlispWz0RKpciOfrQjIHNh9JJPIx+O7BSGe83uz8P3rpbQI7UOcHhMep/3HmLHFcvKpcLZGxWlaW8blDUHkStCEo8x9LIUbRAphfSySjHE1rHegs9gg8vlC6C8CkvrqZeleuIzko6SmwQginFJIsliPA1bYeuuMti605roEA5Kx0gVOxCZD4tpJKO9Fiw+K+X98223QFaIVFQZ3nVoIYkJRqjVZWKlMy0XakEGs7nUoAt0VAV4+lh7MMl21Fk3mngZSVIdWOLfh+qC4/9777jNf1djNmMrJNnJmbQITNEvNFBvvZrSvvOYMT6D+qY0Vr6spJNVrs6Uoeqg0hD+W6tDX5O9/O1mqfR/hfs4yKVg+uAPYT0TqafuTwP08TsscZBVQlbktXoBOKMXQiuFe3+CSnHh/2Vr1jhSOgD1D6FgZMvRxeeB0zFHJVWir1wuNtXGjw8/FNQkJulBSEO2F+5EH9wlE75DSSEAbJimQFizqs2AoNq7EIbXJLeKoOlVu+yfStePWyU/OfBEpSohd0As5kNyOezUCTJ6BSLnCSiNCI/L0PagOEbNfzTga2x8kv7v+cQmhJ58jC1Y+/h9LasxvkEXB8XG2ARDmtFRjrTRVlSN9W0+TJHxvBACgl47CxDMPBWxtDu5M5j5rTGzZfJ0GtsxWTDj5C4TZvr0w+y/j/W43m6a7ka0KyMpVq6VvoRNV84Pxh0wmos2d6Bkv6PKELC2wpKEnJjcQBjcqBnn805Eq/AVGckwjeZBPNpoelAibqErmRsALN4ZSWheT6TBLRY0Hwucijz22CBdIf8nLgR8KPa/gCByHwNOcUDY3PU8sKBdffjhcgmF5gHWSA9zfdMXgRYEoVVNj3OS2d1XFZzVG/0KEx2orZJ20rZxL6dVlmiDGkS91W72/SIC4v7YV5q43A58ZlHkTr/fe3SjLV0CzAKGpG6HlNMHMQXVpEypKceGy9yZEnIl4ACXGLjnxlEPwk4SMpeJ0jOaXbnGDBQaNaJrmzoJPD/4c3rhMQsvnSk4NXKlrmyR66KlSMBrVNqW9EPZHCTlEel5ceCaoaLuQv/Tl1BwDT1eRmpsV9pWkzGgH28f3ZE8IGc87Em1OXIzC0R7Dy5NzT2w1vvQX1zcqAh2PgLWYZLTL0m/uZGWBS64jCRkX1Rzp/6e1XSksxB3GHQwRtETfnUGw7gfWs/E6qVpdJKXhHGkIBWR13UD5+6PHS+HQD2VI/1zJiSJKlkSfxjgsdNjEJIKbHOYLDxaibshRPFmYLwJ4zr0FUbBsKc0eKw3RORJBYP3DBaNQSD9I/nTZJvEUzpYgbtOoEZPNGw+XFz4chGIjn8QK8rF9zCkgf07ch+OwcUiiqMCPSoOsHKf8+jvvSIOzUly1J8utj4yXQQetkx9/fSZuxZCRhBF9z0GwoLGPlMWGwp7CDQnMJhngDyAyh3kILv63Tj8Y+z1Afn/DE18gZHHMV542bIuwO8AQ86NpBQUAaNqJRbohv6YlwA4LkF2ebWYitIGebly8Y2Aebs1Fn0UFRo+N1bwTVaacn1kf5wMh9plqjANLo9vlCVkKLN1hLOdN/g1eNKguQYXJvA9WyuhRB8uo0RnxpSELeLAfF1YpDFdHYYbqhTaKURTTH9Jk8ZxSXZuSp554H0JB+Iol/MYs1RA2c7EzRYYTwgeCgeaPuZ5G+doZY/8/e+cBYFdZpv9nbpveW2ZSJr0nhEBoAQKEXkW6qIAidl1FF0VRd1fd1XXVtawr7FpREWnSIZTQQiCE9N7bZGYyvd2Ze+fe+/+937knDBhmgiMS/puD40xmbjn3nO97v+d73ud9XpVWmP2rvVe+x8QZI2e+XumKEdst+iyYz475tLwt1r5Y3e9Ef9AeKins+R0SwhgQ8GmAzPW5dOan9oEHG7AUIFCa09GWoT/dsVwtrbkwiyHlIHPrg2IPkIZNuC4D8IcII3KoEupG0JmV2QEgm4H5apZjnRwQS7NO/vRz/Tn5dayX9htmvNa3T/WL7lbHikWq7AtTSp2laKRU4aNnK3TUsRSGDmPyc+F4uzjorYvn/u32P77To8UCu17sfG0n/TYzZAlr35EGxf1TLG//Unn4HQ5fgb/DFeg3f3ymzP/u9aYNvK2ArDctxTBe/bVTMQ0vwMba2iG2H/AgxdC2vkyFXWFkGS16qXGyfnvPBM2Zs0LnHEO1dWIDlecFzOEKmCGTH9g60Eu4o1iLTEE4sI/Y6WVeIMxI4R1Lf8dNaoL5uvOP+VrfOFE/uqZOmaVPqTGPmnTEve0tVXpufQldXijC4jxN3pIZoigAobuZjofJe2bnEmtzMzWlgqKDTDZwbcfpC7+YparR2wFkjzpri+xeTMy7yvX0Kzl6YtkYNUcrNLkmijXGXk0bt1Q54W5959fnA/Kq9B+f/A3GsVwjY8gyb1Rq6vco+DKTj4F3pI7usIRHv4toultb6YzleiO+fasjrj8/56eWX7/2pdMnbvm29zOOrh+ee6tv+C5//CEPyMyuwZMGeCqqXgbp6tUNevapTVTKTdW5F0Qc+DJvEwsUjio1ibvfIzJ9g4wtMV2/AbP2joQef2wlRnS4HXcBV7LCsFBWuWMVfeyOSHdmw7QZKCsm/T15WombnEnbQQBgDOCl+2Xv1zD0Z8F8INZffO4bJtrvfHB2oNLwvxxP1i8p6OjjUNhaI4F7+DBxtjbZsDFGuQ90JEVgAUS2Gyv4y6V8vhFU0MSVlRfHKblRqTj6B8SbCXjx3miK9hXlVLg0AWaaNP/MWZo9y+tGYJPF0nReq3T7md+ByDrZAZXYSe3dpMSKx6hm2qh4S52S7XgCFVBtFe1QZ26RYhNnq2TWPPAsWgNer9s8gfhOl7khTqH0DppxYo78Lo1obY32v+7QXn8whsyArN17a++05JVXXHWQn64xgJ7d30dtiJ/08NMPX4G/9xUoqR6m8ePHa/jw4X+RenIVjzDBbydD1oHGKwJLEmFD6Kcp918DpnZ0v8Pra1fGbSDTRw5pvK41JygXWQq9k/RM7Wjd+WiBTj9miy4+pUt9bdYCydAHW8M++4HqRjIpBhMs+5iVpJ1PLFuhMtBOH+nCHgT8oR1sJo/W7b/I0cpYkf7jKvwn8xZqX1apyjMaAV10WIrlIYnBGok4SP6RVzO9mxVksUFGeuJsMa0tcrRInVmtaMbG63M/P0YjxzfrpnPRc2VWKkzfyEZMUjesKdeujeNoAVWi4aU9GjO5XkUjd6Fd3qTv/uhC4nYFKctfwvKhmcvhnoQ/r+RU+lnyOVC2DThkon1tLlYGjJRw7mzEZdY5u4YGgHPMZHOgo18R1eurI9LwmSrR/kd/UGbkRMAIB4Bwyjb8dtuMqYNwSIKA7R5kDnFD/feeL0N9v0MekNluxcBYOwZx4ZAZfBbosUdXq3antTsq1Ic+PGp/z8WAo1f9PFj6BqcrKy1L6HnDms4oqJ3bO6B6c1VaxpDl9zaJ7Pt+z5f0QLAiE5clhEK1nYPtdpKmZTNwZ6QQ1SgD9dOy6iLTpR0opemXcA90E7s48Sxj2AyYmieHvTEAwNyje4gYRYPUNROOOOds2MCAbv/1qzx1uNraaWqRQzk6wae3K5PyYhrDAvK6SL/m5xc6cX8o2K4zz5mtaROZkLyHycesE0Gc8wlzDv6+C/WFcgFdev5h6ZXHmEHcA9KX8W52hmj/QsEe4hjdALJGKDKdNkdHzVGymPQlZ0V3J+dqPZTDrr+xlD5T6bdiciyiu4dvLyAzknIbov9HH33U9dez8mzzLLNgYxqyrH6GxEP5nIefe/gKvBNXoKGj1XXBMH+xyZMn/6Ue6G1OWZqCCbUwju/Q6lEWb6tyTMdAF4Bh4N3hY7A3UDqpnufUteW9yutEK4Uof1FtiZ5eFNFRY3brrDmrFTCKPsFrUFmoXutFiTCfDaqCw3l8IeBkJanACI78dCamVZEtR0ak9WScoN/eWqblrCU/ubpdmblPqom5XkaT7hDALtozFRaMOJC/wtlw+MVGiT60yGZ1RtizTX1ucAKmsJuU0Vylz/yM9k/jovrqhc9gNl6hYBcFAVlm1O2RCeZq72z17bCwSSeZm+85Ud2dpQCyXxCb6RGag3ls6LNKTvvhQQEyl6YkRvb0sLZZEYQ1F+g30LDAHNKRZE0euMVRFLKBXpq8D64ezjLEAV9c0JJo9wbrxTmkkzsEn3zIA7IYIkivDYdRmaQbqSr+w+0Y2KkU3VdI7710ugNLkUgcDy2as1pmzchst3vLNgjhKiuRmTlglUh4ep8kuxYnyAawWUuhDONtLf1t2nWDHggKjHGJhMyUKy1QTVfx7Be1kqqLOCtnz0HYvK78Nh7OauHVV11LCOuzNgN/HTsO6FY9wMBoBbQU2meCN2/csEK5OdnKHj+VD0PFJ+c6WMLSM3iNuFTvg39epcamLKh0zhttRCBMz7VOmqjnogHjs3V10GgWQWeSVhglpXGdfuZsjbKe5XxG8wd0DJldEwM6LvlvH7xJnc8/raxNqxRCxN/WiUYNI7O8Qsrd2+sVxci2zAoh+nLUk1WgbtobFc45QcFcPHZMkRseGiDzTiINvvtdRwsqSYJ40GxChnAMxpDZGPoFxrGPP/64a2xslZu+iaFrJeP6hB4+Dl+Bd+cVeHXlCr344os6ce6J+sxnP7M/hnlaW2/evZ0MmW08zYVRHauVaH4ZRmk94KSVCsdeNqTod3v+7F3YdHW1+zGtOLOfg4XvV7Tl18qzzV/GMO1oDdITt1jVhRkaM3IVQn8X3ohprB3mWU9IQtOvfbTVa2goVDK3iqKnfMDKbjaQOzSqqkQlVc1siCfod78o1dMtCf3PVTFaqK1QV7BCRTEAhZNxuEJFB25cUbjJsdIG9Y4ysBgaAYG0HwEWfEkZbdX65I+P0vDRvQCyRTBkRQCy3WzCT9L63Qkto+9yKwL7svxdmsX7T6ARjLFtX7n9YnW2FztA1pfkMxggC35Gyen/eXCADPnPFmygtmxrRAvMJpksjMXUeIxrARvRg8vAQIdlfHyNWH+PSD8LdPYppc4vzG+rZa/1eoAWVRRAZhg7wlql9mfBZMvw0x0DJj6RC/UGjeC7cxod9Fkf8oDMhgMecO6G0T4So88OvbxkE4PcHOetzI9Fl96Sw/DNmnfqdJWWoP3CsiLWCwvk9+FCM+X8w5hs1vQ1O9toWL5oPyQc+Ac+bDahPyC/5tKUlmpjl2OO931W+ZhuXO1XT9puaDvlvs9SvvvCCy8Aftp15ZVX6uyzz3Zl4Abc7PB7vQ12p1wdp53i5pe194UHAU4Jlc/EcG/CHNJ/oKWDoHTtGtrXq6/uxVOrCcsHSzlCUBuDDs1tZojZpGujVJqa7gqWXGPHZuukeePxPeOJppczrau7ElYuyQ/o89TKbnL1g2pfv0pt9HvcQy342qYuepEV6oSqfFUB1hpgsEqoWg2ZNw+FE115VCzhz5YzBZPVitFp9+zBrsLAf7eA18sOz9C4+UX6Lhn7A98QXv5gANk//9M/yZzHv/b1r7v0jh3mMm33OGPIVWZDOPnDTz18BYZ4Bcy+4tOf/rRrSWQ+YuYr5m9I/x4aMjKOeH3BmDQ9qp69v1FfFzGQmGSgySr0yt64n+uPH/g5gY9kVwimiXCfmVFDm7wdVELSXDw0UX3Zz6mndSqMUqVLiyqjAe3YHjWit127Olurloe1o/4i2Jt80oDbVVW6SqeeSheBSYj6ed8H78rRYy3TdNvl3SrIWoPQfwTxssXFoa1tHYoZyNhxFgxQlAIqDGdd2Iy74rOE9RIGlc0aQ3Adtgzt2QR96ifTVV7Vo5vPp8F3ThHtl2oJse/X46/06M8rsMDAhHXmhE6dPWefjqhZSUV7o/71V5eovbVQ//FxAFkqWwFc9iMZn1Zi+o+IPYOnLHuRsqxY3axXl+/hvSgkyCrmeiCrIa2aAXnRHBm4StwHXv4w8/VhnrY/Qx+4GBsNMkQ2fvzshb/22VoZIBMTyzB0aWTgHvoa/kAtO2+jSOIE5Y2/Qiq5dogj+N319EMfkDEhH3t8pXbvtBRjvmrrujVuzFiE+VYtSEOLINuQQBdNwgM679yxZoXC7iMJW9XhWuQYq5bRD5DFYh1pVgsgYj5icLSvE34za2yz5fTrBnaoIkwx0JNEAEtpGuPW09OO+N2YM+ePvz9AWfXREwue0N333O0WY2NMjDmxliJnnnnmfqdhf4gcjIYsZqy80YKrH2ewPqNEd6s684YrMOk05RswKxhYFm+fJeXE/xlqao5r4TOb1VCHE7MhPeuPiWFsV5S0ak6Ehum0SuJzV1XlUHJcoukzcYdOW3w46SVpTTqleLYXgLGO2jrl33kzfXDHakVTo/571VatSJVqWk6hrsxNav6wPPWVVKm1cxf3qV2FVr3TSdVSokTxOaep+NT5FPUM0amNU2lp7cLKpIXxkYE5ZRGeNbmuAtcdBwFYB5qygwEyS/V+Da+yJu7RLbfcglcZzYVdn1CPqrdU7+Hj8BV4t16BbgDZxz/+cde+6Pvf/75Lv7k4APvrjreZIbPCoaCwj9h3r1K7byN+wCa5sGv2FBXqSAfv11ia16oC7XdF7ZnqyG9Se06pgr1FyuleAnhiw5Q4TjsTi1XJ5tOskzL6aEKOhiLCv/t4g927T9TWjRXqqr6TzSTdPKIx5ZNKO2r8JGXnLYY1q9bCB2brztoR+tkVa5RX/Kh6qS/IJFDW1p6i37wwSutoOJ6DyN7aO6VYkzJcD0eq262AypTOVJ3/y8kLlTt+u8rCk/XZnx+hotIO3Xze8woXEnsTO1xqsp1Y3YuWLZwqVyi+R3kRmIlwEVxEWF+762QHyL4HIEuKzgE59JXUp5SY8eODAmS2yV6zvkEvvrxd9fvIfgRpEs/nz+ijs4mRHUlb5wY+3siMmVzEsWDcm8uv8RrPHyhtaRrbcJj1DPNbI0zyO55WuO7bUvMTzuctWXS5sqb+cbC3///q74c8ILOepwsWrNOq1ZQG0wg2H9F5ElZrb/NukDfaqo5eHXNCDW1t2J1kJ5x3WNhVy1gVL5Mgbdr6dty1/oDKHKhvv/1210tx/vz5Ov30050Q9nOf/oxr/XDdhz/shJOuytL0WOY6b9qnQU4sgZhfIRqW33cXBtOrtQc36EomSTwvV9nFJYrOv57v5NuZRFHTmJliDFbG5HR9FCwEKK328/BW2NDSlNLWTY3sAOvQ0TUpZ/gYHoibP5WVmaQwJ44rpYnvaFUPt94ivBzsZJx2IL1452RhGhgKUmXaxeOX3qfMjY+rA5S6trFBt7+ap4rAKJ0zrUtPNK7Vc/UR3TBpqi7K61At96OsbKQidbuJLJvZ1obUkiimRdUJil9+JaAM0Gwg2PnJ8aZpINONXi3XpZctrYxm0E+TpK+Z/b5uW0CPPbVW9Q2YB5ZXo+Hqov1KlU6bXwa4tD6jQzNuHQyQ2f37OsyYsaLf/va394uf9/swIfLFqI0vAlvaqto1gedjejYiHmPqSp3syyE4zwLYFrso4Ncl7K3S2DhKGNIUF6qbNLs9syDdK8+uTf8K3oF1G29tNrxZA3jXs9CJfwdAnWlg6gNU/50PZOro3+c3dm0w/ysDAPZ+b9bPzv/s/T+333y4v5DYb+Njj3unmxN7scAkFX4NIQDe7Jjsn2mmx1UKHyBIJBlEHkNlYye9AB7otpqPFEanSXRNzoXFH3fIPBJM7iCL+ECHbTI/9alPuUXVzJEtReVfNzcn+e/tTFlabDK7nIyOZxVbNw9NZr42d5Xp1t+eqWiKvotdc/DhJnZl0q4o0UrLImPw8wFXPIfYsS/UzHMwAM+ApWeVD1PlHWKDbnHVJCY5lfWY7I/RV6//bw3j+vR018DUkMfLbsIYnOvVOQn/7w2iEJNKpBpiJSCpc4x6qbq//4nJemrNCH35siUaWVXnUnwZXR3aBzD72n9eq8qcds2YvIs9f5T+nKW8bh73oY7WTV3a0DhFL6+erEuPXalLjn3IzeWbbr2EeN2nf7riaeVy07uI+wwPhfOy6ZdJZWZgmJJsPjMLKYyK7UPH1qVv3XOKmutD+sENq9BcbWIshJWVT7uiyXfAwkVZMwdufdeNr1l2JF+LX8Qw/KUdAKNc4nKldu/dq5JizpcWUAMdMeKrtRWMQbVl2rrbx/uxBrd1bNGHPnKmho1wjIDzonRj1Q31NGjm95ByKrC3aKGX5t4b8UJ7wrW0UsYE58GWW/V5GIKP0deznPuQUnaKlHCigLeAJDGbkbcWyg75Rx/ygMyuYGtzih1LnxY+tV6NLUkVAEAiCC9JTKqCMXDs3NEaDa7I4K7HgNoRxP/7j78BQ2GBz6diTZtmi4JRsMaCWVPcO+64g76sT2ratGk6//zznfjVApgtLJ/++Cdcg9xrPnSdqwC1w2KtATJzJh4MkBlDlmLiZd73J6l+o1oBRkU9EXVQjRKGropWz1LxZHR046cwc3MAq7hkm2EjoCzTgRlP3O40crZw2iRAu9q4L0Yg7dGarXvwYsM4sTCbdG+uhlcxIY1BdpMCfzaGfIhS7kQIAMBEysWXTS++pJ5Vz2lf3y61NubowXoa5KI9eG91lYZnx7Ulul0v1bZq7a48XXpkuWqYYdU5Bhi61WsBHaSXpC9nAY1ku6qogJqBWBhmCbKduQqEsoob/pfknK1e0l9s7do51omFwBZ0uw+3/udKlQ3P1OSpNbCkZXplSaMa6/bq3AsnasRwm/hD68U5ZEBmLUXsWu5nXAGzpAMsZWGVwSkzFk7fmtdFi/SC7IoyQG/2XwoG0L5sLLLeeE87wPj2q3zt9f4WjXjtenvCZO5Hv+a+Tg/piie8k+gPzPxKN6+ll6edtC+Xxu130i5tYfOgnz7Tb6dlj32jC3v/a3QgcHYg1/Y3Avm/Z1Q2QNP/87nrxOf3rXB6ejFnzjL9kuf1l7RNpPMn9Ma5GZb6R5INlwNB/a7XgT6LA7bpTYxdS2vKHXCeP95Y9PpD2tywr4GXtEMFkAU6SBOuOxk9Wb42dhXpttvPUR+NtAsLAUhx6HCqxUMwSBGMvDPYjcZTtTBTbNoxns5g02ISF5OYmPQlnANwyiTDghN989YxauicoX/44I80lpfBi1WFOQAvANAmvMEKMvEXIw0Z761UTm+WRpAqVHup6pFjPLbgIr2wvUA3X74UbRmtjgyQdXepkaTF1/7zOlVkt+kr1z7CSxnbxJXmy+atpTJf3lCpRxcM06yZwwFlD5ul2AEBWS6/37Bnrh5ZXKP6zpCKcpforONbNL6ijv6fAd2C7UVTXVA//Ojq1wBZ3sXSlD8i88XbK/D6Kse/HC8x5hjmvqwJ99+3nAr8TDW3RFVeWUEWqhU7o4E1uD2Y3SZT+KLlogDrTLKpx9eys0lzTxylqTNLaDlo49cqBdJ174w74wedLVF69Qv2smmtf0B9DT+BxXzVa5aQweYaU/js/MtolP5hbspcAKjrN4OWz7xI2WQwnIeqQP57xoKDea93ASAjyc0kjNH258/3bdeunTHQN8xJunJxeHlc88+qoT+a7SS51QR9c5z3Dq+59lCO/noJ+9lf4Gx3tXTpUt16662Ozp8zZ45OOukkjUj3ZPOf9w+f+rRmzZqlD157rWPI7HBaAoRPBsj8BevNztGc+qOpDhX++ldSw3q1ZPWqGKf9Pvpshoz9A9ioiFJs02TNOJbtRom56PAfPcacys4mpE0A36fLFkh6zTGaTW9ukjYXpLwGAOmiRK4jZol90NZxOHygp4cneurV99KDiix5ngCXVEN5mR5+qVsP7YjpQ1NSOm8CprxYZ0RzetRKQPuvp2DtsjbrH6dNUBVi0ziMZnPOKGWb43VPM5r+vQp0jFPwmKOk409QRx6fw5xzOFXr0etmm+mGjUlIgwG7TnZtfWD8+19s0KnnTtDYCWxhYRyffbJDOzbX6Sx+N6y6HWbJtrZ//TFUQJZhUcM+gwPFlv62kinz22GhsIbx6aDkLY9+lbCtmwZFM9RF4Lfq4iCiY3c5LIXD9XGpZNPIcLEG6gP6139y75m2IJtu6M06TPSX7BzwvdLM5hv/5rfKstc10GD/PlC1cv9KZPvZWdykWVMf4A30GX1vwP5WNPazz/L8LZnEg73W/Td4/ry0jaS1AnvtOjMazN6GOWiVyCE+c8BsE15D9u7nuIms7GDzkgFzGoBJdT1d948ly93zWlBvVuRiP9vrmMn2azuFNz/zQwWQBbteUO/aE5UJ+FrTUaRf3XmBAnlN+peP3WskGvGgAhueMvfZgvhzZYS2E/rx4ULjFaBwqA8gQFtxZMMmSgMVmZEzLPPdd0/X1u3T9ZGr/qCavK1qK2QsEhf3rM3Svc+ci2B+l7JKS4hrORpf0qWrr3ic+QAg62rSC89epUdWBnTzFUscIAsY0MV8tskBsg85QPb1K2mlFyArYOHXgrntDyPFWrRiHBv5fJ14XrYuP/5h14XvpluvwEcspm9cuQDeMqQu0nlZ3LI1u+bo4RdHqrYtpNL8l3TW3CZNquwUBJa+dudF2lebAUMGUMza6jFkee9hPbjT+bQFBjVWtTpWk+TQXGVzQvff/yKxP0/FpQCyJqQ5g/hO9FhspgAu1+RBfIjebgoPyjL0weumuE46EYzcbWExCPXafxbJWJ0IHmHAqlofUvfeXyrQ9rKyTGNDgR69bdSNL2Z2coJCIy+RRn0QJpNG71yPHEx7GfmwnMTEodVsHeyU/bs97l0AyNpB7TQg7cnX3Xe9jE6Ipt/cw1zc8Ztp2RFMteniS+fSrBZrBeqDI4bojVUCbbj45fJuf5vDFgRbPPZC5xoj9tBDDzmdmKUnjQV7YxrEQNs/fv7G/YAsxLm51BKnY4DMAmfoQOoNkSgAACAASURBVPmIfqdr6a2uRJPyqeSjzFJduTHlJo1VIZeIX5q6O9BD5Kht1CwVnnCuMsdOcvOeK8Ynt84CxhZacGcnRCsNZ75sGoH0DsUqUtM8shP62+4lgfA0TGD3uI1MWfOS3Ci7mEXPKPrSI/yqW9kllIq3xLVgR4keqm/Q6LJmXTwyWyPorRlkAd9MHfNPN1Pp2vSqPnfqGapMNKMd61Yiu0QRykPz0GskaLEUzCyneS/l4WNnK3POyXQaGeEsPTJZjMxBI8V5+OnK/syK7+vWWh9WyXA7T1IWvPczTzWrbnuPzn/PSJrbtlJpOzTr2aECskSABdWFI7vKBmz71ZU7qjR9sx0jmWYt0lYt7i8WoEgLpwDvtpzaU0i48Fp2lezJnjGms9lgM9I/PTdY0/a3MissbeiDF9cjMN0ax0+V+q/1Rlfu/iyWPdbvMWiP75/G7K9D6X9e/dOl/dOc/R9vv/f/1r9Ypv9z7b18cOa/99+CPRzsGtp7viZ0TjOJ/RhCm5OhtFed3dturB36iA0GgiNGp1j8snjGF77QpNTsMVR0YysTY46FaLFjcSSAJ0IExjwzK0QKKYNxn7brsRd1NLyNLau4ZsywSUiZNxbshnU+Geh4xwFZIkrKkoW360XF8BOL9GZqRXu2fnvXJcoqadM3T1vneRZFzBwSrRnMWHqSEETwQURcb9YRivDdLoFNGZtIPSOwvMrTL1fVaNnK0frU+/+oKZU56kZv1kfx0T4w1KNL5yveVaFAPmllWKRRhVGdc8Z9rn1eMybYTy+cr4dXVADIXtao6lpnSxHojaq5KMMBsnL8xW66dJHC+c0OHPZ2mC6Dt8+p1isbj9GdD1Ro3uydumruo84C7aZbrwKQ9ejrVzxJyyJSlgCysL1mVjlrYJS0K5XpSHSyigh48RbuZpe+/edjVbcrCUNmgGybB8hyLpCm3c0m0OL/YEk9fNFI4Zqdk4Wa392xAs9KNkmYevcxRoJUsw50JMy3k5Ryd0dcRYX4XeJBedY5U3TEkda7kpZSrNcZZFcsm2WYFJUeI9qjIdxXyxqlWn+gzub/tWUF/RrZHhqkxzHk7aMRfDZdT2L5UxUZ/Y8QD+9DlkM4xFg3gvJMYAIuymBT8F3190MekFlqx3y/zC35v39+t4ZVjNCUaZNhxAppA7RWe3a26exzj6anGJWQVD9aILMJ6doxuGAzdEBmwMoKBOx4BfPP3/3ud46hMTB26aVeN3oLuv4C4y9ctiB85hOf1OzZs9kxXEuK0ZscFiP390IcbLgwAOPJWoV//WuCy1asI3poqUELIip3IgTfeDhPweopCk2EIRs7A70XE8E+tZMbMZnCVhWVfhNSuubu3z+1lOhuJz6bBQZpk6BVrnqH4/LsH3z1EZ1CG1+gxHUdKcouJYdXqiTEhFjLbqrwVL3Yt1H/2VCnqsgofWMYhRWNKf3Pnt16unyXvl4+WoGy4WraVqvxTMoRuMj2INLNMtuLTgLg8HZl7QGc9lLePI3PMPdodQ4b7qZstpVSpX3WXs8q9LtoBNd2qO2sbOtoUI61xxbt3Nyma66bQ+rVgsngxiAD3YIhA7KMfSyY+OfB8rqenu7mc+0SpjlpVl/dDx1zkYAFTSbp/clq4aWXPW+17Kwb2X0AunLJI8NWpgLoOng9jF6cJ1zeG4wXBxtOb/XvB9KP9ddmvVWG6Y1pxv4MmN1jXy/mgyX/vZzpc1orZWDMHufPST+d6oMxX4t2INDoM1BvBgDf6vU52Mcf6P08DZiBaO636//nuZQ7nM6c7YFlXr+iG5YySmV0OxIDr+euMduOCQLIJRM2U72uHZb1dcJ0NmphquOCbGaqa3JUUVmoEdWFFBWlF0F7D59YG0Qz8Y4DsiSbOMZ7sHOR4utOVJh2Qsuag7r93ouVV9mlf7oabS2Gr7FkKVYRTCk0m9ahJQmgCCZriJfov5h9CarkzfzaXByKCMMuk8fS8Lvnz6YQS/rs+x7FTqKSzSeAVo208elVY89J2AxtURsgzyr0c3sqccGvdynLaE6T7n/kWD22eowHyKp2sbnmnQBkrVT6f/UHH1RppFlfuOwBNF00zubcyE67I55VqZc3HqE/3V+uk2c16H1zF5BuNED2Pj5Dj75xxUKiBcA7CyAHCA9kFwDIusEedDrpZNwX1ShFmq8bMuJ7j5ym2h19MGTLqczcCQiymHGBUtPvZUzY1nwwQNbL+OrFmqLAWXXso37swQeXYoFkHVos1T2ICwHjzXp1xnvoSsBnHDEyrAsuGMt6YqgXuUUfBROuSp+m8KQaTTXtuFlH4lKlv/n3vO+/A4K3Cgk4RO9s/rADUX8T6xGFDPFW0V5ZkcoPQTZ8mw4GlY5NjJghGwyZ14Pv/5/jkAdkFjhc6yO+Fi3eoIryUm56GSlL2/x0a8cWhOiTi111paWDHK1vO0oL4Na19G8AyKz02ywr7DBfMfMXM8G+sWIHEgb7LIU93jRkls689kMfSqsa9+Mc93qDDieCS7RnmzJ/SRVN227Vo4moxDQwRso2h/6QvZXjlVmNgK6kEjo+W+b+YB/bTUPHMB1YZ+RpSWwCmIoYBoeJ7OM2Z0Bo2xmz9YDVyliM4eu6Z7QT8LaiPU/hroiOKOxVVUUnZoiY6+I19qcVSd3XlqtTRjFV6mPoL/AxOzag2ZFCPbq9Xm2NcV2NR9fw4bZD38bkpJQ8Wq1tJVs0pgfA0Wj3LVvdR05ThNRvqBThqkWptHFsfx1Z/9Yb1n8uAdKxxWnrjlY99MASjR4xCiPLSXwAM40dLCANPJmHCsiMXHesvbueBMguhLdUevVGFwPqV6ugx66H9ZEDUOJK7lS87rAbR7N3Gv6GSfOG8sexIkxXsOQ40tLHoDkpc4s2cPt1H8BnrHy2zG1QhnCYsbHpITdt2uRYsXHjxu1PIdvY37l9h9uImGlyykr5/RSlpSn43J3MHXvc6NGjsabxPIV8kOcL7N1QTes0+wO8/j0T/TTjGzVoBwI6BvoMePngq7+urD8rdzDGzEO4dPs/a/+U+1+ml23RhO+Mcv1IIxrYqm+Ia9PmHdq5ax+i6irXq9fYsLi1kTOeFXSQaVYyNGrtgS0zTViSAOn5b70Gzpx2FFBgaSMTaI8dXaExY0pUWszDfGJhEILhHQdkLLwJuqME219UfMOpCvcWaFlTL+2P3qN80nYfvupBrV9TpGXLRqB5GkH9Qibjjq4qXKskQK0zj24kuOvHOoYpxEZ2eFGnZk3ZrZnTN6u6aoMeWnO2Hn44oS++f4GmVk4D6KPpw2/MEFFnvIj5NUxNfevdnigHp/xgHBauewRC8w7d98g43f/KRDRkixH1e4AsGO9Va3FYX/n+B1QSJqV69YOABqN1LBzxojQVp4RSL66o1u/vyqQgrUBXn/SE1V3oS7e9H6F6r75x2dMwZBkYxrJxpV/xqp2z9NCi4WjIMlVe9KrOPrFV40paVcya98/3XaJdW3sBZMsUytvjAbLM8zCG/TMSmYMAZA6Zm7+kdblhQ84/n31+L1rcPawN9Gh2xURvfhgz296O7VRFGdmrBl34nln0PyZbFadPsjGXiO8h4FhOyIiApPyku5L1hLx1Sm78Pn97nOGIlZQDaSOYA62sW530WzZtZUJdTcTxnMnKHnuLVH6+EshQLLMTcGvX0OL7UOf33/r5hzwgs+FiwSob9z7DCS7I2BAytscyzWgKrIE07cfTiSEz10rDHFsFh6gh8y+4v+vuDwb665rsZwteFnBN8G+HLQymITsGp2sHyDhxt7AQPJ3BKoF1UEDGmGvr2anI7XcwQbrVPKpS1cXj1EclTAghvHIr3XuZT1gvaUkDWVkwXc7pYn+6Iv1z+sPYhtxPgwRNlMTu3IoHrBw7ix2HB8j4PV5ufcvvV2jDVtVu3awX2EK9gpt/cneLjqsq10lzZ6gltkKTOmFvGrJ0Dw3FH020QLMHdFJBueaOLNLDm3arYcduTR87UTNHDacCtFN5uXQfMA0du/0uROH5LC4ZpG6SndDT6Csik+coYHq4EaOdLYl/9Adl9jvvXnQyNfPVtI+d3cO4YsMyXXLxCSoiU9mFN1q+maoN4RgqIMtA1O+GYBLRb+tLijUv5MReZIf7skudOP8j66Hq9I7pE/VpSlJKAdO0EFe72V1byiqr5GLljLiKTOU8/HsqnLekL7q3Z9v4e6us1WCXx8b1v/zLv7iHfe5zn1NFRYUbx83Nzfrql77sfrauBP4cee3ecH9I0xuIOuecc3TGGWc4k8j+oMifJwbO7G92NDY2uqpV+26azMrKShhxcyh+/WEFNVvpkmDPNbasqqrK6TnfqEXbtm0bLa2wzWHMGds9ZswY99h34vCLG3yhfwwLnSzYCRPcQ4Rp2YpGLV+xU43Npr/JozCHlKTZCJjMwFh4Vsw4AM2+7LXyqLb2Jnp6Phj/7RdZWHoMzqetrUuxbvwAi3I0eVK5pk4p0/ARgLocYz8GziC884CsgzlNp5P2F5TcdBYFRqVa3hjVr+86S3kV7QCp7Vq1Mh8AUam6pmL14UEZwG0/AtuYRSeXVPZqLm05cwh9Gde4vKhLk8Y2aPL4vWzua/WL+6phyEbrlo+/qGmjxiPg3wzTA+hij/TMKjIOHRdhrUG/S153WCiuGePvx0h2LOL/rXrs6TkAsvFUWb74GiAjS9PKpvnm/3i/ioINuglRfwIJAt2DcfCvZn42qzfco2XbhuvxZ8s1sTpHHzjlCdcj6KZbPwAgi+rrlz6pAjai3ZmtykFEtmWXifpHwdhlq7xwuc44rk2ji7c4a59/+f2l2rmlRz/4yFI2bXs9QBY5V4lp95PaOwhAZgss46SruwnSIQ/PtUzR1ECPPrJJe2vRoA0i0rKx1k3V/YgRxbgfhXTiSZWmfHXFZJYZ8MjfdLqSwgorbGCXzxs+Q5rycUX2fYd0cJVjdzOCe102J2X5W0tTWxaGXqUZ2DLZxjtYSvXoyE8Clk9zW48A1aosgO/ENH7b3vNdAMhM92DViFZubYwOIYQ1uo90nAGyjEAOunbEvkG0GG7lQ7AOje9Js4YOyPxd/Bv1Sz4w6y8W9u+Sv0Ba0P3EDR/V8ccfr2uxvbCBb6mWeLoLgC0ikcF6HTLyuhCD5z71DN0k2GGNG8VHpPyZ9KdRt+aKYZsY9+VAqUcxGzOTwvg2HDDGxS6G2Sg4Z9v96749zmMS0Qy4q+VJzYPsPBKtzdq3e6eGLfiONsbz9fzOXtW2sBCUZWo3AS+/K0PHhcfoyrGdAFHcmCsSqks2QuEndSxl1CNJJ/xi7069gAP1e3jbC4+dqqV1m/Ti6k06c+ZxmlEBK9C1SrmxY9UZ2KXGgnZVsiPKbqDYoKNQPXNOUeb806D6rV3WALA1VQ/NXqK1K/u0et1OnXBSjWuIHu3pohXH0CfrUAGZq6PNIMBHn1Znwx2KNdxF02BktG5XQZ+8iFVRelVvLlUJpWkVRJZ/sVRAG73mCjNhfNzj7SblIyiepaXbCvXqxpgaeowJZEjAhBlQMgZr4sSJqqmpcYaMQz18lsr0kqbTes97EAxz+NWDD9//gAMJVuThd6Gwv9s9c9WEiJk6OvBvmn2UZh4x0z3X5oC/abF/9wdoa5Eh3HvvvVqzZo0DaAbKLrjgAgfojGHzN0Hr1q1zjzOwVV9f7z6raTnPOussx17bYedrr/fgg7Ao69e787D3uuSSS3TxxRe7cxio7dlQr53/fHtPe+8dsIlr1q5xJsKtlLXZ9YoQt3pwGQ2FShFST1Rh0RiuHewnulCrfgtkWF9Zz+LCsRiMkSAV1CHr18o1Ns2Zt0n1K1W9cl5fyxejnU4BJqMRtGZdmFT3RutYPJM6bm61xk20tkMDM6iHAiCLA8gyWpFMbAGQ4T22orFbv7zzdOWWt+hblz4ssprwK9OVzDMJx3LE4EQ7wmB2aBLSMaw9wPmpbFKZVANapsW0qfZlq/rjay/CK3Kybrj8JY1DB1uXv1JYgKlzz0j97E/HazM5vCCNwK3925HlUX34fc+qJK9aHX21WvTs+/WnV5L6Ejqx/QxZGpB9+XtXqyjQoOph9aQ/kV+ERmKXSXFadJdi2GHsjk7Riu3TdMXsJwBkTyoDLdSXbvsghU5oyC5bACDLpCCqGVccOsvAhJFVVF4YfW4X8g7rEdy1GalGVD9acLm2b+p0gCxcAOvEGpgVPgen/gcODpA5YRcXghRjb4zem2Q0oj0BbdqY0uOPvspaO7AtihWHhFnDMrNiuvzK8WwQrIIcmyZ6g2Yk4e+xHjKthhPKYP3jMqBUt6rpj9pX93MVxXmPvpn8jVgYfNZz/WH9DiTZ5If2qTNZoMIw2ZKu9aKuT5ljfgRL9mmvYNjaXGloG+6/1Rz/W73OIQ/I/lYf9M1ex8C6LXQG4Orq27Vr93bNOmqmA/FWfRi0RrOI3E0Maz0cfXCToAQ9w1Urea98IGBmDNnHrvuwLrvicp3BQrFr107d9+f7tZx2JNddd53mnXLK68DRgc6RDD+/NhmklW/boPZYQs9wFD8qc9y3/ycqRUSLItsUMb/6oMkDoTomAE2BSYWlbGsC1WKeX2IXJCYdNcpqZRdFYwu3c0GqxyuiTurEL2zp84pRTckU172tmfptS7feh2btcyPy2DzW6gfbWrRx1yhdW7xPs0+mUrK3Sz2NfSquGaHNvfV6eHGz9jTO0PxJL+jY4hlasbtBv8S2Y8qE8fpY+TgVxlrUFqlVYUGhmumdWQD4sGCLSY8aKLHMQxibk8E29eIvwgaNpMImJDb5bvmgVaZ32Dlzf7ZvDOrRB7dp8syETjqNogGCRAwNScSVjg5tBA0VkFmVZQZWJWq5jwF2m+KdLzpSIs5Y6ubzluFNBF3GVycfBw1ZWtyTwc4vg+DaV4Koo4vPwWYxZOlMMFZzYrqWrB2lRUspV8f7zYCHgRzTXdnPLRS7WJr8iiuu2N85wBe9+xWqthk4mJTdgdJ9/mt5Fc0G9L2WWvY9w2wwHFfNf86N3GPt3O1KpzPteXa43xszS4WhbXwMIN3y1Vsc82b6zKlTAfFUMptu0yxlPvHJT7B773Ttyb73ve+5v11zzTVIGEZqEV0xrJ+ofWYzYrb3sPn3ta99TVu2bHHMnjF1G0m9Pv7YYziun6obPvrRIY8PP/36Rl81MwouLUXXBJNl5/Xcc885cGn3yYCmfVbre5qTB2vXnausyFhVVh0BIBvpTEP7GBNBJmUwhsegA2OvVUla8Yf17XX2Jxg7O8DGJfUYZJ85NqYMkIaYvZ31L8iCl5NNb0aMscOkMWcfU6YT5tYMytC/44CMz5NUG2nbFkUWH6Oe3H3at+cIffPuKRpR1qYbz35EOUX56qPtD4ORNFen2/Ca5sttYLovIkg8R/0CVd3hEljlZq6rB9jiLUdp4boe/enFU/XhC5Zp7ogX2AflqhOboXyahseoANyCebxpU1OR3SrLzSJWjlVO8S5FA8Va/MIkPbwqpMtPrdex4za4/X8sMF17u0q1bElSa57N0Nbc0Q5EW8y2qWLrhaX5vN9l6EMnrdCIisXKHlGq7/x0jjpaU/r6jdvRuTWoGU+w0sBu/CSdLy2gCVYXxjnex6YeFimD4q6vPn6y8khx3/Tpx1XYtQ9ghHweW6jMUaQvnZ3KwRw2H207btPBs0dpxCR22SvrtHxtG+O1GnkQ9Aep4BKWmJ5oC+xWUqWFFGC1tKlqREAnnjpCNWNtA2jFM+gYLV3p3ryN3+SRvbF1Kt0eCWYsuuPfMdEFgJEFGOgwTW0wNYE50EwWiIrz7LH4WH5XqrxErdzHIpe2tMFvi6J133FhyEl1BDOH9fnBXIBD5jH/5wFZDGAQNu8abuKGdX168okXdNRxEzTnuGpubAtInb/ZwDL6H01V3LkLWxrGT4tayTVsHQ/yd9umOfOCZUif+dgnNJ0+lgVFBXr5pZfxoYnQ4ukUnXfeea6tju0uBjqS1kzXlQ17NghuYTNzUE8drgBgyRVSMq7jDEwDVLYbCaYHIhXEDgDEeOqehqiKMkMQbV7JcYwFM5PgELPqNzt/WyT31ZPXf1XJrexc2nbpuXXVum334zoWUfDHxk4G+BHwcotoiZSnexH514UadGrpZM2nH6bCDWpAPHvX3kxKtLs1Ib9Tl4yr0f0Aime3detIUp4XjKcheqnt/DKVG5rA7F6tztwZWtQU1Np9WzWrOKpTKtm5U2HT2Y1Yd+YMldCUXFXj8ULzrhQwhQlqBQrG7KW06Cl6sW3o0DkXjVJVNZoTFuO+eBYCXD7RwUWkN70FQwVkxlRGrFKpbaGStX+AeaSLA2lW7/ZY9zwvWLvjDedq96iH4tbiPNJ1Nt7i9a5/aarwCHbJH4ElPUXdIS/1ZoDAWBdL9RmAsVSeC/ikyqdPn76/x6o9tr8VxMGmN83exQCUbSR8Hz7Tl/3if/73L6o7+5vUhrkH9jjrVGEgyz9Xm0M2b8wvy3y47Gik/+snPvEJ18z6o4ClvDTD96WbbtJmgNRd99zjHmc/f+ELX9Bpp52mD8M822eIAnS+/x//4UDPt771LZWSulyzerVzl7fHXf3+9wOGoiwmPfrxj3/sgJGlYX0rmjcdAAf5Bx9k9vc827NnjxY+8aReeuklB5KtuMe+jMErYtHMoS9ttLdB2TmVamsJafmyBqwHWgGnCNLpamG+WWGnH/BTkv5CbsDXO7Eu0kUeW29h4vUWPwbQQoiuMzPz2KTk8pmpy+tupJtFTEcdM5zqbyoIB5kfhzog+/LHHkGaeRTMY4Zq9+SoPcomEylERijL9S8GvjmfxUC8Wj17suhh2aLTTmrTmFEgnOY2PbI2U39adIo+dP6rOnGk9ZB8DZDFAcW9yAISKYCsVsJQFaFDK1MoF3YKFmfRczP0x+drdO2F+3TytBchgthScT2jDGdr99fXSoqSggDv3njz2//Zn/Ml2aMALzvVB5n/rz89SU31AX3xhlfJFnQoCrX3m2cuQhuXwn8Tz0lATQyddB+Vp8aeBzC7DWbUqbw7W5/+2EOqCBXSRSBHnQW5yhtLKyfsPgZVkHoojMN6NxuwoTCBcWQpwt27sDR6bD1pfgT+AbMVsnZVBCQ0r7no4DLpzB5OtTu/sZmzy5Vf6Fm1uIIkl143AGo1+jlOkuzyM70b1LvrbjzHfqNcDHdxJhxwhqWsH7GLcTDKzgKGa1pE2nL4VUjyxmCbYdfU/s9HYWlAZq8KHWeZtXfT8X8ekDktlUeoas8u6c/3LlVRSY4ufR9Gq4Em9TaX0udrG7vMRs080lJBqDuNSWLAord02oL+urL+6RgLZv/wiU8BDuIaNrza6VvmIlifNmO6a6Nk6YYcqiIHPAxQpSez43udNYI5pNt5A/q6CMIs7olQm9gLMwEjzjcsg2qfTuw58mxLkzdaWyjqe2LhapXlZemM08dT5Wj6Eiw0ohj6EbS7WfCdQQSLWN/zjyqxb40ySzIIWJN0x8b7dcaYHL1/ylQ+c7sSuaWk0gr0Bxb+x6GjP5dfo9NrAFGIYf931Xat6x6lYyeV6oiqLWrZNlJ3WHlRRr7+saxSVcVMrOAqtcbCtIen4Tq7rbu7I3q8jfQM6YHp9J2bM6lY08pGK7cpW3vz+lR10jnSzDlOsNvHf0hGuQzGEkS4DgH94TdrFevK1nUfnejc+aOUredkmnLZDzZ//ZQcKiCLARYsKGb0bUVG9jQpyyco76b9S2qb283FCPJesGYbDAPm2E+3BtsPAHbMfl2bGKvIZDMQ515nVtygQMX1DD76mR4gnhnDZIzMAw88wOJbrPe+970ube4Dsb9of3MQl8cAkAXbb37zm/t9yYwF+vJNX/JMX9NN1AOANh+QJc0INovWXGxQLrvsMmea7M8V30ssYG2m0ozZVtKPX6P91MXvvZjHXw4g4Xrw3/9i+fLiokX66X/9lwOeO2Ga/+1f/01XX321zmVj4x/fw0l+yZIlzhuwEBHhwoUL9T+33eYqod9DitI/vkFnBStS+Pfv/TsdKbz05lAOP+Vq52afy66vbc5u470fgBE3gHkKbPjso2ajdfPmu7GCJs631mxuArMQ7tqd1MrltdqwoZWUcCaPZTMYwbGTw9Y5j4W3ogjrXOExYvnEEQcCrUozPXYshWyPcYUSplVFktBLs2hzU6+syNSMWSM1aVIJaV6PsRnoONQB2fXnPUI6eJZefjlL23ZiJpool7lLRLjOZr5dG3xZZYXoCmPD1QPxP7Zsoy44d4VmTDLmmbZ8y6brjmfn6UPnLtNJNQCyrEIYMlrJwU7FySgETBqSQpuWsRv9K9cryuYoe5+6uP4GyP7w0hTYtVbNnbaRybydQgJCGrpOm9KZiQnch02vgTGb5gbK0t8dQOudzEKyQR2hUfr+b0/BtDuuG69dpOqC7dZVXfe8cDnsVIjPiMeXFbe5FCCm4ACNSDBX+TBpxZzfqfPu4WfGMhWZjaFclU19Tu0wRgWvx+h/eauNgXTAyRP3e6x32g3AUrpP7kE+UMe1pEqXHsUtrY0u/hbl5SM5SWrqxDDgfpTKkTObzUacIjRLj7sxa2MUlspS7567SgP9SO8iHfwbZBvLAWSW4Xh9UdIbTzCVGME92O4ximDovjjFA7lHKzz8CgWGnce98bSlToOWLnBKW9C+1gJxKJP77/zcw4CMCdRCf8jCAkPZhQTEbr34/E4dMXsC2rR9qt/WxA6lHhFsiU6eNxudB+UDULKxeA+gyguuvpi/f0WbaVzuvvtubVm7HksGzP+uvEInnkjZtgn+GZxuJ8F3C6QDHi7nbiOOAOtcjq0xOM9Jb5y7aYibScVdJqmxgLk8tIIqo3iF9d3Lm+Bvk/8jhUrma/Puat3z0DomUg76GVIj4CdLeGbHc5xLtLFPJrSHalJ80ROK716GAV9Ua0Jl2rwXPQH6k1EFKX2gTKmXvwAAIABJREFUAi1KrEl31LZoe2Ox5g2foHOHmd1IVL/a2qyl9X26cEy1ZldlaU9bnW5b2qWTizN01tgCDa+wN0ULSAVBiqDSi7/aA/XQ+1tXIdoepZJgibZt2IYOqljnzR6liuYtio2fp8hMBP6wczEEpmbv6Lz+nQM+YJIJf+8d6/HBCer9182AEcBXh/dw5sCutHGwiDTw5R8yIDOGzNIUqFwU3cOueRUGiEtpBbNUyegadrgAZlPwWZWlKYl9PwKLxuxUu3M3uZ51KYJ7OJtGyJXHQ9mfwnZzttuFZ4TQbKQd8Pv7jhn7ZM3tf/KTnzgwZoyTjU/76m8hMVi88assDWQYA2ebCjt8cNfRhm2KifrT3iregpNOWRoLC/CwCmV7X9+/zJ6/vzrZI3/cUccGwvqBlpSUOOZrVM0obVi/QXfeeadjvn7wgx8oh9faW1vrHmepv6985Svutbds3aIf/uCHTkN38803I3jOVH1dnWPS7HfW/qeMwoClS1/RT3/yU5cO/cIXSYcPkUH1/d/6e5pZPFixYoU7X3tvYxVN12eHXy1qPxtoM6bXNmwh7GksxQiRxp6oQdu2dHB/Q2rv5Rd2kIMxBszGdZjHBq1wh1Vo794G993pygBe9t3sLnzg20OhTB5271k5cUBqghR2hSZNriZF6r3sfnb2TQbCoQ7IbrmeWEclZPOOHoqfChDFZyFMb+F6hrG56MVmp8lpytyyn5rDta7HFmInDvTMyZ4aPbMhR79bOE/XnbNc88YsBtAMY2MLq0abuD5ilKXizfUvQSzJtSxcD9qm3GayCpmkyY/Sb5aM0lWnbNe8CS8h+7AXRUJAC7reXisMoFsLrJHFIJO3WDowg5jl0oLuZ37qfcWlOvdqon5w51wKkaL64vte1eh8y5WCGA0w8fdE0pzPeQ6pV4slrmiLo7XvaCoxMe8ufMo9NsU2tz4wTsNmPw67B4v1JvfV/7UZwlr9lkvxOjMmK9CxVm0es9RAsdSCJ9Zq67YuiIpqtwmO4X0ZMLDKiZ919ihNnAKyt1ZcFJVZnsUKUDyQ5ylonJG1ZQm6n8f+6HvsPR9FX8cfussAwGb8/uZHRmIqqeYVTj4UiMGUEdd6uQah8qsVLKcqtejs/f2C7dzt2nj8hXmg8CZDC/+DXL2//Z//zwMytzmnWsOq/JRRjNAQb5rbAVFQ15nZAWWFtiESn6QJVCdZRVsc0WYQXwmvt6Adma8znNy5c6czjV25cqX2kYLJIoieMHeu8yGzdKUdrr0TP+9niwe4r0bZGlkSt7SiCfV9HxeebNkMNk4KoPjM76SKr281WqMHIFN+CXVPFLIsYskU5dV8ieh/saP1w6RRJ07mgyCiiCJOSEJzZ9lr2XUwAEMg6du+WomVLyuwFUq5sF49qZn6Gbv2Rc2tuojy5t6WrVraWquJ40/Ql0YO19aMXfrT9g3avCtX88dO11GjQtq8Z4sWrNqr/EkjdH1OWCOtYRnePnspWy8jYLST+1/UsUb/vaNOH01Uq5pKuruoKmra16jPZ47WlPFl2lS8UxNO/ZyE91wskxQmp2h7L3yluSAekO2CFdy5Jcx969Cso0thFdg9GqNk4cValgzSy22wKTVUQJai1VQAlsuMXe0guca5E2wR5fZFd0PCfpdAZtYQ7bCP6Mi8olfP7NPSTNnnUWk1mYq4I0lNTwU1sxUNIQ62McHrvbFsoR3gbPowPyX4ne98x7FBn/zkJx1TY0d/c9bBPr93HenKQEWjpd2mTIE55vBbHnWh6RoKIEtCMxurZofp5+65+x5E1o+78x+JNszSfrZwzUewfyHifkMQ9n73Ieg3oGbgzSom7TMWwYoZ+DmStGD6BfWb3/zGGTjb3+y62Jw0G4/3f+ADXgp1iIDsQNpRA7H/9m//5tLG1ufUwJ8d9nv7XH41qXeO6TvAgmbmmbZYW1FHK2mqXTv3af1GFn+zvkFA2YNFSi/eZAnz53MAjXR2cVGaocQcF3lDgtXUVexyGCyuqamABSxSzah8er2SwsJA1bZ03vU2Zm1gjc0hD8jOehxd5QQq/Wph0GmsnWMCWuKeFTD1os+MsYHpfQbtN/8us6ISi5NbPISWmK+nVtTp1wtO1DVnL9NpY6l8zhlJbKKdHNqABEEx5HYY2GZk1CI94GfYf+XsYBaX66VFR+j3y8fp4rmv6KTxSzG75jXxyUrkGIvV4fS6ie70tfa1Tf739H2nLaNIsmhXdLx+/Odp6iXd+oX3bNfYfLw3EbLnm6behQ7rhWuAySoLeXl3/nxZQb+hHq8JA2nOgDqyzlLFkbfyPMTwg6kEbdPEBjlC4YE3F7wqXudx57IQIS1ZVq9nntnIuCvGPqWKVoaNjKMuzZg+QiccX0Ga1J5nBrPmq+e1gjOLFgN5TsVltGFss5J1f1JL7T/R3J2/sQ71NOTQp9M6Bbz5EUhMQW+L6S3XIJLCboQipxTXNpk9XsG8y5Qa/1XmgZcS9UZ9l+dzZv2DSfEeBmQDXt5D74/JFDw2QKyzM1eb1vdqz+6YVq3ZwkTI0qgxVTprPnl5QwG2AOIjZTpx79YzNehbFgbQGLiyVOXixYudgLeWHbyJqi1N8dMf/KfmzZunqz74Adc9IMZXZrZni3EwgAw7PaaIVd5Z705vfCFZ58nGppivFTXKbb/CdPCfnc9e/dZihMs1ohW4sqvDOnrCc6oc82UFR3wGTyOMBS1oWOqTvmv2Si18IPN6d8WYTmtGub2ZlK5Zrd7lK5W9d7ETGbehoHymOVN30Q12WEdKF1VHNPLoHNXsXacftWdpSWNCH84v1olV1XqqrVWP7EGYikP2J+fUINj3+mGmqIYNUCLb0hPWi7U9erq2XUdWNJOenKvFe1u0oG+lzhsPmwg9XlAyTTrjfJixGmINrFw6TWuyUVc6zUcwyUNPuINAmU+LE0IWOVdnf8JO1TJoIVqoeAK7v/4YKiBz/vrsjpOkH23Dtn/9dQGMtkSuObjlodBT2ZfrM+iZhDq/DCtsoHxfrjuDF2OMCE1Y5SZXJS9jpEvjvZndhemXfvjDH3oi9htu2F+xZ48/kOnrga6UBebPf/7z7rmmzzJGyoCFgZuvfPnmIacs7T2jpO8tnWepz+dfeF7PPfuc08ONGjWK4pd5zKFT3Km1wdJZOtKOZ55ZqAcfeNCdj4Gtiy66yOk17TWCBPxe5qT1cTTB/xNPPOHmpWm47FpMSDNWQwVkdh5+4YP9bGyVgcMbb7zRadc++w//4M71QFYftjHLsio20jEp50Fn8cW0ojbLbaJyj3BLt00jDi5qa0/wHaNYdHBxFlGrbN3X2OSYMetQkosmLZ+etIU4phfkU/kGSWCuBbYX8Jx4DPD59dRm0Wkr/SDNp9/p5uI25gcQ9X/pA48olDNWdc0NaKy4ftYFHDlEPoxTCGF+e+Fj6kEE75igTrzI8L8qL0w5fIYKQM/una5fPEoV/FnLNH88bFXuGPRhO0lZ8r7WlixB0UTGcOLiVjRTuUrh3J+Ru03d9Fp8+aUj9JtFw3T1Was1f+ZSYlKpGutHa1tjh7qQjBRGqqggL0r7xBnTZQViWB6hNfMtYprbEipKjlNLoEf3LcNnrGSPvnD5Rk3AVijWtwKDWyt+KeUaDKc3J8UvmMEGYeDCuAuEEMsnA+t53zxiB6RCcDRs0kSl8uayCf8AQ2j0oHjMgSfbFCEmNHbVYoL97DOnxjk1NsX13PPr0W4SeVxj705NGJ+FIftUFRgYM+2yNTK3VCex19LpfYzNMAKvBMA42IeZbgv3YfcvlexYqBxbcILF6mhuQVoysIYs2MfmM4Dm2JQ5rNMZSRhjXj+B025f8BiFpnyfKlSE/qK9FWt0H/q2MBprqBT+wZPeXRIykxg4xcr/2SORatKOnUFtWJXSHhpik5ihsWoWuhcqnBgAV19Z5VUuuV2Kielpso1Jo+0g7Mt2mcYWGBC76667nGDXSvSNSbDd+yeuv0FHzDpCH4eh2C/YMHBniyiVKBZ+BzqsutPAmAv2LrIzIKNmLspEjO1UdPt3oHA71cy6vWH9UXr12Vlqrk9o6jFbdPS8TRqeD2uE5ihnBKJmBm04k12fmS0yt4JhgpfZx0MxG8T0s6PWYdE6kMdo0h1Z+hTSiGWAIMIi2oTf1bWrhurA02nk3ZK3S39ci06sI19HVYzSnGG5BKNaLdjTQaPyQp1Zk68aayieb3oOpGx99Lbk3Z/fvUvLd7fhl1Ops0eV6Ql2UBtIL50KDz9vKpVQo8uUN36+CkacjICdIGSNZG3ec1qW/nMCVP6/F9ASMcYpmY05IcWYRWacyReLmAO7gaH71AwZkPkpaSfRIBC7AgrTgnh33e6+Z+jpMxs+TPeKOCJ495i1CSSJw2ee556FSWN1Yd8yKt1Y8rtJ+G71fjshSy0aS2MLgAEzA2J+teMbQcKBxqGBLvMAW7BggXuugQw/jWnsyc9/9t9DEvVbJawB0P0H18hVf4IgfHDqf/cf4+uv/O8G5qyi09KZBsZsc2St1ezoYGDkU5Vphz0uEx2iY+R4nz7YJEttDfVwWi3uqx9KzWbDunkYAD51/nxsLSy3TNrQjKM4rMVb2G4kh31yryCAbZdjCs3P0FJV3ufxrGq8w+nGfMlNOmqnfZPd7yn8TldZpkONEWlpbU20l2vEJMxG02dxyzucFmLAj3+oM2SfuhANWc80LV6eoOo7V+20Q+rqiqNPwpC0J6V9wTKKJuJkKrAYioY0aWSTTj+lWaOGo81r7dAzO/J120Nzdc2Z+HtNNEAGe5/YrjzrEWzaXPNrDpZjMbQPzRY/k2YLYDYbDeWhVzxaP7uvXNddtFpnzllHpmKMXl1To+dWxEirU1tI55JaQE767nlVsDbPXWm/d4TpNF6Cj2O4cK9qM/I0umajrjp5mWrwmeyhhVAwfBoylxoeOJJHU8REEHSGq2EzIKTwA+BhKe+siIE2GFJaEIRyx3O+VWRk8AQbFJBYqzwbvx5LbcqDULo7ihtzrt1WRFu2dSCB2KOdFGdZ14fjTyjV5Ck5bvMbT2AtQgslKzKwAWcpVrfxt60jgFa9K5HA/EjJfb9CWmPC6xEkONBnB+mAgxxjoCMI65uE6UvQzzcjRccYd+lss0q3hABZhervKL/kJBJVs9xwjpktTAbg0FKWhwHZgNf2kPyjtYu4564dqq/t06xjyjV6clxVlUV64Xmaxz7dRVl9JpT/MLd78OlcJ8Z1tKyfewcMbdjgdvRHHnmk84Nyw5GF5bMf91JF193wEdp3IHok+JqmzAVvVz058JHopVKE0e12LIaioLETLWi8sFGIdz2vns7Zemn1WC1YXqqdDNApMzfq1Ok7NbuwQKUJzmM8Znr58/k6UlYZbnn/MLus3lgA+wiYBvOSyAmp2XoT8hb4kLrKFc+agcdvfEzBJ5fhuL5DLVVdquhqZqiXaE8sWy+tqdU3C0/St/cuB1jl6wm466+sW62T8O25efxklXTE9AzeZXPGYYsYalQTzNpzeIY9vWmfhudGdeW8qfrfNRFt2rlQH5xYrbOH08fMfOXOPkPJ8bjSd3OuAEj7nTs5WDZ8APhCR8A16eJMCgkYO3b0qn5vI2LlSmdNEoHCtjU+xfXIGKJPzVABWS8ByvrzujIMs7dwUJFgQaR00Mt+ZUPLvt44GCydwH1I8SBnnUH4pS27szDwUJ3t4i2t4AX9/g74LmxZWgBwYukzs4gwkGCWEf0f+1Ympa8b8wGd+24nPQTbC6uyjOF465qGO9sOY26tDyNalXTvWANXVt3sVQ2yiUmnOO1x9piw6xFrN9xjHuzvBswC5tXF7604wLPVSIuNjQXwX2OwCXgQF6h/tw77+Uc/+pFLV5olx1Ew5U6i4Il0nNYumL5fBggD9DoNoUdKsbBZLLK10MdLtm5nsGh5TKl/IvY5vWvuUJjZBrhWW5br7veVTuC0U1mZl+V4ZU9b02/7jcWVW9cHOg51QHbLBx9RrGWCnlpUqTU7AVOApx7Mp7PzYPlTjRqVb612EEJZBRZpyOEVxMYj12k4clarYH552zTd+uCx+uAZK3TmJFiu3MkAsk37AZmxiClaFsWx+nGFzmhEMkiLdjIvF780Q488e5nec/pTOmnWQirQWQf2TtWqPWFaTuaoCLatKLDWkyDw5Y1x0/ghPEkXv7SUblVWLZ1nshpJdc5RQWSPJuYs4+QmOOuTvjG/UlYBgCzXik+M8nztbhnrZ8AnAeAPUlXq2HSzSQoWopvDjBZ61NvAvvmRtC4hxpV6qRMHzry1ztOJBjCxNaDVx/h85ukGLV+6V0cfNY7iNDY8ITbsqnL+ZZlW0c6iYdPXnm4vl8T3LWDlo90L1bH1i8pufwWQSNoxmgftsV6Z5uLRNhhDZgvSNDIDtHZKvkimxd4FM3SYx2iIYoi8j6m84r3Iv1nj7Pzt+sA0O5MNwO67zPXiEGDInFm0JyYE20LnWsPt9ACCCjX07VGf6Ce44HHKzMJUcTjXCaNG6YsYMmTOTiFJTsgmhXu+BR6zNg+vIa11JG7L3Sp3ZRpF6mLCwuuytLGnsD6ZNMrMwbbBUo99KNwjTJyGuhQOzos0ZlyJJk4aQxm9DXjzWOnhca5uzg2ANzt8R+4bPnqlTj7+DH3o+o97T6Fk2XobxQAXkbBV8tTCmpTySpjo0fco07a0DOZ2y7tHYiqIM6ADLYq2PqNk4+/op0bpf0+G1u2ZhjloqR7ZkKWy3kodURLSnBkrNHbiEnqdIazM+zkg7Fp1ZLcpEwUrfJxjmmy82tSJ2HW3uTiw7597SBZgU8/RPmkHO0gqL6O0r9oKLb9h/VZ1VRylK9GHvbgP1+vasGby+d57DOUCVRP0q3sRepZt17Wcw4zKUbq3PaJbduzU56rG6pyCYfr9qlVa2/uorptyvmYESlQ0CUB26lx1Vw7jHrNrBDQOOqG43488sEVrVjbo6GMmaO68MhYZSq2tyMHGxRAX3KECMjcm00PFQLV1VLBL70tA/PjqFssDDKY+xr0ZHlv7X39BTbc+dI8+mI/3jW98Q6ZtNJsHY3APNlU5QBw//Kf0FThQ6yarRDUj2u9+97u0kTEdz7v3eKcBmccX98IqIg9ZfJy6czapYc84fevuozSqLKbPXrWQLi5V+HdtYTMG+DRWi6Dl1oA4QB5dmeFZ59uYnizWWi7DFuveEVpdW6AfPz5f8+e+oIuPWOIYRSf1rUcrlhqjaMUzdDRDB5UYCVBpISPXjjUGabzmGdqMdvX+hafr2BmP6z1nvYoelIrJHNKSNEC3DgHG6McGscGy9co//DRh/0KL6LCfI984ESw21evGwuYsbHkGGD/nJzkoAza0sef769k4N988f1NmhToHZZkDi58yH0/IhASpY0ttWh7WU0TbBnMQhrofQ3ygT5JR6Okz3+zob+g+tCvx93n2O5+yNArSbfosZcMPbiHld66JJdR9p/3OSr/ZzljJr7EM5MspVQNsMfM6QMv0vkok66Bz6f3G41LJNnaaCWVaWiB8GaDgUgAZHlwmWmeSRqkehLxXriXorVIPqiuMqNHNYYCSSyfw9ps2kVaDnKmoQCfmMBg1fgwurwLFxMWeZuRAhw/IPvup69mRHafrr/8YT7FyEy+9ESd9Z58XzCXMlxFvWxqUSigmmtfO0OiRfWgjFiircaGKYk/yZr3aXFuihxYHtGYT1DkVPbNGvIyNRqOmTaY1SiHNuXOvQOl7hqK0LmpFl1KJj5rpw1L79kLKcB0tlUM5fZydWidmpMX47Ax0UFCqCH3JtH29ksueUd/OjYBhpOnsiJoRf47AsfCJvnL987qXdD5rzxfHHa+tOxNUYW7Ttvg6XXH0NB2fOVd3Ll+vu9uW69MzjtYkesrdu22zXi3P1o20VyoGOI0ejWbs2BOUKitST04xcYaqvHYmbsHAES2GyPnF57erdldUk6eO0rSZ+RRf0CqG9Ew4gMP3IDvEwabZUAGZifldGpX7mYKudySGWxyMdufkPELotWO/giBNZfgf39IdbIktKLsJkh53BtUGOw4DssGu0F//98OALIMChBbdhFecsa/fAYTa4Td2N5ZyKMdggOyLZ9+nrOJy1vh2GobzaAIqTTq8lFXKGHOYZWx1EnGqIynUCtJLss+E/awtQdJ7yzZ16dYnP6pzj9+sC6csEE4ObGYJkbYTtY5WgKokLYuCCfN1wKSV4GwV6c1tFWREsvSrZ+brtGMX6ZIzaWJu/XhxvO+j2jOVhYbC+ICM8QN/fN9ccT8K63e9+F33hK+juTqKFxqT3syx/qA3c4anfwdA1l/W4MsgjOk7+A4XxuIam2sZAtuK2pdlPQ5WKTXw+Ek5BfSbH4cB2VudfU4clAZklgKx8mDHwHcoTsVfxrb3oAnYDOCCho6UAC4w6aMvnTCoM8f2oKaRjkA0GAGEFTKT4Ep70BCoDxAVHqZY2cnKmvBd8tUxnIGNosCQErdUaxKUZfQuomoTIIZcI/KU03tkYaRoeMiNodfWPneeSdfAHBqbP1hD3+Cb9PpylWekRr742Rs159ijdfU1V7uBaOofXxNmTEfY1OdM4k5+b+L9CJYSgWY0W4g1E23k3tvuZ+xm08Q5W48uLtGSreNUUlGuEybv1uTyFzT9yDp8t06k8O4aRUbhyZRVSpVQAmbLyztWtFhZNYUL7b/j6wGC0jUSefdkAb3fMhpV7WwX3vywudQLkHTdALatV8YiDBA347mTyXlXkAqpX6IHu47Utn19ungszFR+UP/7Aj0w6T967hFxjaX10X21w/RUZ0oX5nZpPmaFD5t2g53r+SOKdH4yT6Ex45Q5BzA5eaqaAVKZ6DPgO7GFiGL1MFj7I1R/zQQubC/Mcs3rlmTaMcv1WWrwrQ7I1z9+qICsDxBtvjiOSbeduduq2ybDe58owW2gI9u6xbt+YTYQvZQUI8vgHWkECkQyBm8dchiQDW0MDDw/PMf1/sdhhuzvB8huuYoOGBw7doVpJTQey4l8dGR4ZUXQxZrxNWtHrAdRPtqvEBuZsvIuJCgNFD20un3NK2ukWxd8WO89YbMumPUq86wM/FanziZ8HEMVgCs2rFSGB1knUin6UOIn2Ed3hM7u4aqtC+uuhRW68JQ9eu+ZxpB5e6xQ0DRNdYDEYczR5wYcfBnM3/4ayv0/p8dUxviHFMrGQJsMgmPXiQEhUpPO8LQPUDb4fmxIg/+NOtOD0Z32f0Nf8+iAtafSeV1MdhvMIRzmszfQcRiQvdWLmwZkNDpxIMh8Tlw/xr5mxK3kqJdRtozOwqqzg5lQMObHQpWK6yUTAJhl7nOOzK7VkdG4SfQ1vWEmkAmgIurOHqGcGfdTfUL+3vhj3i+WZaJwms86n5dctF4k86ChzfE3hrDCJoWlRd1hwIwvV9LrUXdObGyjyvu3h+DfuFP2y+GvueIjOv1sRKMfMUCGSwplcn2Is7JwyrOnp/q2odulCS69v0y63l37gHqafqaS/Ebt3LhGgc6YHlw0QwtWH63S4ZU6ZSatnUY/TduQehUUTsCS4p+VNeloeqBVqZk8v63bxTBfvJASa1/i838Yf7GLEDRvwMx1vfLwSy2ufh4AO9dL6w6Gd3gI7X09fZlVaG3ZrNTzTyixcxVtSvq0O4+WTe0BVWDyGqfZ2PXbHlIPv//SuDNVgdv+knWr9IfW7bpkyrE6p2SEblv5qp4E1H5s6kidG9mreNY0ZV2EaSepnY5gvuebA2UYNkYej7HwIFVgHvjiQ6THkbGQ1irKvHWCaMkOilYfYMwOFZAlrHoOvYhHzXvjxan1TNxqFCvVQgMdpiuylIvfY931b3Vfad3QoNdHOgzI3mpQOvjHH2bI3lmGzFKWu7ZP0oJHcrVl50R8yKqpyiMm5bTBkjeojg1RWcEIZdGSLdi5Q9NHt+mUY3I0usoaYG7Wus4aff+eI3XhnKd02TEbEPBP1o4W9LHPBpCE5Km9DyBG54QcM2gm3RblNTNoIRdPlWADE9bkijgMWVwnn/AKjgzEISrosyu/wHcLYOwQS0yMP8CB3mv/caCcpVXS09vUFkAr2jAAFnDVshzGrg2RgRxspPuFKgcCNgcDzhzN4XEd3neLgh7t6cCZVZ4PeAxCpJn/wEDHYUA22B1+498dU+BqF73FygTsVtkRb0BIjh/MBtrmMM57+fMuPMLqmrEFTdFGhwWuF++m5u5iFrtS9WLHEGtvVkVBmyaP78J3p05GrkRxQs4+YjGiwDyMAD1xbG9mD+9Ia52krXaUzzqGzDsP++pfxm6MnVdFZefmwfkEaUvPK8pGk1e15gdmfwD4DYG/982f6Ihjxmv+OfMcCEwms0Q21dpIckSZ2NDhBkRN3A0rqL3fge35Fj37jtBj99XoyXVjVD26TkcdvV3Hjl+vYUECTWKMNOIGZY29EOAx1WswjsVDpl1DrkF8x3MKd5PizF2lNV3LtPGJj6t9xyhNOOp5TYSaLyi+lIB1nasocj42Axym7euL4o5Pii2YZek3HL+3rlTfkmcVATAmRowBkD2POL1UK3cP0/+01eqciRHNpMLnF6tacLSO68zqDFVBUd9PDHyFa/gRWMoz8ykhH4fL+MkUHYydQMEBPwOikX9pyUNPaur8IxQriZDOHJgB6oFRDOE1FqSqyt1B3s+JN1z7DrvIg834gT//UAFZwFGssKm2u7Wxs3+UWR65Tzncf6fNdhVYXnBxruuctgUs8xvzZSZxdvwJhLZZCF2ybAB5JOigx2FANugl+qsfcBiQvbOA7OYbHlRz0xjahXWrsRVbi6wiMgpYqNCjN4WvX1cuLdqy2eyiH85IblRlYbPGVWdi14Cqnwm5fNsIffePR+j8o5/S+07Y4qr16jqLtPylTK2uK1JBaa15LKBTKyD+QxvQqSREEUmIQoloNKUJSGcm1eSpomYhPSfxS6SrHmVkAAAgAElEQVQ5fM7EnxFX6X+JVY2Bw4EOq5L0Dm9t8cu8fOIo2wAZW3UvRnhKB4si9vck8cAT47+9h2/R0b8t2EBArf/Z+J54/mbUj2U+OBvq2fsx882uwGFA9lbHhkPL5vRty5U167YhSUoGR+VoT62Cr85TAKPQPY0RLV7SpdXrSqkgmQKyLlI3FUSxglrc54eT+mPQdjdoTFWbjj+mW+PGbmLR2smiNlaZM16lYrAQkbg37rtoMxTGxdgJ29MjwkP7/sm/hroNkHitGF4rb3EAzZr7uoeb9YUHyOzwGRn/d00YlkZyE8orpFIFyBQGdHhv04CMYZOjoCOZ0OI4sbuG3w0/xuTwM6prAJg8e6qCwyZrZs3LGjsMd3fLwJV9S4GqT3O90EfxltHAHvAqYMQcbbch+E/ep1hOrdbXRai+7NHapmnK2Fms8TCJJ52wWuNnIE4t/SCbN3ppsnPMNiO/AY7uaAcVkqQmmUkUIKMdQ31HwAvv2qz4EnpeblsDy4Vur6ebJuHZ6imd4MrOX1m2SusxDDunerSqIt26M7pXz3KPvhoq0unG7U9EwH/iNUriGt6Mhi+bxr+5vMcLv39E9//hDv3DT/5JwVHFqui/gzzAeVpZ9must11ZnxP372G/sqS3OjZ5/JABmflbWIbSAFn6RO2bGYA4Jvh1h20j079wj+X/KN13AkbmhaW6TXfouo34lfMH4dpwGJD9FTf+IJ9yGJC9s4Dsy6cuUqiUSj8yDRm57a5qFJss4iw3ME5Fn1nf0InF7bipJk8x79r5ldVMZWJLsbG5Vt/705UAsvW6cs5mmC1AVU49rdiQBORXK9zE5tPqq5hn1sg9BmVtGChC6bT1Gu0Db+Vm1vBiO4xAU0fRTBVOJUuAmSwW4sLybOiHbcrSSOa1NcoaziOMd5vOt/84GDbswGdhTMNrEdril3dJPI4/OFjO0jngvvlhhYADHYcB2VsdG2lA5i47i5ff60u01entpdR42UcQa+IzhbZr9942NbWyIEUKYHh4fKDV2Rzk5FBZh5YrEe/EWb+bdF8v3/dSDtymePY0DM4XqYfnYBDsFryuQAOArMKM4535XwAzH6+/n1U+OqOnfoc/oLyyYDeKPL7Y8XrWKuJAgMwe4dsCWI8v0/10dQdxKSZtan4ZyWWkJG9XXuePpfLjVFLzLZ5xGsGD5t7dN6q7+XeU+NZwPjsoHaakt4A2LyNPVSdmOB0pWth000LFbJ67l6qr4RGCwS+Vl72VvmcAuTX5emHNeO1sHqWaol7NnrVds4+AXSuFTcy8kdzj9fiMVqI166XQYeCUWTzaSZAz9imIwSKFF4z/bGMKMZZN7K1X25O/VWYn7FzQql0RskbG6+6Nvbp3xSqdNrVGJ2DM+sf29VpfBDOWM0an9fICkwq177SzVF41l1QydkBd7ZSI56kT089/xmi0qKhEn/3yTbRlA0QPNp788QMz5u0irXG6eUIZYrGOCO+sMaw7Pd+l3/5h0T2KIyX2IYqxcrxCibv1KOT3ZtXge1q51jc8L2S9Fq3pIH1AVUadeI61KUFhx1zpRV+WnTM4A3gYkA02iP76vx8GZO8sILvlvWjI0GFE6YmLH6zbtMaIgdnMtRDFRplYXvTFSmjTlk/sYvnOJk5ldjp8ZmvNlt2F+u6fLtW86et06YzFzhoowUKBdZkyrVtROxvlOBtIdK1mtYD3grcOUBhGhZPixNdwDzoQ2jWZJrgjj7Z0kx/BOJb+l6DDAlegdZCHv+70W3/MCNoznzPtMn+w9kzeMuZinK1df+/jQGP+Tc/BNNJ+JZN59dlH2J8LsFVlsPMfBJDZgjTAcRiQvdXR4XrFeO29UwbIbAGz/0tg0kq+PrVykmfDYyyDLbgxQIh5pQQ6vfQ5bswGzNx99e+Nczk1PVm52rFvz538nKI5BU4uZaxSJ1U2IfxTssBaTm9gPZGsCJcKy1Dac8OnVFPssFCUMfhJlbIjCjIBvDlgrsu9pMtM7PmXDJmbNM4Xia+glezgjRTP9Ahq0xWlntfeDT9WVcsedWWvUN/k7ypU8HlAFpim489qWfUpFYdNg/B5gNgReJ/SW5PPhPyN57ItSyymV9+j/4+99wCQu67T/9+zU7b3bJJNJyFAQholIBAEIYCgoKdgAcspZznLyXk/T0VFxQNF9Ozn6ancKYrSFEQFpCidUBNSIL3X7XVmZ3bm/3re3/luhpjsJmwwCX8+uuxmd+Y73/Ipz+d5P+/nbaOavwn7l7MVbWPs8edm24JHDyPpNGLHztyIJ9ome81EeK1yMjIbXsuCjvN98WxuHyomTVj5SWnQR0Ymag/hRhXTrWA3VprhpmuS4R6QLGkla5ZR7OxGa+9fxHF7rHx7oz1N+PJ6nLMrED2M6onY0nFx+wdKXrwByxDcaq3p7EssN7aRcN02K08DNHjeacD0bfffYd+/4Wf2tS9dY3OmzCI0J/HU4B0qTLyIkErqTGbhAPXQ5fBI8eEyZJ0UDU+wSmBEwm6QDky2a/+6Fdazdomltm+0EfQNJYfoKzAIDVhWTbT6HoEh6yXrt4sCyTZpqpVOnmYljWRcsUAocXxwn/Xg3r0KyPZ1Utr7178KyA4wIPvHO23TFjRkD8Rt1ZbJbPAamad72JD3kpDfYcuZ68fVzUFuXGKpbats5uQuO3d+BeamFMxuf9pW75hqV/56nr3uxAX23lOW4PqPHERVMLaz8UlQBaPmUSI1wVxZhCyiv4+ao8yFUTLWhTWwY7SKnmPZPC9E5vUa66scZ4kp1wexRZUOetHm/m/7lfRgant6mfzpFAVQLcggNUglgfLFr6DKg2oLL18L17ZCLW44TwVGxkO00G08DFz4xcriysVBzGFDzc+DA7KYJ0ntub0KyIZ6QLv8XY8kiJwXetbzwNy3hBTfRSobw0v4UpWgTU2jieJM9th5pmeNNZMRmcbcrq9nPGUYJ1o9ac3HTFtptQ3Uv2qnN6Ah6DueHQ6eX4k+BmmiFCoZHQ5+Zv2UoIiSiffwQw/Zo48+6kXCL7zwQps2fZprxtZh9BqBDeru7nE3dJVt0Xd5sei1o0ePtpPnIY4fpCHTd72aGA2BB5J/uBYAVTfnvu0FsnguZnDPhQAhCxKmDydUXhC19r4FVho7DCfxajJLOdeuFkPzzhZsmzWv/LjVFt3rOPR5evYzz8+2e5+dYRu7a23O5JS9cXqTzWsgtZvQYM/xF6J5OB3Nw1ke3JOIUkM4xnlFMKaMUkapD52WSnarqmwxCQEqxWK5Svdhdb+3QVofovroptXW/9QDln1+gQOPLswYH4DNvAuri1TkSfvXU99vU5I4hDeMtOi5Z1n3uPGA1QqroKRShthCDC+4rc9vsO997yqrObzS3vuRj1ll2UTfGJblNVQ5hbXze6uMSqQw4cX40oQUlPsQu4qdSEEZIRnzyrlef9PEIha0sMRQoaHnni5xuIDMa7mplEcG25blT1jvkwssRwmfsiI2FlWk4veQlu8qV7n4ByBeGeGh16eSFlKER1LUVY1Q0L6SULtNO91s5nxL11ZyH4Izl/lpP+aXcUyUXpTIwPG++LnP2waqI3z7O9/xeqql5RjnqmJAgUnpPg7bV1+evwOvArKXF5CxTREcwdGd1KInmQ+xt1jRO8O+84tyqx8zwT75zqW2akO/Pbs0bivXN1gXmY0ySZXhbgQNWT/WNyVk0CewTiqPbrcp4/ps+lHFNnaU5oYm6oVm7H9vvcBmTNhm55/zWytmf6/IiVp3zc+g2jp9dQrLmRXBDhR5ho1+K0n5BH5UPJPKElQyiUj8z/dcTMlpaM2G5vgP6FhKA17jhGLDjWxg5ZQ/JWez0FvnTdyctXfoKLcAWRBkCc2SES8Xi7x9FUVS8kIMVhQOlqPSSN4lSrOUHzgIMikLXjWaA42dPrKf4+l+qTi5n4+OyY+7hks1b2vchVVJDugN3M8ffsB9yAYDZFkAQvMCimN7mKbenltRZHc8mLJlG2oJozXgbowPO2UnOhFrpukRCQSdjSPb7YRjyUQ8kmzCklor63pmUEC2ieLWX/7Slx1kqanw73/913/ZkiVL7H/+539s65Yt/vBVxkQdo4SSLlrw1ClUZ+9LV3550EeSlQke2Z4eN1dnV3+Wlxphx1zHNjYL64MBXXpO4NQgLMSL+yPo3HLV1pxttnoJ1jsfs56VVxG6vc+0lrc2n2RP3DvRHu6cZcUbm+y42i129qlLbNTMRbCBvLz8XRZv+CgZlyRFOHso6liCdx1cZxPEb1M7nsR3R4VaJ3j/J/DLf6mpyMhKsztTuZDBmhvHUpbFYHxs0cOW3LDC63W2cU0tFEOOIz5vrBiBnq/ayo6fazZ7BhCjhM+BNeLNfSQjJAjB3fyLm+xuyvN8/NOfwFcNT7LQjw7jX89y5b5FeJ0XX9bfQsNAzi8csGHB673NrNwb6n24gCxFXykVEt/4AlFqUuDXLGXyQvuIH10ztta1VCIQSFZJpcCA3d34HJQV8ZxK0zWWKRMVyR8B5f2tJHVUTrRNDUfZ8lit9bj2UlHNSi+yrSoR+rm8ohxgHQR8r/7ylbbshefdQb66Fl86ALc+xyfAAxDy2M9z2AE93KuA7OUFZFofVAinHCuL1LMTrJh5ek36BPvhb0ZZdf0Y+9yl/+Xm9LgiAcYms+Rjso0TM1tan2OtaBsbtxqvZZl1U++EFZfJ8R73fqbhBS1Vdt3Np9mc0S120esfxqKxnvkMFqwaC6Ep35KmJYALoV+DnGMLG8beQQhH4QbGqcAac5+0TZplX17+an90fc0fPuNoP+7Nia+8LCf4IbgKTyp4ESGV1wA5ieULGwCLaBE/OmRlfsmyhvia5+9DkuG6koAS8N8Lj4UJ6HqJCAtn0PSlGACpamyyNc68SscefD/3x504GI5x0AIyaZSyjKKeRw5Dn9TkNfxauirwu8JpP9toFQCkmHVYI+xOZ+4vlpXgHfO8TGYdmcdZqweUlMJ+RVkLB2PI1ixfb5e+//02bvx4rz35/LJldutvf+t17pYuoeyFa3kAfpVBtp9q7KmQr0KXY8chGh2Cks5SE60I7y29zv3xyKwLQJl2GHypBI5SLJk0eikJ1JfrYO1Fk5DbQVeEfu+617av+obFO+9EgE/Eq7MejdhEWwBz2AdQO3IkJZ9mPm/Tp6y3GljASOnlZGD+I1T7GMpTZKwhKeYL2ZJClNyjeAw6HpBqW+609I77yZw+jhAYYLDxbA+D6TwjgEiGloeRoypbNEiT6USZBqr8GdYstv7Fj1rfxuVWqooEUsL2cw0jGiwyY6ZFZx9j3YTvUJ9RIIC9kKwfCCuuWb3SrvnGd+yIqXPtk5/4ULCF6gWoJjShzgo+PRzUwbgPvlSk1yuN70ymCE9VrJk/u/xX4SUU6rSGGoTDBWRJJpAy1ah5nrD0I/dYbDPC4XoeRFXENuKzN65X9UQ1RwmQBZ3Jpz/FlHVpZfXWTeZsjvIkFSrkJgaVbfyjbWm7fcUmay6ipht+bbreMLwgd/izzjrLTj31VB8n3/7mf9qTTz9l137jG9YoTRofI/YwQfXpIbrvULfn//d/fxWQvbyATJMXHLyVp7ZZeulUssepEEK5uB/dIH1tlX3prf/nC7rm/352qlkZjHsBStneMFQUEWD6TVPXsDelyijUO6VIeHey3To6d9imyHH2mz9PstPGtdrbT1vM5hSxfgdJRjWXWPyY77r8qbD5EA2mnPy0FER4Ch2og3k++PXBjx8ATVyjO/AoyhBMPju/8sXC/ZK1MRaeKrghcSoo+I3GVkqbZkEoyfWLlAHqO/zQ2Tof3xBrBuug2pk+pecT7XzS0y9ESKhYuSRBXqlz5/qjDXeY6RlGPPbeoPbQmKoOGkAm1Jx3lQg6A2kwciaPLL4wABBZzF/V+tEcRQn19JOG2EkoUiF1xfclECvCs0xxttia4Ml24K5cvmRQQLYVdum7hHI2E0YS+zV58mS79NJLbeSoUbZ92zZramJnxuKlBU+Flru6KR9BSSb9e9KkSfba008b/Emrww2gfoWVVCsMOjtfzFV6rBylozJi3diyybcqLsYMnViUwqzRZ78KUPuae5c9dt8l9peHu0jGbLXx4zfa6edssNnVaJCqT0H8fREL/QWEHw+zJFhICQwqAdIco9Ayu5YSgJ61L8Y35E+Wbrsd9o0C5RorhIGtkcLnY/+JezXH8aF2NkVeZNERwaDXh/+r19/zrCZKY9gGTGOXPmHZVYuo+ET1BEorxU840XIzZ1uP4gHswhIIY8Xs91DrrCxbZj/+6fftwWeftS9/7hqbVDOSz+ZZN//BetoxRZz8MxI4giLLPtHpMecnB3/WYo4K2hYYTZX4mDp16oBAPgRghTUfC1O4B7vA4QIy4DeF2eXZstJSC/5q/cufJFzJva3iGfOQ+ltJv9csr+zigu2niEzXIKbXW5TEhEgJwI1MViPrtatujLVOmGFbyhrYnFDCBDGlwpXqq8vYUKhMUjFgSyH1t194kd1+2232PAzZF664wiYcdpi//lVAtn8m6FcB2csNyJBUMIEmMs2WXU5mdttWW9Iy2a67YZL14lc4ouEYsvAVWSC0STWWfgBCJIO2l0U9wRzb1sKmg4SvLPNHLyAhwua4qJxwPfNbT6rLDq9aZU9vGGdvPzZrb5u3mjKQG6nHyDEoO9d/1HelLM4jhV37S8AaxfK1cgtrhPof8qjlUCCgQ6ATaMIC1ivL+htUWwicD4KWT2jIX58zZqxnWt9SUPqB2ZACtYGyTflLcYmi8zfD99GgNg//eoKCDgTDqAN4cpmYxYBdFDrT/1Q4vTA0+dKzPffPeH+5j3JQADIvURxmWPqzFwxnpwMg2/ZsuWF/RYFccBnr0Zp1ZdbSehjr8Aj0TlnbhCdXNrLJ05gzMGjVJdvsqCOftsYGQBM0dhmMwmAMmSpoX3fddXbvvfdaGxqxd7/73XbxJZcgmN9qP/7xj31xUyfQAteLbkxFYQXcSjH+mzZtml36AYDMYC0fWw/i5yoPpQ5J3UouUfVu1S8FaLwHK79Bx9J/dAvU4Tufs+yOt9vKZ9vtgd+dTUHWXjvlzVsJ46614v52q57wfYgi1VCbbUXVuFNTOJx8SDRL8tmhAHcpdeCab0Hn9RWrTD3vZ9rVfrQt2Bi3Rdu67GPHr7SikScCyj5t/RRojbLrDKaa0FdhiJglk5/Clv3Qb4HkFLCxdpWllsIIbVqL/+ypFp8+k0QKQAUpleVKCNChS/utFcF716Id9tlvXGknnXe6ffCdF1u8bY31b70H25NbrKXzLxaZfouVU1UgwfOmUp17+8QQ08FROsGY4fP1bKTp03c9Sz2zK6+8cqBmY+gJV2hJoivcG/p7uIDMpyZ0erlki/WuXW59zz1lRauWWRnaMSWQpKi6EOrGCruRGDO14uRa7pcyvKrpLxGKJ3PlR86xqrlnWaZhfACEC5r66orly+2uu+6yu/k647TTKb6+Dh+/Pvv0Zz5tEyZO4hmriDf6TPR1rzJkgw/fof76KiB7mQEZPlx9hAITaepIrppruZZVti7ZaPf9dbalOlttWec45jyx+d0wKwQ3mVCVRFOMSbgXbSd0Hyz+ymxHe4pUoBRWvYjKLVpjxuDr2JSosbOmxGzaaBKUylcHG+iyD1ny8KuZz9j8DzJIAvfMoO0k8cO5U4NzKNH6UD3s7/P3wLZJyWnUH5atE1elEKFKCaoYesRLHQWhw+BKFXLk2tLUl2Zv2akAI++tkJck5Io29trSlzP3qQXv+tsAbkcuGXh8+ozO/8SnaN3Lr4FeyzdfzSQ0Ww83riJFXmXI9nP/CKTaASBTCyjeAJD5ivv4PH4pWrTCVmwttzsXULtwWbn1UBopirC/FD1MPNHEAMR5v6/CpozvsNNO32ZHTKFoNz5mZb1DiPoRll9++eW2DTZMnU3hnv+46ioXSf/l/r8EmjFAWDV12rSAC4hp4Y+xqDUgUh9qRQuoXjq4Yue6NsXP6YDaa2hzEUF8qj1BtvtRLlN+U4Qr5WiL91pGdS9xv2/e+ib8cG630tYRyBSOskwdE0rlOThCv5+Y/VhCkRS8hVHroHh3cX+dzKLZbGyk/OdvzZY8is3FswjDl1GQfJw9+tgse25puRVXbbcxkzfZB05diS/Zmy026gMWq4Fpo2qAai/qZFMMxuJQNb6n5054wIveopOTJFOgrFjVDtpUQ67bevASy+F1JiPfCq7Fx6fmq7IsfnAZu/6an9oTq1bbJ79wmU0nG9PSz1j7I1dYcekC18olxkP36YKks9I9G5gcBW77LN2VpTznzgrp1157rT399NOu/9MA1t8KgVe4wwpB2lCZQsMGZCJ72b1jAczdIUwIE5uDxbI1a3jehGRzFG4XMlf4VuUFNOF5rUruJKGAdB1CZcjNCEa75eMmW3zKFLPDpnH/RnqNglLeq2tJ01+1o1RIXU0JKc/COv76+l9aMpm0kaNH2RUwZGPGj3M7Dd8V811hilfbS78DrwKylxuQIeMAWBV1t1tuzTyLdyy23tJRWCCdQUb6Vqsf8QjjnPlGIUq6ciJWx7BBc4yYXLqxVMkKH06CRb7QaxIRWgBIaMwloyfajtIeG6+dTSueihKLi3Uvu9J6j/yclSgct9sW7IRyPj8GrNLO6hmBBiooFTQ8252X3jP37Z2qPtOPTEfJcoHheb6F+MtLtlESjyiIpqoilPpRRXJIM0VyJ8kvGd+U2FPUQqJYssF6mcL0u8KkgJ3h0MA2KpWPwMhbUWuHJzSFt1y1n/ORpFc6Mxbe7oOGIdOq7rmWAzFsATKe0sNn89ARjMcRNEerbF1HnTX11rDIixjtthEVCNCjqzyMbdQsi1kzQAnxJusSBBK/HxyQPbNgkX3hC19wICZqVCGvyy67zF53xhkOytrb2n1By4DGW1taPBzW3t7ubNqIhgY75/XorwZpabcHhMgFiAW2DLxYGza+1MXVyePZzdaz/pOE+Lazq5tEpYwZmL9+gtAkkXS0ZSXp31v3BkATlW/LSr5EaPLNZjUzOCdKS+O71k9JqCwDRExbhJTtzIYbLbX5B4CzxRZjtLQ119r9G4+wh7cdZVvaKm1CeaedMW2LnXBEt8UaT7Zo1bF8vQaPslF5AWZQ6zPJKCO5b/CWZwB1Pe7IwYSW0IhysUGOnRNF3hWi5UpdYk64V/OX5sQX1q+zH13zn/aasy+0N190lpX2oa9q/bUll37BElWlYFOSAEbcFYx2spbcM0SJD34bpXMjazI/oLu7u11LdfPNN9vzzz9v//AP/+Dncvjhh7vQPdxJFWZW7s0gHzYgU/Yjm4a0JjpOPyFrCyGslq2W7GyxkqUAZgExTI5zefGqgFKOdNwI79tWW4cObLRVTJpuNpoNQIKJk3JLEa7bDWL3sF6o73Z3dbux7aqVK62yptquuvoqaxg12kOWno3K8V80+e7bHP7qq/P9/dVali9fcXGpvvvU1ykhV7TmTObIh7ACUjjsDOZK1gUSo7w5JtKGVnIPbW7YyOl3oc1iqFHKyg5JGXx8F8MT77DWim6Sn3gtCphuppmy8lqLlF1Bbd3LeE0YcgtAVwi+gg+VroDNs1Ce47NgRg9/1vwTpYrIwdwysAIxD9Egm9HPnhIZXFpzE7NsKmLIUE0S6qBKgGZ53Yu88r8PNky5DKxzygK3HSy6Kh1TX2nZilLWPV1/EJ7cqU+jADtOA5l+6kbL4829QfgQEvXkhqCJUsAYro7HFxI1wfeOjg7/0ka7pgYJzCusHWSATAxZuOMIAFn7krfCHj1FyvI2z/hjzcUGAkratYKQopkT0AfApmjgkYnZJ50NrFMiiuA+iSC85N5BQ5Y/+eF19hC2F8quFEsm3c14BP6fhTX76U9+Yo89hlkgHyraVLobsWNa3DXYJk6caFd86YuDdokkOqmoLBroWj62fbyyg1LWo0JZ6TISIFOWWgUgan6Wz8H8cxS1HSf8wFR6M5Few2UeZipPGalfz45sAroh+m2kk+sW3UuR9BwdE3PV5PbrWGj/Dc0SacktNda5Zb59F/PW1qUAki3lNn3iajt27kM25QjwXM07ODj6vEayMBOjA6GlroTQlqE7y0UVfEwMVVkpGJ96ozPe+g+/cIO4LNiLgu7RfCUABOl9KDg7yTAt0a6xJ2Pfu/o/ral1rX30M9+ycWMJQ+bWWOcD77XKONmIlfNglY6zaM2t1t5dDV4/1irqzrDyWoqQxybyWfmdJ33iueee8+e0YsUK1wKG4Us9s3/+53+2I4880urrCeHSlE27LzT3cAFZhB1lhN2B504qyVUxDuaoXjpDF+GYEWwY/L5TTzXXh2ecRK+6f2SnRvgepUwXVCl9BesT+ng3zzzK1hsTFw7C76WhBOT1s22NK/OlMA2K5/GH239vd915J6bE3XbVV6+2ejYRssZ4NWS5f2byVxmyl5khY9ONCpZalDyv1eeabbvTKJHLYl4FQ9Zh7Urmwgw6ipRBm94cYyoX7Qrqv/IV7zqexR9tWQQLjGg3e7t2vtBHJdgex8qsalOjtVaxYeG1seRk66kgZKnIReTDFpvzOY4Ho6yuwn8GSk0WdB0ljQ3EKsPf5zdJIbm0f3ray3MUzQOBRksMWeCzKWZx9ao2W7RouW1Yk7Sx4+vsyGmjbeJhtdRPDhLUNN9rb+lECMlo6fYt1rtoofUtft4ZsdjMqRY7eopV1s4KlgXdJo/pqioPVj7pDveLK1t+Euw+Nkilx0P6nwGQO57jjeWZl3kgJVGgIVsL6/+HP/zB/vrXv1ot2eLHH3+8feADH3h5bswBOuoBB2RDXveS+XjE3uvZklrIstlx1okZbIoszHRytXWzkEXLxloHQGJH2w4Wc6ryjBpn9Qw469lg1IW12LSHrJ9wXBKNQSmovKiXsCCtvWyTfeGya3WUm50AACAASURBVBxwNTdjL8GiLfZLYcmrr77akfiK5StYwDBE5XeyE9CX0Ln+rY5coRCRsnhYYMXXBNF3pV0HMbbEUAyT0n5xuM/ADLW2/R8/d1vNiHdYvOJSOr12DOrw2kSoxDehUhZiOeLnctuhhw9nMb7H2lvutVzzT62ul1RuyJeN6+bZ3Ssm2f2bq+xIqhVMmtxmR89qJtKVtZoxU5jQTqPA+hmc81RUWcNvYg3DsKEAj+7nn/70JxeYJwAJr33ta33wlAJ6pcMTqF28eLFdRWj4nz/2Xpt9zBlWXVlK6voKS66l4kDqD4z2dQAOJtFKfGyAZhqd/dHjSBL4JDo6gKSK0HtF+ag99dRT9qtf/cpGKRFj+3ZbtWqVvetd77KZM2faEUeAPofRhgvI9kanNozTG/KtWzZttv/4j/+gEHKrffOb37RGmGCFJ6Tb8O/7KWQZaEsChjRgBgLtofbS/nsXCgevCcqj5Vct2EF3IJfth9Lk+bcnXOzOdPJFM/seLj18TfjnvBZPQDdkR8NnsjuGNDz/gbcPcX9y+Tq34et1DVdiM7KYTcIPfvhDWHRMmfPHcKuRvPlvqIsZ6gFu92oa6KLSypbmmcnmhTkBNcOAQXP+Br/4UOEmicVS9iZJ7oNCTAlFIvoIV3N/FW7bna6n8EDa3HzsYx+z6upqkxxA5x3eRwej/K+NvvXpT3/aqpB1XPP1r+efsQThPNthFr/W9FgKKIskn6GIyV2War7fon0vwNwj9ed6Irn8+A7DbKE9Rf4iektHDyTLhEzmAKOpPodmVtnNklRkyQzLRkcx34+3ktKJbPLZxCUOG+oRHdp/H0CNQZiVlDP8PiP2i+sWsumfYE097TCGOMHFmu0N582xSRNKrcfnezbEAGA8yK0m2WXZJ+6xvqfvxZOsw1L0i67yGqs5Yqb1vu4NVpEZE8hUStp5ktVubh5tfZZEp/OtuQfvUEr9ZbvWWG/l6VY688sYYc+DmSuyWjFyrJ+ZJEkaJcX2XbLFl6OPbSK5TvpXkSj/8PaL7Nxzzw1kG4A3bbbD/rmvm++D4UEe9IBsx3NzrZii2yVYPGzZUmH3P5SxJxaXAU7GWGVFDSHEDSz6YyydqrQM4bCJYzvtpLmddvSR66y2ahGAfCbVfH5kfbVzsJQgq0+Cd5zktR6kEq1286/+6KyJwpKiQPVgBbQuuugiDGG7BzI8Avf0IDvkRSGKfJExJ6u96kBgGOgMuWJrQ2jihSl8aVIBtl4yR5UrXE5oChG3Ox+QHVmkdG5Dk0WLZmph47Hr670eRuR5CnzfE/hTpavs+eREyhb1271L1tjE4ozNP3KsTZl9D+zx0Ta29l3QzvPh4/H4UikkuUhLc7GfeqEmbunt9P373/++G+3Onk1mJWEz3V/dz+PnAspKAWW9Pfa1r37Ngdtn//3frLoBho6bEO1facmNP7W+pm9ZpAf2k1shEqguN50yKK3oQZCOjvu4lUz4LOQptTyLQJ/5LCc9Nz1Dfe6NN95oH/zgB+3YY48d9tUd6oCsg5C7EhzE/n7tmmtsLLYXKtEUzQtlh9JADnUDtegGIOvFOw9Nim49gtZyd15n0rzp72VeJWNnE6DJ5YGbDwvXusl+JahcMEBTCPhJhZIHOD7qBOIKGEIdq4c+ps1TIQhTf9Rrd2csua+ALM2xnJmkOdvAgvD9733PnnnmGfsi7PlovOEG808K79+e7nMGFl0gSgk60hMIl4mU9nHb1UupHsJCmhf8SzxsoGQKZeXl/EMRIRFMOkvd7cDKQZnd8kkfvDjZgQZkA/OjNMVpLHvYYHrsy0sZFVuyOAixFYIs/3f+hiZ034IXvPh7eMOJenjs38VL/NLDnhJF5V8wZO22PT25Q+P32gD5miWVCRR+hAQKqhTaL/53ofV2UJS9usb6SKhI9m6w1583y2ZOr2b+YI0kIamfbPkkviLlPU3W8+AfCVU8YGViTpjMk2S1Fo+eaKmLma8zUJq6n4kWbI/QYBM2jlFlpmftueifKYPIPE5hHkuWHW/ls75GucPTiRxFvY/nSMbQHNBH9ODyz37WNmzYgCSpwce0JESnnT3fN9+FUQ/1WYG1Q7Ed9IAs9+w8hO2E7QhDJntq7bk1xbZyaxUu+/UwLQmKikdwYsZ5P9YEol+Bw3uLNUCAKUKluT5dfqXFx5IxA8hJ5lrB50zw1DjzKQlTUlyi/OdwItYEqdBkuQLne2jha32HqIUimN/yYTs/WiDM1sw3xA67J4dFgdUwmaokVDCTCMelCe8lAWOlWUTpaLBMjJgEVMkRltxyh3W3fY7QJOWKwHDLCU3e99wx9uQabBFHbbIz526xOYfB+CUoX3T0hwnvsosshgo2xOB5+jhQUAafN5wW7kLCcK52MN/+9rcdgJ144om2dfMWHPi/52Hg9+P3VgPV/ACU81cACf/vU5+yc845i3OgrJQAWY4Jt+cB694KSO5Zgc+Q6M1qK++gggFlTlLF6K3GvsOKx32XEGYD92ibdbfGffeuBVZMncKWN910k73nPe+xKQjgh+vmfKgDsiyif5VO2kDmqZJVBMgEhuKhP9Awn384dna1Fgn7lEeyXWuzM24xwI6p+4Wh7sJQ6952SE9DDi5AIFOfE5acGjiExlP+s4cqoxK+LrwmfR+K4UyzCRQIU5NuT9f2dVii5S+8YFd+5Ss2fuKEgVPxcHSeQQwzxsQWD9YibhKla0Srw5jt9MkGo2o+J8Hf+gASAmLi5nPyGUCYrc2NzFE1xjMdLH6EDdLBPx13uDE0Cnc58qiyxmDtgAMyJCgRry8ss7H8FCvWRABCz3aIvhLJlyba08tKpAvRLZAuNX888EbQXfnK47297ZGH3uu0+cmzyCm8DuMsmr09RfabG54k0pKx0oqxMJ0drHMt9vpzZ9mMo6kUorcoAKSkCWwtSruxg3r0HsssfMhqiFzpwXjVggYKtb/3MmyOuMfqwnhgwnWxTrPWbb/N2tZdRDk+WC1tGsDZmYozqTv9JfbYJwPItLXggbu+TZ+VduZ50aJFHmEpA5ApgvW2Sy62N77xjQ7QwvYqIHs5u+Fz70MoCAuUIWtQ2AlM0qsByY8y16eqjJv/yXvVO5ar5fGsStRhkspCPvpnZBnOJVxJ5wGQVTJAi7J4mWlwxzGfZZbTJBmk+AbxdE2WAhqajMPQy54m815eo02U+5PmnY6982nX5UVVB0977vPk4GA/m0YHlOsnKMmOO+ZZLXRfiuJm0Ztlc81MprisE4ZNNr0LJukxK9l2nF2zYL4twog1Ubbe3nBis71u8lprxBqhqPpjGMS+kxNDGK85H8YsDUMYFETnnLMSHzFQhqqNtA/PVjsZxfcFyL785S8HDBX35eswMwoj/ggbka7OTvvc5z7nO5jPf/7zVk5x7D62/WlCkKXcyCJVMeik7JN8TEplVLsUavs+S2/5Js+LBInRl1vRqE9ZlqoMGeKzxRrcGrAFIanVq1e7mH9/tEMdkOn+X8mzkPWFmLIxY8f4jnN/ArLw/outKixNpd/rs9RC37fCcRSGvPz9BXU8BbICwAKDk2ef9G9lgalFER4PFnb0F4UAML/YaDzrXLST1ljXZ+t3ISAqBGOF/WYoEDeACPLgUCH5z7KTVxjv2m9+wxpIKAlOZ/f1bofso7pkLX4iyZzF0HAmrJvmD8rOpTIG8R0mRWiNXvJuWVT7yTrUwBNWTZIQEquBhSCcSOoyX4ypUrIQYS6VpbvnbWdwZgcakAVW7gH/JzmIhN6yuUh73nLK6tMYHQ/WCG3utuU3Ir3MO0XMtaS58DLmfJg4Zf67OJ/PK2bj98puus4A9KSoZCB5jpLFHvzrKlu/useamvvJZC2B6U0Q4ZhsROBZJxlH9B81sa8JmcOuWmJdS56y/h1bg+x2ZEPxyUdY/5xTkNbkQW+0k7sMiSIWMkVG6/afoHu+0cpVaqofrV7VeVY0QdrmcZ4T5nU+eT7BP4rsnrvvsscefczlLqVEY2Tm/k8f/pBruTX3hDYYoRQhJAkOped30DNkydUftnTrEzBFzxA3JlstVUuHqQC0MPlHKBCbnsVEu4H5Z5sbxCZjI2FSXmvR2vlWVjcDv65jwGhlnuWcFuPktH61u2rQU+iEqAcAYbvaH+xNBp4edK9H3fFsUZ8J+7ZPnlCt/K1kIM1nD91CdjgKQ/AeCbNjiES8qyv8ifhRddE0HzmdjB11JE1WUfu/Wt/aGyy6fq59/i9j7OxZa+2kKQuthLk2XfteGMEPM+kea31spxOy0WeXoR1yWMZWxy8WDafzHSKkOlRn1qKm+6eFK2QXZa8gPd55556HOHSV3X777T5ovghTc9vvfme/+PnP7d/RnJwAg2YIbnMI29OEIXzaDZOaOD1lT0fRBrat/bD1NV9ndRWnW2z0tZTZPN40xiVPCU9f4UqFTH1iySdfKOQ8GNM51LXp768EQPalL37RbTCcIaO6RBiy3B8anz3pNDQ56m/hzjW0GdnVkFcMk55TmLmsXW8noF0GzElARnsHdgd04QHABhOaoL+59QzASs9XUgNlPNfBvqrEWSk2KaUKy9MnW1pbvALHntpLBmLhATm3JE7MCrNKIykg9pnPfMY3HF+BISsLfbAKTiBkyoYEe3oPC1EPQFQmAaW6dm1UKJ9lq9FbbsFCZdMyr0krM9SijDabyhzJZ8KJfqyqtC6ojF4yc0vrxlr1pNlmE2HKMWDuYgBhTD9oO9CALB8Rz8OjPKGvyLWy+vjKSuE/SMvbYO3xFVgj+qbRZYz8OGDwGlJvQ2mA92YSOYhf00+/iXotS9YH2K0g4zJmKB2sZUfatm9ttVrK3owYWWoj6iQJYFxTKk9rsWQEfaxJkmsj6CbhYoO1NW2CWMgByEZZaSPASpsnzL+Dm8uKyCLtLK3YC9wTsjseJ0LMWI0S1iomklMGiNPc78+D/iyfM9ixEsazNjtr2Viqmo7G+VQ23ZMPn+J3txCQhbdbkphC5uwgfgwDp3bQA7Ku1a8nvnyflSnMImDC5i+bYTJWmKAIm/nMHJ7cs4QweSiyuiiRO/9lVlKNYWusHqYsP8gc8ND5fMcs1K1BuNPnJLwjIcDQoiAB/54m7PD1RSQKiAULaj/mM1B8whDagYkYSqWljRkmhVkPbdJZNZf2Arq45nQnyQgjjrWSsnfRUQEtIL54hte1rcV09fOWav2llaOXzLWTIVmMU38jadrlJ1LQm08ulVHfSir0cD+ALVKLKOlA+jbdAZdGaGAMMSHvTScOmUUBId2vW2+91R588EEXko8fO84F/+97//vwcqv2xUou+h/44AdYTAnHumdNsNOStLaIovJRtC0CoFhCWnFqtLU2fZ5EnusJTV5F9uUl/qy5Y4BrBitZiAKEITsTLnYC2PtjQB7qgEyi8y9e8UVbz0R2FYkqAmThZkMgR3Uth9MKNy67qyXahyDXWTMW/ww7a/WJNXiwKbS9ceNGz4pV/9G405eeo7Njnjka+MjJK02at7BIvF6jzypkskN9p8BaNQBNCR4CaifPO8VGjBgxENZWXwkF9QKC4YT9oioJ+xI+ZfHJ0udDxu6RRx6xawlZyjbnX/7lX140vkK92O7Kee3pGWgT5VMEY6MEY1OoZjLZnqFm7Ermwk48s8jiJiog/ojiNcxp+VibC8X4uQtbATY7KQBZpqiCEkNEBxqnWfG0OZaYBIssz8NB2oEGZJoDRAbiHcO+EmZf4bJQHqKoRzEyh4L2tyFMzGELApuFP+ttZcwiSjbJypgcGlI2MANTog42eER5OEPnoHhvlvlWNTiVWCPz6FDJgHmRO+2H1jpywyjKm0gqyhIC5Vx/GVYZ4TqiDEpIDtWixOZHgfQK7726o7yI9VEfN1ArU+8jHBrEQGVXomfBKqDwsfMFSJF2s37uSgLsKisIM593lwF9UNz0QU7ioAdktukq69vygOW6H0IL1Q8oGQWbpNzANoTsm2HJRoHWSbEtnkN5GcBZ2UxClEeyyBPrpvUxoBKanDx0Ilolrz1g1yhwpg4QFgvXRB0u7OE9G1J/8sKT7EIRrNUSmigpH1AsEPjMg7IhnJqpWYm81jMoI1xXXHRAy8MkiF6NVv9e2K5TrHbErXwGRqAxWWiQAg5xlm29w/q6rrZY21SLTv0H68MSIkP4roxFLUJat8K3nVxbpXxghPU8LUtBCmXTkK0leS/p4jvLVb30rrprzF73TAuvrChqAGHSj42nEPsdv/+9/frXv/aQztFHHx1omUDMyhrrZ7DrrKIu6OZZUdokBf8Yz41hl/Q0LyCDtPhkdk6VZIwJunZhQMvOi5BrCML07MIsMIWm9pblHOzKD3VAprnuizCWAkHKHBbNPwDI9kMWXHjvAuFtYCkSjiOxXmXFJR4uXYhJrfQfW6mAIYZOBrZeYQFmUyyXQJPEuvoSo1UJsyOWS0BOi2Q46arGrAS+HoIEyOmZ63N0XIE7ZUsLiIeati7sPvQapcnPmDHDE01UHk2ATUCxUFS/L0ApvG4X9edXsUULF9rvYIBX4vv28Y9/3OaecIKDgbB/FmZWhtnIOu/BWtY3LLjOMT5qqGlavBpbgbVL+VWbxUaUWaoXCwc0YRGVBVLWrD5Po1zhTb5KlLnjmgqZLveTqdhu7VjGlM063qqPRVc6BUnDIO1AAzJDZhJATGYHd3Qnu1piOgCatz0xWHlk1jeEKD+htPRAY1LwFf5bHzDE/P3Sp82D5J3aBGHyCtjtA/nGWGO7k534XdYwPxehzVawRjcz0D/KigdTC2etdZcUyMnw9xh9NL8VGMiHSCtpx/1HpO+UUS52UUkdQ9mtwSa8WDkA0jpqieJLiQLkC/i87hvyPCDTZl8tlERorCoqEmZVhtKDwvE8lP7zIHkALzqNgx+QQalaO4ahGSYuhHvZKF5j8RomwRI6RC/DSOEIPJrEgOmBDgyvXkoKIdrHmwyZonoNTz4wndN4lildMbA/KHQa8bCJFobCEMxOj5ad92zXLKyt133Pag8/0kqPBgzWjQQwMnHwUQn5R2mnHWaz7enpi7nlnFQLTIVsStSr2wF5W76LFcYvLFUzEYf2B7n28dYZw0+HP1OKDaZsE6ELMi/rmVDzKVX9CCy70GApiFpOGDCh1CqhN00qQj35agh+DziGwpgvznHb9y4a3i9N3GIbdC/1Ow0gMYzK4tHAWAsgkO2CficbhkJbgz55B3FOCQas+3TldTMKS0p6nJDWTbZ0nG+r66eVPA2g9vx/FqV8iDI8Fy3QEvrvDw3BKwGQSUMmkCCbEQHjAaCqWXWYiFz3fNcsQgGiFxC1L1myxB598CHXDbo3HCBM4FxWJBM4j9q6Wjts8hQHZvryPsE5hQAmnIC9V+6BtVImpf9Zy0PeOkNmzlsAOi2As1VrVjsbJ7CmFlpyqOzZMcccY6effvpAp38pgEyrUBpmQfY4SiaR3YXYsY989KPO8u3KQOo+yLhYDPKTTz754ozt3Qy/EWxAmimCXQuLfur4kfba0bXWkCBEalusM9JqVRlCNh7T1cQhap57KDpDtIUMgOMjrLNlM5rRbhKeFMbl97CWqWoc7UnwiJ2P1nSQdsABGXOYL/SM+370YGJnMjzzGGM/TrJTISMW/hzKB/V919Jifq/CF+i6CdG5mD8vNwmgh/4nlh5Ru7yWXtENBpKMxjj9JUhq6LXeNK4GcaJDrEgx/TIcf0zSeTmXC/rVlSJiw/LJbXqZnA4cUEk77REYWbUQcPcNN78T3RlXCL5IW2qr5pi9hEqLeA7EmDxBJYhXqv6odEC8J7+mFCcCurJwo62P0jyvOUhgrTBS8pLG8wF+1gc/INvHG/Q3jJYWeCUCYEKXYHRmWPyFlcoS6MiUyTHEDkrclTup0xNFmbvboHfQIG69/fZvWOMyLCs+9WHbhmnrKGLvyWq8uLzEasSqM+zA9B46ljz71S2lOQtkqkGfdR0brrfao2XpnFHtCrNr2e0vtJr6i5lb2Rm6n09+0vV3eq8n85RQRpn+HVDD6T6GEUH9/iw1LT2cujPLa3e3sgdPolK5wuuzxa4xwCI+omRUKkRXb/jwUgi8Ey+kXtKaA2YtEys1LpWsmkDI7YMR5iNkAQboYp9Nc/ajH/3IGRKFLAUKwoElBiHUFYVgNyhqGyy0B3qX80oAZP+NH9bdd9/tJcKOnzs3AAo8sz4msjj+PrtrYWhHu2NhIV/HxFPI+iUf1NEzijLh6jghM6myVaqjqWxXPVeFCAV+5syZY9OnT/ekAnmgKSMxFOzv4xDfp5cHDFbcNwkSAyvpROBUk7g2YWLm5s2b5xm5oQ4l7MOqziHmQKFThXQkeJaTeZp+nmJ3ru/tVOyQD95C2DGxe69//evdd0/VIdyYQ5g335/1/W6Eyb/59W+sg02DBP9HHAWbP0jLsdD0dXdaBVrLSSTATC2N2Di8nsp6CdVlELYXj3Tdk7s2yBiVWUT7rn4vz8bnw95lO5oJHbFrQ8+mKaSPRTNb12gl4yebnfXuQT//QAOyHKJ+tqmABhZsAQBVL0F6UoTYWzKRofYTGcK8EbTCmqEgVtEecw+YWlK9CNOLmev2MalpYFzkQZ0YO7VgfIQt2IQGIt0hRGjCHwI3eSytd2h/rT2pjlceTIMvvYU6YZ9POaoAjsciuAfcj5KhfI9yTToLTkZ2IIGWOVBHO+3Kf8NMtpd+isN5Z6yb69EUxjNO+7NW3nCwqKukVswd4w+d9ooDZH9z6+l5SSagBD0vIPN3EtRZ8puH1NCoDISH/IJiqZrk4vxK4ns/3PJfmd2x0Fre815rGT3dDvcCg4CwbnbkbUXWAVtQNRLdRjWiReLk2hk4Ia4RIbEurvgZsUg42jtUk7ZtYGQr9Pi3iNEz4UX46TyUES62y1PuWXg4SW2Ee/Aqi5P6GR/KmdZrhgYhAQmIRSSmCYEobV4YML6d66isoGSZ1ABQ2LoVvD4DcFQSRUyToljBfBbZQGhJXlfasfBd7MEPfvADX/Te+c53ejWEnb5AAbDdVey9P8KN+2MYHuqArJ8dqdgYhYrPOusse9Ob3wRoDwwU3YtsLzSEXmZJOrA8sCg0WA1Dds89t8h+fcOvvY5oBUzzHNin4447zs6cf+bAY9Az1nEEyLwV2Fbsj2e1p2NIdJ+CFRI7GwJRbQ4ewbNOhdjl0abzkrGp2D1dbwmMnSci8KX+qZ9DnZrPIVyLQFqocxPjJyPimbNmOQu4cxzkgSfvV1k2scQ6/sUXX+xAtYLQ7GBN26xi9xhQJiVZakuetJaFj1ls2xrPGE/VYy+g+QTgHIil/eRgJYAOfC9TmSA+LwgNYNjZm7UOimlXTz/Oyo8+BoE/voSDtAMNyNJsaON4jhUCsixzVpF20gKgEWVh7rkFBiGaqKmC4c7y+QXagcrfaoj3tR/2542MnaMNSNp9akEOk1aFwEPOh4WyZ51dIonNN8cvvSUUeqC5LpOqITmOGaUMnTZVvr0fAi8G9KQOoPMJutFAC3/30k9vP7xTpXu0hiksKqKYBD09Y2fLRTAcWn5kr3xABlfV00O2I+LVVnBSkm1BYwO4Xqm71AUcyho1i/likXxwYJF8sOhLO9L8OOlejnP07XebvfV8i04+2eI7EN5uus961z9tRduztnXaPBs/eaoVNQDKBFw0ByiTRQdSmaJisVAiavEKIjTnRXK1a8oLKqPlYRBWnx4uZLv0Y1UJoMh3jPBdYOWd//uuA2h33Z+aYprY0kzcus7QUFKTl+aC6C3fs/ZqBNInn2d9CKz192LOL92D5USJqOid9cRCYXToseRmuiy+P/vpT9177KqvftV9sMSODPhF5e+jl/IBIBQe40CzY7pdhzogkw/Zxk0b8X37ik/KclQ/glJSav4cdueIr7k3v7Lou/zdBOBCXZ73xPxM3ryjyX75y1/685WYXqDkda97nR0942gPV7thY8EqJYAeOvG/yKB2P0zNezrE7sC9WEKBjaamJmfNZCi8aeMmZ8EUylTiifpkaDCp8ImuJzSidWDJJDCifoSzYQJhuv6wedhVDGKBWe2ypUvd8kWM3GX/+q95o9vBLzzFDinu40i7MEAZG6TetSvQmCLq72y1hpQsYvibPICkwsYSIvA/9IeIhILlnmPkSOXOlZHkVD/JohOmW8nkGWYjsYwIaxfu4TQOFkCWgSHzW6mEHwdk1Chm6uwfQKG7v4AMGU5eBtdxGPcBTZ6mnD7eXEyh3v7MTopoj2BqEExUJCPvgRa+MARBQwOeJDXxYoT3nHd2llMHy8/h+hkZynBaNp+V4HZjytXVxtn7pUCZ+sjgR3coHzKw4Wu1iAno6/xcm3zgWg69tFJagiLHnJZIwPC6dL75gNaBO8N9++RDHpANlbnUj9loUaTOa2D+9a/rKE/UYfNOOdrGj5PrvjivwSlNEV7OCoVccn68JOml1L23mi7SgH/5HUuMoSeMwAX9+c3Wnt5qKXxYyspnWWzqFIz3AWMJiesJE8nrDFrLB78mef5dRGxch+/uI3ZfrNClslgQShaVeKaJ0+HEIYpUoFqBe2/hdlhAER0A1Qzi0Php1T0E2MUBkCkdLzH4Dpw4pe/EtHPykL3OSyFayhb14Ipcdt9PrSteYRWvfxshDxIXFLKEMQs81oQuOXeFdPILdCimDhf0Jc8ttm9961sesvoYQuc1eITdd999/p5xZPydeNJrBmwJwvi/vheyEfvWpffvqw91QBZ2k+uuu85+S/breeedZxe86QIbM2YI/6aC26iQZGIXA1NVW1j+wnL7zW9+46J6Pa/zLzjfzj77HH9nZ2cHjFRgSSAtl7IldQyxVdqdS08m7dXfo7i5ykPp/AaYMpiwQrAvYLph/Qavafvwww+7nu1Nb3qTnXHmTnZvt71qTwyfFr38jR9gAznAEwsW2A8JH8vI8i1vfaufT0mBoeXupevbvQAAIABJREFUPsOnHZh82YOUyahPg1SparBl1rzVck+gL+X8cxioZvnKYT+Q8zGtlQlGDHPOGLYGCbzHSuqpkzsOzdnow5iPvGbJkBrSAw3IgrgEcwxTTaAWCUJkWYTomzZ22D1/DrSBe2rl+HrUj4janOPGY4tC4lR+2swBKgTuXJw2WBsCsEjjurP8VCAjGTJMWfB5WXTOEfeqZG3QvKeAm0ITodZNSR3DaFlCsop6iJR2jB5GiVyKIEpv8OPDp/IupahJ1qJSWYJoaqxN2sQrgewANuJL3LlSX5/DYgt6BCGpl/eVPYBnuG8f/YoFZGEITRmHaWwyFjzeQtbfGuvpTtm8Uw+3009DewHVqYyRwZpHFvlS7UzpuPRqdU8X0SpzcwuQ7bZrSS9fYj0TjsaXpc5qJk+y7PTZVlR2lBuyurBR2g6HWgoJKsOTjEJ+H9dOg8NtRxLS1tVBZFM7bzLJuvspkE45KHa8moz6MSrTAAg0chphmqrlzq0wDNlm2S6LYyutcFQUF+6JE1Wvs5tPHByQaQcu7xmfStJ4+1CGyVpJoNj0gqU3rLH4tu3oXXZY1dTDrbmj1TqrG23S/LfAljVaBrBXpnyBAkBWmBSh0NbXv3aNh4TEDLSht/kx5rD6rvCRrEXOOudse/Ob3+yLYMg+6HjS/RwMacuHOiCTd4/KVQkUqd7nAw88ABgbQ4WEc5wFmjBexZPzqw4T9E5mLBgVQSZTsT8PhSfFFIndFNtzxx132CJ0WaeddppdcsklFB6utm7YNIEdeXJJzF/Bc77pphvt8cceH9Aayi9OzJM8x/4eLGiY9Slw0Ugpo9Nfd7rNnDHTGS5p5BxsMq5aKMXy+OOPm8CrmL2PfOQjXm0irPfpRrVilPJUilg29VHXnvE3+TKJYSsEYeHY2Lx5E+DhHr//F154oZ+DvJVcuDxICz9beyQf9nzTLBJhPspBo0fQlbGS84XQXzVvSIISgxTohXh+XWVWXgtz55VHRHlLzwo4ZQOW4tqrh4hZHXhAFtQTEPkXeATr2kgcSkZt0cI19te/DA4oupPb6O9FdubZRzInUm4tf6+Voat5skD4tYensIfnE/aB/J9DUBbhvu5Tnw4JMC6DaLJnxgqPBQpAbJOU0DSMpg22+pjfNW1M9F1gVGuXxPi7lC7b9aOymTbuGXIfyIEca5Y01UoaE0DTqQ/v7IZxYeFbMTvHXoGQPWNPOjwAZDaX5NwU7YlbiTsyHDrtFQHIds2mKCxPIlfvpcs6ERq/gL6KXTrM0egxEfQ0U6xesq6hNiCdDFpIoS6EYz3EpHHzsaoUpSRWszPbTIr9sgUWISuypAoW7Mx/orLRUXig0XmL2fnQSbKEEZUWnNVAze9GZPKori0urIjc3+XLu9HeNFMYG/o6odADiQdJWC4yRK14K+dIBhqdTg6yrh1jwhVoKyK2mU6WcSxAG4OspLiMxSFlFdUZu+AtmOLyPTFESCL0sfYkA3bWyRULre2p+6x08zKrJrPLKvAqaiOjEzFxkjpyuSPmWOn8S7gnk7z6gdjDQkAWZjbqd8omuxZA9sbzz7e3sgj98L/+i4X5MbsccKYwjxa+RYuf86xLhTJDABYeb1cLkgMxrA51QKbwhMLzKu+zjcxD2TI8ileWQpUCWRUAo10NSgv/rbCdso8FrgXOxHQJTO2gwO+a1Wvskve821k3AS/Xk+XLCIXPSsXeb73lFmeeQj8xARnp1xSy3FPIdH89awn3ZXmh0KEsMbQR0PmeccYZDiDDVsgCPoTmTuBVG4a3vOUtA+cqfZn6qECXmsTxOm5M9ToZZ7pvYQ3PkLnXdwFQWX8o81SGye+/9FKbiObM79UQDEwfVhVxGTAKO4S4eeCsmZOcUwkOE1FJJAd4KhrPNxZQLaZq4TQX8uoeggVEllDVY7B24AGZ/OaAoKz+cRfga8ai7xDCfPjB5bZw6eA3MJIpYx7ssdfNn4Tli/LYg3vl+1qF4txoa/dtrySOeY3WwBHypxM8/6CU16AtJJjA+UouDJ9VxCsUEH0ZYkM9+MGV7S+fMQ5MdKXP0ygVeaHPONMgXcpenl8Y/qOknyeLKLIkec1QJ/Cy/12MMf0i9M2IqtapIjd1AxmhL/sp7McPOOQB2e5YFA2GUIi7elXOnnhipa1Zm7T6uvH0R3YH0WY7Ye4om31MQygN2/Mt9fGq/zByetqsd91KiqKusCwC3VhPl5WNn87O4QUraWLvcPr7rWXc4TaSztsd77ByGeGJbveOK/F7MKkGc0EwcYo1ewL27tmnUjAKHCODL1mmBWSfoKB6rW3uXkV5oRoWzlpCnWUeyvSaeYAl6ReiTKjSV+S8ekEt2gh+l2i2i997lJVW4ZQuAcUgzaVsnIq73nNvchtXW8fTf7XYmoVWnu6yTnbccUImkTieUBOmWvyEeXgXzeI6WMxE/hUwZHoWWsDFLmzatMluufkW28z3D3/4wzZx0kQMM691tuyraMnESlz/i1/YnWSdXZMveh0+y1A0fjAI+w91QKbO5qExOdfTBDxkA6GsQNlSdLZS1XeXVmiG7DpAD/dRViVfVmz1qtVuWSGW7WIAmVoIxMSQ6XV6vmEmpUKT0l8pTKkFQaBdgMArPAyFSPbDZKeEl7CF4VIf1cwTYvFCYBZq2gQsFb5UOFb+bdo8CMiJ/dV9CLSRsDT0dR1DDFxMCSwCv3nDWv1OJrX6ndhfsYF6r/Rjn/zkJweKuw/FkPVzn6Ispgq1aY330BNzSIax2pvqtcoEDEAeXLzoVuWRhySlKQZ4L0kNOqdSwp6eox0isyEA4YEGZFkiBEpW8tMkKqANZxSTW6WSPvLgenv4GYxvB2vJ0YD+bXbaWeNs2lH1HqYsUVZ5/vp3d/kDzhgO2HYePJCO5G93/v2yxxLpKPZuwOFIrBRipn6+4rEhjIWYz9PoPANAz2cJJDVvwPoI30WVkRuSMRhigBDRsHqkJkoOQ9KSErAVSbqXz1/XO9BX0pxXzwtk3+/gdwp36toObJal+432MbfJy6oItqyI+ayI31Gpxqh3PWRMfj/ML/vzEIc8INvdzdAkqYlRC8stN64mBX+7jRw1FeBUatubtmPz0GnTp1VSLPVIFoXBb+dyjjEGMW3FasJ4zywxW7nEcnh7RWaMMzuKr8ZzrWvz/Ra77SbLznuLFR8736JsblphukrQJ5SK2NUuRPgrzGUWRlKtSpiuFJPtPX9eZ4ufYbKnJEXTjseoYv8QFGwbmbwxrCXqrK5mFPqHsZ5tpEm9ByCYJvVdIDFKgfQoTFp1xTRrHDGTRaAcK4N2e8+lM5kgWvmbQpeDNFg/oSqpF3y3I3HwDhgxMrn6yOhKVOF1Rj3NlnSVlR5/upWedCrGrJh/MqBjuh7Zm+VDlqF/lFiDZ555xo1IL3rrhfa2t7/drRZkvSCG5khE5cq0fOKJJ6xuRL0vUGJdQiB2MBWHPdQBWYryQw6E1FsUchRAKPh3Jm+4GLDKQT8JAVlYFDzMJBM7JssI2WjoGX/q3z9lowh/aiVxRkCZiLskCRQK9wutLrwUUj5TcX9OaH9zLAnb5ZUGi+WhRM4xSWhP5x/WxHTAioGtzic8x2bE/gqva2Nx1FFHeYawfPZC1lbhToFMhYG8qkA+U9gTU/Kh0DAELwCqjYjCoQJzn/rUp7yUmG9e9mA7El5HGrY8EQ/YCJ9D9N0RVfAPPjkfxgyRg1RCqvkYvLS4H0sevcGRnDRoaEuT8ori+rUyD0FxHGhAFghG8gJyrrk32YVhMNpEfv34o5vtwScRBw/SSqlF2Z1aba85ZYQddjg+k+js4lE5wivKyyYjWuF8Tyh214bX/5e33fF6v7r1yrhVooaPE30PxklfGkkAVVSqq8tJ6iA8XI41iq8pQf7kUMayqmUskiDOc3GQ1LLDuhc+YT1rFmPq22WVKkk0jLZ2zuts/BFHW3TUeJ4/Gk4kKgmYRn1WfwqwXzJ4iCilsKl8PFPrKF/3iKWa77aijkesDDuMaLTGkshlDmRLJtowRpbrQQPDd4dhLkD2/1SrqPuAldS9gQjP9AN5evv82Yc+IAupSjy3sjnS26Hhi6MNOHdnobQX28oNaauvabCmbZ1er3pMYwP15rZR4i1G6GKajZ4Ig0Cp+cCLi4K7XTkGVX5Xw/GygLgipW5s22yt7JbVag87zFiJ+FvMkgzssk4cwm/+rpWTcVVy7j9SHWCED8eivlaqCwwOiPrw9/rdTZtt02pS6PtX2tPP3Wrx0lo7nIzNlvaUlSda7bln15P5NReWI8rv1lHnbxPp8qXogtoYzHV8FePmP9nGjDoC8W+RHTV1JKLkUYGw2D3JAg81LY6aVBJ4E6XFeGhh5kRVRxOIx31D+yM3/zUvWNs9f7Dq7nbrTBCiZTeabQOEHjbLIm+8yNLVTHIcm8AslHhgeyERt5dC4mexMP/2b//mIOvyK/+fVTCBRnl1y7YWu/fO++yuP93JtfbZ1KOm2vv/+d/x0yVjCufxYjk4y9A2x3ml0LKpLEofIdNoC+nfuk6ZlrfDSGsLyqQM8s04sGUHzWSzbVuW5xehXJMW1iy5B9IIDk/lcKgDspAh80SSfB/YNawYMMrazec9VPKi9FALVRiKfBrPra9TGkjsmHRj2iiHLtm7qyen995+2+1euUEARMCkHhBeXlbuIcTwvfs8c+3lG3RO6odi7KSjUzvt9NPt+ONxqVfj/AWYFG4MXb9De4577rnHfoOVhxJSPvoxDFS5L75ZcD2WdJ0Z7GV6BpIX9DsHdDKc5n9+//IUjDYbt912m+vu5FX2djYpe9M8aVI+O0jwg4MJnDEepQvCWzGuZCF+L2uf3h6Se2DIvQyQQpmEyxIAzeoaLbr9bv0h3ywvDB1lkUVrY0Ns2A4GQObicbGCAOAYO8Gc7C6YtB55eI09+SyuWDBMgqZxrsurL0hzyxyRo3MOkYQ59COIdbqYPcMaIENaZSfG8GDMcA59AJoyzkc+aTE+aPr0Kjv9zBGcq0Aiet9+MhmHALwKSkYBfRpHaTbHiQ3LLHnrDVaC3UeyLGMlW7ZZckKtbccJv7I9Z7VYYqeKA1+9SvbO3WRhlrMupDH/jWCdFKtssCR9vkS75QwSk3QNc/Y7rO8IHACYPyNS+MdVlg4W24sFD34LBLe8+3Ux12470WI7mq2vfYslIN2UP4a/+gFtKIMAXXxlMWbPPWf9TUSI6iuticSz2vH3WLTmVUD2931AdLIU5lvRWKDnkO8MRJM9/VSPPfLQctBSBH+hGuvuIBOJQVxVWYL+ZQuUay8+SUfZ/HMbYJsQLqPH0iSWhpWSa3EwWfdZTybmg46aDsyJec+bEuwlGJg9HE8WFqU9zdZ0248BUuySzrjIUlWNvjcq8Ul0cMq6B5bshv95wTpaShhkS23FmvttzPhZNq7xeOvuLQKAXW8rnu+w42afQzmL0SQlaPdWTKHXsUzA+AzVtrAoKMtSIRJCI53bsR6osje8cSodlILrkTHuUZZgsdWuzwWnnNyaVSt9URyJyFmTQSc78RJ293H0cbZlrW1/HBsDwpUleElZZ6/1rdloXZjpVr/mdMuObpSl0cCVFbIgAmPyd/rl9df7gn3aGacFa1Jea9FP+Kq5aYf/U276AoqxYswrPXjLfRdDl9pmndvuseLYcs57LpMOmUyVp7ATncRzA6SpWG2W+4rjM4oIv9fLV7bZvfcuoRhujx17zBFk0k4kQYJQwFAajiF666EOyJSdFwKwwhCwHOVXk/G6lqLjYnUUPlTrBagn0UqFZaiCMBeFffmdQJUWAunC3vHOd9j8M+e7j18YpnOLi3zT79RUy/I73/mOl9FS6E/gSMfpQdulMk7SeL2cTeetcwnPTWWZ5Md26qmnuu5NYnxdu0CZrlmvC8O0S0lc+OpVVzsg+9CHPuTgUaWblAjgWrJ8aFZh2oT6cD4sG7Js+q6wZljBQjo6ZVnOnz/f3vOe97idiFjHwZorxNh0yNYmAFpoRgEcPFYPWd53N8wFOrNUks8iHpVOi7oJMrLdXiSexFw6ZjXVMWscM8KOnNro+n6Nt2SqicxNVtZB2sEOyBY8HZTU2WltB5Pl5KtsMgBnQ1UXH6LzRfrKebbcd9mHECnpR5/kujCxqQrnKRFqF0AWwRvNywvtB0DWhwa5CvNvtwkAe3dh1ZFJZqwCu45Yeb11QCZUVQDoWqh/zGY2h7ylhzFajKQlku4gQkLy2jAAmZLZinVzuzqtY+3RVtWlzGnm4CrIgeax9J9FL+fwHfrYESpRZFp55lNsVM16Qr1kHwNeW/mqPZxzqxzcZ2/oD/j7vuLQZ8jkreK0vfakcXawRbZ4YbctfKqN0B/hivKtHsfPUicoxiRVUkK2GIgtUpSyhhFVdv75U6ibpx2VqGwBMTRf8gBTOiWp45D7eV98sS0BPavPERAQCybIVZXpts6H/sB7ElY++1Trlt4LtinhpmODb0E6GGe/+tnz7DRKbOu2h23NxgU2a+brqQE4zVIMus7uW2zDunabMf0EzzRqQ1vQl+m02po6N4HtJ25ehJ6ruLSR4t0jyQfoseOPG2ennCJmjmmX89BuWhmdAkZaUDdt2Gg//u//9kXl8is+B9sG9czvS1mMItry9LRb76bVVqp6YlMoQixh6OZt6A+iVjxusvWp0KyynnQzVA2jQMy9EasMLcASUl922WUsWhKEq5wTDAz0uLQ2bkLL2Si0Eoeb00HSlAOI614RArD2P1vnxmst1v8wsofZ1suiUTziI1Zad5aDZMuhEWB3l9XWTb5CXNh992/g/kW4tg4SzdrsknefYCNH51wzM5x2qAMyR6v5JquKxx9fQGbaX7yUVYrnJkCg59fDhK++IdAhvVQIYLbDaNUhRBd4kDu9vLa0SCtTUIkafQDs8LVupBp6b2mcKIuL/7W2tpCp2+H/LsctXkkGOt6IhhEO9F7Opmvp6uwKNmsKV3INKjqu61DoKcxiLAyT63UCaNKPXXP1Vx3Qqj/r2pTgoGxMgZ0QaKk+5lTKQTnrlleCd6IZq8ReQ4MuLMumUknf/va37QRqXHrh8b1prmEVtaHZhnHCUNyElGfhc6uQYqxE/3ash9/kNdbPLiksBSd7HGX8tXW0MQap+0nljmIKB44ZXWfjJlThRUcJppGqJjJ4O9gB2SNPBJpF6ewCpleS86B6SIzNQo9C48NpnVVYk6i4PeFJ1oi0z2X0G6+cUgTwCRiyKKBZDNnr5it0tv8A2dbKiTZpC5vkLrRRtchaqMpSwkY82Ru1HSSXROkQY2CErFvsL/n/iXI28sgU2KwWCZBFRw8LkCVhYUtkSMnGaduyBmyephL1qbIudIkLnqZSRWbEcO7usN+bLScy0rbIaipq7bRje2xsjHuVa7YWHk/dtCdYnPNM+LA/6e9zgFcAINNOXLMWOwcWZ+ZKe+aJjbZ0MRNRtNb6S1O+a9SOJYqGIihyCgXd38sOsdcuOHeSzZhZK1Kav+s4sSDF2g1MAv1CEDBAf5NXNxZBKakKvV7RTF+tlw5r/cpA1dk4idqUwd9iopEIww3WmhhnN/5iCS+L2/Mv/ME2b11iJ590CYTcRDa6ZbZ21Z/YifRSGHmadXZtsx2tS6y9e7mVYhibhLHr7a4HWM4gDDSDkOUUFsecve60aaaKLDKXtVKF9ICQ7OrCM1n4zLN21ZVX+qJyxdeutGlHTHNDPZVo8gVcoEcmlEw2LUbBcn5V4o7+KssSWHfAr8vKljcFrIiE2yqyrNIwv/j5L6jlh2XACSfmd5O8VaHF/B3VswrPRWbZQnd9GVKUdaAcA2rHTyyz+TNo1AIAnCovtcToH1ikHi803dhsEDLqI2SZoFCyyJjbf78EAF4CQyZGpNkuvXQeTKM+ZaglZ/CB9koAZGIwxf4sghWT/kt1Hc8971wbN3acHY6diRY0ZQALiISO/AJrPRTmrq+rd4ZJTeJ3+cip6oI85K78ypXoZXYC3l096PzZFWjY9O+/lxls+FTVL3XtYVJD4ebBz4UwV2HIMnyffnfzzTfbb0lMmYx+TC77AqwqSi4QGYZaZSwrsCZwpvsofVh1TfVAwXF19BDsiRlURrHKSr3vfe9zlk7gcLCWpsRPaUXAsre15kjG2G5LFu+ACYf1E7Pnm0RtSoIRJZNMsUMCY96yDYBgacMZYwDyLqx1DMAwa85oOxkWuW5nouluT+NgB2QPL8B/kfsQI7lpIBlF9RWlroPNTBQFc8VLbTk0eEVsTAXe+6RL5j7HZKpKYkEOjwq3HskDsmkAsjMAZLq/bpAk/dkQ089QIctMHwldmjljeMzJswwLJ8vW2lPt3fabLSvtktoqO5wapyUZNh1eN7IMP0s21/hZRvsRlmSrhwXIZNEU1yRN5KTp+ZFW0TWGda3cNvP5f3mwwx5ee95LvbX75X0jEfC3p5ZRjq/Yzp/fYVOqmiBg1tgOun/DUY9Q2++k/fI5f6+DHPKAzLNA6PRpykJEAB1FgK6tW5P23KJ1DmRa2tgxdMH68PsYpqai/RPsLLSRbSbDbNZRETvvDWQkMm7Jm/LJLQsTVCRhrlzF8z5lKhMkrZWaIpghE97KesTeGUoZZsdrDRVTtDsogeRmsoPbnBGiTNsdtzzDjqbIli3/Pca1G+zkk9+F4LIRwWXCnn7yf4hY1BOGO53FDY6OBUYhyGJ2Qum+KN41662yfAIhxBqui7Mt2WpvfpOMbyWExbqW9F9xh+6Z5BqMmGu4bsBdXbv9N73jLfb6M88OQooOxgSeuBwvO8Gg9vxQZeWQUUZoJKt76PcBZkQ0mQq259uG9et9sZbX06Wk9is81dvdyoKC/w8TGMmaDp6YK4J5Ki9UzsF0eY1P7ldR/xZLrr8azd73rSQLR08NP6tikhv1PYx3LwpuKkkIen59FFeOZkq5phJbvHSHPfXEBtcAzpgxnpAlVH0c3ZvqsA2jHeqATE79Eto/TNbgjTfe6CyCfN/OJGymRaa3JzAnHcw7SV5mavIWkw5LLI+e9b984hN2zHHHOlAJ7THCW61/exiU5/SnP/7Rvecq0HV0d8HoOrsgZjWozvByNp2zOos+R+BCY2EGHmTTEOqraekOr13XIfCpfiubCmUDl+Ph9onLPuGO/DpveY3pODqeQJtMcaWL+/nPf25iExWGFTOsMeDJE4yVQm8+sWSy1BAoU/hUDv+DtbjSJFEtx8imLC4ew6UoUxzGmPJHZRVlLEar8gCM8KTLEXaK+3XcFMy7SsjolpdiDlsESOhob4UVT8OSNdjZ8yljNkg72AHZo091OkDyCiW0qDbAsIndve1ktrZbvHdw49ih+l6mrC1IAMgos7aUObOBSMRopBQ1sI5K1tJGFVsietK06ZV25lkKAStqoWyn4QOyEnTEnZVsKKoruJaEdW3ptacBZfd2pe2BlVvsi5OKbMZkqkTwmXGBcubZJPWMSxQZoApLLo2tzTBClpqvY1rMerZb+8ZGq+oG4CFv6eB+L1vZaF2RwfvvUPd3uH+PbxxpvfHNVlldZkcfucaqWQ8tkbQdhJgbJpOEN+JVDdlw7/E+vV8skKy2xHzlnNYPWJHOniS7wR5bv4pQ4OYm27aDAdqZhuqF7UFkXlFRjSYA0JVab2988/F22GTJAoLddBQhv1o/hbeViZU/ZPBdrSDjSeE7cQSBTyssEMwa0AUhpASlGiCDX84zz3XaA39ezk4mZavX3s3kvo4d+WlM9pxDPGUtrTdbefFkm330fOvupP6gpxrjUUaGSybNDjn6pNVUTWdhHQETxgJXvBnt1oloRoKTlIGfmk49zAaTWetSDD21q37NvJPtYx/96MCOWu8S8OxDiCGAW6YNuCNLQK3kKS5AZQeOaDSrcum5StcDSeh8AwvNb3/7W7viiits+tFHBwtERCFJ1RHVpJm/hV6uCWowRQZnjgUhTvmXxGiOx6KX22Ity/8NPf+tFNY9kRN5wPqryRJqBKSNuCBAwx6g5noAZJEMpoAwc3A7aJswyIVpa8QSTiA9k9kOgzO4RmaoznaoA7IwZHnFF77gWa0qnXTia050b6xQtK9+4Tqr0I0fACObhq7uLgBYizNC0o2F9RzFsP35z38m263EPn35Zz1rNgQ1Cgnq2GoCf0kA35WwsSsBICqmLZYoDPcJ0OyTieZQD2s3f5eXmD5P5yR2rJ5i4hdccIGXd1Kf1eZGwDHM8NUhBMZuwTtNmcKnnfpae//73z+QPephzl3czRWeLKWA7O9++zv7A6J9XednuS+1tXUDc4bOQ6yawrs6/p133umgTGBusFZZXGrNZN7lYN0nTjoe/7J5jPFxsOU8I+a3qso8M1Zg6hv6MOq4JRWAEz67pzeFBU61lWAToMhAVVXWpkyts9efgy3CIO1gB2RPLISNYuPYD2DqR9sXZSOtYuKSduxoQhDf/MeX0Gt2viUF86YEgUy/rFzqbCTJUw18lZTUM5/GPUtfc6IDsmkVNv8cAdz9CMjIpm9FhlJdOZYksWq7b816u575tqOozibsKLZzxzTb7CnjIQVgCr3eXqnbnBSXiEGQb17FsACZl1xSpKdji7VuHkuil8gG1lkUMa2dJ1pF+vFh3d/hvhk3JuPRowOvQ3JDeIz/K++lKT7ORkzi3OrIAj+E2iHPkIWsTrDXzVkvwippB6QpUIuyi2FdsfUbWwk7tLOw9CLqT7LIKO0dkEAMfs5xDTbvtWPoxOz0ETHGvSYkTxkckgWMaMqLALD68yG0qATyIHCBAg1EpZKrpdhNZKMZ31E424OX2ACI20OnePCRTlv85Fb0YD2weU/Zps0rCKOMAZChF4hssHIml0ScnfS4o2z9OtB/tIOJB2NaZR0RNkyUZ2zimLNY2I5y1qyqpsv+8T1kQ+q0GJ+9Rb2+m5eoP8STn7zsX8lEHOusxqa16+1r3/i6lVZFcf2IAAAgAElEQVQ6RejSerXwtRGPiOAK7loJ7QhFxevG8vnZpJd3EiBra29zn7FJkya571horRAU/2WXrtJPjvYAYZ1PW3LHfZbseJIFZY71lODn1vBGNHjnAsxarHXFpZZo+x2A7AQA2QLrqZ5t8bFfsXjD+Q66c8q64ljZKOGErNLWAWLi2Mi09NC1Iqmwd6pgQFbHsIbjoQ7IlMix8NmFnhk5d+5c2J7L3ENL1g9hGK8QZEhn9uQTT7qRq0T/G9AESoelUGZDQ4Mdc+yxdhxfcpyXl9noMY1unqr6jwJg0kvp9QON57R61Srf6GT40t8r0JGJaQpDocN6QEO8WaBL16TPU38XWzV23FiuJQDqEvULFIbnIsAoMPZHWD2ZFb/p/At4/Thnv9xvjC9ZtqgQ+UjCl2oCtGIhNQ7EHgq8nnvuuXYO2ZSdiKHD+xHaJvh78v5lYg4Ha5I9yOsvzcq4jQyyJUvabPkqbXLKrL6h3npaCzV4gS1D2NwXkFBWSSkmwGwsxfBozU4l23luUaQaY2zW7MFDpgc7IFvwLBoySkMJkPX1MXsx8cYTeLRxja2AsqJ+LHyG0zCWdWMRNtdRmPjyyjrA0Sh+5vf4P5JO8SJAdubZAmSEDwHQ+4UhIxTT0w5zG623FYSp70f/20LEoaGczev6ZjtyVMSOnTLGarMtJE/Kt4wIDfciTnQlS3mtSIzoxDAYMhftKAzV1mItaw+zijYSgLBkIvBirV1HWW3s5U3KGerRZWIbMGwPIi+14lEYTuIsmoqrbMTkpwhZkqV/CLVDH5CxECv1PBFHWImeSWBeXjJCLEkyvKrKQh+bwMuHzaItWdZmK17YZt08yXR3DNuFpJ37hiMQL2uClpZMdhAAE/7fTr3LYlgmlWItyocDsqAdIApDMQNFKtQG4IAqJ2/AtQuo0BzA7Q0gu+32HbZhpVLQ5aaOtQau+Kk0rE8CjVsE0W5uLpq3MjIS6+z5ZYvRcDUDBvGAEUMlEWum3g6bcJqHMhKUaaqq30wG13GcOgOFSSSbr3YvgKXQk97znne9295NBqQW2Zuv+5X94LofWXld1UDUssjPXTnNGo3s92DvsoDMBFk8cTI/vd6lmD/N/WKsuNcqj3PrLbfaV9DITJky2VkEfV4CX55+dpLShQaAbA2j5be4WvwEmdoyi0C89fJoqsZ806KlnyCFOWntKy7E3+9OT+vu52/J2qMtPgZAVn0+WJDpkSxTsehZgGI/NH4czNVPkV4xeu4gzYSk/IxcmrqJbgvw0tuhDsj0jMRcysLh3e9+N2XD5rleTABCpqhibUKPribc9+//y/12JyFtMToKp4lNUzauQnDyzpJeSu9V6E3lk26/4/cO1M444wx3wA+LcYtVk65KrsjOOgNkCoFf6Iyv372cLeyHuk6dt2eaeg2/wHNMXTLMMNU13XTTTbaecKzGhmuT6FNi8qQVk25O16VrFCA75ZRTnGmrqq4a8DiTG7/Ar7Rh1157rWehCgQKyAn4FRpZF4Yy93gPlMonoyVpN9mIQJCgBdxhq9busB7YyFoYtKAVgjFZbgS/zaTIZIbFybiWLihUXl2Ts1nHjLBjj6sdqpShh2c/huWHMqJ1PbqOkE0MkzbaALFiXnVPruHa1XStfq/z4emX/oxl47Fn24tHn2rFQ07muJiesiOW835puZ5vL6CsjWue8NI/mndWihHVPpT5OQN492umb2SYI6VNTpQo653VgQSugCET47gfAVmv5DWlthiW7rcYkqeaUvaO2fNgPiP2fw/+xk6aOMeOAZDV9WMhg2dZhLWLXGEil7yP5CzDZ204gKybzX+51kNqNnetmmsV6HQtRxi4tM02bauzLT1BvdoD1crR1XUTcSkuOdqmjqV2c5oNf2KbtRZToWPqQzzAUw7Uqb2kzz3ggCwsMaKz37UEkn7XLZ8WN0RQBS2JwZ2vci8aZRXFWelDir4w/FFI2++szxfyPoHQVmGZhU81sdOtQfsxgnAGQMhDjEGSwP5oafxkZOiaBeC56TIMT5qEAunZIjBrP7lulZdgSRJSyCKyKi8hzZr6Yml0AD3dMFB5jU04wRbeI/3cG9mOzqXRrT6i0NYTp2TIHJ2109G64CK0+C1auMgnVtXp0w5fwuy3ve1tXvBYLVwk9mqxyB9bflIyfdXCpaLMWrTD0JUnnTGZpclq7UHPVa2MsMUXQ/Rtt67ie0nfHoeX20Yrmfw/lhvxT7x2meWeJYSafsg6Sinbk5xoHSXrSGFeSj2+iV6KKpLbTl9hYugjRxP/OFUtK1axPzEdMgJVAgeTuKcbDPMxvhIA2ZVf/rKHyb70pS8hfmWBymcChjYYYUKGgNstCNlnzprlYEP2DAH63nO7+6677M93/9lZuLknzHV/rVlkHaqFx5V4XnxyLHDMDFr+HIIfg8oaGttawAWi9Ds3Xg2rAysktzu6mUW/MIQYmnYOGQrl8x0oIupXNqVCsI9i16LxpjqXjTB/PhNwfAEwHU+ZoWK09LVx40ZnD6cdeZRbWEybPt1DohoDV111lduJXI/1iwDZYC3p1xUw0hFlqMqyokAaMWDnIPaasSRXi3b0Q2vXbba16zfbanKJlKscg42P6EuyAsCBuBuVWItjvaB5UmC0bmS5TT6i3qYcgUcTIacsdQBLIoNrLA92QPb4s1kYITR9jHNywLkXxV6EPd3XTGL4SoupDMkgLVrcBVhFw6pi2+iiIiolpegKN96rLBClkACvD/CehBErrRjJBmQq/aSBuZKsxjSbmngVIDVphx9eYm84f8KAD9neMWT0QTTL2uvv9CH7pZX0twU+ZCkSzlJVdt+zS2xLYofNnFJhM+on2NbMaPvvR1fYceOjNu/wqTaC87C+JsuQUUug0sqKYD7RnxWVck+GwZB193VbeYw+QoLP1uUVVi9NGbdn1bpj7eabJtlqtMsHsqWKJllT70M2puYF+9DbttrselhCWOBuhMo1U9ey08e8/RBqBz0gY1TkJ3DtAJlkVLaEQaPfancbTne7K6FU+BxC08rQ9DFYCHLW1c4wZm3PV5bxhcPLK3na+OCL0d485xCQiVJ3Vw0AWQYbCQEylTz6xc/7GNhoAjqVObUFUEaqMt5BRbxOvjdFBSG3wvMJf+4l42VE7STuQxmTUrvNPaneXvOaMRxbYE6C3sBVOryUu+68y7PHLr/8chuBnuYzaF1mzpzpPktu6sqiUugnNdQ1hou6ds1iD8LwTMiQBICMa4bN6sZIp1qmjYsvApA1WXfpXyjPRHg2u9mKp/zMsvXvgwx4znKLBMgec0BWlTwMQLbGqqaswWNnovUCwNM6EguJ25Mwf7rhoWvdAm1vUP42HyId5iN8pQOyFKFLFR+XA/83AOpihcSIqPC47FIKsyh31xfkMya/LpUaUghToOUILCAUrnsNhbk9czO/qRBAU+gwdPMXS6bjB4TVLsi5ADCFnxtu3go3WPp8d93fNTlgF3PbwnNXiFFAQ+DpZ/97nddcVW1OjYOzzz7bfcfECIWATH07TFLQ2NBcorJT0pj96Q9/9Pe99cK34ug/zd/zhc9/3lYRpv0+CS4qMTVYS8OAxejTRQP1acJX52c2ypf5Ts77sf6zU8AOAWzIiwxPZhi8HFU+2kky6MkzkkGmeHldEfYiVTaqsZxQcZAUHW46vXzVELVuD3ZA9sQizdjMWVxrLk2SE1btiEv4vs02k4XY0ze4hiwLg6jbW8QzcK8xQKxXP+B5SALc39bo86HnDBAOrMb/cdTowxkzIwGCaMgUQXkZAdl25vRRlTNsezMJJIQK62LtuPfHbWmq0r7/3Bp7c2nSTjpyuo1mVmTLYDglsUmPW1l8BMmeXBPnNxxA5sawqvuAHq9t8zir6T/MBdOt3Z326NMR29xxYEXz9blxZNtvx5ojbfPmtFp9dDPPDdsLMGT9eLIsG1/NshxqDX/R33edZH3aKQRC8v0Lt5D5OVv6Lc3iEvIHpXV3tj3Vtgx/v2vYIBQ9O0DTEZU1o7V9iKK7e3uR/VnR11gzAL4EyFRLLQuSKIIG7urM2g//+3n+TTo6E0hT82prb1sHECNziJCk6PccVHyhLsTPU1vlfGtsfLONaphMKKKULMpme8vbZqDjkqYiX/MtrykJxMsJ+y4eYVp8v37t111DdM3Xr3FhsYT4CrPsKyArfH0Izgqz1ZxrhOrPct3kAVklVH9m0TkWk9C49FEr66sn9AvdPuV6668ljNr/lGUXfZAs1qetC8+4iq4a6yjbYVVHbrVsxSgsRRRJFWsqIT/Muav7818KGXuCh7JjPX8uSD8fRnulA7KQSfrZT3/qQvOLL77YszAHEPxQgDbfFRX+lK2GCnMvBaykATHyODvp5JPwxTuezMYZwTFDoOWD4cUH981Dvm8PlGAqeEk4Dl40P+wcCn7sYEOVf+DyRROo9MScIES5ZfMWW7RokRvVClSpdJcMawXCTjrppBdlPRYK/XfXhaQ3u+k3N9pf8HU77bTTfFOjz/kcmx2xZ1+75mswbYOLiiMyExSYdFSqeS2/8eS74gGqhxGksIRJObxcTJimPRexgsZcdC5WUQUV5SQf/ClswW3GtoewflSCNKUhEQYLtaCDDY+DHZA9ihdWEbYMJQlYpjwgUyGCLAxTU+s6Nm2Da8gyvVXcMjRXMJlZdKcpfBgzZBHm8KBUpn1xP3oxstedAOCml5IMVlMzmg1AFR58hGXzDFkadDxVDNkFE/crQ7Y1udAmleFC383mWt2khIcJIHuhrd++u+QFe8uEMptz2OFo7KltjKZXVkW9RFfiqueYRPc2TEAm3wEs0LEkoKTTxnlW3jWJHS+1nGu3GQ5DViOT7gPYirF+EguY6p5uJZWcEJ6DhkypHyov2ngHIUt0yIdQO+AM2a5gY+ckkp+Jw5JguzD/7v+iXQwUSeEEvbcFqXeGSkPtBTsrBqBrp4Yb5yroABgLcLRS1xsoJTsiqwhahMlzGz36xluXezmQLBYVyd4O923SgoKChVepYLEyZYLptTAMG/4uWjLKaiobYdZI6+/eYB/8yDxMY5WIoBtX4BGVFx5/HPajHqPPLxHGUrvjD3fYDTfcYF/5ylds8mRMXwsYsr0FZ1q49Fot7qHwWcd2ETO/i+o7j7P7/2PvPQAsLcuz//u0OWf6zM72xnZ6RxBERBFBIaIgNiDBKEb9JMYkJhpji+WLxpKoiZ8xQY2hKFJUREUF6UUpu0vbZXvfnd5nTv3/rvs9z+zZYXZmYVh2yT8vjjtz6vs+71Ou57qv+7q5X3VMdtnlZ1gVY3yg5g9WM1xLPc9+Sy/6sRWaLsLo8F4rrrwEpmyjDSDMrBmgnmddhzUejvFhBiWpNILDD8OMoV1A0G9FNANJwksUYlcr+eLDJKR1XWnpVRMwABON1f9fADL6l+qJ6j5+/OMfdzuGkZqXEwCySp8xvacTI0AlAzz00EPOIKk/SMgvfdERhPWOJPtW1hAzZqhgt0I91GMFnAWGbHTIMdS8dLyindkoEOc6MG520CuNlH+KBozXoty+Ywf6y6ecyVOxeyUXCCzK9uWPLnij16qUUF+H+nFU5zIyktW5h5ClWN/gQab36ujF8Fb2GLK/+Ou//muytReZMloVsvziF7/oSQ/jHWKPfb6RBIM+q6pgjlXVjRnDvc71Rn9H/0ZjG0Va+TFZUyu1SIH7qGqFtpRDKonGTws1dfVeBxT8nXKLiOiMssgkqqjDON5xsAOyBx5ls0cRbpXlNLSqBer51NfhCYfYvJuSdtUxKpaMc5TY/Mrk1fWnzP0qt6TpVq3s6wrzixjFfJFwtGyQWG9SVWywKRk3TGWEFIlDYsgEyJYsztj5FxzC+yJj2BciZJnEUzFZpG8OJKxNCWdow9KxBnsCHH7z9h22lKSuZXNmEHkYsKkY/zaqNiri/iokHVkquKSwYZoMQ0ZtAoLANEjPAPU1T7L0LsBOfp1VoXwoJOdaoh2d2oE86gjVihVkeXAP8DZ+aIKuljS65LstPv1lB/LsnvN3H3BAFs54b8BskJ2K4vsSyLNR8RClBN0xlTJixyKj0rDLH60lG8uoMmQ66T0acKK7dXgG4ci0F4GJypIrz7llR94Q7TGU+acMoBKpj7LSUKbg6lVtFNtWqjZBNsSJRXZnytCsQoguPURBiQVSvWsyHQOU6fF8FaLLampnsivK5bbbBz54Gt8z6KaRIfsznIpKvFxxxRVeOuZdpPJr8Vu1erXbEigz8owzzvDQjBdIpm32FZCNZiV3a5MAzcyUyXKCRD/3shYX/aEVx1tmsNEG6p6w6iH0HsxdVYtutFz9+ZbK/oqQJsAMU8OhqhloKfLWXt9uLYeRz0wI1jqetOLO78OgsYvFILaI/0yi5XR2QqcydVAzUyJxgVhNSGrF/9WQ2XgaMvUJOcr/2Xvf66EZMacKYY4I8CcAZNJ5hfEXsg3V3+Tq397Rjvj/YWfMxEapJJOATC1gSKFt/QikKTw4bfo0gGCL1fG8yhCpD3p4U4Kq0eFMHxBlNo3npcEUcOrs6oTt3eUeadLMKdtRj6tPKzypKhEzAF5y1j+RkmCLSD5xnVDZ7kP9Xce+huwji5yEff9736Ns129dS/ZaxtbnSWwR+Pv617/uDNx4R0xZwyzeAlBa/xU+g+QYAWFRYUYMPql5a4VOLpv6hNQoNLEhMtgZjuIEAgoxxkcSgXssAzuiTHH6fxZ9VJUKD2JPoz0aRRIi0JeOrGwIbo17fgc7IHvkiTgVJ7CZkGaOEGIRoCQNWSHfYTta13Hd4xdbTKZamZsFcQE6qWYSWKYhX2mmr9Q6w16ksbwKjIytlcClTbsXlOd7AWhJmH8Bsiyx4wiQLeD5MiAjujGW7LGywRUeHU9DFstW4dvYbNvoE7e1rbPVO7aiq8XPj83o5nStNW3vsLnT6q2BvnHUjHo7cmaTNci3TjQpspUSTNlkAFk3WfGNfD87AwDZPKvp21KOnM+2jVtfSbe66fkvjS/EO2Pn2HCsjUo3eTv8ELTFQ7vc7qO3usXq58oYdtkL8S0v2mccNICsEnTs8Tu2Ddr5aQeoWpO+c4RtUiaj0vaKUPAjC4ILfKOdjXsgsSg8+OCDtnDhQs+QUietBA/R75HT9ciP0ywRINM+STXhJndov0qZJf4RWaOadNKPKWX6Dw9ttEeXt3CeHZQ42UT91k2cTw/nKYEzYlWfTKOdeuVR+WcGOruuehrUdAJmbNguu/woLzqsRaUopKPaay5eo94jGWCf+tSn7ErKtpx22mm+6HYiUP4EO/oTsDK4/PLLve0qAe5EOjotdjpCppr+1vmqrT0LNMmCJ2EDbTpIfbXqgZ02uPIQgNhiG2hY40xZEdPLqiUAsuqzLZG92QpPvBkxMot6aqGlsztsW9OgzT6CRUjs4tbfYBT1Ybb325kkKYVeM98y899niRlvI8sVvYeEZaqcQAg3y0I+udLiZv/TGbKibB9op0svvdSZ069+7au7XebV0SYQ9e8RGxs1UCTm1xhUtmU/ouCnn3radWZi0BQmVwkirxJQZlZTqqlJv1XflW+YnmsEtFX2ybBRUj8TINqMuF6MV2C8/bOccRMbjQITQb70cAJhS5Ys8XJNGYlGR7JkVDYtyp6rNKkVgNN3iEkbK2kozC865wcfeNAF/KegmRMoU9KMANlXv/pVrCnGLy1TYKEvSExeBr7uaagQkRaWQbb7PY/xL5uRwa0wXHjBFQjd59sABO28DmYInWWJzakYHMltSzHMX5PTuZYpbPzQqc64gpAcLF1ClT/k+QerzOXnmRfkI4gE/iUNyB5+HE0pC7CKe8fJvi5iy6M8ij7YsQ2bnqLEzx3jXp8AsKLGRUJv9bWzSOxayDw6DwuhRro2pYnQqkb9L/oYRWV8I1+ehOXpJkA2TFbVksVpO++ChbR72an/BQBkmR5MsWHidgCgf9G91dazwZhdaLI4+rBhKjhU4zPmWaVIXg6bUm1HtuA1lwU9afMPkLcYpZwmIepXKlaaLH/rLFj3RjR0QvVMC63bZ9qvf3mk3TtwYI1hk+0tlqt6xma2tNtbXt9mixrxtGQN7SYiP2PBcuz6j5nc8v0iv/ugAmQBiFUCkHgXed4KnqfZ4dU2uku8TzxMQBJyynm/EpCF3zWZKovy7xHYKvNPGg9Pw684xIDFtR2VYpOQQQA62pBLvxUR/ZMFZOXyS54EqCLBsGBMlLmhuP3udhaoVZQ/Gmizjp71gKPVZAe1uc7MCwgPA2bIIhx9VIKkJYuvQD9BLUsWr6VLa+0N588DkPV7ofHcsBa4CFwq1f8Xv/iFhyeVlq+FSVlvKpas0IpsDtRWodjxvoZ+w7ntjV3wAY2Xj45hMi3TgM6BlcsIVc63AYrBYiVmseopVrX4BstmzrTE4HVWfOodeN2wwUtS1mdws21vztmMYwj98n7b+jMA2SVR2IVryw9SymPhX1tx7uUsMs3U3dQ3aUKqYv+fnqC0+8Sj7X86INN4kW3BFe95jx1K6O4zhLK9KDODITL2Hb+NVAlAOi3XfvHaiF2NVq/RfUhZiF6aqezZJSG9mKxuHNV37tjp5YmUsStWRmNTHmoKOe/BDiv8XbEjaYZZ03cKwEkDKT2YNl+z0W7p76lYclQeQacmeYKfZ3nz5XNB2apBnx9Ys8rv2mNeCis0l/0EJsvf/OY33SD3/e9/v30PxkwWGu+mWsUpp7583AbURs0F5R6SZQHFqNp6V1ipdzlE73pLdN5H1Q7MY0nwcVlrJZGvuQEtkdvw60cMsno8zvGGYbKosF4K7xjzQ6JuodVMJft1JiGc+HyGTwu+h3lraa7wjBvjTA92hkxZlgpZqtKJqhAIkFXByudz7bZ9FxnsMGXjHXFAU04lRABfKTaG9fVTYcmaSDZhrWBNGC5G9kHhkL+jLDDUF8Teai6uwutraKjfAdn5b1rErSgbw74QgMzmmO0CmMP67cDKo4D/2Bw24AXGSX8awT51JXPoyrrIWp9GuLJeIWmY6RJryCBrWzVRhMkAMjc5V/PgM9m+ocWmMAXL0mlzV43dec80W92+eOJJdD++IpNrJrN0k7U0DNkfnTVg8+u5X8kdlNubYumF96MnGz9kvR9P7Xl99EEPyLI/ucaKNQ2WnkNHXwj9WMMAYnwQyPS1oqoCkHkIocwGCSBoclcZEzltK0ynCVcTjHbegdHxsKaYt/KYU4TASTKyAqP5erIcSwTIohJPAZDVuE3Fbb9cbhu3axJlh0vNtFyOgUwoMxFHH5VXUW4WN9Hf5ZMb2dSXWcAI5Azx6bVeh/KYY6bYK8/EsR935yrMucSQOcFXFjtf9Z9X2X333Wf/j8LiMsR0Y1Cu+9vf/rYziV/+8pfdssLtNIKP1ATdqlL4HBbgkNGqhTLH+QVAlkXbVTWwzvpXHuHZlYNN2y3WS8i2eoalllxv2fQrLT54tZWevtQZslxyKSFMTGenlKzxqG2RB9nWn9jAhj/hmskowu6jfniR2SHvt+LMt8OIUXjXizUgsECv18Pi1FCho3s+I+R/OiBT3xAwEksqEPNXf/VXUVFsDumzJsqyHIshC95iAZgpu1KHkkrC5yoQFDIjK8X8WujUp8RQaQxnXVOpcFHEEmsRjHSKsBdiwqBDxKjpx3VfZVF/8BrzN0ncr3EeQFi5I3jpKBi84J3m1hsVDHFl1YHRYfmRvzmthxg7/0KyzMsBX1de+ed2y89+Zj/96U/xpTrc/uIvYXPHObS4RaAXQfLQBhtuv4efWxgHP2Vu42H2MuVImeukFJLLYmpdIrs6SfmeYhVhuUhw5hOXFyvRobgkc1casKLndAd6JVWrfZ3VTbsMOSZVLxgdEw2Pgx2Q/WGlNuiAVZBSfpgscS63HuYoTfmc3v7tlk6O70OWgLXXhsLZfO3NaSvdW7cZF+hShqU/KIF8OVrhZsLqv2LXcP0qA7LFi6oAZIuZi184QLaR5K45PZgp40OZV+1iyuXVx7FR4tqKFIdPwlxZfca6B5AeEGqNGwC7HS0yBUyzmH3XUTZwMoCMSCAZjPTNgVbrWHuI1XbCADLmSlOStrGz1uYWWp/PtPqCvWeQEmB9w9ttGEpsyQyJ+vlogiTdLKuNcx81m0NCxEvoeBEAmfhgT41jYmCLF8T5isnLL0fGh3T0bkKPwlKSysZayYxZ+YDZqseYpnYhl0CAe/ZbzA4/yvoT9UZdbVeuwlBTmmVgZEJVu1cCBGU6ffZTn3bTyrdjhBp2+xpsQ0z2mSrkihK7l/Vko+9bCI8IvIUwqF4robBqNkp3NV++TuMcXlJboJExLeFnqkqeTCpvkbTvXvW49dGpY3GynyiTlGZ3UyK8V8DfRqHSFKK5LCFOgcTIrkNsAXoHKEJpV1LU5GyDRWtkDOaHttlZrz6UFPxmdnyIOckcypJpGXctWnSNKmys3/Vv5YKi3fynybL8+Cf+HsuMl48wHFrI5L+jxS4AtBAqEiPhAIxrE2FRYmLTOVfae8g6N6awKZlfIzXROv7bBrddbtWa/QY58TTZloWpVnPS71ltYVuW/43Fu1eQxbMWWp7VqA8dzPTLrOowSifh55bf/gXLbfymVecxsk11cf9w4z72aeupa8EUkHqe9KMsXjwC0ikVSHf3/ud/TBaQSQMY45wE+ncf6oFRNq9KPkX/Rv+FI3LdkhkxYRiJjMn+FZsb2SPoR352qirRFC0g8rAqb0Z8ceE+e0kghNv//LWv2WMrlrs/1iGIzgXGQ0gvxocPUk/s/X/2Pvel02sUNtxXDdnzb9nn/k5dV9CFjlwrbTGWP5m/loU2UWayRttquPcZrwm2D5Uhz+d0ZtwKZW1+CZZZLPwVaPG2U1rq5ptvdpPZs151Jv5sJ3slg0YMZDU/CVxGRrEwDZgtN8PETBki1NJ1qxW7voEmaoUv9o5fYblL3HvdcRwQOY0AACAASURBVMekZUmFkFSMcFoCUboO9THfQY7UsowSegq11GJUebncNIaXTAD5vOozLDb73WbTzzn4AZnmFr9sMtPp+0XmP3Ii/cF77l5lDz2ymzGNwHS0s1YbRxq/KMJQ6d8YQtAekZejfvmIHo8AWThSStnU9/tz0ePhdfo9DSAY7E3hH9nA/e2xd1yymH61E/DGHF5CezVRyF+BYzYfaUfb3KgNT1rnnbda/fZVlmwCeFGLcrxj95mO/aqBLKzZBeeZYY3RCbqvLqaoEUz7CaSjQ5Tn5XhHlNHOPNr7tA2tYyPffRXXqgZlI9x/mA3WMm8fwKMq10oLkrhANZtkcjubOLAjzRhrvNhqp11gyZms+y+h40UAZNqblQ2iNFDoQVqKZFoo+wo1YD2ZI76zw3G/sGal9a951OId62FWun2nUhiqsZpXX2jYS7M+1xnl2UYAmWd/M1CCg7baPkyuEvV++u8/Ya84/RV2yWV/7BO0gy9+cixKcuHeF813YN4C2FOR5jvvvNP+5m/+xt3LxzvE6igkqu+ReWAyJXfwarJzk/bfP6D4KahyZ9tqam0+6WaGSZgdCfsl8lfZoUR89kjoJ7LiQF2mKAWLr8IqjVMvsoZalvfhbfbG846zxUtgxrSDI3NTztLJMiCRP9SHP/xhB6fveMc7/JSDKLkXI6O/+9jf2emnn24XX3xx5GhezsrUwNV7JcauDJUG3U1C1hyaLN2GX7ox6rqVQ1Z5AIP0PAUAN7jR1Sr57dfYwLYPsY9DB9Mz13I1WzBEPMnqTroXs21YzxV/xcJ0DxmYqyzTM5WFabt1N19smSX/ZGmo+qGtn7Xi1i8ysaCPYeeWygHhj6XPEPasLuEpROfKkVmm2plViFqpITKp4ThZQDa2QWlw0eMeuWHwboZ29MnKhSXyr3vWM+UHoh4cWMkAVEY0UdyWr3/lq7byicftc5//HH5UFKguAz/1jxT35mAGZGKpgsZsdFsGRisslJXsVmit8JrKBbmyJTVvqM1GC/krP3PcDkT7Po7dhzItZab7AerCihF86smnPJHhgXvudaZPc0c4B2nrdF4OnKciJqfEzRFTB+zMEwfs+GVb2EOsZwFkMe9fSOQRCkhMverH6gfWt1BSWBKbGxW3Tj7jpzeCIRxQ7P4p9lPkvBq3fsrCUcTKsgyJ4dQsy8z9ONUv2OTGovJPezsOOENWBqCRlY00uNGcJtL1/vuftOUrAC1lY2Fdg/pImH/0t56L2mdPMBX+TqV268FCG+wtTD3W8whjSFZJ25Q6hcdz9vZ3Lub7VS5u37IsJYvJqYKCiqIrUW3bemu977dWXLPcMCGyeG3ELu/tCIToXp8n4an2jNOtdMgiYgqUfgKMxcWe0qdy2IXAM4/7+Zq3fUvb+5T1b/ueDfdgl8RbFMZkOaM+6rhv3/9PyhE4jyxBOjdM0pUAYwnA4tQ/ZsNxNjTZafv/HF7Ab9jvgKyA1ivmuxSYFP5fxsmuzCojIUnCa2F0bPNGGLEnrLBhheW6N8J+wBrVKN9YobcGy5x6rtnJp1l/nCwtdSji/vm0xP3RB40VOpNo+O8/+jE7WWJbCdbLMf+w0fQBXG7MkH0ZwFyY3EdKgJR32gJ5sohQuRiFQ5XOP95RCciKtIXqKwqQ7dgZtxuvf9LNW3e2PkNGEN5N6B6SosLdFJbMKgZ2Xz+ZQx5WDan4WnxloQEgA2wsXPYBdiy0YnG7XfqOVzIpcDaeEaQUbUI6CpcyGT2OzuXPEfOLAVFNQ7EnelzGmqo7+VFKn2jh+Md//EcvMq1dvnRE38U4U68977zzXBQdCkfvDmlqh65W1HSp+0yYUjtyQqjOCnp4WTwhJpXaDW662nK7Pmt1GbQdXXNsqBo2zE6wmmMe5p4Tnn3qT0GAP8TyAgDHzhNUZf0Nl1tqwZc4r2k2uBl2b/vXLE37ZBOImnMkaxx/PzY9jaS411F2E08mABnqI95KvyqHyZ7vmJksIHN2OBwBVClKVTYCDeyONIOujfRde/TjBdKZpMU8un+eCA7he14TMvJUqitYNAQdVABl/rU5GFH667r16+yr1FmsRuSero5Aqhgi9ZCDGZCFplNfDQkkkU1G1Jijq3MEFk3P6TWVQn3XpfE5rmMrj+fROrfRYcsJ+w33QlmkAmQnn3Kyhyz94Hu0kXkY9lmF1WUUq88OxrK6Fp1DTgVw0TktmtVlpxzXbYtnPWmZoTZAmsbQywFPAqRkK0u6EO+kX8MK853qB553pM1sGay5WYN+rwBwqW40NGnGAebKmHjgNg8zUrXUMrM+ZukZbHKRaY93HGhAFkk9vLeqUX3ui8E+aVO6Yvlau/e+KMQtoOYhRl6sWsYh6hHm80qQFfqG+keuEBjGPXc8o/tXJQu7h/MK9hpZyrc11jWzJsTsDX+0gPNTZEJJVfS/ifaDAjaKNGi+1DUO9Fp23dM2vHmNJfp7rKaTrMbxjmdt1PZ8ceG48yyxdLGVGomcAL+qlPHuCzBJb8xBFBwc9+MpDxqJduRA3HMvUpufcw8eYS6iIxUarWpwxYRDZH++IEbyyiAs4DDMb5IsfmI0kBFLrKr5bEtOOYWo/P+GLPdof+XyePqwJsjyM5HTlwYYP92dlDd8xvqfeNDiW5/GOBSES2FUnO28o3r8nqyS1LGvMnv12daHnUEdpSY8hZvFuijhezltvfKLJRC+/fbb7Xe/+a1nP73HTRvLcyX/eNCIgZyuLOdS/oDRepHKSfuWW26xaygxI/sI1bGb6NgDkMEKxtGmUW3NnllTsNt+sQYzQ8J2MdyGCFkmWXiLOUKWhClUKUCnFkv1RqEKtRmPaULSDjsKaaasdQAKmrqX6STC7D89lQlfL6TWJuAqBR0tYbQmJ5WHkUO/hMYS7gdGIO82Fyn7d3RkqsMnUbdYiZ9xnY/hIyWPHgExJUaotmHQ+KhN1O4C2Nu2dvgE2dLcYPWNADK+W8XE02mGh0AZOz0FGtKUaulf9Z9WaPsI9f94Y0/ChhCLpZKvtMRhd1lBxcIfvwCH6duxxACQ5bAMwEl+sOW9lln4RfRCKRte/y9m27+FncUQGjNMdEvHWObY2zCPrYU1I0xJqZg87aE2rnLxzb5woHu/i5MFZJW79aj/RTt2/22fTk9bFoWpJSKOFt8Q2fQFWSpj3fKK6wz9Vf/mBobcqLQL4fy30A6qK1Uh/vUFSlrEgzxkKUAQfMEqwVVoQ/W1qDZnxCCNPkI1gDGZSoWhHBxHE0NgrSoB20TjW+NLbJj0lyeddJIzZGMdeffpixIJxGCr+RVOjQ2yidGEWKUizcst23ozIf1rrS63xRK1C3Bt2MJ7AG1BJ6YPr9xRZvEgHAH6+oWB6FEAfajqMG5zuUC/PoKHquteaakmgBief8XqeRMOjwMNyPLKqHcWS2Az0GXRfd6xowe/OywP2Mno3umI+kpkaaRGk/dj5XyuexAv26moT6Ba8WO3TjdKsCg/yNy0O+QZnnCms7yYpKhlmR2OWTXeZJKjLEbYH9OYVJKYpCb7wCC5JEH9UOev+UE0JsBM/wpIjXdU5BuM+bK4/FQoRi4/yJjC2R6e0kYZ1pY1tnqCpDUcHZ30gEyL+l2eBJOsEiX4LKJVliLp4EAeCbLtWWsKrIeKLPn8Wqpl3akjqQuyZwLAeiBPfazv3v8MWcX8ob6puoYeK+9qtyyZG/Hbf8JiDuhQNkyJuo8qbA0lH4MZK6kOW7qTRaPJCsteZlVveCOaPWLiLLxCKbkULFA+2gWLsfFiwGW7Bemi/v3f/x0NzZCdhIbjg1de6fF8N35UJhgdVBmGKRkCamdVsaBV7pIrhb0qMCzbCImAZaCpLK6JDqSmDDS+z0GVtnt08WK1/eGRbrvvblgohdwckOEJpAwZNGQCZQJk0pAN4QQd6WaiiaZE+0SLM5MJs0k3fydi3TZ9atYue+cJ0TyCFmsYb6YqCg+H/qjPEGOockkhI1LXLQ2NQpT33XuvfYf2asYUVCygmD/p4y66+C3+bwC9ArGapPW8fv/DwwVb9fQm6yfsOWdODUabh3NukT4qOyQqXuBavjzaUZG4sOa/bWjnRxGg8jwZlrJLStShZ1n8H9wfdvKPXwZD9iMb5vm0sbsZfMyKsz5o8UUULabdbPNnmYm/UV7E8BAq4fp/9I+tF8o1w+4vRSgzC7tqDMiq8vw90T0a7/lJAzLvVwpBS9ggOxMduo9RhvDAQGT9IOZxT0BR1sBwz7V50HStiq672cgA6KLi1QFgi3nRjxfT5rt3btnmDFlDU6N9AZ1T1EEiA1RpxQ52QDb63gQGzENTattKEY2Hpco3fQ8aQw+rNibMs+pjBuaM9w8B+NV2YwG2ffHhEyB78okn7Stf+Yozz+97//si0KWQJN85DANdU0v91THAYpT9HIELLSUF2NDC4HKmwXsgzW634a778GVjXgysqKQKvC5ZUjFzKgAo+cceii45rEVa3PVTbrgC42vIF+DTYcJPtYZmSsk0k2mZpuQMX43UdNzjQAMyacEEogoYgUfMb/mEdU2aZtQFyn1AtzzUMtdjaoeyrHKv1yh/yDAmdidN7SYPwhtHd6eRDxRfoPsirMP5pEgPh6Mvn6fOdeINYbh9vvXiD/ltRlJR0ebjM1jPljLsealR5ZJoWPi0rAlIkRFVbkAfNyFDVsJxFXuhFCFB3/sFtUV5YRlgDT6QRxVZteRyRKQf1wQd4JKMAYzXB/EunTVRAx3Ikx/ju/c7IAsTg3NkAmLQsLZjiw1uWGW9mNxNX/MgkwPPUr0+Txp3gR4Uy6Utla+3RB5Bemojk9BU65tzmNW95R3WA13d4GU/mOzIQEmXsyAFyKJFTSVS8h4uUI2675NZKPPJ/wMgU6FfTTAK24R53JdLMQnlRSy0kT5DRySmZ9/Ca+666y77GgJpeQ1ddNFFI/XtxrunI4CsTBGidiNUVG23/26LrXyUEkmMt53tz5DZjIYs1wHTBTMmXRYpzgpZViWXOQjTgqEsNQFJWQwkEcqnMdCsb3ktf3fYYctq7fxzlYLMoo+eS5UBvE4gjwR3cX3GsxYZGkKllFYiTL7pppsc0Co7TMXGVTA5S9gvhFnUDiHsIxAhgPrd76EFE4D0ttpub7v0WJszG+gA0M0j6E/KUoTdfwl2LNYP7d31e2rgXgWIfIKdZQYB83prmvY6s2VX8V2brWrVX/Cae42IrgdTMvybnfGnVrXgU0wkWFmsBljv+LHrGvp5rgatQPzw71oPs02dJh3OJS9WET+mgwOQSSc2TLuR4EAj6R7GVRjdi+PtxhMhDBXCkaGykLK40hTKDfOKgJ1YoRg+Q3Kll6ZIfVoATPfHgVY5CUWf/5Prb7Cbua+nv+oMezeCcx1BV6O+dLADMo3LSsYqJDCEENS2zcwljP1eLG5kczPA72KQQ9JCAz5myhyWE/8UfNaSTiFXHBU76LGSI8bWAFa8nz4n2wtZx4iJf/8HPhCFl8Xo8J/HB8qbvdHX4vNObBP9oYVX1TvLnHamROZ866yACbK1P8j19NN/KBxNGaAC/2JywPXBHPLaGv71Bd1xKDIEJB2xRAPfyQhhMzSceA2uFxTDbjg60tYwl+Iey5hUyk32JeBDFrV1Zb8Omt5orEgvGh2VZIi3ietgKxG7xht/88ZIosJ4TE6u9I/jJo9CRAyrx36Qi7gxt5/Z+ElFApyS0oStRNCEOW7iRxGm8Y7xn1XORlQ8roh0QTLfuJc0EIApJ/ZMABhLaOSibYCuh2QfvjAPqSLHANVbdkbvQB79EBqRIio6QkNKtl6WfRzI03uu373/AZnCMt67YD86dlkeIJZd/zSL6npL9XdDn9OgxKdVgqWglHbCbFUpHqOjlpSBiRliLD7DOloW2pRL32XdtH5jORd8KDnMcrSnt1jIKNSErOyVv/0rypksXGgfuPKDzgi4E730U5xSH2aVfV14IGFSqUl9BllmCxYsGMlWq2xMgY9/+7d/80lf4vg5c+Y8y2dprMYneMhEISYimjEk1i+wbb3l56ttzeoiJpURINuxSxqyDnyXGEAS9TMpC5ANDkwZYfBSqgys8C+fJeAkoLTk8Msxmd1lp7xspp1+qgS60puIpldGZhSxC4WRdX4BkOlfXct9995nt/78575o6NrPff3rsc+IzPQk9pcFwt4y0MSkXfdjFgh27DXcs4Jttze9eSnn1WZNDUz8yubRThWdxcOPPWLdWwasBfaqMHgPkyWZtInpNr0Zp6QW3PoP+UvrzW6yuVv+21J4+myLP84SVW9T0kusf8rpMONXMCWQHbvpE5bpvImJ+BASBQh3F4+12uO+T/QzryR+LpCwb1Uve1QAWVQZd1LHC8WQ7eYslDUXRSTIYbEtu8jSou910Q97+7TwRiHkmppqB9xL5k2F2SCLSyFc1g7s5corTznkT9+qZHEr77V0S9/6F7L2GAfvfu977LgTTvR76fUdZZKsjzrIQ5bh5mkjoPJEOyiDtHbNWnvq6ae8XiRSmMiJnzD7sELl0ojRl90ag3Gu61XYUm2kv2XtcRTFwI877lhnfktsgMT26vWaO8LCP1Z4dMyORCNK1C9vPwdk+JA5ABuhW7T+R/o3fb4+N1ju+OPUcfXalCzcRTagSuLQJs0ruCnTaVjZd8yd+W38bOL5Ddw/1WfEtR9NWLGPXu/hSeYGwEWCMFKyChmEgIaXSYI1r8fcltiZtKse1eEoEvIfIiJRE8PFf5zjQDNk4dSi+6LIQXS/pRNTYpgquOgYsVopwzLpX6MJUMbHHpfe8yoFyvgZHdHaE77tKQUY+YBAyfkD0pWEsShUUKak/IMjU/DxDt8c6TQDg6o1gjeECGGVtFvjHROE5IrYH0USvDzyEkAUrJizScQ63XJlAgIumiR0HRFULKi0lOg77FRELqSo9XlAj5hE/BEb7aVgsYpydAY2UDTKYlFJtJfKsd8BWQ5k7hkk+Ell16+yjuX3W2z9EzaD8jZWk7F2Fp0MtGw1dcjisDo++eDOn00A0BDAZwhJxhIzbVf9bJv+rvdaL1RkfYzJBjp3SFYL+LJUhmvCZLcNUbq8gFY88qhPvB/CD0jMmIf/mDAHCVWsW7fervnBD1xwq0VLxYVf97rXeW07mcgGcKfJQK9RrTplIb7rXe/aYxEcd8A9C5BxXYWM3XjTk7ZxPWFJHOUtJtsLFgwmTbFNeTRkWnWriCeU0IZFHkyhAgETUjbywVHIMpdusXx2l736zAV23NFkGbJj1BEjnVvieq+gpImHCcnZQa5Lv8skVp5kmzds9ALIl2ALolqW7tEG0NMiJnG/ZiyxjcEwNgC6sPDf+VC3rXh4mw31xwB0GZjDBZxvL2AalpNzkMZhC0V+//17/21/uHO7LYKtSOSfssFiF3qWpRbfgX9Uw3rbMfut1jW4yU7cdZfNoNTLtupnrBmNQ0MS09zmUzD5e781VscQuX7WZuTvsAbCLz34ti07YqY1n/BDG2qIUWCX64RdLVX10ep1CP61I5zcUJwsIJOoPxJZRyxGb3fetmzqsrXPtNrWLR02XIpqHe7OAtt9vnqsOqaFF3jZkLLFS6fbYUfMw/09moVVMD4Ah6CB0T1WSF1g+YYbbrCH73vAjsNy4YMfhnnkEGug7EqNmQHsLmrYAB3Mon6BMDG4kiDI/V4FvSsTcGSbo0xe1w6V9T5+jWWht54Lfdr1j+VKEnqtHj/59NO8vqZY4VCfMtyBSl/DvfUisSNKmPnql79ip2AZ815YSM/mdl2b6iNGYKEyZFlpouyFQnTg7u4Ln0xd+fGHWSy1mDr77D+KXaqzqDOVw7VJGAyxrbIUKkcL1M+cIeLH/fx4akBeh7Cr9VhlKENOzzO1TuiyeKABmWeMAzizWdnvRINZCUvlmB4aSWQtukbWmcD8Vuo2E2SbR1m0kW/YHocaYZyIYmCr93bvo5MRQ6ZbIHBI/ZgyoNYNG8A3rIYyZOMd0cwOW0f8NaZ1UjedviPtbYgOjvsBEzyJACb6INBKFgBbRGsl5laRxniei58o6UB4UJtAR/LlPlcmF3SNyhE4kEeWkmJKTYJHp/0xAqdBueXefurfe9I1B/JM9+279zsg64QZy8iwU4tld4f1rfiDDT/9B6vr3IDIW6lEQrCUMAWEFaUJA8F7Wq4EDmQg5ZIwBNTX6aNp6975ceuon2LNwgmiF+IUeWUgBN1YCA1owtME/h//8R/4lA166ZTPUK9xJD2Nb1y5coV98xvf9Mn9gxTc1nsketckLDsLeTK5YaW6IQDuIx/5iNfF++hHP+pO4BOGMkL7C2OKJWScDedkSlsj02O79r8es+o0LA/Oeyq/kaf8hxICozRsZUmWd9ZeBgltHFSzh7WUUOnmsWkeo/AJ4a/2nlX29j85yabN0e5H/2EzIa0MbFtGOyzQQBUfnmXQiyXYTr2/73zvKmcc3vX2y2wh1h31DYRMytk+skWU85FP6J6Hx2fA7EWTPrXyfHmA6UJfkMvPcz+5qG0Jk3qhXoHHyFcrhR5QTtgKKfX2dVMAHd8xGLAh7p9AXVeXxMzo9EnC0D0IdQfVvjIsDWHWUMom2G2E8FIvac5nv/xV9r53w6BhzaGUiRy7Yqr6sXGSXhGLAXRlMTRmYe7I5zFOpCRVjrZrmmCLOFlAliOMm4yJLUyTWGH26MOttmlTu7dtNcxXlo1K8E8KoCryOYo8karo/0PDvUw2mF+yoDTWpezwI2fa4Uc3oS8iDMriLfCs9wpI600qT3Tbbbf5GDj+xBPssssuc0ZXbajFSeNlJCv5RWDIxmJYg5+YwotiBHXo9wCqdtAf7r//frvt1l9Elh5lq4jxLC72bcrb81UZNiitgFcVBX/zhRfayehNE2wSSwp7CuCVGQj3iionDznrJh9DefExzpRlKYZM5cjeB0Omc/WEIZ7f3xKWHGlwEmw7JhMR5Eu6/AE1WUhVXi4mHlAdoe4Q14nAwPgMzoEGZBPd01tva7V1bG7ybMQ8AxkpdyZDbdBBgWAYNaqipJIDzG+Ddtmlx3j7qGLKMOtCujoqED/uMZoyG/1iqoeUZOBdFqtp1nQyrlz5ZawyrJUf0V9iZWN9jHu1GOQ2Mp/lTT4iXIrGBtszTwSGxJaG21f+hbXQfQUdaOl+6oSlPVOtZzTIbPgzit1FuUGgFYC5svjRKqe1KE0AqDzz23EYgFPyfv52wixEfIT4D+ARJ6HCMt0yXgIYk/XpuFHrlU6a8TfJDfmLfWn7HZCpHxQlQufHw9cAD2vfYsVVj1jbusdtKiar8Rro9FokeUwWOUBDnHThGkTZot2H0DBlkt2R/9g7P2k9OBYL1KcGWcgoABu2eGPZXsip/wuf+7yzQp/+zKd9p6VDE6YE//IJ+tCHPuQlYyR4v4OsTLls/xmGry+HLfOD+61szf/8z/9064d3vvOd/rCAw+i0+jFvXhjQfE4/bsrVNXUwI1n70TWPYAQ73zr6Hkb30sui28egYkEi0O+LMV/sWhQyTAVyXNDvi7T0WRlCnXUAVUAqC3YxttUu/ZPTrMnL5mkEiTYPO+jdA6aXupX1TWTtcC7btm5DUzOFRTrvhpV+aBIvGxlGXtVqLGXnlJ8S5goTe/liC2xHxOB5yEB6O4Gx8nN6fyIYVTpTEJ2fgynt1sTUAFBHHyHbTQyGSjrp3gaxevjdvZ3oK0OFQWuubrClyw73yUU2G7pi99Qk5FpdD2PGFk9ziJKX0rSvZ21pB8tsUxtUwGPevMnXsiwQGqKionVQ/eT++7cQ3lK4qYrEJwm9JTTf8/pHsr3K59NJSZSamrTVwXoN4njY1dlFaD1lRx7TbLPnokfzItNcC8zYehjfBx54wLWTjYThFi1ebO+89BIyv6Qt3B2u1u8CGF5Hkru1PxmyynGp+6q/Q/1K7w2UXgq1VqWN62K3cv/9D9hd+Pzp3iPAi/y63FA16jvBzyvSak20Yu7lxpYf1hyhdhJj2MX4EJt+7rnnOqsoYDbAxiE4+Su0HCoCjHwqX//M6tVedkw+ZH/OfOKHj2Hpg/YzhaCdmzY/Gl/lk3KDoUCTaRMVBqSep895NK+cKVBpjDpWSx3sgOyWX+6y9Wta6R/4swVARpmhYQCZdHRJdMmlUhfguBdAdhr3Tz5m9CnWlbiHD8a+PyPAf4LbpzqaJaQZYR7LyyWAOU7FySMR0/j9T/fBZ0xeKywR2QRFkUR9dcBf4d8QyvQuxk8z/ydWzhMaxgg/5gBfKQE5onf+ge7RqIQhNvleI3D881MFiSLEidhV1Q4usYmVzMG/j/+TvdCBPBSZzGProquSCTpOWCxhEWuWJxFBCRIvpWO/A7IQDJcxu+zIvH2UaUnB3OGuNqt64g7LEobIt+6yZD8TnoTGGZgv0dQKMcAQNcQ6rDOOOPftn7TuafPl8AIVDxdC5whZQmHiDzvvIO7/1Cc+6YvA/8Vfy9kWFqLNWzbbp+Xgf9ZZdvnlfzISTuiBMdLEqhCGwpI+8bNgf+ELX3DAJiZNFhCBwdgXlqygVCC+V7qWLIuLmK5WModv+elKpAcz7amNXwOodfODo3ZJi2u06Mjuw/2l8HoJnlValDRU01V4sdU2OShrWPBGgFXWLnnrcbKMpH3ZGZFWJW2NwJF2Dl7mA4Ysh44mpV07x0Bfr9UoHbqiwwaLDE3mEVumbbdmOf4NyTS++yv/6KnqiMqOxLIVKePRmuSJQjqi+qBhaOwm4/OwZwJXumaFjMKCO1HpJg9JOQtBlk0vCR219Vq7EfRH3+FSK9o+20miQAs6B/Q0SuBWskOa2oE+00UU4LjHZBkyv2i+Z9XqYbvz9sdt565BBOZznDHdsm07O/epI/0phEgqPfAapqAWasObCMY4gyt/CnPgEqWhdrYvt6fX3kl0f4eHlsXWhMxDMbgCB8efcLxnzeqIHOJVrD6aQEd0Z/uZUwfJJQAAIABJREFUIasM+422k/FEHM5d56YqFE8RkpSljEKAAkF6fmpjVIkgMKLelxiXo32mxr+Le39WGkmxcWqbJsLpmh/02WdiafP6N7zeps8hm7F8jN70BWPlLcwnH/nrj4wAshCiDIzz8z23fXofGxLNqpG9kIYmc43CpU7NsGC6GC06wlDXMuqD2DvcS5sh++nPt9v6tSq2LjlKmSEDkCnDO87GNQ+Dni+yMavpt3e8/Uz0grJuc7txzYy0w/giKmmAxzuizGeFmUMDA9CcjlK7Shs4ge+FPt6ZqwiM6WOiGVVJIZAY0hCGTXBEf/rfYQZ1O4rKG6w5WbIcGX16FxBbpCQiwqDyryxfexGZkBKvJgKMwSYjohYjsk6hwKiv0YaKQhzAI6WC8lhGDSmLXYXWVQe61Gq9gOQ8yTItEwLOA3jyY3z1iwPIymSNuukgFLtc3WsAJj4xlLZjBruZWM6Tllq/iZWCHQZFUwfQVHUQxpyaoCROfjsprPhMXfAR6517OCyHHIYVViPLzEMecohXF43Ci/p93bp19jPYrtVPr/JQw9f++WsOvIbQzUhbdsvPbvHMqBlkXw2jJ6sm1i/tidLXJfL/7Oc+649dc+019vvf/97OOeccd7n30JBn0Gg+i3br4x0y0ItJyEQPzqJ30YT5zOpdduvPVgCKam3OzLl+TkPDZCIqUAiCyYMsIkAmBNvnHb9Ax5fgW5epcGUmDfqHIWsHsC5d0mAXXwQg4+U+BsNWGdYqW7FDz2Ea5kV4eU1/3yC6MNliRO2W4/NVZsQncs3Vw5w3oaRCUsaJ9ZFg0sFYB+fGIlCsh2anfBHIL9pNRh5Lla3hhNsYAyJiOsR8MOGM1DeM7mPQpoUQkRblsAAHP6rQ3lH701ZcVwzA5f2L70xysmnaVDu43rtuprwJAv/588xmzOdJCgHn2EuJ+QvJQ+PcwMkCMmdIaOJNm/PYnEiILj0eZWwIJbS1o6OjcHoly7OnH1IM/Z78q1IY6VKnjh1qDmfPVLrHktVkF2bX4rNDijfsWJJQ/yHzD7Flhy7z8GR9fcR6SmemdgvAKISA9Z0egtvPgEznsLeQZbCtaCNkqExQMdFBYC+tmHSLOWwjwlgLtymAMbVttEl5/ofCvNKVafOh/jdEf1OfEzibNWuWXfj2tzrDGFiy8E3BGiePtkmhdUkaVOlCPmQaz14nVsf+3qGr4okvkBpokfl2NKJ3MyxhCFau63u+au/td7AzZDf+dAtaXKxBKKKtaUib2kwZkCWJsBTIcI6jR65vKNh55x9nuL+4rlaNpM2ZrIV2zydlQBQaUHPJBHiKXDS3vFCGrIZ62qPUAslah7RxmIBBEo4QyCnb0cU1wfd0sg4qu5G+X8sJe7WBECeM+pTCmgV+hokhCrhp5tW4UJ30vIgMie9Bby0iBOqngceqK5g3NmegqmjDPf7YUUg8NeJ9yNosHzKAnXJ085Q1TCWOfP6D74V4J3psI6veacISNlRiIuLd3JQ5EDoqNP9CfMmL9xn7H5DROdUxBDDksOxzVAAMAJR+AIIc3D2ms36N2dqVlt31DItsG7Qvi1VqtiVzO2h0QpjnfNAK845gANBRFaOXcB2Xf+36K5kx/S4hsOrJrVn9jE+mctKWqL+bsERw2v9b9GA6lKElvYcAzz2UOvkW2ZR/9Ed/BPNQZT9GGK3sKYl160mhr8xo2zefIlUqiHQcXjaKn+3bOnGZlhAe/U93i1tLFNHaaUBrd51TmIYXaiGOp0SBK8QHGCv7KImx0s5Lz8/JUHx2QZ2deNqCSM2ovPmwW8NqoYtFV+2tchca12IUNQaLDFTX64itBLjItV06CC+0rveXPWv6Ac9xJpaRianUzVMUz+WuxQkrJ3iuMow0AlDDBjykdZW58UoNkNp+tHBaOj0xZfvStn7zALwlNIfS+0h6GIUruVD0it7gP/427UxIoyljNUefarEjT7ViRuUMJM9gNsXQcbxjsoCsiEYkQfgin0tgtLsDXdcm62glpy49hetsJLM0Ks4bheGiM3FmsRyKkw2MNruqqZdDb5jHAmHJofV28stbyBhEsF6C5eTGeijQnYSjD/F+BFhQCHp0WaBg6eIJAS8CIAvtq+/VfQ0WHXp8wzNr7Af/9QNb/thj7uvnNTgZz2L8BHTEoFUeIVwZbC0mC8iUmTkFFlHf20H4Ut+tKhw6z00bN9q0ubNdgyfGMdiK7OFbxr3avGmTAzLVgf3QX3zIdWVBCzfRgjdu59uHJwPBEkBXCGk5g6JxrxCl+pfPP2xgfGMVLd77chzsgOz6mzZR5KWT2YUSSly0AzJ2ptlhWQPVUAlB9wJpRDpnJ5y4kMdkqq0al5EnZXkf7wy/M7Euv9hdt7K3O6CzsVuriF6piqzWIsCloSHJhmiqtbSEMRhFBsY9mH9z3A9Nt6SRoSXZZiUq1gzuXAP7hCyHDVkCHWyCiFBMWVqe4KUxUU7kkJeFEuE80YFzVSa+ygOwRuhaBk59mdUuO8aKjQAU3qUVOKE5U5fltjnjH12lDdYYm0+gBJDT/kMbbL8V/bbv2JlvHwebsW4fwEO1fgtk7qtpkoWFRD+o8UscM9n0FquZdTnz+0kH8Oye+1fvd0BW8N1CVADY6V3PDnKIDxKiiwjhgrWGnJiBDenZYqk1T1tizSr8INpsJ69pHqTsRy3i/nM+gF/V8RENK6odoBPqU4ZwQuWCr53u//38F3yx+tjHPuYL/YYNG+zTn/60e4m99uyzvcVUey4wNQJzX/6nf3KWTBPy2eee44Ds2GOP9ddW2kZU+j3trekLFPxK+AApX3b5hR73dwaJdHiVOeKPhEJp2t3A+HgRXQk8EaTriMKWQX8lzjoq6t0Pz11N+2knkCsLL1WqiL0SNG63tVDfMfpysZEavJ6SJIpMBmVmsw4pbwPZ8/C8jFx9Jx1CevrasqBBujJ98u6Ao9sZ+eLvOgivZxl+9LhYsAA0IruO6P5EPmW+JpRnBAFdLdSVjGNo62AGWslIBtalSsV/Bc75bGX8aAJMkME79OSjlgF824O/pXPtID+kw3qmL7HYyRdY0/GvpPmg7AWWYuOb+04WkMXjAtRqwAxsVxyGDK3Xmg7CZNg0DHLxiSikqHuzW8wfCfp1VNfnSL4AtNHnZ85sgg2dw6Tf4JUOclDzsWE0htKClRdYZx/5GQsQVFrC6LN9zNAO+1NDVgmsKzczytxV0sG1V33fmTDJBXQ+CiFKZC9gvoRknE5lwJSPAMai8RB1nNAP9jb+Jnpcov42gJ/avgXTZIEuacn0twDi+q2b7dBDD7UzzjjDzjzzTAdvOkauhfukkKeMoqdPn25/SwkysWs6PFNZvjb78chqDuHHI1nOYOuXii90u3g9zv850yG9WaTz9IcmOLeDHZD96MaNnrWcwHdQ80uBsV+Fb1+OPXsyCcShlJo2u1k2MlOm1KEJ7PEIRD1WINqwhLquYd2oBGP6XUBvvCPHHFsNIBvoG7bp09J2+isXI2spEw8V89teP4NzG+K+KBkJq21DAGqlB39n2U0rIST6rKcuctp0mY53fK0HhOm4XgQw1DqF3S2S1MN1lfIYjCtcTUm9YgIpAGtET/0im3ra2RZferxvo4lYInwXnYechTljIgKpE8DViHddvI+kq80fscHWqyldqLaFgU+JxTvAeYzIdizG/DjMOMsrQXAH103yUvOfWWwBdZHrl+7H0ffCf/R+B2TRuhLRqv4/WCjHFqFkkWRTiPn7KbMjT3sCCFhgMJrYddq2Hda9lZDjzhXYJFDm4VWXUSaH+lQqisCkIlvDQAiPxahIFPyVf/qy2zb8A1mWNQCyX2L3cOONN9onP/lJmzlrpmd4aZcdQjsCZjvxOtLuXINV2VehsLYWDC1qAg6uD9mnXaaoVEKqdGLZvpTImlFoMuEDK1Ig7BY0hd2YHhWlVX4qzJoVs2dIbOxjRJFTCbgYQi9BOIyQXA59mPtVMUBtMzsbylP1Ilwb6EK3xwKXIFWznlJDRdjF+vPfawkMM6FXLCt3cdpDpdCVjOX6/hygLfs0nRzPJ0qtWAJALJ2C5nZh4jJwcATBzjPiqPgc7qHctZNVzQ7Fd0/9EZAcuZTyL5r4gw5KWYJH4xWlY6I2zg6w6FVHi57aNztICd3Bdtv465stgfHwTMK6yZzKfbRhjbHAho88xzLHvYqivZRZYooiJjDuqJo0IPNSOZr4JbpV/VCsL/jaDeva8NTqtK0bIqF7Tpo4WMvA9HotUMBBbQPWH2DqBQsbbc7c6dZEiQOXgXGt+TylprCAeZbPEl8iNsSBme4rTICOYJER2tQBLuezPwFZ0H6FOo46D53Pr3/9a2ewhzu1QDLmAUYhcUM2LGKhVAOyXjEmXa4ziHvCh8kK+vW5YsiqYeMEapVUoHMJyQKqj9gya4atRrTfwhi5/PLL7bWvfa2fj+YUnxf4Twz7N77xDU+meB8JQSfh2K/Ds0a1YdifR7TL8eQH/G+cMWYyUNw+2vHlmGck3pX8wVFbRJGHbdNEBM5BD8hu2GBbmOOSVC1QU+QAXgJkWXS0ymjPYDqujeQQscUkVHP/IHa42AZNaZqCbldSlWgGDhug0beqXJFpr3ewEOMz0o3W1dYNgI8ha1kKgId5omErN5x7/QCWukFSA4d5Q6Mskp58wkr3/MaKbSstUc897RXjxQ3zH20+k9ZHhmQ71kj9hATqq9nEUOG7HoeC+DChTlX8Vgk+9HNSyWUxWK876wKzo15uA0RUlIWoQIrm6mHGE/U8xu2dLliRaoAEqey2P7FC781khWLVk2B+Lj5JWHAfMlX3Y//P0Qal0g7WtCoKnXMu8fU2JJ6n7vVWO+/jZjNfsR+//YX/6P0PyFQxRvNo0E5CvWjJlq2C/kvgI5IS24X/FukS0UrNP9haObc2ddtqFNF32/CunZY76lxLH3kCLxnwmDlSTWsoA6PRLtgSBksgvHP7Dl8AvvMf33Fdx1eoOSd9mWrPKYRZmWofDczIM6gPMFdHiFLnM1qfVum8HfRke7s1+ZwmAnYsDARplsQCFqF7o/At7urSfai2nTsnR2L+SI9QLucyYjoYwI4+Q4ttBGwS6zZygqzwPZSiaiVJggEytHYd3m6wiAzQjioACwNUQnYX9A+wq9ECrYWC3zvf8FFrXrQQXEIGjTQI5QHqrs4CTrt+bD1dP8fz6w+UXTmPH9zek4ui+5kA6BiCeQdlQmkCHoRWub4cQE6ArEBcX22UxjNN9Tdd51LGneoXlVlsuk+qp6nFTYXbVWEhtH3lghwMPHXP77/3QTvxuJNs6kxcyLQGsTuyXJcV7v0VxXkBoLFGK2541KoHOdfDX212EnX8Wqa7Zy3Ild3k+DvgyQKyWNCqqR/5Pljp6LrrWCv43Y+0LGJcpJsL7FYoA1YtwYsvpPQZ7o7q5sWpVJEM2pcyRnGrlHJZoJC1qLsiBnl0FQp910jY7UUIWeq+Cgxq3MiM+De/+Y3dcccdzkK31ER2KwJDwUtsAHZMJY4aAWND2UhDNhYY2xcN50RTpidCqBJHOQNUn6lz1ffpvIbY+YRQpZIlJGV45SupvaqEG14rWx3NIzJXvvrqqz0T+8KLLnzxNGR9LMKykKFWaQ7tUWGIJVQbWlbdGMAsOe0kjPmr2EzBZGj8C83TZ3w2Ydwe7KWTJrp/Ysi2lgGZROsCZCkuSiFLufAPD7WCRVWMHusldLfCrTmiCvUki3R09FIHOOwon93Hoo4X9b+9HQX6SV1Nk7Xv6mQKzZMotsgOO7QWgI8Exde8CTRkTN3CzH28VjNRcuVy67vnFxbretrqpjEf98HgK6eKe9kD2N7MBvTpzgF7qn3Atvbl7Y2sAwvmTbcFc6bg2Qigk4sBRwlAVgKAJTXRnfVG5r6TbFDrgGZg96stsB0tsB0d//yGYMgyedL3e56x3K7X4/u21jLM3wNDx9uu3kfBaZH844AdpRlsSFsBpFU2p6XZUvUrvL7ycOYsq5sDIJvCnP8SOvY7INvfbRGMMMP3BE2SHL1/gOlrV4f0WiswYr3R9SEfJaSgcMg3vvlNDykkJ1D9TcTQVPoTjb7WfWfR9t5KCtg0rVzrFQsGkuiiNm6x6h5MdgdaLY2NRh8TcbY5YVPkO9O1y4Za6hjEJcDkodaOPuGmjVlr7+i2v12MI3e63zYPtdi0mrh1lB60pupZVrP0jTb4mjf5bqpuuAeXe6AhIEamqlkmlaq2z9rgps9ZpqfOekkCqD7sWpJZzlL2NJqCz/IiFoQi4C8LEzcM6JHeL7uF82VhYK7bNfU1Nn3+P2AC/ArPIgJqo3uSpo50dNhC7dEqF+xbb73VGUxlu8qCIACxyszWIKj+1a9+5fdYiRiqshASOva4Z3fdZpsffdgSs+fY7LNgNyjWi9MHlgbVHnqdKGl7soBMW2UBxaDtURNId+paHv4d8lpwXuredYIRW+oSXV9EqnNo3GSPgfYlkq9L54cAXe+Por/jHpV9sFJnqTf5c5yfdJXKIFbx+L+jELmY5BEWrZKVjZB3FJ6uOKINQlQCJjwXpdajB2MRKCA7kIWEdiW3A8aUUCPj2qi8zMF9OFiE6VL4sl8GyWzSPvHJT9gMEie8DWUCC6jbin3Pd7/7XWfTXA5RZtJGCGQPDygJZc8bFhzm99YKcSXQFNj5iwCThKiaMUkoy7axEXuKeWHNvd6uJTY/IVytHfBI4kMX7yfsNUDlByW11Cw90Wz2oTw2De1NYOj3fg8Odobs+ms32a52JT5hpkvmYBfzYQ062qZGPLjIXk/GJgcYCkMk4OBjVsRKQTchjkYroUQQ2OnBLJvbOE6GGJJqaxWP9yBvmWUvO3l6lGzkprbjzzCka8HuAO7pFjnCl1Wbn7KhG69mQ91lQzWMeCQ9ucKQxZqn2eMdMbvxvtX4WZbs1EUNNifebj8ukRm8+Rn7y5NfaUul5x3ebr2Lplqsrc/q2wFlsHax899u2WWnuSFwTNVvmNwR6ViVNB7jJ5laPz6JtWKeSHxrXV1tTcL6vGft5pPsphsW2VPZyZWemuzoz6ZOsPaBu2128xP2vrftsGOmdjjg7AfkNi2mIlAtff0ldLzkAZna2j2pmDQl3g92CAp/6fjB9//LHkMwLI2HFnWJ+5XJ9UmKhDsbNkHYsVJbsMciVOa4o4K30RHsG0I24EQZmPvST/p3PGyZX9zOLni7dVSzG+8asjosQIqqUgAI+4fHKNA+JWZvWbTUlpApGae8fZzBaHmq3ZOB+a1num0DM/mnT15k7dSK/MWTvXb2wiW2aEavVXXRRi87zYZfe7GCqFaLAWkRij8Py1ZFp84xcBObb6Hk0YVWryhxyxstsfSr1p/Ebb83Zv3bvmw1/b8nvZj3adLC9T2eWYDh4vEkSRyFj+xcEMchBq3mrGcPdfhqUpSpERvKXJCPdzFhNjmDE+oVKoz1c9iGT3ziEw4QdKgdKwFZYM1uueUWu/766x2QqWj6mICMCa5ryyZLtUy12sXL3P6iREFagR1HOPvZ9qJA+r2sTmJ4n0mBJ8AVLckKNfHPcHnCHktvopenZZUdpdCHgLar+FSKTGJe6hbu6zEakAURs0JuYiTVhp9iXMhGIxTHdiG4/OXo55VAN/jBBbPU0RsX9w4DkOVgkGrkNcCx/OGH7cYbbrSnn3rKw9OeSCM940F86BqkM9PmTZrU2QCxpcuW2uXY4lQDXEOtSm3Mfve733l/FPv41re+1Zm0ylBtuEy1TbAhCfPU3pqgHzmC1EMqgOMWB92UUFr5gPXCpBR2dljTTIFjAeLIr61SQ6v7VoBJjbMjyPWykvK/RMNcK8xfasVjjrPU4iV88vgat4MdkN18wzbbuLkNkIIdUIZscGj9DEMqO9xhGzesAehMTnQeT3TQrjD+AjLFJpz3FwH2FnqYUiGEIdjI/QnI0hjeqjLBAFGkFVt7bAVZ2osOPdyOnddE9OJJu21wrj35zIN2/tGH2SvYZNoQLOmsOutCttLimaeTA2TqfWnpYzparWvrHGvKaU4mAQZpyIOPVdm2vkhacqCOJlxqswmSf9j4veK4rE0nhFyg1mtXdb21zP8dm48lB+rUntf3/o8AZJqEguFkCGNoclpJjbnrf/gjZ8QEyBQGkqBfzMuVf36l68fygJhKq4HRrTgRQxYE58ED6nndhXHeNLjmJsvcRGZLP+wXJQoyQyqm2sQAJRSKw//3Ns201t7N9v7TToYlY/FOtAPeFLbAGK+2ZNdtompBa4d98pQjCN+12S2/32yvm70QiwQm8nUs6K841QpvuNR3TNUA2wIigywmi7IX0++JrnYb3ng0uAD2q5mkisX/SOYrQLbIrNd3C6DmOL6Tc0qoBJTiAVutNLgCrcZDNphbZdWpY61u5rvQflKonK9MoP9LuC+OaHWSOAg7V7I4CjNrYfvMZz7jtgOVR3hdABY/+tGPXIv0j3jMCUzoeDYr2e36kTQDVGaBHqZhR+voBrBksIXjHZNlyFxWV/YOcqZL+jo5awuYiVHS/dQpOK7Xi8OhBJgoBybQaSXeL8epCJDpYvW+iXeoY9lOhG+RzUPrrlZv7wULFjgz6Vm30j9VONWHCgkCZpUbDdlTBHCm71G4TyL9UGqrTKrZGvRV1117nT3yyCOuF1PGsgOTgxyQ6RwFUAXGdGjTp7Cr5o/TEPpXkoXaWPyCygI33XyTb85kl/HGN5znrFpzsxZzYJUbPEuSIBYLrRNAbrxjgFBVTlpCJAfNMOClh++0/t/frWq+lpi/hFqWMhoW+bY79Fb5eTuZCxpILKgTU6NSOQAX6j7YgKpzHHO01RwRJTbt7TjYAdktN++ydRvRx8r0uQzIUlDQvd3bsTx6Egx6x7jXN9GTcTL9xVwXqI6StHnU6D3Opk89yhqxkkiwfgwWehieke7WGbKXz6Law4wXjCHL9Mv6iHmBjeT2HqIdAyWbO3+hNbHxLmxcYfeWTrL7V91lxyybYWcgNq2lGgzxO9uVo050fbM1dbOhmgRD5pmZVGmJAcj6t5xutT1UZmFeL07psF103SnF8SUfE7XvZJ9PECWKo4fODhxp1Q0wlr1EamqGLNdC1vnsX8KQnTbZr3hR3/+SB2SVBqKVmpJNJAV87WtfszYWGz3+pX/6kjXjUn/llVfaiSeeaO9+97tHajWO1+JjZXGNZr4CQAjgzUu98KO/97lI8V5OYrDzMUtf/wuLd22xPPUMk/0SeTawB8DZHwHnrd0Fe3jFb+2Dp59t8wgT7rJtaCWwQ2Di3zk1Y7c/0Wv3P7XV/mHxUpu7bKZt6aqy2Sqd0dxhvV211jgfh/s3v5d5upYySwjMAWE53FXd71U02Y4nrH/rqVRGADxVn2mpRV9nwGOWiemeFchg3HoNkzyz4aC0LARYc6qtJ52UDHGL1jojbtNmfwtAdo6HLBMqw+Qu4ghf3Yoj0haFQxUUxGh+7nOfG8loC89Vgi3dU1VPCK9VSEnHaEDGVFpmpiIKKk9op5YMJAczsoWgfNF4x6QBWbmqfImLlbZBHkEedFRbcEpDXuvUz4zG2B3YjMoeo/uDrXTspWQQskI95IkfmXRkESAbf74IovpKq4PApDi7y4f/BlD734R+zyLMdhnhNh2hyHllBrIeV3jzKRiuh2G7FJ6THk1ARP+GhBfZRsgLTaa0F6Cp2oYB7u2//a09+OCD6HqSI4xTYDRf1BnvOX6Z2k3Cf4VXxZS1t7WRZNHo7O1HP/ZRixOKFTsmkBmuR6BTGrlHH33U6pPSTir8zAJaLgGleyJdkzaEEyUmzBrK2FasFeIsMqfChL9+CrYcEqLTV7YjSZjm0Exa/WgMVbLJ+jsJm65SMkNoV5MCgYS7BnoG8OabZakleEidd8W4LXKwA7Lrr9tkO1tJDEHUXaJUUh8LdJp4flVyyNrbd9DmLNCTOEo52N0E2k6KjiYow5RJzyCZo8U9G/NoZYu6t8xlYiEDIDvllJkRIOMeheLnezuFiUKWmSGSzggXJmBjjYzCHGHzEv2IAgQqRWL/1TrHenatsxOXzLCjm6jcIvkIUY4+LH1ksF5DJvdkABmpWXCoykQic33DyZbaibQit8ZSBD6K/F98x+QYyEncmuitdYjwmML74CGIzMPkMTTY73eyPDXMvhUN5esn/RUv5ge85AHZ6MaqzJJ64vEn7H4KaGvx+PCHP+wT6fvJglI44VK8haIZbPcnjE551jOVIcnwysrX7ZGxNoFJ7PO5sQP9ay15rTIG19hgbd77n7yz8uQeJxEFXNeWs4f+cJt9+LQ32TwZ0KqWBLs5o8xOb03JbuoYtOUdSbsY2vuQRfXWXjPHUu0bbTO7nELqCDtz/hRLvu3PkSpMMfT/Vkgr1ITkXNigqscGn/wHKwx+xeoABrkE+q+Z32DXcQ4NsxRR58+srvUS8BU+TrG5hOUOxQLnMOaNw8kQIlGADEvr4lyqEf4zWAZIw8goKxAXRG3oC8kuSKP6PdpY4cfNmzfb5z//+ZFiz6OZsbDoCHCriLzYnVAYejRoi0lXoexR6WrQzBSk+fGFS2miexZ9Huv+TBaQFaHI8iqLgi+Q1kzPhRBDoi8T0gqkWIS/osfCv/xaTBCSVj1CV7upVuluckwvnUgDV9ln/Stp+EqTWH3flzBIXk+hzSuuuAKvphOdHdMCP7LBwBZmzZq1dt+999rdd91lW7ZsoTqEfNBmUwYMmxpCdAJa4fUSyYekhBoSafS4mDSxQRLAR3VN+2wK9hBjbXiezzjZX+8JIEvnqRCrW1nAmAmovOc977FXnX2Wf3UIQwbPN1X20KbwsfsfcoZedWOVmRk84ML5TrRhmzpA7Vl0lyXkCiez5zibrNvmjDJE8rYDkffUQhRy3Csgkz1BrN8GSPyR9xalf22oF6+u5rmWXEa46ew/GbfpDnZAds0P1lqfN9fJAAAgAElEQVRbBwwOdY1LJEr1YudTm4lbPdGBgf5O5jGQwySOvNuGIMfAvsaZTTaucUC2DGcHs71sKqntq82lTGAT3XbKyTMpuzf7hQNkeD0O9sPCyUQbvXOcey5+PQ9jlkWL+1ePrrMzMk12zoK5NqWGzElYugzjMdHHeQ/DxuLJNhlA1lvspiA94VmCLoMbF1h1H9pFHel5mF2/wuL1N0+idSf/1lySyE+xjWQWysnNhwzol+6uynoyM6xh7q/JCkQ7/RI6/scAsrDIVBZQFnt284032b0sJGLGVED7U5/8lL3xgjeSLUXmCYfsLipLsYxmvxR6qHx+LK1MpQdUJXjQ5DuRRmTCvtL7pNl1P4GaWGvDeE+lyayRv0NfqdOq2aHdOni6PfDk7+31i4+xWSzYnaTAFPvJyOvN2xocqtev67YVMw61xa3brDu1geLsLbZo1xaEolusKXOGfejcZqu65G8sVj/DkKWhIQO8AB6SaCbytsmyq86lTNUqmJojyYR7wpLNr7fYlD9jO3IBu+5VAIIWJih26UyIvuDHstSIRFg6vIkC5602l0ymRNOZZBIdgiaNhUALPROJZ1ommOhYUALoFViQPYnumxiysFiFNg1sT8hwExBTG0tvFrRMzyrVM1KPhHbDWsWzZl1kHymyVM1xvGOygEz+ekAV/wrBKby1Ixaxj0kERjHe+0W3CMlhUVKgDI7CIzH0InHCqgppDbe8kt3wElK6TwB9HcUPC7A85xzo9ZKCPr6PWri2PVixigv+3e132L9hhCybkfdTGHsq7Fble9bCgt1Hke9fU6xcdR8PIdQlvy0BDAFnhfDU/1UaTIesIkJ4U3+3w1Ar9Cx2SVq1ykNAbrLGrhOOn0m+QH1OTN82RPv6fcHChfYoDNghhHfFBH7uS1FJNh2V2asjWq7yji8frEfKpatGTmtvfgvlF/TDMotDRZ5t1b3IBn5/h/XdfweVJkiYmL/Yij3jhyx3DCHuRlRVpz5FhQqlF/cRvhwmnNp0/LGWOGR8W4CDHZDdeP1W3JEA+7kkFiM1/EsReCQVBUJ2W7duZK7ZPqkeoCxKY06T1CAB21iN9U9D3RyyxsnQZ4bUZisAshia2FNOnuWATOazLwRDlvLkJ7JFyTYuDgGO6tiC1bXY6rYhW76+3X7Su8HeO/NwO2Mq8o5Mn3Uyp9Yx06QGeV0/2sK6yTJksKnUhLR26sxuTFuTurqStbZNt1/deoQ9MDh/Uu072TenOjGTTj5j01o67S3nddriRhltd1g3LNn0BStJZGHOfAkdL3lANjpEFRZkAam72M3f9stf2S7S6z+FdkxhFKXcL8PocR4p7NrxjmbIdO8qwwiVAGwsBi2IdvV92gHr9Y0wcS+EoN/7EVmLQ//1Q2vd8bTtbCSQ5VntMduM+3yaAut35JfaQ+3rbCkp3dVMRrtm4E+D7ms2mTGrMQ18a88Mu7d6vh3V382OARq7ocmOH0IMTOgjXTza5qaftqo//pj4XSZs5msvPssOiwyifvzHSk+eza4ag5HCUdbX/7hhDG+xGRda3YLvgjAYqNvRaOTWM1k8yIJ7r5WGViHab8DvBkO+4gzbNO1WDDevtnTzOxWlLIcsMT1BgxbHFyiELIMWT+BKLIPAVghBV5r+VmoFZcIprZIyAwN4exYgCzfYzc80uUYMU75slzJBxM8mC8gKAit8H6odbhyTReeTlu9ebqW+xzmJHRg18hhmloiB+NEuXJYomvUEcquoybaddkJ8XXUCzX28xeuO4V4tJmw8iwUhw2dPkMXFwh0ArNoz9GcBqo0bNtpVhH0FgN/xjnfYK05/xYhB8gYYMwGu7/77d3ycqH1DH/fsZIXwYIqUkamQpR4TGBMwc3asbFY7EqrjOS3uAmAyTtU9FlP2go2T/TTpKsNSHmTyJdQ1ziVUuRprFhUk13X+5Uf/xkPrwdYj9MPKzGGd2mgd32hGbW+n3+1VofFYouO6Q0PPNsuuuN/6Viy34o4O7F6iFXJvov5hSnSoiHShH1iXxQwZx/bc3MVWOvpYi6NFIrVi3JY72AHZT2/aQW3idhuEDUoCkgTIEoQXB5gf169dbdkiOqJJHMVSC+ybTJoot5acwdx+hE2dcqhbXajOcRZ5hgCZmO4YJXucITttDuPghQFkyQEiIQ0zGFvc4xwWUdVxkrMSdvvaHvvt6m4746iUnc09nUaYNpshBYTvrR8Q+19jQ2ze0nL4n4SGDAWdZ2dSxsJaN0y3qW5onrBNHVV2x11TbFX7gTVebcoeh2PWBmtp6LbzX7vL5jaxQSHyMsw6lV5ABnLmfzVkk+j+z/+to4GZJhIxYz9ESKyd60cpk3TkUUeOlDVRCrsmVIWwtIiLWdN7wo/+1uIv1279q89Q6EGgSz9axLQQKRSjSVgmtHp82bJldu6559rLMIccETY//8vy+oyrvnGV3fXQb+w+Qg+5wVoW5+nWRfithV3v7O7N1lGbtcMxQJ1flbCGQ5qsAauPoyla3YMG7NF0t/348S47rzdrZ79qmetWMrueMpsGy9Z1CCFGjCf++O+sJEAm8o24pcTvyYE2Mmkes+rVH+Gvx2FpCFMOb4AC32Zdtb3WOOcewMErLPsHJneyjQpQ6JbE6RwmLFV9ONoaQpbUIWUbz6Ag04V08awSESR+hVJ2w0VCojFEoQIJIetMyRfKghUwC/c01LcUoNDvWsz1u8LQYilUhaFSv1fJuHWhncl4WrpkN2gvMMiUpcSzxf9j36TJAjLlszuBUoDd6HjEBnb+AlbjJ/j/bDJFInMUx5UYzAsSCyxWarP5PZ1VyAkXbunN5BZSBzhr+KClGi+hbUlkmMAIvhKghmsWGLuPUP5tsF7SWMpb661ve1uE/wlPqs9fy7i5+847MaJtcI8wARO1sRjfSvArUX7YwAQfMb9PvEfaK91L6a5c6I+4XSBMz4ewX3o0YzSJsbI/3ppVBjDXrfMVmNQYl1m02kjF2y/703eNhMsrq02Edh9bar/7TINT/N7OPe72MfQBhRpll1KjrDe0ml30p1XrzR4jLDOe7QXhSWushV0nu3nOIkssJglnKmOTUOYAQqSJUkIOdkB23dXr0ZCR5U0JnUSq2k2O5S2WIMzYSlQgRrmxyRxZLEZi7FTFXKtUXDo9x2prpjMGsL4gIayIg3YAZJiH2cuxvXghAVlmgIhHop65kw00E3SW/rBye4fd3Vpj62sX2SeWoSOUEezANuupycKEMne30atq6mw9lT0WtnL+kwFkdOBUjA0jSWUd6xZYfTfzj9ZNqLJ17WlkMrsraUymnZ/ve7O162yAyM4Qp7FA0UkVD+DvPu5T3bw7zeaxeX0JHQecIYsCR1G9tUgfExXoif6Vj/rEKhntRkMZI/kbbaTkURuLgEDSA3fd45PoTIqIa3EIFhmaUPW7gFfIkPQ0fR4LC4YW/kFE4Hq/3qvMMAEwZVnJ90oaGn2PzEzf9KY3+d9alFTTLpRYmXRfUDWN2262bfdcy2520HqGCoRNjsDotMMa89hG5OrsH9f2W3HqXPvwglqbNrwa2ppSIaS3z+C1lmu2f3jkHhbymfaxw09gl73WOosPoxVjtzOwCCKGUjCvxsn5sBOti50P8z2vEVoaRIaGQLbrBtoDAAOoKWF3EU8xGVUhWuX9eGwwQcDseCkWNFJMBgq3eZHxoKdjPQB/eVkj3eEkO1hlmilzaJjJM13d4u2rBVv34gMf+IC96lWvsksuuSQKZapjaIEntKwqCoOAZumQBByu/OCVdvSxx9j/oaCzjkrwEVg1nD0BhCQqwDTI3SshzY1SFxW9AQBNVGxhsoCsj5BTQ5ZQ6U48ozo4z+Ff8cWLMGksWXdivc1U/0bzJyDmLIrKH3DEVDRYodUEYmzXOXKv+rcCyGEAGs62fPMl1l48zoYy49te6JqlPSngiKmQ4xNPPEG91nu8PJEc8d943vm09xnIEqOledumzfZv//qvePctpzLA3GeFGUf3ZwEWMcIyc9W4URhSY0hjanSd0kmPhf3wAepLGs86V/VBXYPKOEkHpzG8s63V2TDNKerTcvVXu6l/HnkU4ZAKDero0xvPo/AFuxTGgUrQZQfwJmQcFdk4xpQhK4Nbzm1oASCsrOnbl1Jvo8/rYAdkV1+zFnCM+77X3pPpLbYX1TLF7Uen2MZ9nByDU0094Dhjtch8lWMt8AOtboHJI5ulncnSTlJ1RRqyHOXZXvaymTDNc9yrSybPE2XpO3rAeV87xgJsW2L949Z/4zVWSwLPYD3XQtki61lPQhWhy6bFdt+Wgt396OM2Pb7Lzji6xZY0sOmVk7hrUNUG/IlMo0RVkEQa9po5PXX2W6y07JRyOTw2zyQ8MFItOcRcE7lD7fXwinvMHbH+LdbXebTV9820AiaxMXIM8KelNB8JXpM5iti4aMrTB6rajJIhIv9wxiRzF0lf4x1yUMoMKrSzkKIG6zE6nkHkaBgNWd4aDruLaAKVZV5CxwEHZMq48zIv/BdptaLWCzvLqOzOvh1acORh9VsyusQCaDdfy05GzNZ0AJQWDYVLxG6pIPBFb3mLv+YnP/mJh1/e9va3e6q/XLclEhdrkK6rMRmQCpDJhVu7YH2+whjnn3++Z/kpnKF6l3pNZb2+F2RB6qeW4Z0/s9Sq+10CtQtKpH2AguK1GWtWeaLhfvvyil2WI/PnL5Y0IL5n5yyLfQnq0ZK1Vc2yrzzxiMWrp/P84eymdgEENjJ3TbWm3uk2jNls+nUXmx16kvWjWVJlsjgu6VSy9SxJGwZwOW2jHwEZAE1S9cOiwZ8jqyeKBAY7BInXKwKBDGb/z6uTAw9k5UA2Zww2T7hI80EoeyWBvphMtfNFF13k/cHDYxKM831RMfQo9KhsP5WpefVZr/HC75U+ZeotIwyFyhZxPtonyy7Cy4a4pT+TloDjBF1rsoAsH+tAWMyk2noXlRTeZcVeqijk0HukjyDZ4XEb7t8ZIql7Lu7lAZCyEwChpBDZRnyv+IcGG6SixX1PH2233o/B7yCfM84x0EcFOzGQZSZLoGMaGrDDDjvMazSeQI1W9wnjPik78MYbbvCSRTPRfWlzM7q491hfpfBkCFnqniUFAMrA7GAPSQpwqF81My8IxKvAeCMZu8qalG4sIcsI2kx/H3nkkbZixQrf3EkCIR/DQsWiGwyLR1uD7Nvs9XxfpRWz3Fn070jN2mirm5N2rJygUXkvgkRgoqSCgx2Q/ej6TWyK+zGBFb1M6JAJJonZ8jCG1bvatvInRtWTOOLFmqjkHXOGahLX1U91y4s4kpACwAf156QAWbE4hFQEVKHqAvidyRh28IZrrDrfbb3VOcuQTJXItbJGFmx9rsnubU3b2p6CLZ1bZ6csm4psjPktK53XIGXzmFIUKiXDssC8KS/BBPN+1esustKhr/DEd2V/5jD8Vhg8RehzIh9GFMFs0NmsEWFpXzuH+sBUg9HbMKrd1XWqNaV/OonWZdppIGqVxY9QTCAh2UySdRv/yxIVB3KwmykSNMY7ssheqocU1l9kfVXr0A9Os1TfkPVhoFx3OCHLWoyQX0LHgQdkAXlF80eUflbJ80+Ax8SuaALUhKlD4EhgTGJjgaqHH3zIrrvuOt/Rvuo1r7FBXr+cSVUZUG9+85udlbkXxkC7x1fzvLKVHnzgfhaijB17wglMuIURkKXvGUv7oe8TS6CjMszmi5MDkckcbEMe+B1i3rvhYbttLSjmN48tt9OOONYWE37rnZ20rz/M7qWQtA8ua7alGdW0TCG4Z/LAqbqLNO0frV+NcWuTXdQyD0DWaQNpLCqSjVbT2WC7Gnpt+msvotbZaRR4RxgritpPFxDFRJTQoBVqEf7SxFRGMCOAWRYXfkQsmf9W6UaOOW2J9PxB2C0MD/wVvtFUtSw+K1VOmlD7y+38q1/9qp111lke9vXuUC6I3IvRoQqSi6FU2FIM51VXXWUnv/wUu/TSS0dqQIZFJ9yHrBfgKhfApY8lsY+IeyBYmZ4UUp8gM3aygCwehyKU237fAzbc/pf80LeIQlVJpJ/e7LorAdi4MmSdOSzHLQWKtcDmEW5j6FtIb6T8DQ/xkp7iFHt6x9n20KoZ1j+kgrp7PzLoSIKpay3AYjZAaxEhtwULF4zoxfTuFYCxH173Q3sGEb/aRBnJAnETtU/QiAmAaQFQmFIgTiBNHmcTMwSTGRuTf69CsZondK2DbLY0bwiAaWOlUOus+fPcDkSst5IZ/t+3vmW33367fe2f/5lN2RRPVqg81O/2yGKd/CmO+wmDZFt6UoWPP5mlBMOUMEKfPf+EzYvbb1QYW4/1RQc7ILv+hq22awfZ4PhzJbCziTb0ZJhTyWTb9s021AEjPYkjK3aZ6SJHZng604IH2SKbxk8mM5XHmc2k1JgEQ1aAFUpksT/BPFr++VVbnrbhm66zNMa2vRnqrJKEkUQ3ViBC8NDmAfvVekon9VJVpbrAppxoDlm4VSQDzcaXbNmcZlsyq8Wm8vqkTKPRjxWKmHiffaEVDz0NkCMiDrBapRJ3VVQD4PwncuovbSFKBQvWEbeuZw6xJm34kbW09Sy2396NU3/35GpZ5vrJjsXWo8a6bd7MLjvm0GE7dGHOauqYJPO7YA3H3zIPs2bV4MdpsUNsoHojYLEeuU0Wp/5hTPofApBFdWVfKscBB2S7mTCaLPwRCBn9uw94Jvh+hZRzNb4mG02qA4Qt77n7HnfNnoLeKEuYrIpUfB3KfEpKwxIE3+W7Jq2Rp5GXReCVN3MkFMaDgdkJi7+YOE2OAodjFTt/Pp1iGzufmWv/YLFbrsc3ottWAcIe2LjGzjr+ZHzHCmRVxu3ax7ZYL5PGhUvqbGldwvpztTbIyl1H+Y0MC85K1bijox6dxmKCHU+JgWxViFJ7q23n9Jk242WvwfD1OByPASnggAQj18eBChKLLFNTRC3mP+6ZpbCy6vwpJXrkGAM9s9MpETsZ4INTgMkq1fAU0nO2Td+x+91r1qxx7ZjAmRgcF4uz+9G/oZiz2lj3VYvohvUb7D1/doVdcMEFzwqPhYSAPqY5lRFPqdSmzh7Wr4QmAg6HZAUA2QT9a7KAzGtZ6ov5zkL/VTbUdjWTG15xAxu9gHte4V5e4G1cMfc42cGPbkFemJguqzDJcP5QEiTeZlUzL0HMuhQmavxepfZTeR9tMPb4goq3rSXkfu0119rjGCnPgP3RItyJbYMyLicyLlXfV9hSxrwaGwr/CdSo/fXeiRb85zMmXsj36NzFjm2HndWYlRO/gJgApTYGb33nOxyceq1NNle/w1/sO9/5jrO4bp3j+Dli98Oh+SiY5E4EaCd7LZVWwmFP6/bD3HOFzGql7ZzEcbADsut+uBkdZA8ba5XnJfVB4wnN1yAeXR2drTBNYpef/5HNdDC3IJnQOGZDWVs73RobEBqkGhmPScYjDP4kAJlyaKWp1UYrT1QitfFJSichTymSVIW1Ubwzb9WNRCRYpzb15O2J9pJtG6QY+TCWQUP00ww2RtTvnILyfsG0WpvXXG8NsG0JDGJLMj1PIUk5mw33spMjTRCz4BCbRAGy6n0AZH2lDnRpzPEdBctuWKh0Lz6jz9bvyNuNv6yxlW2YI0/imEppqyKMXo212iGz2+yYo/vs0CV91lSziwah2qY2quMcw2S+1gBeBcj6AWRpxmgSJ4Jeprv6ZTBk9f8r6n9Ot6c310+Ig10ASBf5s7/X/V7K89u+BCy1YIeCwKMnxwDyhgBi0tHU1NaMMDiRZqececZAdmsAhSBgKiKfquhENCkJJIRC4pWlfiq/r/L3ytDlc2qQUS/egJh0Qft6y1/9bcJtA9YOc6UalQsbqi3VuolOO8NWbhy2Xs71mEMSVoeAFxtlsm1USgQX9cG8tapmYHWdNWkHSf2VwQYYjFrCMghhS9MXWHIeBpH1M31SED0vF3kt/nITJ8bmbRH3BaeMniqBs0KY4xx6qTJ1BOSQwXpNNNu4np0dYtNZ6NBmYExbPlRsWu77Cl2qrdWeaRCHFkSxLmIukhRFV91EMWWqxHDRxW+x18BsBuF/1H+iBVL/DvAvdmxO17vlBQBHBLfAkGetTbBDnCwgc8Al00i+Kh7byRzzIILsFfzcR3LlbQh18YJil0zaJSfX6Wyih3YREDOb4v22DR0jJ0mx3HT6JNLtj8Uem11f7UKsL3T6YRke+yaIYRRzFQqOy/DVEyPKSG4Axvhfv/FNewSvPtk7CESJNdKmYl8sKdTGYsUUwpdgPwj5BZwl2K/MWJ7MONif7w0ZoTpXnbc81V53zjn25gsvtAH6qzzXwtEGAy+PPIVzv/f97/nGLRhDh/mhsg/uz/OOJsuKbxA4LP+5m8FGDFKRKeKA5Tn4JR7sgOzqazYBoCmsnmWzl8p4UfoYYbs4A3w4O0gt1coN43O/Gwk2UjG5ONOyWdULZRJJAnJjzKUlNphZ6kxOGpDB1GveZWRa1drl1vPjq60R7eggnnOZPtYrzZts2jSnD/O9BcJ0Sa6RvGfLxpsJUaIzRZqh6F6STbJqx7rNinbVuNinz70YhuxUV7LoGOKaHJDtQ8gyii8wx6DHy66fQW1jzGnRDQ81pgCIh9ms4gQitAmavLtqNedLeSgVg8ckr7E+Z/WasAGTWQqpZyYw7s5SS7R6mEz1+FyE/Fs8nFrFVNrNPqQBhizW9L8M2XPs9bggl2NigXAP1f7Ufyba34XsvMCOaZerQ5OjJkrt0LUASTcmjZcOac20K/bix+XX6fGxTF73Zquh14/+7kogIECm765k7Z5jw/jLZWofJ24/fP238SDrgP3COZmKA0l8dqwLQDbvZDy/GikEXrLGKUwOZMVZbLYL6RN1pJ5MeTkjShoF6SsYoAzqgswCNanw+bEBwpdp9GbyYxZgkW7MARmiYHfpUngvMm4YcSUNF6IbVLmBeRa7LDE9+iV/9//H3nnAyVmWa/+evj3bkk3vlUCA0AJIb1JF5Viw8Sl+2MtPjxU4igU96rGf6jlHQFEUQcECKFKkE0oCCRDS6ybZvjs7feb7X88772YSkhnIEEL4fHDdze7MO295yvVc93Vfd0ZlxLn5nfb87b8HjPTZnBNfZzbrNPd8XNil+Kx880w9GzE8slZQGynnw89L0O7JP+t977+UUiVHjzCSpVoyF15WhlyaHVR6LedK6DA2nXAunJnYJ51/BcRfLSDLBNZxkwG7TGv6KAdKVXw9we+TndRm/zHPeJjQZRdf2/gZ2wvddyYaeZE1Nr2Hu8bzrp+P1RvgVcWS0UWyLbZ+NBejIjvAwm77F9co/y/dS4GyXZ33r/nv/7F7YH208DYyPnwQ5oOySqWNxBppbAl8KcFFWa9tsGWSBCiMX4lh25sx8XK+R2Hc1atWuSQdZVKKGZS29C3vuHjkYxIJb0MWFl3JGPqDaqiycbjkve+1k085eeR1e/J6eznP9wXHcptKN2Rpjor1KG1/o+FLDKTTLQIzH5C9GGD2agdk1/9ik3Vt7wOQKWTJfIE+VTqymhhmrSCQQZXHqKKFE4SlsQ4JhmGIKE2XyRImFKMNSCuAokJQ79UAMmAHYIqscx6ZC1kCyPrRkLVgBjzUFMB5f4zlhvEoFLjUFK5JC92Zq2Mr6l0bNyVhuR0fL+BytZkuBMmGRtQ/yDnHznoLSVvH8zl6mYCd/ADJ2nwRgIzbCsgjLD6wwfo3Tbf67imWTa2zGqKYqfBYi22vTqMHJ+A1aYu1cUXkqyBKHrCbwabFaX7LtEwIHV2KcwhMQNQPIINEiFFZfIC6nk1z7yPDeMeGv4pu8Iq9db+HLEVLuolEA0eAQcaXsGXyfcnKndinysrcEt+df1e9iiYg36TS371qZysw5lJ3FZp0iQTequx7Bfnsio4rQCWQ5xcM1+t8sFW6I/Z3yf5pvpjJ7kU9ZdE50NPDK+6zPLqpBihqq4WGZkG2UezO61noY4wOdky5cBclzwBk+H9pgKYpo5QjC6a2OCmrlqTglYsIKzSpHZUmAcFeVe5RmIzjikZnw8VvATB+SE/z/i5zm9PGlwAat/EeAWXei/PMNCJ40kwg9dqiDfTbxsWPWDLeZzMPx1NrIl/+mNxFIO1KzpTI7v0QmEDAgw8+YP/y7e/YFV/6J1uwYIHTAsocdldAFlTmQP/dfC7mujUAo7a3ImqdWnTMB9RW2EFXC8gKAUp3ydYzQLhYFhi6VtWmc+60/MuPOUmLR3jaCwsrRqmtiFK1GB9Kl6Nwu0Cd3uoeYJjdMa+P7hQy3k2P4rV+GST91QdkSnB5EMPX//rXf3P3TVooZSWrBJXT8+HAL2ClUF25pmckQK3+rrDy0SS3aHypHqlCly+GZXtR42AfvUhWHENct8av2L2ZM2fa+y59n03l+yAsbIjxX1unUBhOctwzbQ5UOkpaMrGIn/ncZ3fKqC7N+H6x1irVXVrR1sFpDIpSAB3QX8h2s+Hwx4heVknj92oHZL++cZt1bulCQ6a5OkZf94BnFKpE3/Nu17X3LYDtTCTCLERkIedc8qELYKikl80oIS1HhKeKkCWjGHE9thXYGGEza7EtKyzz2xssgldkXxST1wRrGFGIAGsikUgemNy75UbP3K/qdJLfuB+0c+fvMEtZ1tI8m7YgVkhZyidFTnujZSYf4aICYTTRWeYOtqlo1zwNXLnGqODwKBQxGh/oPstaMJu1IfTMEGMqGxnSXr6KVjeAJVKQ9cz59/QDJPERi/S4JD8uxdPWlmlZrH9iScx/WecGokhBSLaIDrdZEp+4mrl3wBCyFh5Abf8Dss1rXcp2PEUKMZ2rsbUd81KxPBLVjIiXXvQt1YSoSUYhTLcjLKHnS9mBbozu2lo9dsH3Aipd/Ec+cBd9SKk2bNdwjA/CSs1pq2XIBIJSQTzRcOYX39SEwLIwHCJTksWVQVGXpVRIUOEtGXMm2DwAACAASURBVFX0Mzk0MsC9WTgRjluCyYL8MUJ2HANbClf6Q3M4Kdv6oY8sUxkeCN+5SZwMHDlQu3q2/KWA87N+8sph+/M8aM2J+aULKzI0LtRJ03f3QxG9uYQe+dMztHnOTRjYuj8RQsYwyPriww4EuPOFadEi5wujHeDlnBQKa2ikREkR/cn6QmDipptusg9/9CM2b968kcSLFwAyJq7Mxn+1RNfnYQwxV53wNaoSHOfVksyuZYaaWrZvVQvIHCDU43BRD+1kk16x4kKzB3BHNGxCWqBvXqO6l94fEBLzLInGEKrQJAqLSIHoqNzB9fyF017ASu5yOaJY3XPh+cnWBfAko9dVq1baj3/0Y5dd6Rud+tnHsnnQ+Nna2TnisbWnm+RvalwmLAyctFVinX527bUuA1Hhv1dzk/P+cccf78Lka1avtjPPPJMC0cdANhQZZfVk7tu/AcCWYAUizzv5kF17zTV2Dz5t73z3u5zeUdUI1Ernh1cCkCUxFHaSAvpKyH2X1GIHCpN+UK0S8NrTM3q1A7KbbuomGWgroWYE8PTroDbzRblFlr5dei/2ph/m8X50EQP+yxAOFQsXJtsyR0ZnOgOAqpIhEysWcEyVxAf4JA4CSH5/I9GPLdab67YmQqJBkgmAbZZiHgyiDaupayRLvs3iMGsB6X1hzCLoz6KAT/LSOU9CkpqHOddayVFOONfizbO94mucc54sRgGyQIrPreBjmCost1iATG5ItYGuDyOTWWqhwfssqqDF4Anogx/Ym9s68p4U9w+8y3rTwThjY2dsgsQUGM6uJEPlonLe33PbFZDVopmLJDosV0/x94P+5EVFDqC27wGZS8NGmwVI6k/RoRkwjYLqhNvyzy624KO/sOeSrbaWpzIntNam1rHAL3iP2aGn47HEgkSWR4A8D0xYvJj4yM6PgRKgVIT+mh0Dk8aCHlwLIJnqrT905gifZZUYhAoPy1m8iKiAQhYkSVOct8BgjLEbi0Ml19c2vUCTUboDLYghYm4XLlE3o9yYAyTDDKQ8xq0EVvlC26X6k8W/+RFC9wYx1AUGYk4gtbgZ0vlECJRTJKPSeoxe0gm/ffYqw+em2XoUYB4DhC/rKZGkz0tBfxNk5RS8Qj8OpEk6ABjEDtHTYOn6c5i5UlBZW6SCdBsUvUxTpDuCfsM/F0VRfPsSEUHlmhaMkRAKR/DBsXcPtfHzMsG08KvpZ+l8Hn30UbuGRfEb3/iGC43JvmTXkJFbHDEESiefJxtxLX0Pc1LsJoISujqApGzT8lu8qgFZJaOzfTxZpPGp8vVijoiGSYzAiH3vW992nmTVMljK4uzs3m6NLc02acpkU/WEX9/wK5fdPNTTB8NUfoe7jy/fCfFdOBYmTKFb9SsxfwJgH/3YR23RCa9zYMXXfPpJO77Jq85Pv/vUpz7lXvORj3zEDjroIFOtyh/84AfW07nNac1OP+N0EkT4LGnnaskmpp8WA4j7+hL36fFf7YAM71fr3NrlsuulHxPb6zYBLvIhvVdxQ8B4d4x7UUPn5WvJo68Y23W/925laUg3wiQ4Eu0ovqB0vpJfoD5Hx4nS16OY7cYo7j0y7CvNf0yWpbo+9U/1N22cNOfVE6qspgXbqqzl6K+32iiyCc8T6cgBAAtsKl1Sl2P091/LYxXiPUlJcCT1UKatGEWQphZuyTsOoLbvAZm7GTxMbldKWXYiDBDKJp+42wZXPGyLl2y1hwcRX/OHucNr7dyZlPMgrTh2ytmWmzWHDumxZL6+qQBA0C5QCichiVCBavNZfJ4yCajap9lN4BETnVqMy7mXVNWEA2SrleH4kYi2E97u0/mmCUFhvLdreHKnkICChGjA4NQd4Mo1jbFg8yg3WYuk9gGQy8Rz8i3PtT2nch3seEKo0OPpNdSjm8MA2EYYlx1FaBwgDZoWPZF2OuWaYIyAZBbtgytQK8dCzRbFdOI0nVYOC6q9Jn2TE5CiU7M+LI9JH05TuzDKNY5ox0N4n7l/0PHF1KDX8ACYd18yosudsNYLAcdgwapp0jD5Or8dn5GxW265xW688Ub70Y9+5BZbMZG7AjK3uKqfUKC7kMfvSzx7YJIXKtREqeKdLjS453agAzIBXmcd4nVaUvXT9tyzz9l//Nu/m6pV+GH6vX1GTijMIpThcz7woQ/a4Ycfbnf86Xb7HX5mBbGwbre7/5rPYivUqOtVWFX95Rw8BM87/zzMMwnY03dKgZjAfWl1CI3vFdiBCJyoEodCsWoqLfWdq7/pNKnncqy3XYzuTMkk/KfjKYOzvvja/XcHqvvkVzsgc9oj+V+rq2n9lVWh0yMV91zFy3cBXR8cjbD43uv8tgOQ7fjdCMHsH2cXgKW5b8cBXnivK+3HfKnLrh5xvtQmWklEVenxVki6qvT2EQJEL9Su3CUdaVVhfQKQCQTt18a65TEVWtMk9dD5FDXPerZVrv+v9LXtc0BWVBK59U9ifRfS37jWNt/5awSBz9pXVoyzzv6txOOzNrk/bl8460ir719mkUVH45/yRhb68U5uI5NSt/hzDCf616DjK5YiTkxNRwQwMCH4bcXwQWojzbfuGDLUYHmqXA/yBAKRCTrqOylMpVCeBj0FZ2vxe6kIyLbivPzQQ1bfCyhjcu6bNstqFxzNLhqWhhi+yzx2IS3dKaF9ATJdp3e/IjnclkkzDku8GcQsjxPwfha6UwiyfJeRsZ9WYrcbFDhROMOhW33lrZfFVOxzVDczAWOED05u00rColQBkHbp9R/i9WGydjzNqPtcBmM6jXCWUiUy9XXZmK75QU1/llPWannA84Ks2F0ux8cSfrKGXzNQ7NjixYvt61//ugurCaztCshcyEjgS9g9gODBXT9X62UZ7AgllrmFBzwgc2pM7/nkWLWGBgfs2muvs79Q41UWF5VE+5UmpCwZXTVNDdYxYbxdiZ5PZYb+8Ps/2K+w0Yion+1nQKaFzrfmUEjL6YroF5dfeYXzYwsUGTy/H5Zm67p7VqIzLe2rfjhyFeDW+betfN7OPuccO53szFb8ydT+zpB5Gev7suVZN7zKIHv7KeU1ZqWlrXYvadllAi6ex64JFHs6u90BstLXeivB3jffuWCvj1B6e9wapV/smN9ddYRXc9vrfrF/LmqfAzInTRItKxUSzxEIYblnnrRtf77BxoUG7ab1mDDC/Dy+SdkaHfbFMxdZy/a7zVpYOM+81GwKpS/QQVFEkfVeZWZgnNJdpMrKN6jHwv0/ZVG528IpHJXxdkqHSJud+Amz8V+xYXxp6qqEyAXCWoNxwqy1jbaF5LhHHl1iBx86yWbMaqVbeoVlyzFk2Vv/w+JL7rcWfGVQTlvvuLnWfPKbLDBxPgwCvSUGw+bmLAU1Zdin7h7mbjn0Q9hQ9PV2gOU4twFIykLM3wiUhAb31H1S6T4YJomSlSjhYq+INBUG9Ch6z/yVg25Fu7BqqQ2teNwSW1egIafkT3OTFd7wZTSXZG3yOiLOrlqAaOpkAuvZGoCh3xisaeKjAohBju2EpWxda2Ilr9mLPu6zb37ihg4h8PWd73zH1q5d6777Or1dAZmj/7nHjrUGQKsJIGpHLYyiosEKoZdrBzwgc35UXthX4OsxQr3/RcHwrq3bAWQdFUX7lR6Zz5C985L32Eknn+xeLrf/P936eycizkiZux+b+oCuvZsQo4xd9bOSQz5JCHI+ZbfUDwTCPCH4zouLz5rtWijc/70Db9SGvR8t2TVo5qRlfP9ll1lDE+FRPkNZmwfYevCCJ/VqZ8iyZBGKAXa+kbSc6ks6ht7TEZc2B5JKLEAcwK5AYTmwV+YpqryPmhd2LGHhRj64OkBa7eipVsFZmujmX6c3kXoXWMow7o9hXgrER7BjCYisxFDuj3Mu95n7HpAJVLNYu84qNkUme0sftr67brTR1JtKbYtbf1vYfvzYUnRk8+2i6ZPs9TWbgCc4aJ8HOzMTHxGJoZMY/CXXw+I8xdf9RCjvYIHZABCbQ9bIc14mIeF2RUmiY08wm/ZTS9dOr5REUvF5KEyYyVKOgUH559u32rLlj9uFFy2yqdNbXAp0BOFk2ZDlf3zV1f4KNUuQNWzJ8ESrOeWdZvMOp0B41Jod/evFKz1WTOFBQFox+65/y1WAoQju0O/lwigyi+9LmF1nPrAGuEbYEIuLsk3HESkm/ZBCVkXSQvs6hTKDfWuNvH/rW/6UBbdvopYlRodkFGX1xUnkjn6H1R92LFiYLDzurcpzhLTIE5KROC3LQZWFpDIyWUo5aYHSBypt+UUkyFa8/yoW7FdhKH2xvKCkA1LIstSdf8fk6E2Ubv51P2pq81X0miR1Y0S5v7ZDlrp86cS0OCk54r8BYw9SWLypWDR8b8Xe/rOI8rxHjx9rV171ZZhj7RQCdu/d99j1sHBRwuHDSt7Yj02AQqWi5FsnH7s+viv5QGbCb30nBdodUbz7moO7+710ZE7bU7RiGe4fsH8jU3X12jVkZ15qC4880oUsZZAr/7y/A7LqAMl+7Dov8qP9NGn/5bs+8Rd//b7UZSevuPIEXuVzrLIDal73mg7ElzBtyaeWZtlXPpl9/Qr/XHeweGzH9/WHvqzH3+eAzGn63TbaI39DKi2y/Anr/+tvrRWWC/rJ1raMsy89ssJWhibYocm4vX1es7V1NNm8C98OJYM9RWo9da0ehwG7D9nPQ2T8IxbW+qqvwGGWjS3Bc4UspxSsWuJ5TDOPNJv8DbMxp70op/9yd1QMmRVabNmSgv31Lw/Z3PktdtpZc3eECnfJwtSxdtKQ/eArFs9ttuionMX7KP+DD1bjSfjCHHSI9bBIYuHqOrqniSu6uvt6Mr73LjsRZm2MtYy/AjQ0mZdiBOju6SbgBH7zeFyVbcXsSXXVETJImTkD22yoe6s1PnOPJbZhWEoR5Rrp0+pgtAS02Pqk9KxGT7Omk883GzvVhqQB5JwchFHUmPGZhrKrQSeWI0NGJYBkgarz6yZCq2hQtmRHuqfz9Dau3jD3JyPtbPTV0Ogtlv7i6Gew3nHHHY6lu+ANF4wcdlcDTPcsir4SYut2l7T7/wMg84scL3niSfs+palk79BOhnHnli0VsygrzTYa2ue96UI2KdTL48VJ9KHbOrfa5Z/7PEaVYnqrXVEqnUH5v+tapRlTZq6yS5VJKtG3fv7EJz9pU2fNcEJwf9yq/4kR03fng6fEEJgzX/RfypZ1koV6J6Hf63/2c0KVZ7rjBQB7w+hFXbUCN7IP7PaqZ8jYEO6QTOxIJtJdl541UqJZ8aeiUialEsNS+vRKpzL/Z9XN3PGUnbZmrx946brhH2RE/7m3R62yA0o+s+s1ld4H1cbcn80BRrGTbqSVhFKLJ1Vpft+f5767z97ngMyFyCRUd0mEkqBzAzs3Wvr+P1rg+UfZVcft1r52u3blsM2gkPec4fV22MxR1jo7bNMWTcMS4wGYly2Enp6GNfKKQzuHpjz+JYaYP7+R1Fgm2lCSwtqkylKQtFDASmPMByw25WrAWXW1tkg3sPhAnf3xlpVM5sN2/hsXWEu7dF4SwHtll8oxZHbLdda3dTWzQz9MXq3Vz0Ajd+jxlm1pxQ8Lcz6Z/Il/g00YGTvynjJMXTOEOZ/9NKAGw9fRH8B6GDYsMp2LB3yS/ZNLocmrlLZMFqVoeWePIPW+ioVvWW35NU/bEN8jm8lMxVcnIvQkSoswTBKascCBQ3jYyHun9nXnkvV6oiVDeNpIQ+eQdbEWZVHjoDBOgQQEuVc/vXSrLXl8LYejQhnFz8s13/MtX+S+nXct5ysWQxPtBz/gFYf17Uw0aWmh1AKpJgF2qdWF/1k79D4YOeq8VPpELK3iaIqdq0PqOirMnwd6yNJtheSxRQjtv//rJ3YvJrBiipQlrHqg0lVV05oaGu0fv/A5m0CGpUaFJsYBart+9lOfthSbrYBYs/3YfONaadsUrpS4X6BeVSFmYO/x+gvOc75pfjbeAsKY6juumkFJCHNXz8KN+LTJa+2u2+6wWQj93w7bNmXaNI+SLVLDA+j1xEQeyO3VDsgyJCB5tVo9TXFpiEpZl56FQplWab9QAdBksklvE6nNtOZwF+IsaXsBiHb2iau29+w9QNQnv1DBtvNvnKxsPzYlFriMWufduPPNdq5X1V3+K35l+x6Q8cCyGdJkyWYSFyS/8pAW6SfvscJjd+EJtd1+uDhPBftWu3hCyI6uw3ekETAyeQ1462lvwSwOmnwAk7cwbuKqWG+DsDg91sQDSVHor5dyEE08k7rsNMowrLEhdLVNUx8EkOFUX0UT8Hpi8TZ7+L5NduTR8+3IRYAU7DaChBELOaXYlgdkwR6u44nHlJsNyzTR7JjjLN2E4abCHiqk6DIevXnc9af8MP9bZan4cksnNtionn/F5mGNZWpPsTQhkFDz66y1/e10NIBmhrIgFZJcqAbmuDcsU62wdYvFn1liBbRi9ZxXEFNDi2GWSr1J3dEgqG1kYtONDwKfEfdnDj3JIsedZ1nVcNOzkLssn5sSeGJCUphSE2NEYi2e98MPdtsD967gHsG2UbKpXPNF177Nhe6BM+FVijpfl76frNRd9CA+W+aDLl/nUBp+GwFkpGe7ky1l3nVCPgtZ4f4d6IBMXmAKWcoq5JMf+7jz1BIjJDG+WrUGxnNnz7HPXHk5wD0HO0ZVBZghhUa//51/sY1r1u13DZkAma69lbJQAqBhacgAW9KTySohAzhXBQ/1l6lTpzp7C12D38f0XayaQpQ+KNO/f/7zn9udd95pMyZNsY99/OMM7QmuTq4sRdRvnUEsgG4v1uMqZquX/62vdkDmhUm8lqEsmNMClhR4LRXl7+7uyOB1d81ngQKe38/uX6PxMxIS81Z+F70ozi/OfLvC/FJJ1C8r2moaFXureTtkgN7uW3/sCN0opqMWfDWJ+ncF175Cpao78Mq+ed8DMnXSYmZTaaFhTZL6GnP3wza4/hdk+a202Kjp1t/QY9kxD2LayoKRngX4WVH2jsgs1SW5Sgui9GfH3tRatHYRGVQzLD33a9QCHENmPm77hfUWzcIwweIUcDzOYsYXxOOrkG9hEQEHtigISPHqBM5gFAYL4nTf3zvNrv/lfeyiI/amNx/jyldo5xVWHQo3WCpQVJWepyusWHyRZu/MFsv03mPDPbdacug+68ivt96hcRad+C4Ljz3DApTPiQKisGl3Mch8GI8ynUPe026pOUd+vnR2OnR9nAzDZRRaXXK79fatpgxPO5q7cSQeMlwjK71UVL4yzgOHoYZxWpQkijAMVyHRb4E2qgMcf6INzT+K49Zbi8gpjt8dHbA2rGrz+NMEKeibhomigqZd97NllKAR41djsfooIZxuFij8mTBUrIkCIkmEyGEjIj+bJLVMo4Slla8+GA9YU0MHYVjeQ5moiROa7OKLKRUkLRti3ZDowCJ4zeZY8BQuKGBBUDoQi1IHJUl4hZuqY0gPdEDm35vrEJ3f+tvfUhh5lDP1dsWy5e9WIaSYgIkUgyRNlEJ//bBfYtgE8sSK/sM7L7bzsJDQwu3bQQiwCKz88pe/tBpYA7/WrFhPCeoldncsJ6/b300gS6A1CJMnP7t/+vKXrX0MBY+ViKRNgb8ZVMFwVtgwm4+VK1faFzCIPR5D2Q9++EMeo1v82t/X83J//qsdkHWyIa9jfgBSe5n3/DvBYhBik67ZUD3cb7vLmAwUjRJ3V+OzUgb4y3GvtV/UvlyWEunsoBP2RCAdpLURKHRkBlnuBcfoextmlWsKYf+knXGg4NXqdK8lGhIi2uGxhOKrxViXB2RUn8WClVJ6siRQ4r7WN36kPo0O6OlSkKPAcnjrHeO5EK2zDI742upS/9zwKYceyZNAF7BGZVYXs9iH+bGuAkOlVVTlo7KsITE28GLcfCcL6vq5SI1caQOKFvG6AutSLtxKSTzkBPymkf/rjwyQGLjVWoJTMdLWBRCtYX0ehiCobvZ/OZ7wSzvGKwLINOlpgvOoZe8JuVR0FoWaAZijDXebbbuXX24CJG20fANZfizwcmzI1VXaYox3g8+LR/UwILc64Xoh2MaEOtaCB9/A5863tLwC0YNF8K13HYbFnLrb9uwzCbv3Tj6PkkN1dXFbeMRUm3+Q5xyvCN+DDwzYuvXP4z801o46eoKXmYiYf0cGT3WcaLZAdiPdRqV1RLu6cGBOtRfxGcvBFHZ+C4f7RouNO8Os7RhG40SGWb2383bxKDnuy3SX+wWoqVX9TA0cDSYWvDzOyt3PrbLsyg3WQZpk0NVC4/ihbkvUYKgRXmj93VsIf/ZbS2OttRI2DslNFm2YiPg8s0UQEGaHHmXp407GrLfDmor6+GSU56fCuA6QkQ3KxBEgi/FnP18OIBN1H7NEkhIeYWodcr9dmZ0kCzSDKETSQF2NLmAUOQwqGpKxgeEkITQlKjD48102b+5oO/f1c93koovNMzmInNb7ZTsiA0ZnxMkCH3LJBJ6Gw8+idJGjKimKAx2QKVwp4PS1r37NNlJfMkNtUAEyaahklFoppOgXC/cBmcoriS0SoBMD9InPfNoZpZYySuqaTz/9tLMkaalvdMxUFACmMkUCYWLS9G/NB9X6oL206e6Fr/ZD5trRqXzUlV/6ko0d77nuD8apgkHY3p+zVIJK57x2zVr74Q9/aAsXLsR7DLZaIROBtxeETF7Inld7vq/0+1/tgMyeuA+rHk+L7JmQCTRwl9j8eUbi/HuX7Mod6ZDMb296q7ul+wuQeSASHRzzoQei0F1CGGzbmsPfbtjWPt/PZihpPd1D6CBTVMGotfETmm3y1HZrb6+z0fioS/brz3PpDLVw6Yfe+vQiCskXN7iy8fIIJbS2Q/2W37zBCtSm7V+30nIJolGpQaXnW6yhyRpGT7LouKmEoJAMtUMONNVQsNwrd0gRAI8z5P9EDFQyxZCnpWBjUNUV9NxUPi5FUt/AU8wVq9E6/w/rYAKJEhnNKK6jsTmU7zwUcfHBTGJIeGKHAhAxZPbuHBIgAULWAhfBYd0hJncgtVcEkPl2BP7EVXqDEjBUtcmNlll1o2X7fsSCTMhSoD7JjYUFyok9KdPyAgGI7sN59GOq7xDYTHYg1LX8h/T7Sd+20Oj/A5PE4ZxlA89KOIAFffPmlN19b8CWL3vGpmKA2kunr29I2hvefBBV53mg9I2fX/cgQv7x1OibQtaU2LFhZ5UQlKGoO051jztT2EyXxGVebBTf1ZnlkO9bvmQ2f90Gh2PW2kHotWE2F9ACgKGgs1K7AVd17GxktTWkEDpHqmXHE0mgE1u9FqCLT8djf7Zca5P118XI9kzYqD7VPwTw1KUBZAm7ftMMS27bZGO4d4dP67CpLQCylADhII78MXRutRYaYJR1TLfgaedZcjIDQcL9YT5LPmyaRhwgI+iZj+HVFrQbfvU0jCPPRcydMgmkiQsyoJWJCZjKwcLVRHl/XYhBN4qwFr8kUzPL1izCRJpM9mKXMWCLFs2wRUeNH1kQ3YftSks7OwtJT+UiLXsV3JoB6E7kqRmmOsbeDnRAprx02YN87nOfszE41Hdt2w7hWHBeWf0K4bmqC3tunrlvzYihqlhtZb2KBRuLj9lV3/j6Tlmwvvhd31VmSG79em0NfcmJ6mHSVLhcwno/QaO6EVTdu3UOAlPOMg2w9cUrr7TpM2e4673rnrvtvLPPcecpZsxvD9x/vytsf/bZZ9vbi0XId1dHVwxLtVms1V1d9e9+1QOyJb+07uXLLde5mbJsgAIx/S7ExsB30cYiIPNvhbOnEID2Joaa9/3Afd9vgGwk5EofxCNSuHLtqn6MiLcBymB+EqOdZVFAhqeuHpLshCi/xjwv26VFC9tsKvP2pKnSj2gmzHgmxzBL2rxWXJ9EUjNNx/XSwrBFe7ZYYMWzll1NTc3e7dYQ4gXIUiwJQ6XC6mLTIiyONZLLsFE/jHVpygwS6CayDlE7Uwa9zNEK1aoqMrNx2U6Y5FVRVYdx9pas/cMPW2Horxbvv5cI0RJr13UDyBwdVrQrcotkdBqgjyoE4z4KKETjHGmmxJwiJlxDoVjVxjaxFhDdOYDaKwLI/Puxu7ReCl5g/ZC21KrrIYUutRaRXepYScIGYR6QONRyLcbwk+G6jEsJ27kJULuDkMrjNFkyNsNqZv+Mn8m08iJePHyYHSbfJY/12G13MwJCW2Fw6EzDTYTdttoZ57TbQXOmk1W52tat2mKnnbHQ5swhNAaQUZgtQsdzhnjqIFVm1TpXaU7ZJ9ZdTouLhjKp8Mtg4AkbppJBYzOh1gDMHS9WdFJRQ8GQMLUNdbmyyw1od7F1g+VWPWuZ1c9bYVun1arCUhM6oggLI3XvaqLNALoOW75pyB56bq2tYEcxnkX50LHNfLVaWxgwm+gB7MEGjGqARQQu9vNpQUpEHUvW6glnUjGTewFYHqUtjZh0eHeVEMkWYiz4Zr/93VIWXq+UhVzyt3atsq6u1Wh3htx9DmuQCRRyFRFKF+UA1S1tkwmFTbPa6FiSKACODXk74/SZNn22ClcLAGsyUkfwsn5k0tu1PWFjxnM810VkYQG9r/8AdgEV4tZbqiMwD3xARoe/9dZbnPfYpAkTbAibBhnESjcl5/pKpY0c88OXFmaFKsWqiRkTyJIr/6c+/9kRl3u/yLjPHquSwh9u+q1b7BSmFLBRyFLCeh1X5YeqTSqodq712bosAzHGdX3+8ssdIFtNXctv/8t37NJL3mtz5s4hXIt2ldVGmrTbb7/dfnbddXbJJZfYqaeftsekHt8Drdpz3J/vf9UDssd+Y6mnnkTpscEa6mHFyPrKsSCIBQ4QKikQc941g9vF9FzKNVvYD/zbfgZkcQUp3XqypTMHObDFVj/fi2+eioSHwT1eMkoIPW+IjbiyCnOEDzPS/RJKrMUGaeKUBrwxR9usWYT8ACXamOdhIFyGZiXCQEBH4CmAfGTDGcISHwAAIABJREFUM1ZY8phF1jzHpp4C36wFab4iHCdCKNRRWJ4vOAEUJb4QOm2bZLEp8yw0j+jNhNmsLcViSoqIsSaEKiyQcY5Xrzla0aDtf7LE9p9y8DtcEprjZZIURQcUhoMUSSSqo4We/TxfyF5YN6IBZERt7zMbfTbqlRYnVFF1FhdpgiGTav1Aaq8oINONKc0g0USd4gs7MvwdHrHhdR+12sIjDogk8P7K1hLWysCslGlBAJk6SEA0pRpAwu0U1HcAG3GA2KhDHqHTz7FUTLsK9b4sVeGj9vgjvfanv3ajO+/HT4xFph+gAYA44dRRjk248ZdL7PjjZtnBC8ajLxEAwoTQEazeAFK00FVqqKapzwjUOYbZz970MoZk6AniYiHIYwbLrkS/k+YAYJlzoUptHAhTipbvBglpV7P6aUt3b+BQcdgP4VkyLLso2CoFfkOzbealf97cY09054j7N9p5o7ts6uixNpOsz0Ydh0zSPDuSPPo6VlFCU0MMes5tmLj8lIMtcNoFFu+Y4XZfkGxuMHuAjImQe7Jmdcb+dNvTTCZiqUh8KAwByFbbtu3PYbdA8XMYswCgMo02Ip+FbWESzaAL6Bg719rbDgKQjUM/NmwdowN24RumW9sYSG1VJNCgFeml3SHvX78mbosf9vRwU6eNATDXwtToJV69NYXqVAg4oHB2Fe2AZ8joQlehixJLpjqWdZgTZwlbqrmEigoTdpjQooCIQpfSiMlc1oWe+X7eBRfYP1z8tpFwpW8R4Ycvt2Cr8cPvfNc2byIEwesF6Nz4L+qzdKxqkwqqeLQj90Aga4jC9xL+f/GKK2wcVQfWE9796te/ZpPHT7RPYmehgukCo/Jau/HXN9of//hHlwBw6OGH7XQKpfPb3wHZvnfqtzv/aAOrVllgoMcaiQJkASRJJRqxAERZsNMuqWdHG6ln6YeYL7t8pB/sj5BzniosATa4ff1BCILNtuzp7dRIDjNOm+lrUayGqPnsEnPYfks2zHhV3UxpmdUX0z2jmHf7mANjdsyiyTYZcKYh7TSigLgKvtey7gSyMMFvJpHsIRLtlj1iNTnWXIBgGnPwFIljQTQgBRX+5gS0MQ6i3wqFqF0MQMwNbbEU5QrDM4+x2iNOIpIywTLc25w2OG4P/WJEZGic+/6CJ+lPMCO4jbAj1+hcoBZaV+BxJEmu9rpbA3S0PMrlXI7ygwXm/PQyPOMXWqj9UguNeZvlatjgC5QpBKqYaXVJ5NVOLy/5/fsckO2aLq4z3CmtF4bHtdxWK2z8d8v1/gexQoTt1ODKoFeqR+ReruXTM3hIIGKQvAWxwAjFHbuZlMCfr2jrCdY85d+hZadzTGLR+HgpHKjNw7p1KbvzriHbsnmjje0Ya1s7+621JWxHLZpKdacttmVTxt52yVQWEm8gZNmZRFSUG+xekHmrTqxKBsYKXrmkEctjKTz9cCgDUALLONqqOsI97rwlQNbWQYCMmH4202t5Fr7Cc89ZaO0qC/YDzEhgyIAtcyQmFBKbYKTH2WBmrD3cnbVb1662LfFtdiJZYafPmGOTwmusiUxImfYSp/FAJqIEmdHG0ZEpkBqup87CMMkPwWYLH3Wm2XGnwEXVWU1SYiSZwnoeX3mEp0uXDNjd98DO5TkmYYMIWr4chd7zgS6nDdPxszzzEDvXKNuggfgwi30BUNwOQ6GSMyEbim+2iRPT9iYMeBsJa2bYKboBKkkIoFh2IyueGbLf37yUygwtPLMamzK12aZPa7YJkyLcKz0YTTVC+gpl73070AHZIIzYx8kCFCO2mdqL7W3tFLvIOu1dLQCrkpO+AJlYLTWxXJro/UXtsg9+wI467tiRm+uHIP0yV7KN+P3Nv7M/AV7WrlnDM5rqtC0CaFr8VLpJ57E/m65FjN92fPh0fl+66iprHAVTznXe/Nub7Te/utEZvp56yinudWpXwKItXbrU/vM//9M6xu3sA7irl9TfQ5bVTpDle4ey4LMknEQkPKfqicTnStLQZmwkZFl6CKfzE1LwvvJ1JA3pN8VQZulLXwlRP3EFpt2gPbu8y55e0mVd3QhXalSbtwaRP5tP5DeOIQN8OU0vQCeNXjjJpkqh/9F4eHZh6B2OxG3Bgg477PApNq5DkSJdlBdNKNd6+fzGro0WpoKHPYkrQRJNdys7efS5Q8lBa6iZJ52OfIdYUJkHXPop91mFQ/Uh0S4b7oe1appgDQtPtOAhhxORaWNFAwy+GEkPj63QR13rLT+wcPJmEjSKZ5vCLSFNreb2NV7IlM17HmJChT+cJLD4uhhzvfLbAvVHW2QcNlctJ7POy9qBcY0t1IGm6t/ngKxUtLs7nUVAWjEtoIEBy3c/Yqm1/8GNvNHFoNP5yTDQ5QFZLo7GiBeHACE5zFLlMJ8LshMPH8zuYKpFxl+A2emb+Axla6CNyrPoq58iMo/T0Z5+EqblofXOq6qhPmuHHDKTsF6DPfnYWps2bYIdc0qRmpHPjDeUS1jgyh2+0mJTIADozFTFJoG+dB6aKPx5Q5+elIDaFelWFmgRaQLSMHyy5NN/sWwvyQzbO6nrOUAmo7YTQUtlU9QGRzxNYsOqRM7u2dZnj/Tgi9TaaGdO7rCTueej4tyPWpWkgprW4AdYBSW2JqxYIC7q6hQGWyzRGEff1Wd1QwDfSZjunnqOZTomO7Yu6nymhBQFyIJYXnTZ/Q+uZXfW6ABZLhVl0d/O9QHIKOYdVKkpjq2FOYYYU3YJ6Sy5mbVKwpD9QIBw2GZ2egl7w4UL3WvSJAa4tdBpJAb4qBrbsiFqN9/wNEkftWRxUm+UwdreXmuzZrbZzOnNNmYsoC/sZQVV0w50QPYUwOEbV3/DAbIB+Y7BeoZAzxLXxxgT2Z2qI7/wTok9kh5Mmi+BMwEyLV7Sg11+xeU2YeoU96ZSCxKNc58t69m6zTFK9957rwsJ6hjSrinjUufkg71qnlE17xXzp+xKAbJDFiywL3BNngceOdadW+xH3/sB4fYuO/roo+24445z1/7973/fxo0bZ/9EAoA/GZSaEu+8/leKGVVz9vv+va/2kKX4r3CWDaGbmAmjqbSe+qq8HTGGLbjYleaNonq9mHjhrESdzti7h/sPkJk9j3B/8cMbMGrWeTQ677Qs9kfZPHpfSTpAIX6xcbeBwNZD1h4KYSbSCdjnFBZJ8rwr2OGHTbKjj+owclE4mDZS5V0A4oXtFnr8Cau5jyz8ATTHLQVL1DHPJ4isDDA3sykP8jmSNgRkJyIg5lpRmweLhsjZUrCRNnGmhRYeBVsGaxwprrOV8HhynSU3XmtD2660Zp5hmLXHMtMxJR+yVHidNSVP5bMEBLXBlhaZ0CbyG4vyO60H2Q7WIcgcp1L6IAzdZZZvPNRlRwckWC6xQNn3o6X6T9jngMw/xd15Rfmp5YlCv9ND1WYBGM/+2GIJhPja7MRZoKPlU+NDysYsjjV5puRDsyxWx0NsPgt0TDZGMw9MDv70pUAQYWLCM2rMIxrPEDcXu1bItZLFgi4Rm7MkGSW/uOYhft9g73nvsSRs6NwY3so0dG90o7uYFSMGpjrbC/ED6uLqt6XQIae8EaUC55phiAiV1spSF9qYARgeSlj8uTUWJ7tozMYHmFTYLREvL5CtIJPXkERp3LZ8Kmf/2z/Xntu4jN3UFjtpylg7dewY8hp1z7jXdOqa1BToZZgryS+U0QrKSktbRPHwkDKV8h3WXY+Tf6jfxvQxidWTWIC4Pz/3YIaJwqU7A7L7/oZn2yOUtAqJIdMs2Y02Yrlt6oTNcoCxjrBlAws7pZkYMKNaGhgzbdY++mBrqJtOVhuZO1wLORYUax4PnmLHmwFEqD9wtHR2q9PBdW6IAciet/5QL+9pchYdiUF2aoRFx3WE7LCF48iM7SArqLpBcqADstv/dJvdcMMNngYMRiwBI1nLJDUCoCrgBS0A0lkJTAmo+IBMIbyrv/kNrGKanYbMt30otb9wjBl07sP3P2C33nKLPffss65PqJSRXt/d3e3E/vuzSbw/Hm2dTFyPOOpI+/BHPwYjHXeZpFqrOzdtdkyYQph+9prA7D+85S121uvPcgDTB2Mv0Crtzwt7mT771Q7IPM2ttBxagakuAgUfUDhN8+ku+7GdHea9G6Twudp+A2R8/P0PbEA+A5mQbYMdQ/eMeXchNGyNTWg30Q8HuK6iOYG7TGWbuxAm7yUwSdUNTMbZNPUhwp+Dqfrpp8+2Voi/dKqLmsTMp2VaIUEW5d0PWPixRyAtOAY6+U6qbXT01VhLFva3ZTvaYJgpPjfDhl3so6qfKHVCC2E01YC1jUTQrFj1zPmHHm21R57CsjiO8oaARwHDcq3zVsDYNZYf+o01KQqSOowNIMlhjY+SyUnokYovIyyIk/EUywoqjKp/hw4ixEod7EKPJfh8G/s9qx37PifFCNpy3nvQyzQSXpnDvGKAbE+Xo0kvj8g+pMLheup9v7DezveSepuy5uAiGwgtZ68DIOIOh6Vxgpou4NlSALWLRgtm+7wQZfRkyzWcaLHmRVbXfAjImNR1gYWKonuYtQS7jdo4u6s+W3zPBFieXnvDW1tsMskj2mAVcuSCAFQiyiqBBZLtQkTWX4AmZZJIQB7ms0bWNrcz0ZUpw1BfTqXPd09w5qxa1IeLTmHufboHin07Z2mBHNgy9cXcED4vAmNRa2DHZ888A7X8V8t0PwpY7bZt+enWSkixLguiHITxIkUiQQbKfVvydtvyDZYKDtvZ6MNOG9NoNe0pG2wY4p5mrW241mriDRavoTNHFLJEaE0aao3AnNI8qaAwCBtVn2Vya2mznj4WTy6hDjPcwBTc81//VsuQdqqMVT2PNPdIQOuaa56yoYEGzheWLjdALH+QBU5fPCdE/ak0WXboxzQBRriJBawEUgzqmdIgxNqtp7fLWB/t2EWjbf7BIGSJ9WG5NCEJeAb5Oc859DFQf/OrJy0brWciAmTEGgldyk+uBxf2tRR/b0H/d5jNnF1pi1Z+oFUCZFCH9mU0Ws/wXFRX07ngy4ZDCJJr9Kn1fTmcs+ygfad517/ob3IMV/v21642abl6AD+6567IOABKE6tAViXbCQEOOe83c2+VYanKCGLMJk+ebF/7xtUMsfL3VztVfcaf//xn+w0if9lnSMgvZkrWFzWAQ58lU0hQPwsE6HPFXFXrVaYwqzI7db3KDpX/ma7f18INsfgIfMnO4ytf+QoC/rn0T3ncqQi9VnTP0uL+++6z66+/HllDpx162GH2/ve/38bCkr0Sz3df9p1Kx+7f3m2f+cd/tPrGBvvuD37g6QWLz1xLc5AboPqgn/3sZx3j+c1//md3yJFyZ5Wc8iudQKW/L1tiyXXPWQ1zi2x+nBFlPUILZYmz4S5EyldK6AbYtMdgZWBB0/UtFj3xdLPxkzWsmdOLoiX/HDQh+yVx1e317yo1SgPxLBvLZ+l/tUSE+pzJdjo1CiAF85/qJMJRfsMSIuqQI6oiKbvmyGymx84751DkG3hA1lRmyGz1Eovf/ger71vP0hFBM4Z1BMGXHDriYXTTdWyGy7UQa2cOJJUZ7geY4Uk5qsOC511ombGzOCcsmCqUzgusugwh/8Ms/ZiV8xhQ4Xi2SPIIldenrBHKtDx4ALmyqyATyUyzDOxYZP7NNhiIo4lmva5u+q/U+172v+93QKZOnac0UDBAz1YGR/x+S3RehWnpHQAACfQQpDsdEJBfgAabjLRJY8bL+aqJvcPCDVMt2IodQzPxbtV2pGNId6RdRA3MUvknKodn0nNhi7Z15u2Gny23uQdPtSOPw5cMwB2Tgh7Akc2ySFA6yDm1KLXXYatU0QNP8fIiHHNpyRq1AhGAKm3c9CcnLt3Bg7mkA3YZcm9RKzgAJv9j8iWLjJ8zumVdb9TxKHNkVDYYXvcUwAdeL1YHwCF4N0ytzUTchij+PdzYZusHhm3xyrW2FtfwPNYGZ07HrwbwOhltWqvur7QAgKckYc0096ZJORMgrXQUxgr9RQBtQpgBGQlz8aQ2p/rXWmTMWO43Z0gIFJRlmZYpFqWcUnb24RYDiAqQ6b8AGT8//elTFh/E1ZwLzDEomtB4pfE10wAPMcDkfJ3jenyGJgioTJD+2tQ0ncFYB1PRgxFpwl5/1kw0PXruMoX1POxyFC93pVDI5hyCtf7lzx9HQ9bKIk+mEtdWU6PFdhCgMWwLDp9sxxw9xQn9q2mVAJl0ElfjtyWz0O99/3suG89vTljrxG/7rmnhK2Wo/ExH/V7lkr565ZdcNqO0WgoTKwQp0COAIkC0p1Cbf8bSmQ0BVpQZ6Zzueb8+b86cOfalr33VaVrKNWV0qvX199mdf7nTfve737ln2U7SjFinKMBRANZZTzBY9HkCTgJl27Zts2Zl01TRdL0CX6UWFKVsfQsmsOvXrXOs3VcpWN/AdfpaOQFEgTIxg+75fve7Tvh/1NFH2fnnX+DuY1glx17DLQAy+cTHP+H68b8QqnXaQwCP82/jugW49ysge+iX1vPUg8gpqMWrja7WCMB0ilJ19ehPs8nyK3Iy0mYNCmt19wJAxlvdaW80m7mADaaUqljxsONUeFNTpyMPXD57Mc7J76s1Ht24ZdBu+fXzsGL4MQb6kWYIkOE7WVtHckInoLA8ICqwSQ0g3s8iAxGTls/22HHHTrYjjxjL2qexV2H+eewvNnTPX9nsd1l2VNgBsnqBMNYCToO1oDyFrtBmrhnpAyHTWA7dG5q86LlvtByZl0p8kxinXAssP98SeI4FA+uJnghQCg8oPMrKKM23jF7LNG3oWTYcGxpJ11qyfqHVHHwvNlB91iB5UnWKlVd8ZO9/QMaNzAdZUBGRqwxDINuFSP0n+IL+s4VTKuwtY9B1kEwJV3pJzFIwNg0n/hPw/oPCar0IHRSTdgw7B7YrJKa7myiyLcRiEfW53j3cWs8puRcA12J//kOvPbfqcXvnexcy8WqoKX2WD3QFVNMAIDFYMEkCU/wqRAYksTx+kEDUi2WqiLV2BjJ5FRATdep1aTFpSkkuaa6vErRExJ+XZouB5ah2vpwgUs60gzBfKr306F3E+HHVbyPc1EgYM8EukMycWAt/H2omM7HJ7o6n7YahddZZ6LTz25rtgrFTLNS4xcbQMSP5dpiNgC3r6bTt8U6biAHs3PYxEGEafL3UqUQYix4gFAQYa0DmagmD1hCq7XPGe0o1zqE5U1LDIAL8xLyjbNxJ5zuvN6Va63Tj8TAlZdixYh8imjuIrq8B8X8iOQToTTLZoJRDyK/74m+cckHSq/NN3MsONKPSlOEjU9NrF7/1aLyydFSlV6tIMPcHD7gg9zpIdg0SOfvfnzxkfYl2Jl4mAioBDHKvxnZE7chjZgAYWpiQqt8gVQJkekaXf/GLtgbR+ve+9z0b1czulglei3gpm/CKj2w+UEzWJz/8UccyZemwYrecvgsgoXMTuKoEyJQdqde0tbWN1BPVtRw0f7595BMfLxVU7v4SS+Zjacd+9rOfORd/gS4xYIO9YgU8fZo+R3YaArLK6GyEcREYrKZ5HmLycQq4zFCZcPp1LV3NSvqXSigp8eHsc84ZAWBJNjQ16kCafQBs//Pf/20bSIqYz3Vfish/VLNnHr3zgK7mTF+d7433DdiHPvABayDR4cd4rzkPuaJ3nRJCIqTx7VdAtuRu2/TwXeidtlqjhKbMzymASRaj66amWrIAywOCQWohN7HJGx6EERoz29pP+wcLTD+cpCWBCRgiNo+O7dN0TF/RTAcnxfoikQ26VaQS1bRnsbj4480YoBbaASHMwfgxJhMAMjJGUw6Qld9RZplXY+gypMNVKDFEXGPy5IhdcN58NhbF7LVyJ3jHdZZ4/GFkDKyvjbgSkA3XmmNTyXXnokWn/jLvDyW5C63cZ6IgkRR1bkJNVnsumu1ZC7hvjZ52r0wLPDYNmcl6NmkKzQIs0QvLHiAIQZCCMYy6CjR7bqE0JtX1CcLWbNYhSIfrJ1rt/KU2HEtTRUClFqt5Oq/8e/c/IBOmQdCfVeogOi1n3r7tHsts/QoGcXfK9QFdGBhB/nBM2oHa16MLOsfCjadBicxydmCuugNvU0RPGEYkCgmGbq6sxFgmqasZgx3bsLoVxmW1nXpug02aFiX0FbOWVgpXc9CWVoErlW0AZABStCtMo4GKKq9XOzAnOJelhgCZxP8MZh95CXzwZw1ofZGozPsJwymUqc4WJpyCmD+dw1NF2TR6n9BG9ybL95E9+dAfrNANM6EU3rpGykZkrB9WrJ4PiDWifSNF+qHBfrtt8xbE0gk7qXGcnTF1nE1oIe6Pb0sEXZUM2DqT9fYAi9Ndyc2EJrvtTU2NdtLoKfjvxhHyYwar/3Dyd3CQtOokoc5urDMmkNKcZFGUzWsQt+bgKNgv0p431462See+FRbuoCIgCxAqJIz4m6cAVi1OXBtjhzawdan19nbDkAC02c1pwvGy9HRPAWckHbSPngozMg6wW8vkItDWbe+7BH8Z/FCcMz9hiAgUdhaGLOj0FHouZv/zX/fZdgrT19aSFADL194etnnzOmzOPAljdd95fZUav0qATMD5S//0T7aJzMGrr77aMShpOHd/MXdhr33YxFCJidU9lT2FmlghIV6d0+Wf/ZwDYwIhYn4cCBFLShMD5L+n3CkKvIm5UuhPxxKzdiQs0Qc/9CFnwFyuSY8lIKhQjJC4rCN+/etf229vvtmd57jRWBLzO4VDBZ58A1nfs6zaLEUBCIVm5aXm69/8bEkB0yDgQnUtr8QQVqE4PTedo37WOT/++OP2i1/8whUjV0WCj3zkI47h0/3oJZlGpahey60AQ/Z/AaACZN+nOoHbTRUfeRp2IkqoYL8CsvtutgRWDbXavLs1hA0lIDGLuXRYZlZILsq2MNIW0SuErq0V5/cFrzObNIt5WaJj3lk0v/bowOKRXLq7Qh/6XXUUzLPPDdntt67n1NF6weoEQwhh4oTWSeJKZTfDIJcXweZ4BnUNRDJYH0QGxABWDfUD9u53Hu1ZXpQfnpa/5d8t+8wTEByUnKrP28BQxjrQLcsaKks4NFBiiLy7+xhIA1EBZHmqWkSolYQgwerOvcjCsxdyD7GmcMzCnlvuQS9ZzkWcsrLkwYg8xpiqGbA46xR3ouz7Q+lxlqvHowxpDISaDdVGAGTrLMHa05Cb9HdA9pInJ8eQDXuWngAyeV4FWbyz239sqaEv2RBrR03NXCaEt1mogfh+PaFJ1UOUyJvwl6oVurCgsiB5sr5AXmOmwA5O1Ge5lsptZHfUZPfcLqPKYTvp9fXogbps01p0QLWbyLRst4mTG8ncay0yXKoMoDg3xbQBLs5dQZkmQbknCwQAbvw+KIf6NDYUEZSSFANXSSe1gHZdADANcvxTPeNhp08QI4Y4av0yS6x+zIY6V9roTRvQNNCxmloARnhskTHpMtVgDLcMb7c/PtdoG8lgDDen7PiONjutttlaFSYiIcBpwQrT7NGtm+33ZJGx37IZhGSOrI/YfG5hBxuhYbRnoZbp1pdttc3r+yw/OGQT2wJohqhOFuhkoFMuCmZtFGJTBxTlKTE8ZBvZyUw89c2WP/oUrk3h2BDu0kN2x21iM1vI8EQYjSHtcO9T6M+6CHd1FcOWPkQWUiWsNjyO0ORcwiCj0RZJ4J8FACfs7W85xAE9QWrhDGnr8gh2g8411/OAu/FXD9vmrYRuSYmeMb3Njj1+lnVwqwWMU/jX1YkDl4ltFa0SIMskkk5DJp8vlQqaOm1aFZ9W/VsLgJoAgCEJeFqNP9O3v/nPO8LD2ukDrnyfMAETH5zt6ZN9XZdfZFt9TwDmtNNOs/PfRHjnRTYBP2VeOqsNvt90001OV7Zp7XqbNHmSY8xUlFyAsQnmTGHRbYAg1c+spvlibb+ergCegKDOQckKGpPveMc7yOi9EIZV9Tq9zM8NGzfYkieXsMH4jWPwps+YYR/72Meclk5NoE2Zpq/1JrH45z7zWach+9KXv8Qij84I6YLCzK+GkKUsDzTNxqQ3crUXmZPZoBnMDbSN5yZfpuWopSsdFhMr9RnRYeFriNtc0T9fx2AjQx9hn+hNc2qlGKzKiPXKVQm77ZZ1HLQNPSbAAst6AbJorRiylw7IavDaHNU0ZO+8GEDkAGP5Hpr/039ZdtnjbNYygBi8KYeyRFTo46ydGRKq5GBQrgmQ5dtgyABkMaq0DLHO1Z7zZgshZ5GZeCVAVniYUC1gykvWJ/RJ2DGs9T2atGEsOGIVPn8EkOGdxhJscda/uoOftWHKAtbnJv4dkL3kCcrVHiN7y5HBhKYUtlSW6wBassQN9Ke5FqnFv6huPsBniutg8uaSID6Pnizmamh5NFTQubSKGtG/9UJ+KUe5Mo09gd13b689/LeEnXDybFu3eSmlKyjzEppsfcPPWSsbl7HjwnbmWYdYo/y46KhRFfXmtJ0fmOKoMDliY4BoLuzq+a8QakmsQWBBOnEdacD1RzDgoGTF3KlIsQRwjPc4gMxlKLOw26aVVljxBGnAS0lq2AJdLXPXQ8F8PQAwaSQKpAY3Wy9vunPTNrttw3qL1020t3d02Hkd1MJsilMiqRehPwkI3RFrgrH6tqh1aqA1o6c7qLHdDmsfbZOpPcYHMIENIj/rs9VUQ7i/K2EruwE2raPsrJnjbGoNZn29z1h33TR0EiQEqIg3WrUcQK9PCQztM631mDPMDjnU/U508+JHN1P7swc2pp26lH0OkEUBXckkCRM4S4ex5Agz8vKI5OTyrO9D/cPc38lYY2CJwOBLkaRx8CEtduapk5icZPIK+1PchcoiQzUw9YxlFvvwA2tsxcoem8b5zpk9DqbNK74r5a3TDKlfuMq9e98qATI96//93/+1P99xh13Ion7EkSQ8FFsOdiEmB8d92ARywmw6FNLV54kxi2FtMUj24DpA4q9v+JXLJPM/ldnmAAAgAElEQVT9sVxmc1Go7uuryp1eaeagwJgAjcDTAiwijsUGohbbkXItgrWGWCcxh2rSIEmPppDgE088YTfj87V582YXVlUIU6FeZ4vB57ShMxMrV00TaHKaOb9EkhhCn0nkM448bpF98IMfdKWRBMjiQ3Fb+tRS+9u9f7NHHnnEJS8cRxHxC84/34U+dY6650oC2Eaos24/Z4lWc29ezHsfwA5BJriHH7HQPvLRjzixeQZWxlVjEMNN/9+fDBm1XNAZ4R/JMJNtTwqBewitL5Cb+Z8wHBmX5VpY85oDLWz2nKzExQK8OYRoRg8yGI1gBTA1k/iriXIHhpiz2zzTw71uGzal8OpbxQaBTQifFmRCSSUb8WVE5E9GeUjp72VaJpNgPJKZKYaM668HkM2e3WBnnjGFcU9WfgWGq/DIzRZ/6D7YpD5L1uqzqcBCYpgWuEwNlhc7++q+4Ezkw54ZBdNOyLImG6XWMZmiaMgKU+fTN+RiWSFk+dTpyG+Q4hTWq3oeOXtKPGLR5WZnMtudCW25FoIBzdRynpAFIUKm6cYjLIqGLB5m/cEh4O8aspfcNYW+fOEiWYKyl3CZHQyHzGJ8WI7y4o7yGcOLRFmNIV4fInavyUChQte06EgNr1CYo65f3ELMukW23nIXfjzsqLn225seQlgYhRmbYn0UyB5M4p8V6baLLz4afRIPnIWkXlk5agKTeuJwpUTcIbjIsOM3Tk6WXW2pgUct0HmFRRvfBR3+NtT5s1wygpQr6mYBlYVSEa+uzZZesZQC4E9Q+mgN4kjEjFKjU5g5OciOiVBdlok/jTh/7cat9tTz662XsF395Jn2pvZRlBliF8cOIRdHT4aeYLCxwx7pGrQ7n3ve1pEw8fqWMXZWc4tNa+CewI7lqQ86wE5oiNqdzwzFbHXPBts0sNnaRsVs0YyZtgCAV6v6Yf1bLYHpai0F2XO9ZLMARIcBc3kAVPt8apiNmck1NbpkgCDZQHfftRpjWPytYh3WA8tZTxXyQhK9G4MlwE2JRlUGhNAaQFlfmgkjQbJzalusf5DqCYSPhhLb7NxzD7Z5c9A7kfqdh0kMA2TVpGeSDk3vDfF527ZmbBMsyrx5E51PWY5khQyhhzpCu8qwlUBUkYdqWiVAJqPeRx951IW1OsnAa4FBUQjOeRyJrVIMfR823RM135ZC4EsslEDDRjRPqjfpuqoc8vkqTe/3wVm50xMIc9dS1JzJJkIZm2KxOugnysAs12IN9fa6173OLrroIsco6TgS2Xvh07xt79zKmLvZAVoV9xb7ps8QKBM7J3aqmuZnWeoY+lngTM9HGjB5iX344x/D7qbDZckuXbLEHnroIcd2SsM2DbbznHPPpYj44SMhaJ2XWLtrrrnGMXw1yvZ+DTfdMyVgnHXO6+2EE05U+uQI6/JqYMgUoAiw2asRQ8Z4U2XEICCMWYY1QfX0KmyI8NlKsnkOwcwrm98VplZigAv3KYmJY7iSJwqL8MVmR18s/zIhQiNbXcgSCy+yLFcA8GvIqsQTDNuiTEoJZY3kTwHI5D5QpnnJUjKR9TRk9WinTj99rs2bSxSB+TAg66JybeNi67v9NmvswYuz1rPTiCk7kXUj3ogqmzBkuRbifqUamGsxDq8XI9aMVdG5F1q6fapGHARLBUC29jOW6HqIz33QavGN1NqpQuOGFk0Mpx9V2tM5BAGdKbI5Qpl6siynWL4Z7dq866nNyflIC3eADc/9riFLUzoiCpBxQLpAeA5huyHGc/Ud6esRbB88Mzrla4j9gvfwS9MXx4jbrBUjYWIIMnwpHV//VVqP/3p7xp5c+iThl5kw3rX2wL1D1tn1pLWxjvVuhhUDkHT3PWvvevcxNgkGVDv2JiZrnW+W7MEw4CYPya1lMZeFrtU41rUknkSwfBvhx89jw3GWhTo+Byt9PKAK8TJ2Gfp/VCpQ632WeeZJG3jsbot1Pm8NKqZepyrmNdYfZ+Go38QBJ9rania7bfWAPRIftJYxITt1YrMd1hCzCYWV6LXYvUXbEdI32zOruuzup9fbM2RdFmbNsG/VN1tkNGdXsxm52npwLUA31GaPEXK8r3fQnuyeZlNxeT5lbICvGsAuExAC117sJIbbxljr4NNWG2izRA8L/TSSKKjxabOoWdY6w3rZEbWxA8to4AMQ/3zbs3hNEbRFD7aN5IH6JoBnuI2dDoAVzzHvGREyY5RJ9yRrAepVMaFQOmQwQ6iSeqN451zy7oWGvRWvZ0Ay4KXl0bhOCGyh6XA2D0GegeZgdQk9c/pJTdHNWhm2KFJdeZEq58uKtSwLhIfFNCkL72+Yn8rWQXoohXQEKJSlti+bPsc54xczJfSzb+0gn68NRf8snYNveyEWyi+HJD1YueZncUpsL0sPXauAncBMM6BGn1WuxXlmb7/4Ynzl0CuymYmymDldmACiuG2XfRO05/EouxlgthjHcIEh+Zyp1ma1GjK9X8bBAnYCqmLiZLkh5mvevHlW39zEZy62foClskjFgEkjdj6M2PEwYwKessGQn5rum99u+9OfbDlFrVXn77XcxIYe/7rjbd4hWAlp9Ar0wJ6oCsOrAZDZE3+zxPNL8fnGu0txCkUosPExtMF1REfkW1WuBUgCSJNIlsUoPMScEhiuYQ2vJ88K01U2muEQYI1C3dInJ9mQRjrGWf1MNqJjqbUsO54q8yw1O9z6m/UYEwepWrLVDeNMqhkJB6W6sts4l/IrWEBxOmiAjOp2Mg/W1VJd5i0LqMih9RS4qrrL5Vpmk/XfdKPVrX+WZA1laTI3QxTI0qmvOW+jpKkp08RQJYkcyfaikTXIRiOvwfYiUT/epRjUVwJkW35sQ1vuhCW7DZ9E1lPJ85wUVlIFkrzABxUuwPAeZ10juzNHZmcr0aiZ37c4Wrpa/DAq4fFX29jd74BsX98QlxasDCsWxjB0iUBdEuuGGGU2enqT9vOfrLHZ86N20qkzCJHm7aYbltrmdSHHLAwnemHJltqi4w+xoxfNMZISi7tDDSOxBlRODGyDqoUalf6LchPDZCXGCCtC7ljX8/XWHj8IRmCxBRuOsOCUX1ihZZYN4DYcIZuwLj3eUtCtseefNbvnNstvehL7joglWDxyiQgdFMHpQNqegU6+Ycsq28BCf/K4mXYGDrZjKZlksEk9ze1Wh//X9l6KHq8btL8CplpaYnZ+e85OqE1B2zI5qegknT2FZ8uGrrQ9tjlid8Vb7KlQs/1gwnabQDhprOxBkhjnYkpITJZkCXY+XGXNeuKeU2dZ7pCjMINdACuGkSwTgPJBg86sR7Um6fjMzr/45TJbtz5BSGq0m8AESJIylSnXuIdyjq/l3vWTxNBOMsI733kUWhXE2AgKlfDgszN+rTmf7fGzn/zDC6jpb05ATtPCoQVT73OhPWlEilm3pVYR5U7vq1d9xbEn3/zmN23ipEkjBabFtIg9qGT7sK/7t8Kyrtadwrkuhu55QGlxuuLyK+jL+AtV0QTAtAjrWv1sTfl2Xfr+SwnPHlnRh6zSR/vPQ89NzNXGjRvt/vvvd0yVAGUjJWTE0sktX0BN4/L5FSucmaueoQCpbD0EopQ8oO/ShymkKEZOzKGyK5WMIJ8sAS/nOwZwbh892rb397rfK6Q5ceJEO/vss+3QQw91YVmfGax0Df8//z1F2C7CXPGed78H/8AJ9s/f+pbrL+qPbsO1C8GiZ6znpPHojHb3AOgFLpQR60m+vc3EbuuePnSX9T3+qNUMdcGwkzTCnB2h9FCwjhB5D+Iy/BSraangWhL4x1gcr7U0YnP5a43BgzHV0GEbmbtmVAI8FT88ZevWDKJX7Lf1a5GwMO875U2M6iNEXiLKpncFZ70vp5Nmvynphn7uT7Pxk4QHW6Y6dGALFozF7mcMWZpsUlOsMzFPKrDHxl596Mn7LP7oH2xMYi2MGhM5U3a6QAiVeTySpAoMiQZ5Pi/LdyltFIVScFeFjIOFQWse5oQJF2Y6xtsgpdTq5xwLywagki1meV9a0Ge/DW/6V8tu/YI1uaUCWRK653ToSYvHOH8WIc1quDQVyS70bdhaeUIf+hcelljrWi//DI97lzWO+RQ/YAjP6eUN6QNr8YHUXvuATI6+Tm+GRgB2KseOSQAAqQh6p+W2YW3Ojjl+PGn82GYQ5lq+dNBWLB8kgwonfzRWUyZiWjp7ok2fSX2xkXik9oaex1gaJ/pYju2IygRoQgDRR8iMCQ7lrXdlo7UkYPuyawA4ZI+IJRt7CbH5yV7yQZZyUfh9BbuomfH4vZZa9jfwDTsyFoMcAsccJmRRnNWTDTW2BjCViudsarTJmhWfiwpksSvqTdlNq/vspi1Q1zg7nzEWIEYJjUlMHqEY/mHRLbAQbdY11GgPdFJncjOh1FyXnTZxjJ06eQrGf1ggAKqybE0KDLoIo70GP7EwyQN5kgiClIyx0WQfTYQd43s6QkkkDRDdAnndBobxjOJ0yNT83a1LbdNmsjYRdg4wUWtx9MHRngZFkrpsWoybKTKey5HVOa6eouIHu92esiydgJ+/+xogHUcLsZoAVgKBsb6Xfo6X7epR5e4+F0GYFgE/bKcd/otp3/nWt+3pp56yL3/lKlgVTRYKiwPwxUTpGBVEsy/mM6p6jQvTv/AkJDqXWedAt2bFvW9+6SQxabrvWiTFlF32gcvsMADZvrh+eZApq1FA6/Zb/8BYZGMEe6XwqECUjG4bOQeFOPVa51+mMVMsgi42TM9Gv9N7fTNb/b5PxyAEp9fqWGMnT7TDDz/cZiDaV7jZL4AuRlB9xGml/t72fAfoftIFygpkEhuWL1/1ZceCKnQuHzz5tGn8+fdRvxOI9hNL/DHp1zzWnFG6cSr94FKLlhFwdv+11oMoPYYxYT1yhqG4JCY15B61WapvyNJt1YUU67rZMIqt6iPJCZAXpDxQ7CxsHRrwxdwN4HzpXSWFNDeCbrHLHnukk0hB2JrbW8g6LBCZ6baoajKqVJKsIJib5XGZSg0TLRh2/o5jJ8yyzm0ykE3YgkMn2BGHT2DTwlk43YziNhUqYYhb6Ou0wlN/syEsRMJ9G6yW7H1Tke4U9AWREhf6JQrikroknhar7TxBQUltJGZ14hUKQA0fd7LZwiMtUdPKvEtigj6+koRWrxm8zZLbvombwN1UjuHUI6w3RFoLoc2AUiruKDmDNcty6/loalpKIu4Fy0gk8BwYMk2nW2zMuy006kImfMCgHjvuDWTDvfRHsh/f8ZoHZOLV0zjDq5A1+B6gwy6afdeqlQW75bd/s2OOnW5HLkKDFBmE1ZHzdBNFxdHXUzJlFJqqNsIyzS2yVdADlnCUvE4nMndiMAAYjJjShNWxBdjRHcSE5YnJD66dTzkoxOxZysvwOixTLDr6uxZu/4TrTOnQCl5LKSIyfGz9cht+9K8WWLmMhQS4z2Iy0N9jTYC8vHRXyuoUjRvhsxIpe5Y07/WxnP3xaew2cj02AzB2GtmY8+tVQ4zBE+2xAfxZsvmZ9uCqbXbX6iEbJFR5yMTRtqgtZXND66wh+zzMMMkSANQ0NhopBloyrdAMoK95LNmX4yx70kkUF6dTk62kjFY1yd7cNKcxHwHkoHMQJli9FoPaIWU7hG2ITExNrJmkB5721NJMMOkE2XfyyOImtrXVoR9T/TYmA9kvcy+d7ogFdHcT9e4UCn7dOrnVa7fuXPNpfvFr/awFV8cVW1KufePrV9tTADLZIsyYOcNj4Jgg/fDbvgAkL2U+ELDwfLa8a/XDatJ5feYzn8HfsQJDWeHDtJjGAT16PiNaMsDZB/CmOmLRMVUzhH7dS1dAuSQkqNNyizeLew/i+fUksFx37XWORROjJYAmxk4aOrFgfjaoBPt6ptKpyTds2vTpzjtMQEshVtXvnDlzltOBKdEghdBQYcrSthMDW6HW50t5Vq/F1wbpd7L/+Mw/fsaFpT/9j58e0du5TQu6UH/s+vVNdR98ICZwtrsmVrN0E+YzZH6JrpH33HuDJdatsVq8DjX/KnSmKi5ROfTjy+iEpFW0odYxzo4o3s9GEzPuAHNi02FsUtsEGpg7qqz85aqPcJBBCILFlE9aunQTm8wIm3/OH9TBzAy7C9tFyEH6rhCJUbU1hN/rvVrAfb1p5rIemzCphlqrs3DoJ1qh6y1uHCvNT0k2tDWaHjvXWf+jd1ty1dMYiWvuZUIniWs74z8IuAtzPIIYnKkq0mgzqwvnjfi9DUq3N/tQqz+eJK/2cei3qEcsU3ZAZMXKdXo8AbTPA7+2/k0/sVz/Y/jCMetT01N6vnhE9aelK+PLUze4jw0QRg5Snm8Qa4xw3dFW13oZZZ7OZz3yisV7O3HNfS9u411FF3lZ3/r/BSBzmiP8u2TZKqpzqD9g99y13jZvTNqb3jKDUKSeXhxTPKwaapxvAuFKXgkuGrFRUQYI5Y202Csb0AdkaRiiaJ4XymkCm4kMArKonPW33m7JDW/RsgJgmwxLtZ5QJP2k8RSLjvlPqgrMpCoEzsw5vFrkYE/HSjz5gOX+di+uycD+ZhmqdlsWnVgWFiucpLZZmE5KCPLJLd32l+1b7TlQ4vSu0XbknLAtmJS3ZpIM8ghCc/lGfM0wY0132s+ebSErkmxUCqcfNrrJjocFaNGmn+KtycKQBRrImOLjQiltSTDyY2c5PHaCRQ452KKzZ+N4LLsPb6PjYI0wUtHrzY0SBivkndMWKYLpnMw4fhKtmkCwc64o01RLneIAsj5zFRD0XaREnklIEThNRqWT8a6H8l3XXdiumElXqmvybR70vZQV8xeJSqPpW9hGrMI+4vNf+IJbcPzm7/ArTXiVjl/130sQadZlv3lb0u3bt9mVV1xJBLq6kI3ufQIQJAAjwCQQpGu/jMzEY0/As+llYAhH7iXnrYVYTc/QPduij1scpvhzMH4KK34MIb7+rrBkCsAZZAHISMtX9FoTm7d6zWpX1PzkU04mqeAEx8q4axG4BJx55cw0jL1NhtPh0fw+os8WMNsVJFb9vF5jB8hw/8VCfhFzZCVkaBPgm+a6El4yIlaZLECzQtACvz5b5mQEmizEgMtQuzjW/Wxed6v20L801p1XHaAkQDm2Onk7AiLS6FXzgMQadKkIq1ylk2paRDosl6TF5KREMc39MXRnABL1mIqAo8KH52GfAs4wTMC2QOhyHaBsC307CsvX7KqgBPl7iC/1X90nEQxeCTrMTxG0z5k3lsz0DsL4MaQfXr0X2X/k0JVVcI2wuLzLVHhJG7dtZDuveMqSz8M49m/Cgi1rw4jRRGWElLGpCiskNmh50+3QEIohewktWGjBw48BjDE/ZqXfZWOPKa18B5orACI5NDkjBAx6U31/tnTPrRYavA/GkwxL1qVAK3OM7rS8TVivZOekoEBGmfYA1tSYyejmLsQJC4N4Ik9Co66OpT8vVvn8q+k7e/Pe1zwgox8VhX1k00iEz87picfW24P3r7VjjjzRDifq4grCOzd+PUUvRKENhtYD548iITq90Gka3I7BY8gEIAoAvbAMXgVGwmRDUmopqlDk+u8Cyq6mliRmqIQMw3nZxm9EhA5garqUUpuXWaFptsM0ST4S4teinWtt+K9/sciaZyjKSueHAYvjXh+uYzAy2Sj42tkzZE+s2WpriNsHJ8ywyxpXoxeY4litdKYPi4xu2wrN/VRnvT25nkmwqc9OoITIGVDLY5pgzoJDNoRoMxYEdEUmcmXLLImWTuWpYpPRiOGwjDGYZceNsV5o6VEIGpSkQPlLb+elCZJ/ZAMCuIAu0rKd6JmRH3BljQTSSByA3o6SMVQprUKspXdkqUW8xVEf4QOmUlZLf9O/S8vglIq+/XCkPxAU8nIsCEyLtEliTCTEXrqUummwKEcccQTZsxeXHTeXf+GLton3fvGKK2BWEPNqeuB4AUCAmLJKad17MyhfyntU1sUBMRa2BKJ5x67ST59BJP91SgHxVF7K4V7wWleCiYXTFduWzgtApt996CMftqOOPbbq63eLTAmL6T/jESBUAjjf/a532dSpU+0qak66UaksVp6D2MGdEgb4m3R/Mux980VvtovefNFOgnz/IgUYVEJKn+WHwHYFYpUqGVR1c18Db1b/U/LFpz71KcdCXnXVVc7WJJEA+Kov0v00ljUWf/rTn7pyWUoIWbhwocvSnTV9hmccvAtTVpAxqYrWE/LcrXaseO/SAuGa4/17WbogewR7VW1QWfyAvYgT8GuFkLV33hX1Vhml0VWOL8lpVErOgVGyQxPxgK1dM2ArV/Ta9q14oxWlMYoUFrBycoXGYbD8rxmTiSgcNAmGTH/TBTM3ua2zNvJcfqUkU3dNHt8VlA66e4sNL4EYWLecLEWSaihL53bcClfyPSc/SJjCAp6RaEqwmCDbHgucOGWnUHRaPS9ztxySIYW/G6Y3Ze9/Vg6kLmqlvREIbPAeS23/vQV68UajHmkmtMx7v5+sruksiEQoOJH7hXxmyhcIq1JAPAoYdGFMIkaAt6CiScqYrRQyrap3vPxvfs0DsqzKHaEIdIBKmY2kXTxw/zNMDL32xgvko6TQCB2ySD3LgV9eWdo4Jwm31ZAK7IXAPONZr+l4HjgIAu9D+geZjrnwEJ+BuSmZgolV77TanrsxG1TdQFz1bQEDRcBtsfMei479tkXGS4AoAaWoWBYGymBkHn/QBhbfYzWDm12xVYsXU3fpaGmMTuMsPl09Ki9TY82tE6yx7j5rHOKFuOMT57TFFNf+K+zZev5eUzfe/i99tQ7GrRkNXQOJDC5jVTsYrjkLcKsjmzMPaCnMnGORuWRSTZrqSiapWK2uMyaGSyPWGa/xXa63xCzFNqY4Zp1sSmjeoioxueSelBhh8IYoO1BQRmyZpgVRC2sMKxEJr8U+JklF9xmNUpDlg7FSXY8AQo3iyUUXeL1GLEtXd5dtoXrBbXfc7umPEHH72YcKV86G/TvxxBPtzDPPLHt+3//u9+xZwI1CdCoq7a6VhcK54atVh3eqH9E8Dv98/JClAPKyp5fZtddea33b0V5U0cRWSI+me+fCSDwvuelf8t7/40T9tRiGVtN86w2BZgE9n6EqZTB99uqjH/2oY8i+AFvpA6c9aQFV7ugKQPS52Fa89a1vHTlFH/z5usTSvqRjKiSq5AG1Uuaummt8Lb9XtUrXYBPyVYCYTJGvBAR7mblFtrY4PmQJI7++Bx980I3tEcYVBlfPdC4ZrxqTk0hlb2uj+oYvJSgZX7tuuHRfsbJ2a66y1rVmi7XSRlGeYgqvRYta0r1+BvKcZO4LQ9c71l4JMwCXAJvNl2P8Z4i6hF2oXrpXHV+lZ2C4GbYbWKNWrt5O1naShCf0cJiw1qO1HdPRyj0bjeaxyWZOlW+X09kXjbSLVkyavzVvV5ifigEPdynO6UMsg8r1bd9MGLHHcjgAKEM+AytX0OYH4+SadvRioxGqIR2w8XNcBZ1+7ra23806oPPzZrVlzQpQfaVcyxX6WY5GOcmbbmmAiJMNPEKC6DLeS8Z61xWOFSzggBBE7B+LzLFoPaL9+jkwYpxDPVYsWmMkJ3IqIpg1rUEB1kPsq/4OyPa65++bN4oSFuWbA3woBCa/2A0but33SRPbGLI4EmNGp9qTYs+lSVGZHu26HQ5zD3dnUCH2TLtr9wKlwzOoVMMpG6IuY4C6kYlO618x30YhBDUcg0nepMNMt1rVJcssc6xuftQ5Fm17v2U7LiQ+7x3K0Wz9m6178V0WfGaxtUC/q/wEtuv4vBBeRI8RGq18ZuAQoRfZZuTIcBxObLH1iW57iJjovT3sNlNBe8v4WjtzCgMGH7VCIxYW7F4jfHAU81cKBFqyCXq/nSy02CKLzYL5mTvbUnVNMGziVPDXUfajl0zqNZddI/Zrx3OScF73QsArgYi/FndpT+wuUa/+xn2tQBnLAsMLSRaBhYqeM/G4tHp3TzzvrFJdj38GLmMLMJZAVCwWS7vvFWTgCUBtkdkoqLN9HJ5q6PEk5FZBbE36Ys0k4JamqFL7DWGvX1x/PYLZQ50vlR8OrcdfK8XxcxWKa1c6frV/j8HSybdL9gyu8DPXKtsNCd8ffvhhQHh1xpW+q71fVNyVaAL0nnTyya6+ZVLxiyrakYA6CerVSjVGpeJvPWc1ATFl8n3iE58YeQ7+ewSytdDrfWK8dP2XX365A93ve9/7RhhX15WLYUr9LODnW4WUhrqVVHDPPfe4fvf3tuc7EGRsrl6F/yCs83nnnWcX/cNFjjn2S4bJqqZUp6nwphIxFi9e7KQAm6nUoGenDZOYZ712CiyoxqmYtEOOONw9a/XrXaUIenZRStqNKLzlC0kIs9iZ3CYaz57qHh/zq4vPYb6ciRPtoMYkYtsdhuNVMjDD3J/aYm1Qnaj29gq9u6ijvhwz5qZBTxbGn1SWz82rfJe/oxs7SQCwjJvFYDndo8gHRyeVvf48BIRIBfl9abMV4bNlXO5ORPIBHPMZIE5Xog2zkF+hlkQ0vsTe6T99msyCa5S84S8QCkMr1FkhZqpXeeXb9bnFU1UZFiCeM1dPTvV+ScIX9Zlc9AWneL6KYv3i5emtTpKEJi/sxncx6aDKx19d53np737NM2TK3AuB0onmMGGzu+EBuedObH44TQo8ACCAEV4e3yrneehQiCZpOirhR4mk3fMtmZe9MIcOIrjG30XniiIWIDMBsk3W98xEax6UpxiATAOG90fpL1SncGMkVzvThmtnWWDWb+HUpEbl95pLEHQNPscO4f47rQmmaxsar1b0EOGEqBBNOOziscEokG4sALS9t8PuGYra77fjUj2cszd21NnZUyjMTC2wwTgZlrXTSQnH8V+UGMxWkvBnBl1aXT1ZmBRlz5z2botQCy0PKybRvlhr1QEdKf+ksSFZgAZssX9J1C+/GLUs90u7R2X8CERl0W04HZMmEE0mFUIGrhKSBrIXiZEAACAASURBVI0LbagsFBC5KFT3gJkfQvYegB/i0o5bXw9hkbBq5SqXleeHubQwH3zwwXY4YZFmwNf4CeMdACvVA4nt8YtOlxs2Eodf9eWrnJu8Fm6dm85BO3yFYaKIxPdnq8M5XQL+Fq5TjukKG6nPavGSxUNtpZhFhZP3wbAyGn3GSCWNxrJY6vqD1Nyrph1NFq8y9MRKCUjqu9N6sUhrEfaHnXbJctQfTWbkFwkj63WlVh8uXFssBq7zUX/41Kc/hUnm6XbJey7Zie1yrByLlTYNWvx2ZcLUN+TSf9111zmw+/e25zvQSAaitGGH4FOm5zhuPP5TRQbZ15Dp3f499m0vfMAtnVdPT7etXr3ali1bjo/hs65klisKLx0htIk843R81RJVJqcP8Fw5L+aNWlnbCD/wOTLe1jzlnCPcB1f59JignI6V3fpwmrqRCqHSKdHWe4hppGjx3n2Ok6dJr8jcF9WxFQ5lI67wY8R9iBepyLNwuU2u26Cq/xKu1+hwGIkrLZqrO7JA7xBDxTrmCfDLNK86ndtsS3vl1SvYMdfrWDqCK+3nq3p0OPdayDRe3MzNdtFSgWNZpHOvUG07kFVpdoB4I/KpTySLVeee11rkrTdYolHc3QOh0AfuerTeSvXmfD/5qsE2I+qkI/KqxCNRCWfuevTlrcsHUnvNA7JdH0ZpKRj3N1WpdrHIUm58x88qraS/FVyerfQMKhxe/LtbLaBYKREh8ai6FbIvy/T+p3WvuwxflXFoD6hlWWxuIBW3Af4hAlO/iLD+84Q70V1ECJcUtllwEPOWJ5ZZ30M/tOYgDFkzocqMdpAFa6mdALoj8j685f+xdx6AdZZl+7+TM7JHSxeFlraMUspWZpW9hwoiiusTxA+QT/mDogg4QEVFZE9BRJFPlsgUBASl7A2lZXRSutvseZJzkv/vet7zhJfzJTmhadKkPQ/GpMkZ73ne57mfe1z3ddGOnbIzXltppTQjbDey1PZEXWCLMrQ0yaZFcCgj1PlXohNWQQm20EU8gJ/jI61lIqXJ7fey+ORtnPM10MN3SYY76fQ7OTjKcCmaFp+XHDn9PkHUqE3myoKuHBwcyyo9vo7cjjIXiylJyWCvqq12YGLhu2SwFVmP51DI1j35cT6zoncd8Dp4dKB4sLc+z/rOoDguJ+ZJ1+W7UDWvKtk9+OCDjsfL6zh6ySR9dh14Xii8t7moRDx7BdkmAbUbwQUVIpXUyuvvhiO1NxgydWn1ZYSbKzwGUNez7fTtHIaohIPdZQZCwyUGnAqEHLRmymHnOaf6Fz//haM5Ef4wP1XqDHY7eyOmjmROCYkj6H6d88Nz7cCD97ITTzrJZRDEpaahJeXfim3itr7LOri31y9StnLNCjqtl1gjQGsNZcmDx+gYFKZU4GKV2IeXwe/Lvfq4j1Gm1BMFf9zndtc8sWrVKpfllmLCi88+50rlctBld0ez13fYcQeH/5wC/kxdnL6RpauzMH0RTkye01n7wtugcIlbTSGyE2EuPN8kpN/1hl37uJ9zo328zIOSF1FluXVC4qL5KD1IB/Y6Ndns64Z2jzY6h6y3ux9wV3k9xPQjMyoWivrCVQwBMiNI+7QTscREmyF7XnWZJZb8wAqcBFT6wEqnnPWq/vn63lgx2SpG30qHygxrJdKAZMDiCXBZkMWuefsmGzm3zvIrVQ5NQCEhepZyF/V0pOogpa2zR1YhNwOuYLOyOO3KOGEJIg1hfsBkxURXgcRUEo6eerFOj5tg5dMQnd0S4H7l5mT0uxLMA2YvMrsZlXGQ0RN1gYYrcbquivTEhDi1xDuV4u8vv/yKPYUTtnAhfG5Mmjq1lClR6fHozx/jSlUyrMqaeEyQd0KyMcn39YP7ri49PgwCz2Yw+vr6a/s4h2lhTnRo6bP6jNZbs96ya669xtobA54uf53+oAmXgXt9b7wbdTJWIp1Qj4BwnAMwxesd/ZnP2LGfP9Y1uvQ2fFayO/yPu+WhJysB7Al7Y2KC1G5U2YavJN1dF0DSGwED+tOf/MjBC4IHBN2Rfp35TExdXS0lzh+hcbodXZlnfPgung4g/Zu0u0VwgDSYshS06seUlQ1dmF+SAQOGcxPTh7g6e3v//Gt7X4fL85TpkkMdLgOHnZq1/RxdDhS2YRnNOK8SiEnFQYoYwo2qcUBZ6j3RU1UQts3UbRwEQVkkR0qrzLBunLIt6U5drUWPE8y85vB1Zu6Xtf0MueexU9ScCqFsZx6BrHYORN9KbuSzsZWcyBNmrpeRzb7mHLINbJXJyLr8mE+K8V2OmcpnnUTABWgmupGu2Tsv32k7BPnblGMMVlQO/YV+1U6de8lvrXXpb6U5blYBi79/AWEaXISgFwvAVXWRd3jIbZCuftkQunevSscw3S04UasetDhcaVFeOCpsJHXPFDixSJwOEnWxtKwmZU82CHoJx6ufIJun3LHKS3yqRg6Z8kZoNUaNtLYtp1j+VITaJ04mHSyuGrjDAv9zQEeYjNU7DP4N5UR0SIAbg+4Gh6UoShS5vvHGm/Y8AOD5c+c6p03ZMEXhKkVuDyXHZPBcTu+NDI1et78SOz1Ngj9cemQKH9DZW/sXV4bhnHPOsU1KA8fXHzLeMdJ89UVcvAPQth6reySHrBjsXDMO/xe/9CX7DE5ZX9bP/8lKpz+Wy5JCDKzDUxp+4aG9FwQ/4qCjiQReo+9//wcuI3X55ZeK6s6NphY0U4uBHPA4cWK5xh0uauGihXbmWd+x/ff9jKkZIMh+0f3bJsJXFbRUGtLa0aP1YtqPAe7EZdnkHJI8LyrzhXo9X6oIAW4ynG1c+7u04T3Tr7O+7hfP2O8DC81ImGtM9sHtbQy0MulSbHj7nbftFYI04UXlnIl7Tlk6Zc32gBtvS2XOPPEz9i2T0kT/DpPVdncXfECXoz3p3xqVTGBgJFyazJV/tfdcI4JDj/WeYc45ZP2b/yH37GwH6kfjWw84UMeL2pHhrqIV+aNDwEEOCxaWShb54ruhXt+BQxZJsbiS1da65Bb4VBBs7RwBOevbbjHmCY/Gl/uOkycXKg+AZEERIP3yE+kWmWEtALdS6jTjolzc0DrXOv9+jzWtmo8mGKB+hGJTAk2gvxZB6qijuYYlTWlPAAqRwdIN4wQcwYa1kbVroMO0sHwKDSnbm02nM6V0JF2aAdbC4dmC5pQBHZn4HP3b84W5bI2ugBNO5UiV2SSZI0esjjKhMmBlRMK77bab7b7HHmBJJoDZ+mjXjjuIQ6UuXwJZ1wemf91sBmJAJ7ObF5dTqzKMPzi8HqPmUkz9pbGAf0t8UE7SxsnayHlRJjh7WSYAyIrqg04r5nrUmNFWzb367Gc/S4bs81mJYXtylP18+kyYmN2154JypkDLvhvEyUe7rzPPOtMFM5dddo2ObRx1qGGIsANqmo+OBVDDXHvd9bY9GeHjjj8afFnQYMxTKId/+Ng25MJiALaV5fZA6nzxTaWHdFrdXFFacWvVwRfSX4BdhGvZ2Id3vjKdsJ6youH5yqQ80d+cIkSaB84Tx2baETlVys49+/QzLnP+7rvvduFIRY0imyGt0s3o2gx3zbpGgLSz5s+GcMNQbzQ6G/t9XpvPn4IDMw98ch5Nb9o9DuPmtpFCJ52MuZJleF43+JJlTw5ZV1oa8rskIbGT4yE6jpIiF0g9GIrKJHAsh0oLKgCr+y8trto1iGKPa+Ogw+sXuzASFi31rxILSEJJTM6iSlDqHCOvL5cdCw/Y8iMTeV6BNbN4ZfZpCg9AksqXvfkvq5Hw+JoFLiMkHFgraMdCSpK6kLrkakR0KbOoU4draIadupkMWDGal8WbTbbOPXaHw0zXRUdhumwqh0wlH7mf/WwS6tMeDTthmRHnCqgp1AqvL2G05GCo/LgzFBOiVZhKOUKlia4UZrpEJoPc1NzkwOx+eGfJf9f7rmvpm74cMn2alAF6kDrYdAAJcP9TKAhqV61x2Kxw6dAfdio3Sk6o18GacpqkZCLy8HwqRhJkkMFUhuzAgw78MBOx1p/HKwl4wxxKVfOaarBM0mkipvJrrvk9na0pdBO/DucaDRYAwusb1LDTSuce0mFpTJ8ypsLUvTlrDmWscmiSpvE3St2VpRwIUAcgRaZySSnfN00Te6d9LXdQdBDcdDpsqfRBg/XVASFlgN/WdaoLLn29Ax3RrPW8Ds4TwwFKuHzk93x/IQPa57qvmcGDhxAIaypaFuHO5JSps1jKGrouwRp23nUXp9SgLw+T8DPj94q7v0E92o2ByrYPzh0ZWu+SAIKTlyKBIPJw7RUdOKpCKXPJ8eZUAnoZ2QLgXMlyaN3vrFejjdZd5OZT0kHpI4h8u1qEhVkROJ+zQjxlGg0NSauparE1q5utak0THW0JDoJ222n6ONtpDzBe8HlFiAQ6qZe3dqxBW2wT/B/q5a4IGbT1doc28a2+KpmisOmeoSYBQGNBG0v7cmt89nHLm/OslUgKQjQHCTJ3rGwxkrcUNllSpxYrPFpIeap4DALlm1vBJLJiOGRrKkqsEHLXIlzEiMdV6jOLEo3DJ1uEknWCszzAY0H0MB+ZCkemNnmV1V5/9TVY5Vc7A6pOKtEUqCQ5YkTa0UofeDLAThsPByIMzPWGWQ61085cx+VLfyCEHcmw8V7fBsF3nbquNOZG86hr1ZxefvnltnjeAvc7B/6XTmAaZ6ZOOL83eruFKlkW4eCp27RdLOGUlIUhOwPqib32ghQyhPnr7nV8ecg7S5nZCHH0uYqUnszSV6d9fT17rbqeDkf225pKq6quc9e/aOEyh/MaMxqxZ7iZSkogqFRpni5iBUTaD+pCU+lSnHgitG0mA1bK43T4VlbQqk9HdFEJLOs0jlRUEvjAeF7Cv8eMLbdNN6uwTUYTzBBHxZxYK11qaMq6A8SV08nWuTK/sroKzlhv2drI+ruBhvjz/fpfW9yVd9z8WvTOl1sOWrcZ2W/hw1y5PR00y35qLbuMeDqrugA6DWXa33jjDVuybKnbD8qa7UGWXZAHOWo+a5Y5veH1OcSnflhcHq1DdOxz4DiJJL4i7KF8/aMUO8J9zBLQ5ByyYXGb+36R3WU0vBFwhxfRv2vkw2/Sd9gbMPBIUVa3QdfVbG/Pne9+nwTDkiQzlYTjK4XkfQdfStZM2qrCjvxcBXw4SIK4fuskUpO1Vhgb5SgkQtWPj1y0d84I/B0hHoxiQfLWdXMKRBak7vOilKPmvm4dLzxqnavec/WWhLhg6Bor0YuXkg1oQE+MLEEUotgR26Gzpq+Rm7L4C6wGjrRCMDrK7zkqCwG8xbys/SEDlo0orO9T3e0jPdhcfxQH0auvvmovAc5VmUGOWSmn33777WczPjXDdUd2dUzxeBnaNoA8oj/wQ/fMy+vodz0Rg64LYHHXoZDhdKzt4dPPqez16WEOLz1QXWn//ve/7cF77nUZhFapC/A5pPso7JhzlPF+sjmUKlmqw9A5cBjPRrKSleB1hE+TlFSEct/aDIcfU/aDLlASbrZyRQvcYTUEO83sO663NZDkStoIRxSse15ZUe6y1XX1ylpLKUFt7oFgfLFqklozOGZyyiSlpPcQkaYOAlFqyCFtIpNWDpltbR1yYmSVSwtjYMnUUi/Ra5ADZYg7V9IoM7bSRm5SYeNHwTXOS7iEtDs8FCWlMTGDgMFcm7kdzOeEgxN/ePalFO6vsbuA2e872T+xVLnu4RAXpGAKvpkk7lRT0hkulbvTTpnIkVu418+/+IK9RkOAvrSGtt56aycmr68p6Jz6EmY4K+YhFdn2xmDO83B9L6wMiB7xJvEJnPzR+xxx2B2qQh3s7WysITmHbLje+Y9x3T5ro5v9/gdNiHg3k/KuIypvZRNjzNs4gNohk2URpRDk9gew49Vx2Jug/V1Goi2esP86aQxcLBCvOi1TQIx0OloeYH7Zbkc85kxM+ru8tA9zZfQY8l9rgE9pDxwP+WTtyEikKK+05pXbiAYEVp9+0FpnPUXTJJ1CSFd0kD0okTenvK9CeshYEyWjLDZ1R8vfcQ9rAy8GtZ6NTrcdJ+lGa6M8KzwMEHicM/F0CI+WJWf8Mea1p4cKiCtH7Omnn3aSNp4nTMbxoAMOdNxZnvk+EyvieOPSEjeab/FHaQR4nsBpC5cl17WzFHbodW3hSH4dTE2/XyITg+NK7wzNiZyQX1/4C0e6WQfuS+BnZZEcv5N0ANWZmaVkWci6kjSOgPVlIwg6CAgmQ+QqPckIdW9hq7INLxSt7KXvdFNJVXi3F19Bl48yZFMzBM0JAg34ACnIu8J9JxF0S/5ysqUVZMlqyGqomy9wriorK8gCVll5fIRT1gi0LZvcXi2FCFmYNI1mXrcIrjh1Qwr71dzcapuNH4dT1xAI2LuDPsAh6rXVJKKuUpFDl5YWW0nBIpu4xWjbepvxNmbTMt4LfKkCJjJkbilu5CVLn5kOOy9+D/YV2O/XT3fwEt1FOdiumcLlQwksQ1mzJPyKXXQmaYxkF6A/MBRuz4rn7PHHH3c2SOtRwYSahE444QQHkZAN8p3H3gblHLJsOzv735PwgEbzOZ9UcmqZb42tEKOTIYsXb08QhYxMLkP2kUncYDBkTqhWkRSb1R9SbmM1B/WQlSsabDWSQ/WNnbZ0RSOkmcpkdVhdIeSWSp0ixhoh6yVdyijf8/l3HgY8VSqdMQqPfHUIxKvfqyvLMfnn2zi62D598GibOEUaWnT8tBdzyHGouHMKx0yaWv0YAu63FFdYPgdY6+2324jE+/hgdB7yu+KOTRBEb7HSIkxVK2WdptHW8N+nWlklC13NK2Bg2tv4XJRVklxbhNKlJJGWLkFaaE0dBmmUjRslyaPAKcuUJvKGqbs0vh6rMlCZcG0aGXQCcpTE3nzvfffZW2+95UqUKhPMmDHDlSUVnYY5gPoxRbmn9jIDykKqa1XOjxwZz0yvDJq+lLXU0O91UCXJLKnT1QOr9TvdNy93I1Z98b2p4cLddjp/pbPqOo8Rjg3kyZArZp+0gfGKOe2toIsRgQh7f1GjLZxXZcuWQlxcT5djUf/2x0Df/EJALhI2114YO64CIeexNnlKsZWjzCLyzY5EI0zr2mxB4CUpIQGY82nACVDL+r2y3iH+M8euTvYXSbN4LLtaxEB/xg359cMZOK1pYc2EMXsGQmmVNEVsuxd8evvuu69tudWWgf5maCh76rPwsnnhZqEuctv1OIFZM0hiWFWAj0SR6JLyAa+sWi25wDYbP1H/Zs1iuzuoACngVcJb5LRan8Jb5gGfAT5pSakQicV76Rxr/sc91gz10KgvfIFFPR4og3TcyVLLDnipPc0J77skuoJKzjhyYbzIikusecFvKfMfZrFtzrKW8p24IhIgrj7k2nS4uiCgbCbgaQO3Xcm+UYZfXd66d1decYU746Wd6kTsu3HohjMecINwyLorT/mNeNOty7mBZAsaW50CBrE02C/hQbQAVcojEhdhq6IwPHcxJHeKBdkZTQwuRlN4BZXSFIkH7ddBSUR4hjxIWA85crJtv0sx7wNZZAdM6QG1MK9IZyS18n4NdkO7Ggo4UKPvzbamp+5k4VdbwumrVULMSUagmGtPwFfWPAaH7DQrK9+2yyFzODYHUJaQcoG9PafKXnpxMRg4DlqM0RePH+dA88ow9AWIqwNaRqBLc9Ax3getAQGnWMw5ak8++aTddeedVg0flPAbKhEIqK8DPRNc26/5yT25TzPg6Ue8OoHuoQ6YcMk3nF30P/vso3fYdH/975zDLnB7OlMkslT9rLKi1p0GzCtkstqQ11nO10pbs6oF4w8RbHElh1+p1bF/hvJoJwNdzCEdlXQORIDtqQYaSfJtytab2ISJo2zyZkGpVAdcHk6pyqBBGxkOKdm4SKFkwbQHpQigrDoZHlflDQho0/3UQ3kKhvW1ZZZE9W8pWOhwV5b273+7x6lwyNHanQaoA/Y/wJFMa50rWyr8pGyew62lm0bkiGnfDAXwfzaHTFlhWr04w3CwoGhqaorak4+/YytWVyNKPsn22nuUwzcHFQdY8AnGlDX2NDE1OGWFMANotNa+b/E3n7PUs89Y3ojRVnbYoegP7gwbE+dhmo3ALW02fTQewb3inCAw4605HKjyrDzPEsuvt4LoJ8w2P9s6R+xGMAcJOlyZ2hQuplfgAjUN2AoYB0h+pM8XyWr94x//sD/96U8uu3kG3IKS2PKYQt1XzYVPygzXRTvsHTIP9NZmWbp0qdtYwiLpBqnT6jc3vemwMvk4UXFueiGALeW4dFwUsqlSZJVwuwC4c0AByJcUUFLOi6QnWKOjUhWuXNMOZ1FSmpUY3Qi1ijhC3dJwzG9vsH32n2zbbl/Gv7XySM8Kl89PnQilIt/dr7Xh4msH+RLieY2tuP+PNmrVQotyOCTZNQmaCUogPOtoo0OzdZw1nPptor6tAzvvWjWhlGhYYxVlCIgTuT/z9CrYr1fw+zJKOcV27BcDolWP08osGeqdM3+nOfdOmeN+0mbgS9kY4cO0cXQvpB+5B8Bvz6If1o70DoJva+/XJOWenHUGusNS+nur79nKS5nP7+pyIxssQ6pOSHGJOUgP/25o6qBrtoamjVbWRTNl0xaA9EGpL49ASNqy4haLFX3Y3Zb1Q6yHB0hrNQZZrGgxRB7biq4sG40SFyXNspiNHxshszLBtpyMY6akuc6jRLPjN+sSoA9jztx+0oSrTKoPlL3kux4+9gbzltnKp9VrqpxMlvCWH0BAq9LldID/+++/H01GEGinS55hugxNjpw01yiTrUt5gGcym0MW0EsIS6N1VmDvvdtqTzwxB6WXTtt628l26BGVAa1LOtXkq/COnoIycaJ+nhXFUIuhcaZx8WzLf+8NK168xGwEjtR20zggR1tTvNjiE7a1zsJNXH4rmiLzHQEiA1VTQc18nsurts4Da3CjJWufCWhqKr5ubdEtqUjRIc33wnH7WrQSTeXgyHKXW71mpZWTiRflzhNPPGGPPPKIIwbWeXXIIYc4apMCsKPSt9V9yxxh/PIA34Z19vLD3iHTTDgsGLxLl1xyiYtazj77bNt8883dhvnbbavQRsM7d4eOIoCAIThJOa0AHb7GaG1XRNsJ+DAlPSy8epVZtC4qJdaqEg58RGp9j0SJGIqiGGNyawUxmzJxU9tqmwrKhIKf4jmhKeaiDZdKdaCyft0sNUYWks0SeD8ZrbfWBW+aPfoPK01QfrJaGgfKcSxh8cchiyU3t4ZTyJAVbxnw8DnZANK/dJoVFeIodsRxmGrsxeffJxIpspGjR9mRR8cC/cMQLsNfcG/GzEnvkHLURm6EMHTue3NdVkz0FSpxSUNQpYBpSOP44Q/xbKSM/Zqw3JM/MgNhR0o/exycN+Q9NUVk3jPtJU9h4EvbCoLa0d2LUp5z3XA4GuCobfmKhC1cgPTQ8jVWvbo8IHZlvQiEX0TpQdkm7UMH2kZBYiiPQkD/rS1JyiYd7HcwZeCNlD1vbAKXR2NAUVHCxowqs0lTyJZNHm3jN6Xso8ZMBXUcTHLkAqmmNFZTGQB1BroMvc+WDeUZGN7Xlrn+w1lgV35MNwtUgXN9+ZVX7PHHHrO5kFFLM1Nd30cefZTrytQ+0XO9RJn2Qk9BzmDOWDaHTDABV/9hza1ckbQ33lxqS96n2hIrxE4X2d4zRlttfbVzoCZsMcrGjqHzWAkK9ms7AUjLH661EXHODp1tbSvhvlxt+W0saHCZK2l+GYsHVl86xgp33NfiW+6iLqvg3OsAIrHiXSto2QdINRjh1Ba4g6i0ALlxPVJskgSs/e0kLSKjD7SSLc5A1m9PmP1HwVCQZ6uXr7Db/nKzvfXOXOf0CnIh2zGShiLdq1qynJU4ZhGuQ5yIBx7Ia7A3ZVM0J/2lWxnMexh+rw3CIdMHkkN26aWXOidMDpm8Zo0n/9Fis+cswEGBVBXnqBkJljzSoU20vRdArpqSGDauVIw1VkBprxBrWQDhqxroSZjZ6uJVrj1+7NgRrvNqE4xveUWExYzLJmYKzhMaxZwD1p6k05HOxg/BoAqF+xcBN/MSxfLKWOdVEcolbIz2fzxs8fdeJaNXTflH5UaxVTfwkC2siQxZSXyL4B47hyx9EJDZSyWjZC2SMF0v5YBJAM6eYFttKU6m7pGV/vD2UaBKkWHaCS3+V2HMVoQ5c+ZMBxT/NFiMAw84wMmZOHBt+qV7KisPhbT/+tp8g/G+3WU8vRH3IObwdWQa+Gz3pw2h+2hEWeC4rUa29e23V+GcV1ESV6yMpBXlvhT7SV1vIn4NWLpxRPjSWajO5aE8YnE4BvkoSfj/RDIucucomXY5l640S0mntr6Ktd9im08shfNsIhnhSoIch24IEtvAG/RDUE5xWIYPx9D++EP51vTp2rpzmsLNA61EECpL+rGc8uWbYMyeJCMjm7bVNlvbkUce6QJMj5v08l7Z9kafLrCfD8rmkN1266ssN4IgmsNoVsaRaXTOljQ+W+nUH1kyggrKcs63fNvr01vZtlNHuNJ7W1KYY5pqbrvZSpcsI8u1kqIKSQdkxfLUEMbiXhlvt1EEK+3jp0G3eaDlTSKjiDaxA5Klqqx51bsWbdrXyAc4SHNBdCsSCOqKXuzE2SWrlCrf2qKjj7O80V+EPWAae4xEB3ahrma1/eWOW+ylJ1/takQSFlafV5UXr9+LVck5ZP1cQ+v06WGtxBUrVrhMj8SmNVQWe3lOnv1n5sv8XvIqkAjigBWVFSKU3EB0G7PRtYUQuAImzGuy0vw6Cnm1VhzBG+ffMToTp3zre9x8XixkOBVtSMMyqFmrBBNg2nXoxKWDp2PHVWJ8LX7tPzJ7xgmAt8OXlORwA5li8YWLrPrRvyJevpQL4XeRJj5XPSR7U6z5tNOtOIpOperxOGQpOtekf6zsXqAYEHFYulYyGyXFXJ/rZgvwRDqgw3xbPqOSSeYqgyaQvkgYhsD0twAAIABJREFUxSMmTIZA+kcddZRLI39Yqgk6pDIdPr2Xr/lny9Cs/czlnqkZ8IdHmC/Kz0y2MqXfQ94x179lCMMSWNxJyFmTZBVW2aIF9WSj6ZhsAlPYQUckmbPOzipnUGOU+LWFUgD/fdel5JFikX5iLAf4NndQdhFdhnCoHR35OGbCqghPFOX3MTgBwWJK4Bresg46yjo6mhDALgIvuaVNnQohZshuiENNDmlX9J6jzRjguxdUT3rtlkw7x5kBowL8N15/3e69/z6XFVPGTFmYT33qU10cZplUMwP+Ybp5g2wO2V9ufdNWLW/HEcvjuiuo7NClnyfIiZJURVa/vAMamzzbbnq57bDLSPCRshmU5cnwxpTd7Vxj7TP/ZfmzXwTYD4aSgKS0RVqUHEroKzeN2cJKpu9mNmVXa41WupIl70AXP84TpLAF7z9nbVX3gwX7t7MBral6TrAqzlfQ3O1AaybfzIVwXlVOcKrPitkkNqMsW1N7rT1wy4P2KplLj12ural1HIP+rPrhBT/JlSzXx8Lr7T1723SLF5o9dN8bFqNzUhqReS2NNhqx7ta65XRHmY0vWIjRpM8jHwBnfiPOTwNfsOzDuB8lUkh8+SbnpGgROJ07RzQrRv8AyN6WRHA5irOXogzD34JfqxyDsyb0bn8jYK5ZeDZFFgUQzypal1PW/NwDFpv1H/BlRCtkzhJg2YriW1nTt0+nq2W8ozKTQ2Yqv+rSnUMmgDG5P+Ei+Hcegq/5iJX31pUSzqbocBfhqJwxtY/r++hNRtnxXzzeDiArJuc0oAYJsoItLXSR0YHmDWK41JXTiFv/u8g74roSf4/Ch1c2h01dm3WNJfbu2ytt1luLoI/pwOhXkjGGSBZMWSvYD3F46X1UttTvgnICWE5KDQWklpvgFBvSI62nqUAvBuWHnDPtc5FCt7TQ2V0s6TKcM9LlefxegVMkRuZgVCmBYYXt/omx0Ha4rRdk0QnaRKnhetlcKbN/GfQhPXdD4OJ8iTJsx8KXpa5YT5OhDL+G5zJTp/iqNasddumBBx5we0TdmHLKRNnjFETW88jmkLWS2V3ygdmrL64B11tncTry29BbbqYEX1EGTRJO0y6fmGQ77lTouPbyOFtEZq5AXeddndVb5Ll/WvSVf1l7FJaCVMyKW2I2AhvfCYtAzYzjbORue1sqSgYuBM/p7CDDxtIuWrXCWpZcapGmawjMCi2RX0QmjBIp71TUtqvZbi/qoHLHlDLH4h7s5KyLsa8EnX77hZftzwD556JbKgyyspm6L1Jo2Wfffeywz33G7aEcqH89L8TMt++R9qIpYf+5/S4r4SaWUVIsba22zUYC46+dZ/mkXJPSgGThqSU40sGKBAMG0RffVQuPWv1pp3F4AFpkMQXlR60ccXipu0zV9iqiBaJ8nicDmyfnCeyIgUWTaHK/HTIcsOr8JivOj1lhgmvibdt42fjqdyx5942WL6A0DlkzGbLSoqnWcurptA6Pk9wmaenAIRPOJ+Y0KugUg9hW7OJyykRgW1zwf9vu5ThphJ0mRYnvvfuePfSPh+zZZ591GL29997bvnDcce5xrntPh4xDg4r6IJifnj5/Xzo6h9gSG7aXE85weeFmf1BlcwjCWYAw55QoM8Qv9+AD8IgllPEE4MzC6swPDH7K4S3VpYyChQto+EJ3Ul1fAvMrW6TvRYqShvCIIEOmIEO6tUm0YjWX+iyFZMXilGfqWmsJxvjsfDbR3XSIzLityUk1FVJ7mbaN2bbbTbHxm7nTjucHwtYa4oWLiR4jNwZsBnpyxHQfHVDfGcOgQuD0SkMEtLooxznHkJD5vffe63Cyaho75phjnGPmy5gD9gGyvHA2hyyJ09TZWY7mZ6O98NxcgiXWbgmQHeif8vPLaHSbYwcdsptts7WihmbKmg1WHJfMHhgvEmUFjXBk/PMBs3k4TjykmTWen4QsWZQvbausdu9v2shP7sHRIgmzFGs+oHhJUh+NgrPurL/Fat8/1wqrgwSIxaajswzBOfCgWMsmVrPN3+kPmMjWkHOrDSJ5QZ3DiA9y9l150c9s0aKFrsFAQ/QXjZQuBQM48OAD7aRvn/p/ZihHe7G+ViPv6znHfOlSgEx1+In9XU5EYUG7zb/9PNsSNntbTrcIUXmyfAVtuZRTVo609hFLudkSKK4gwi2EqZ+SYKGwHjD116+yoq9+xSLlePIs3o42SO06X4Rygk7LohkWLf1kVsg+vQDA1+g46VgGWHEE71VhLzwPsz7Xs+8BE+E7q+daVLYJvPx8gGtyajqTEE8GpXrnPLmuTfcofiefp57OtSoijbsvwVmjaQHc5co4oMz/+plVE63oFePgIzrTDOY93iK9F4u9i+Haa8d5wljee+XKla4LSQZJnZSHHnqoHXHEEa5MmRvrdwa6w+b5knBYhqanq2zDWc+nfBDF0HpsUxBYpNce64GkKv9Q27yKEQWUqM1ef72FBhHa4LNpn6zf6Vnv795MmL/55tJmHYvYNRk2SkFJstkKXmJC/zt0TXpzp69W+zxFR7ej2fEY0F4+SSaQ2WcL+pKFVse0D6jCb+HhCikcEgWWgh7osYUOIxR0Xut3AbFuz2NDIFf1AaoywpJkUrZMtBkKSk/8r2/YxIkTwRVX0A2YcA5cYWFA8O0knULEyd4R727PDtRCVWi9EmTLg/cuJqNL92MhJcNktY0eOQVwP2uMbNX0HTe3w44AdyxMdAc4YWx/I9JkpSWcl8/+x1re+o8lqhZa5dacg1vvDcdLhyXee8Hylrxoq/f6nG22AzQW5cCEyH65OoyqmXzXymife7bVt82yVLyTrNbRZL6gyuA6EtV/seqm3wCbucLKxx0ELmc7xx2bioqsmiCvpcOWv19nv7nsl7Z48WKwmTs6rJjm7v7778dJWwQkYKr9/Oc/H6ipWy+vu8GA+mVAhGW67rrrXAbnRz/6kcvgqI129eM32eiFdCfWBDIqrWXLMYZ47zWAAwtZdJCq0jqCbWyB8gICvSI0JUsxiwB628cTxRaXslBhOm97lpcD2IgTXzzqBouM+W8WUpb75nD1ypjRzYn4eENdsT380Hu2BiHtPT813XbdRTQZQVQhHb+YyxgkHaN4PIZUjDoLAAJ7qRAZQEf0D5alAwOR/9ifYd+bxVNqrWbUVjbiqz9EPQBGcW0IScKkOcJ6vEqHsYCST6UUl9ELaqwtAPhFSfHs88+5lL2A+zvttJMdR0ZMnGLCwShL0pMm3HpZzRv5m2YSV/qDs7dp8dQLKif6BgABzwXAV2YohlOQYm1Iwkv4w8VLW1BdWGJLlqAuQWa4XUjh3OhxBgpKysDUVVGebbJttx1ln9hlElkVZZM51KDQKCD7rqNL8lDaeo7HjBHAMNQV2/vk9pThlJOmr0LVjPs4fNYo3FHrM6RhRYiwQ5EtQzPcHTI1MmkOw86UZN8UoIqKQS2zgmsoSN0cnJmGOvg1HD+jwxcH3c0aHwcS0Mfb1uvDdF7Nn9ds/3xwDt2io8CJjcV5BNMJsHj2rFpbPH+ubTah3D61z9RAZxIkl2x6I7ydpSWUIVfiyEEGm5eosrLNtrHI5tMdPMdWU2GqmmeN8XFWOpHAvKCcdwJnKcULPrNLHOjwraEhABxpByj+/GJoMsQwq6loW87P4NKoREUr6c6EPFbHWhKHDKSZq1LVrUrZn26/wcaNG+c4LLfdFn5NhvB9Ul6Qgyzc8oY0hr1DJqfAc2gpghMHluR5lFKeNo0FwN5of/tFa3nmbivH6CVI4bYV0borPrLECCgj0NfqqCGTBIlrBQsB5v5WW+wql1GV+ZoBHstmarUkRrNwVhvJK8sfdablT7yYx/eu5adOkua2aq5R+K0RkGMm7eH737Dq2kaI+SbYEUdOcgDJ1StFo5e0LSartEFGLdkENo0yaKe6IMmeKR5VE4FTCOBaHG8tP8x+yhpfesLaV35gkQnTrPz4b0P1pAYGRa84ZKLp72UoUg9wLcHj9LNeX1mxZ5971m79y19cV4vS8wcffLCLBgczwtuQNttAfJZwF6XWv4Yn+VWGuDt+nvB1tNPk8qEcWPgvoVZAAgaacgHu19rst5ba4iX1RLMSty+iDLl2WpYDMRdD8TU7sTPtEIy2U/8pK82zaVPH2vY7TLBNx8keKCORCLCmodq+zm6VZLQns0HMdI99JlT2QT/7Q7+7Dtvu5kjOltMV5bm+yUbPFZDa8wTqZ/0uzBuo52SjFxjuDlkYnywHQGeNnDMF/1Igue/v9zpiWXX1K4Oz2+67ufsmkmzRu4gOxjtkYZZ/3Ye+3p/+rOskiYDa2qi99/YaMI2jbOLkUtZb0H1fTx6iFtLmeGEnJXWdNRJuDzjz1cCic0kUYlHA9QWdZP8I9Ovh1dQj5H7FE7RVC8MstQ3S6G3YiTxxc2o1U5LMF7m6xzDjrDkOfn7lVruL+zl7k1Vcis7VIrhA9bsWdgJ0Sp28JsdxdUu1w455hzh8PzJl4/ozT0PlucPeIfNGwW98GSjdKHGSOFwZLPf5jcttzcOX2sjOD1hoKy1V2OySUhG1649cwCKiOyq/1vILkBEqIvVMtiBBd6KRAShsA/hY9DrlTAroiUNwVpZYQ/QZaxl1jI3a4moWyvje7yXnWksbzQIgJsVv9srLzfbCM4usGdxNRUWxTdkySno2zxYvWsXCi9l+B21nm47nd201PIcOSNUiP3JOBnpubujMrF9i9a88b6nlS6x49OZWsM8RpH0py0IKW4DuXidEnL0Nl21jd8iA6EDXAa627zuQaZozZ45tv+MOTupIX94Ya17DUfRQWcy56/i/MxDGU3Q3P+1wChW4oEIevrBd4CrFL0c2TJ2Fal5JEDi/924D3U6L4RhTVgcSZDJnLch2FaB1mRs9z0A9SgQjR2xCw02J0wRNgDmbOLHIPrnHFjaF4EtEs2pw6CByE6g87uZT+zsIvDyGqad3yMyQ+XLlxxH47u619Tra5ypROgUOxy+lbE9AuN0BB0gJtkJly17tSw+UOsNlzYSdpu4C0eVLl9ljcJc99NBDzqE9/PDD7cCDDgRnFtAuZd6/8P0aDOJSOWT5UF4k29WIpitCH7m9zgrJMogOIyImfxVhBIuWogu/1d4XLjSfZpUYAUVwiwnUwUkq5NM/4SLnpQCZwckp5y4pbKhYBVx2jPSBKKDIinV01gO5ocJEaV640iQunbIkEOJYPA+YkF6NkpPYbwJITpM73/KRbHIeXOj48jyWnpB3uDv73e2BYe+QBUYiyB6F2ZS99+xigdQKq3vucqtoe5wHv+vSq86NL0RUAjCyygatjQusHUmlMsjyIgbjMIdQXidEc4VbUP9+1MDzqoLpbGWNMP+jj7axk24hQxBo+vU03FpW94g4zaByeemFZdTEWcistGbpUJZArNrYZrVram1LgJWHH7k90YreJzgggRC7l043aQVGWqB7Vr5bkHktllq10qKIJRvAf/Qk4CcrYMmrUaHD4k7Gpeehxd/QUE/HEDEPB/Ftt93msozKikmi4pvfOrkrQpHDpvf0UfRQaPvu9cNtJH/0hsqTVXqHuU8qCM4h10QFwHXhCUVm6s9ZaIvszdeX2Jy3VlKiLiK6pkwp3hTWZ7yAnqy2QHsuN3qYAQVF7SpRqjkAZQ1ImaLxRjgAy2H4H2277BA01ahzM0EzgGSavEMWdEr3PrP+3vvGCdlBnzXrC6VMZsbBZ1mVCXL2xXW+kT1TBp1rfIbu6qeeesplLI6kXDSdgK1X+zLMHTJ3b9Ki5eH51FnjqBe4QdJ+ffWVV+2vf/2rzZs7z/bdb1+HsZVCSRxn2w/fXBPWDh5opn9VXRzJeddaoohJVlbBlrKwcYJ3UdEoS6tgTM5YBDoXibnrbMgDywX4WbOggzZoZkOFIojfIJB1fwsI9mVGXLMk+Ed9RelKFkZS751mgeJxVKL4RZQcm5O/xZ+DI9mZm4gkC3H85CBGAPYrWUDh3Z3tYTzsugo6hqLN2iAcMtX5PZZJN+udd94B5/Kq0038xJ67AbxfY4l5f7COqp9bEalXqW13KsVaSMoUbeWCcmr/dBwaYFsEHvmZ3/M/qR4l2scSBax0a7HQAB8WAF6MtVpn+eGWN/bkrET8a6rbYEiut5rqDlu1opX6dy2LL07kWYzQeTOZvFLXmVVREbUddhxtO+xUwCZJLxXeE1pK94+YsGPpzJiiLvzIAPvFgi3UctfDkHfqYBO0s9kobrpNUKQL72X4DNk777xt9993v0vD6yAXJuI4iceG/DnfGel13Ybigt7Yrqk7rrjuDpCe5kXGz7FyI8Ulslax0bugld9XrTF7+oX36aZssKrV6kYugi1bJQ8iYgxuO6zI6kzLjZ5noLSsiPlrBPCdD81AhetwbpXubLTJyiCY3m/PSRDKVqL+kT7R2MgqI+srQoAlibZe928vpM7ZOmj1up76RHs7zC+nv7lGKXmEOF9NBG1/v+fv9gqcULUI0qvDdidwPT//1UVrdX3Dbc1kOr7++iUZ5zszFy9+39lQYcvUiamqwmFHHP4R7d5BJ5XVXpYflXbIlMFyw3X8cu1gp4MiY7qBxB8X6ce1gSWVVKCMgvBoIruQ+KAyGvqpjdeIp+vq6bwuCQPgN2QgIlIqV8ky/JquXKoL4m8k5Oinc8gbXU/EJSFkewKb4t42dP5kJgSG2xrqy/VucA6ZFvw///lPB+5XF8avf/s7COhIkzY9YNXvfdXKWQ/RztHWCDCxKU4rrqOBmETLrrBa5RYtKLEVa17noKm1KTvuz/OQ/imZiEfE9xiYNDUBdAr8iPdfSO2794ogZb8avpbZ+wvEFSaQZ37QMk/klCJP2wTza0lpvu2y86Y4Y2XgTGiLJ7XbiQBzW3PUIhUKUdkIfAuwJrw9i1SIM61WuWQR6QOmf5+AMVwdk5JNl9ZzPoSVvQ05ZP/6179c55C6WXbffXf72te/7jARypjlU5rSRpBxD0eIysJo9CUK78tCzD1m7WYgnKUUk7UwlYq6FVF6kH9vr6xoWODxFIulg1BVrPtQSKIj124rltfaY099YBXl4DnI7tQ3QJKKs19ZSTaVx1ZV1UI0mStZ9j6/yjxIvIb9I7kdzqcEeDKVh0Q4PWlMJ7CAKQCrS93hmGT/KmMhipCiohLXjd3b8KB63XdlxsSNNXYsQSQjHKj2dXXJftbgcC1fBswDjzyP1rdysucLALI/QuZ8BljSPfbc09E/LOJ3v73ist7tyzDPkEmyp7yc5qqewHzYT5WiZQdV1hUlg7oAxdMou3nwoYc4LLN4y9ZHA5SIVt2lO2dH+rFpHyfwszlFVK2BQxNtWZUodawkcabyOHA6WaMR2AjkQgXIMinYiGkfaIM4MflNlKxaXOTo/F2OWL6qNBpKexHgydkKcGmQ0Yp83FEHyKGjJE4ZKh6hwpR2xGRjuoZLzhL0qTKVLpdnLrS1Wd993Qfr63HD3iFziwPAqW6ap76QAKkcjO0RiT3ogIO1Fiwv/oLVvPpFK29/nxLmHtbAja7Ke8GWV21r1dUxm7uA0l1ikjV2jLJ56GhNmb6VfeXrX7KdJ+yE01VswLws0VlHAhZqCtTpE0rFctdwz3odyDzS9bnaZr2OigCHXXEp/C5gyhJcs7oaE+yYTTaJ0KmzjW2+GalZmJFpjCcyxajiNAozqRRvJ4bRdUFqKUuL0y3pPCtU+RLyv05Sx44azW0TRlI9zCzqgt4dsltu/qProNRhro4VpdrVwq1OId8l5D9gT+DU9bV4c+/74QyokUW0JK+99prrCgs6pcAuZumyS8A0X1KitUwxoYEsGfQuYzbZ2kaNnGwFdPlGy3a0JnBQKiWUlKj0RoaHtvVOPIsismltOBC50fMMtMO1VkDWWodfKxlFx2OWlv5op1N6ZLwG3rZ6W7T4LbQ/52AfiP+wEa2QzrZAPiu93N6GDn05YTqctD9FXKqGJvFj9QU07qiB0mtEVAJytKTA4bQDVbZMl6RVnlMX4fe+/z0n+Hz5by+xt8Ca3vjnW3q9vg0F5xMGkOu80VBGsR3yY4+vExF2kSs5U+aHNPvWP//Znnvhefvc5z7n5JdEE+Sdi0yIzYDtIZcdw1GKgFEkW5UPptplwPly5XAgL8oqJDlohPnS8H/TKZOH3J5LaKUdOMQoA2cLhywgB+A81Osob8BcRNK0KMELfViqzOPEhK2P30luzMV3fK/jvGO+HI+TXkQZNb7pb64qCiktpU2neeu0cBU8CvMWcAFuiGPYO2T+pnjRV2UHPtL943AQzSyGenvont/brDdfoRTTThRI2bBkc2tprHU3u4JOjtVwy+gg8w7emd/7nm23Kw5Zfwagyvb2Iqvi/W659UmiggrEiDfH8++wBljKJWNRUpKwE074hJVXYrxpPWbJuk3S2kQ5suRDnbWeLiP82fWYsJyUTxd3OVjpF1kwf77dddddzmCIxkIdlJ/8JLxqOLZepiKX/erPjV83z9WBqxJyuNQRPmhVMhHA+qKLLnLkldOnT3fZTXWBuY4wCEobGiAqJeNSSqZDa074kcqKMjJcVWQ/klbnNOIKrGLkFvArEagAkhw/djoGu5TImVJ+bgzYDDTQTT2ibCR7XbQ6C2gAICvVWYP+H80W0XF0dDcSsG3ieJf0XbZKHdDCdyorluTfav7RUDlRe/f6668Pui1FQZAuKftMmv/eRePA2hHFyZzZc+zGG290rylHXmvoS1/6ki2ElFOlSnVX/7//9/9oOBpvzXQb6mdl4n7ys592dVpmZs0/YocGbAaH3gt7jJO+/+mPtzgnV07vF7/0RWdnPcmp435Mlw5d+TBDum5D7CIcendraF3RsHfIuktbhqkwFMy0JeqtKJa0G676jb2/cJ41ocXVnkLCIRHjsCN1yqFXTFdmTXWNlVGe0b9lTE785km2/+EQ2fVrgEujhRfFIbv3wbdxBAW1j5B9UK0cor4o5LQd1XbQgVNtqogjSRWnwPPEYT5Ot51kfffwAS3nTEbXR73CncmA6vM5niEm5HU02lTWfQGSw30P2N+OPvpox/EiA6D59JIgOYOQdeoH/AH+3oYPN9+d5UDC3LPXX3vdbrjhBtt7xgw7hmi8Eq0eZYlXou1aVb2aNSYVCXXFldl206bbmLGjcLSCg3jp0nf5OeL2hJrZP1jSbPPmg3PsKLXGZnp8A47L3BigGSiAE6qxDik0MFrbTS23/Q+YDF+UUg86qYELtLU6dnINv4/1s2xUivsXaIvGKS8vt//85z8uS/p1IAeHk+nWUBekB+5n6pl60P6sWW/aLTgOypKLU0tZVv18FgHpDTh3sqcnn3wymLGdHF7qA6ANp516qgP1n/zf33KvHw7etNYUSHg6ogGauiH7sr7JTN8babZ66aWX7M4773RlTwW9xx9/vKtCaO8KEhIezkZLQaCHMt2Q/dC5C1snMzDsHbJwW39Y00oRosscRVC6F/SKpNMVv77EXnv5FddFEiGqrKquI+rEMYNkSRugGWb7ESNHkGFLUL5ptCOPPsq+8s0T+zXRnakGDGcZ3Tcd9sJLy2xNTbvV1VcTXYrHCQHVuk1t9ZpFttXWJWzUnTFi2GFKjf6ag/pjzyOzFTt8cMuhSrZR9sCgC4irg/qhBx+0e+65p8s4fPaYzzkx9u54XgajLbtfk7sRPTl8Xz1uzN17QBpXX321E+D93tnftx122BHtug/syiuvdBnfxkZwGgVR23TTTW3psg8cRvDMs850jvndd99lf6asssXEHciybE5n7TY44+Md1rG4rNhhSlpbAPPnxoDNQCMcT0WxClf+LSvBsYmtApM0j+aJpE2aOMH23nc/54j97//+r+O7kp2SEyYn6ROf+IRTJFGwpaGst+67HK1f/fpXjjFeHYB+b4dxUN5pWAk845ZbbrHZs2fbKaecYp8GiC4b8Sf0A7VW1BylgO2EL3/ZvUcrneEzn5rpnvOVr3zFjjjqyK65CduLDRHf090i8JmtzNKsn19xeWnO3nn7HYdr1hxts802bu4mkHX0tCEbSml3wDbKRvLCw94h030Kkxj6jIIivIAbJs8OPugwSjQV9rfbH7T7/3afjRxVwUZoZaNQqlH7LwB1GauWNJGmok+l7XfDIP3g/HP7uRSIZNsLMGKrbfbb6N4BwC8r6wTAP5qMRbvNe7vA3ps7C8D0B/atb32G7hwY0dPv6Plhsl1AT7xgziiCaZBBkHG++qqrTCzT+mwHHnggouBfdClzPV9fnkTUOXJEaX2iTch2cbm/r5MZ8Os6jJnUC7/80st2yW9/a3vttZedQRlJ49prrnFM4occcgiH6l6O1qS6ZjUO2B3uEP/uGWfY0iVL7ac//bFNnLSzTdx8O6uvpYkkUYrzPhJlEzqvwJxInD4CViQ3Bm4G8nG8UuB3OhCozYd2JNG6HK631wHUz8JWVNtZZ/7QtttuO/sSe7UMcLn4FUePHu26HGuqq52jdMSRR1CSDjItd999twOVn3vuuQhG7+SyaF4kXmsn7JRpTT0z82m7hvVy6GGH2Ynf+IbjJJw/f55decWVrgT+6U9/2k4lGzaScqke34ojePHFFztA/85AHfb+1AzHpK6SppxEZeY9zqcvXZ4DN7OD88o+IPYOVaZjJcUT4crE31aN1N3ll1/ubLBKzl/m3k3fYXt3oV5ZQTbXv8bG4tQOzp0aHu+yQThk3ikLGwCRmkrnKhZvsfPP/SVRyXRKO2/bxRddjEMUterVH2A8SJxFR1DSoSuNL2UNCklR+ahHPDJn/+icft5JGdyo3XffHHv73SqXpdplly1st08ib0TZspkG0Pc/aLI577xue+05nb8XEB2LlFNvKyxZ9i62sEPmqSk86NG1H/PZLv7Nb5yB1Yb/r//6L8co7WgzAO97tu1curyft3oAnp4JzA7LI+kAvPbqa1yJSVJh02m6TRlkAAAgAElEQVRiqQc7dvbZZ7v7fOKJJ5IZm+DUF5548lF77rmZlKHOZC9Ms6uvvM5mzZpt204/zDYbvxX7gW6xJkr46Kq2QeoYQcJEXU7CluXGwM2AkAktzdgIiKgL48U2qhLh5uJmcHyzaDR6zkog7dX9vPXWWx2O7DAcpwMIplSi/NnPfuaA4ipRjiMDqjGHTNcVV1zhnLjvfPe7ONVpvkL+5gS0MSz+wJcT8Pvrb3DO+28vucQ5CQpGFcxeeumlznE4A+ddXZVCYstWyCSdf/75Tr5G3Yf1jQ0uk/ad73zHZe68M6Zr2ZgcMr9CvPPbtWKYr4Z6eB6ZKw11ZN50003OFgvDee7559mkSZO6qDHCXdN9acoYuJWZe+X1MQPD3iEL48XC5TuJv6o0V1rcaV884es4P4UYtA/sl7/4BV2JrYD562z8pmPsg1WNDpyqsp2+JBirf48l6tuUr8wa/8e9SR3QCaTQ73rlVfic4CSrrKywHaaPYQPKxrVSkQz0NevqaFkvVzYC48xzIoCx813LeHZQf2YHUBjTsWjBQvvjH//oxHBlIM886yxHWKiIzYlQh/AfmZm2cAfWx/3cucevmxnIBEb7f+seq5Pr6iuvclmvU049hYAiafMXzLef/uSnLoMyefJkSu8EGTj4tfUrXev9Oef8iCzLCPvdJVejZTfXpu10HBmXTcEwaT1SyqdtPXDSySHDx1cADUxuDNwMNDmsVdyKUfJoaUTDlkae8nL44Irg+qp51xbNe4qGnxMgix5vF15wocts77///m7f3k1Tzsvgk87+wQ8Cp4lRU1Ntf0Hu7MUXXrRf/PKXlMUmdDlGmQ6ZMjDfO/Msh/W69LIP6Svk1P2S5+666672P//zP10YNlUSFNw9BVZNFDkT6Lp8/Il/2QqwigoI5BzKxmxMTUG+NNmbQ6a/iQ6jlG5YUQmJR/Kev91jjzz8MDRLcdfdLrC/p8VwCjPM88bg0A7czhqerzzsHTJ/QOm7jyg8+3E9kUl7S9I2GTMSjGwH/GIrHcAyRUv5JmWb2NhRY22zLSe4jeApHfxt7NJo62LSW8sbjCRKJ1gczkrXzquXo4oKaFpl0hTRsdriEa8NE2yKYgO9mkBouPf23kyiQQ/glTOliPquO+50XVnKmPwAwy1HU6Mr+gqVLDUP3gjIqMqAZ6NNWMtZyT2tjzMQLlUq+vbOtxxsMYO//CIHMhkUlad8SUm0Bbr3jXCuFBWU2ZKliznQR7mDfOut4dNjvPryfHvu6dessX071h88RGRSi0qgjoEGIwlVQykYssYGdOVyvK99vFNr97DC4hHYgCrmGa1IArf2RBwuwhICwXpbufpde2f2rbbbbrvZNygnqgtSmSlpKlYAwVAnrRqRvnPGd2zfffZ1jpOA4rPnzLZf/+rXLpv2xRO+9BF9Sr+/degrE3bhzy5wBNoqY8tp0BrS+6gp5MwzyabC5ejpHNxaZA06XkLslZoJHn3sUQdY/y7ZOPE+ys6EbdDGYD96c8oSYDB9U4ayY5Kb0hAuUCTcV159lUsA7IlDLTygytEaOfzu2u2n4f6sYe+Q+c0fTvXKYHhyTMfF1cVrAi6G/1ob4V5SvRJMdBudjvF023jmzXQGqN9M5GSh0iSuAnAmKAcVUausrVljIyhfiuq/HXLYGBFybU0LJcugra2trZPfdalWZl1nOqj1uX2UNQuOIAG2F81fQOfWAQ5EKmqPcBZR0Zo8xExOFxkDX37I+sa5BwzKDIRJXnWoindMJfndP7mb/T8OzszhsCs42HPeetf++ejDNnfebAz+/pQnJ+GMvQuepd0223SSJfJ2IRMGUSn0C8WlEWuoQzicZTF61Dg6xMjgxgL27NwYmBno7ID8tXmFy4qVlIy0+hroKjrjHOI0/IAnSyH3Jm4xEbIKCygKilocsTIO9uWULVXu2m///bq0E/1Vfo9MuBy33112aZcgdvgTKDtWDQbt/B8FWDN1VGoIP/bDs39g++AcfJcypDBlyqY3NTa5DI9GgmBPToaciptu/oMjllYzgUqesh1ea3BjklbLdMq6sltCnaTLvXJg/ZDjrHLy8wRPwv0tWLDAvoAyijjLfIIgB/QfmD03lF912DtkAz257jgSD56+0j9/hI5CdPhiOFb6KyRL4bRn8AYRlkC/HsJXNl8M8s1OdX7Gcbo4VBvYlGVZCO66wxGEsV7CvYnAVTxj4rcRSeFLEL2qS0plq2OP+7yLvgS61QiXwDZWnqCBXjPr9PVZd10ZCmnqOSmTCID8n7quux8B3lYJWtgfBRZhIeinZ860m2+4E5xkkxXSQNKMkHVDc9wmTdjHtth8hrU2l/GcQHEhN4bmDEj3/bOfn2qjxkF+DZM/TM+B7Iw76AncUpJ7JqxzXHUwq4t/jLSmOiWlSbsvzppoFmQzvCagHDVlrpRhU4ZM/GY//slPXHZNpUoFc5dfcXng5IWka8K2SFAROXQX/eKXbh2K/+4bYBa1Fj1myq1Fl+XPjd5mQFJ/oq159913nUN27LHHOmJf79TJefbdtR9HFm1IzHpnFesV2E1+kdWzlsrFqbv4SlKA8HuWs3apIFkSOcLUG6Bz4DwceQKPHY/NUkkJofHOCnv0oX+QWSzBtgXOvoIEqV5oze+w685dhLHeKfaOrNbrcCOQzTlkWVat0wAL1kCXEZQchP9dtWS8MYASj5C8Uae0KuSAOc4/5FIwnhDpOzV7qUvGpDKuOpAA9fwui/LSR64unLnSxtTQ4RzmJ5L4r5wxlQ6UAt8HoVsJhftF6jMtGzrj8ZAwRuviIkJCC97ZepGoWmUlYcdOPe00B+QXr5GGHLN3MOx/ADjs7jWip8d/+TibPGWSzXz6efv3zNk2ZvSOOGWfJOsRx8FDSiI3huwMxLER4yZ0APB/HAqbVeDMUlZZMoaMZhug8GU2Yhw/Y6SUBd8DcH2gAmBkQKvsvPPOs4oRlXbhhRe6/S8Yhg/CZEv0JQyZyp+/VtMP6+YqOrFFkSIwvzo6RYcWPuDCJU85dBf89Gfu7wKoC4Or502aPKlL3zHs0A3ZSV7PFyaHS7ASVTQE9lcArUYNdb3rHoXLvp4oWpesbnnfGb+eP0KPb+8qWBx+0l6ujTRbRUeNNc7ZzUoSW1lzyVJLtS6ywvYZqOC8bZ2lY6x0zCUWKTvcUvlBAPHczP/YX6F8kUOqkq/Wmn4fZS1rXi6+/FK3rrUuu6hG0jJXw7EpIueQZVnJKeRN8qPqTEr7Y5KISAt2a3Ek+L1iwBglH0twuCXgbZK0RAnpfcqQLjrARraSYlMB0vWs4ZSliAzyBNzMQjQWXlSZuAKHH5PbJ++Pa3r2uefsNgC9WrjHEGXJIZNB1vClXXdop2k+PNZuqG7m3HXhyHeTAb2A7jqBqoUJnCpCX9aBDmI55s8++6wrgcgZE9/RTrtuaYccfDQLIG5vzVlhT898F2xZAQ77JqxNsio56aMhvcxamhKoKTTbewueIthbbU31lCtxlArhlistjtjquia3FpTlOumkkxz9iYyVDqg/wRX2t7/f4xyynRECz8yuyA6cf+55JqytqC9uv/12Rxj93//93/Yp6C4cvAH71sXqn2aT98GccKox9HVlW4TNFc+WoBEipVVgIPC6utZzo+cZ8PZd34UL1b2SbJXK1OJ/E77MZ3nCZ0HYng/l+XValkp2cQbWWrVVpN63+rd2tSIkNBvoFypkicXJmjkpQI7MkrF3kjn7gis4aTzz1H/sj3+42a0xOaA666ROIntXgJN27R9udM5adw7ZcJmj8P3LOWRZVnMHEkf5MbzvtEOWxMgI2Iqf7oK/fLqibPF71rJotkVqlloH7WoJ8F955Xj7m2xq+TvsbjZ2tHXGINlklckhk9ZqG7ideLFcuSCi7WnICdOGDBvFj/ycRl0/AY5D/FNj2MDiF/sEHVKu1ToN2pcxDjNnD8foYSgbnoG6NvE+iUPOOV2sA3VWXnjBBeCG9rf/AePjS5V6/0cf/Sedd3e7xwnor8xoEWWBWP5IsGFRe+mVZZDGtsN5l7T6ZpEmI0Lf36aVgfrgudd1M1BcJBB4AxmBGg7pHWzKFqUEiBxu1XRN56WI+Ua4Utfvf/97B0uQky4yWMEYli1b6kraO+ywg5M60gHl9Belo8t3lR1v/+vt9g/KmxfSfX7zH/7g/vYLfhZeTCVM2b0uCh2eH+Yycw4bJ67K6CsgmP3+979vO4FHO5fMnBsKXHPrK+tKVnOF1FHkIKucLGyoMmZi9RdFkcqXmfyDw8V+q44DpaHrTZMedFFqjSXf3NWijWOtrWSWxdF3tfZdcf5fs/ayCVYw7iqzEZ8JshyMV+gWvolqgD6/t3X6WUNrWOL2csi0RsP46KyTPkQfkHPIst0Y1bwF2WAxtWN4OujWjOEEBXqTjPnvQpH9liUXvMkiW4mrj1grXWqpgkqLkIKthXSz8hO74ZTBgA6lgDJiMArATYbYakRNA9kjyDDWS0ZUC0/lhMDomeMR+hPUFhpfI9W994y93SHuRggDkvlRc9JI2W7++v97B6LyLgPKEJhampUfwMQvh2saXFM6NLU+VDL68Y9/7DIlp1HGnATlRXD/EZ2GOmXWm/X21JML4I2KgsdAsLeTbsoOJLoc/jE3huwMYHgaGuvhSmy3PXefjJII+raFgOrjSZu8FfcYTJl45q677np7ngy5MixbQW+i3wlE/vsbf2+PPfaYwxxuD0+dhg592Q85AG++/oZdAI7sKFRJZEdEv3DSN78ZyPqQZVNDUiaZ7EfoGNJmUNmdn4JDky7uSUjOySHMOWTZV5VvfFCWUrxuGqvR2ROX3Msvv+yyZAL7qxwcFjXX48KUT9nfaf08ImHNVpCSgDidpWS9itubgYvta5GG0dZevMBiHRBxtu2MYz/LEujrFoz9FQ7Z0ZZS2oyz9vmZL9r1rG0NOWJKKqhqoDNQGLIrr78255Ctn1u7nt417d2n5JClwIvBD0aCVLwR0hGxhn/eYC2rl1le3SorpXW9KK7yIdkw/p7KK7CGNsTE99zH8qd/0jpGjON36FU6PykA/XeFAr18vMwOUl8z11Nm/uepLjHhb33rW84g+m4ogXx1mIczalrIvlQ5XKKs9XTnh8bbhjBkb77xhnO6PvOZz9iJlKecv+UwPvkuO/rUU0850s7tyYjoMGyRQgMs4fPnN0KPscSWL0PAOFJGYAHXHa0mRXRVdlA6yI2hOwPqtI7kQ4tDCJhMrLI33nqcg3k+2bCRTvy7qFCNGXEnDi55pV123cVOPeXUrkBsNgTZAurvgySSyFszg7AVy5bbBWRcfXZGGbZdwSZ6h06d4b11+0ktYMSIkXbXXXeC9fmr/QSnTAz+XWSovQSEQ3fWB/fKenLKlPWUM60y8JFHHkmTxfiuC1MQ5jOeg3u1H+/dUpBLR1Kkuzgym/LarKR9lbW+OcEKG0qtqbjRimW/kpsC4VluyfIiMmR/xiE71mXUZPpemPmc3UAp3K9BOWEKTHV2qdPXO2S5kuXHuy/D99Hym1g0gUPWZoWiwXDOWMLayUo03/8bhwmjt8kKwFPE4HTS45OpVspMSNDUNFly4jSL7X6QxbbF0FmRA/+7nEdeurieZXa6Y2/WhhRu43//cpszsjqkDzzoQNdg4IG9AoGrZOA51TxXW450cBgtx3TbvCJoGWjxQ539g7PhjtrKHZodrD1hTk7/9rft2M9/3r6M8fZdmSpzNjdH7dF/vgtNxkobNWYS2ZUyW1lTRXdek5XDCt/RmDsxh/JqaG6vw65UWn6HMt716IzOxclehcbkTGwI6iLYJNEk6EsaplICuQTWfX3XEBed/i2KDJUUReYqMLS3KW3YMRFoS5RcJW5lYJU98zbEZ9h7xOOkA4afk5lbsmSJawrQoelpV3Kg/uyrS/dCDobuSxior6yjsH3KiCu7KQdcTpnsuGz+cJC2CztkzWS8ilOrrG32FIs3VQLqX24dFJTiyVGci2uso7IcUP/Nlld5rCWjUphAGu7pZ+36a691k+gcUMqTygxGyN4KkvO7q67IgfqzL7EN6BEhhyzVodZzeWdUxmvqrGnFSit55GpWFOVDaf61E0+C38hzjpbA/XzFyaJFwIHsfoSV7X4I3SMlePiUHeIUL+kkyYYhC3eOhOkuJAastLZ4o9SRc9BBOHwsVnEGBdCNdGtoN+etb4EPy6hsQHdsw/ooYHSECVr8/mLHmn74EYeT6fhuQOzLPdb3P4D9UXbsNxf/xlEVSLs00MTLp0xZb/PnLQO4DR4oAncUpMh8swJC05ZW6A86A6LK3BiaM9AZhdw3Bt6muYBS4yrbbPNm22Ov8XREvm4rl68iFmx1h7i67+RsCUtz/BePdxQ4jl+R/S/mfnGFOaJY6dcyurr1sBXz5s613/3ud64sdgGOlWeVly3J66FjzdsllTarIaf9gZw9MGzKkMlhEAee09DNgfp7XVjhYNsroziCZ7JAcrKlgiCON0kBir7k0EMPdbAEjeFQ4UhQkixIlVIPyjeU2axMLBezj7RoYpx1lC60Tqk/pIBe5M2zjopii445n7rmnpZK86EvgUfzpt8HGDKfEdScyb4JW3fKd07P0V4MTdM1MFeVIOXaASasKBJo+rW24UyJLLOpwapefM7ic+6kDh6ny5LMlNJowoQBNqRH3ZoSDVZCO0kN0W10j6OtbLfDLBmDDZ/XkV/X2QmOjLJmWCUg7CRpw6Wg+JejpfKjw2UwBOxWK7DAvKd/539s2rRpXenscEliMHjGPG2oShsdHALyA5Wl00gpk9jPEeUF9Tl8lm9jkmVxU8eBqRS9SpXKhOleC7zto+NE8wf20L0zbbvtP2XfO+d0OneTkB1j5PKLbeH8Fnvg/sX9vAO5p6/PGciLA75vbgIKAe9SPjYIEPT48VEbuxkZ+I4lKDGMdAdzNAqFCXhDlanjdGAWFkgCC9m2jojTpHwOfJkOsuuuv76rHOnocuB3ElZJNCrSv5TTL6fO45Myu7xlqwSidktTlAbsT2XY1Nn7bbK0n3IEtgFIPTfWwQyw/0UCLXs/F8f50MMOtWOPOTaguQk1Teg+eThKmHNuHVxBv15CerhJ1mWM9emiA7G0M+BHh48zeOkkTAadtGIK8yhFG/rfeGiCMmYjqjZoS7eiaoPOawLsWCONLMX8jLaIRTmLjfO0tzHcyHVzoP4sy00OWScOWSEOmYD9CRZPYVQly0ZrmD3Lyp7/K04YjhLrLIFGZqKNXkr+XMTiihSwWOqXW03ZOCuZ8TmLc2gmcN6UtFJlM5VqxqBJvzLg+ul28YQwRNpowmxIVHjp0qX2ta99zRE/KlLwQ0ZXzotvTR9oPbT+u1y93wBt356cMPEgiUNpQx7qsqyvb3Di8O8jh6SMg+6t7rPArVtNHmkrljTY10883fY96NOsQ9YrpfOGuoj9+4l3kFDKlSSH8/qIFRdCY4PQKFQ6MZqCJKOQSlZZY8s7CJC/Za1NNfzKE2DmY3/QQUSbLYYDl6SkHUWLVA6USFxF2vpzOijDwV1tXa3LwIj/6uSTT7a99967y+ESP5ayNJnKHd5JU6CkUrqEyJXRkXySgoWh5BAM53uva9f+l4MsapNb/3yrvfbaa46W5Ej0LwVJ0Zmk4Z3ksAM9tJQSpJtMo0iXOZJl1z/UhxlwajpWTvDX8jPzOESTwCrywbx2JkRzId1B8iA8FPPmmjDFVtAp/E8vI+eQDfcdkHH99KJZZ3sKBypwyFpU62ex5IPdSNJWHr31Ykvi3bcVwghWghGkrJlsboRfhawannwjWpXJLba1yr2PNKPjsjUF9xhrSEGmcGbi7++1dMhj3cZLp2zVki4NNHGMyYCWlH1YcvIYMe+QDcataMPoKy3mHUrfkqx/u5JaukV5ba8lCvHtRj3S9kYi0oHotzIhAQWGpFduu/lGuvBa7Je/udhGj90EA5XHARq1N1+rpWtuHnQJuUzFcF4/zewvZbtilA4jkOZEMEJtbdVWXNJA43bENhnVxr/VCq5uyGg6EAuyV+4E47ucJfFZqSQpkXInyMbeXDB/vt19z9/s9ddft0/DO/ZNuis9HYYcMV9CU7ZMGVnXdZnOevuf77vvPrvjjjtcKVQ4Vk+R4Z87nOd+KFy7I4NOSy5JgeUmCJ/lKH/1q191mGERpIbnesg5ZN5fAj/WQhJDvMX5zisDMtEidRHNch6/F2en+MTSaP6046aAv7MFonUwZRqggiyiHgH5cPyxM5pzyIbCOh20a0CsxjpwyJRydRkyZ/ragOazEDB09thfrHH5Emuny7K8MEbFktUCx1iS9l4ZvfrSbaxoq2lWtNMMs4pxHJYsPgyb2AY6oB7gXx/JjPnUs3dwVP7zJItXXXkVYN5Xndhwl36hGgg4oPV4z1cWdooGbaIG6I18lK6XVznWR9+eeXyA3nbIvKwXJ/b6gW4e0kSw+vnaK6611998yS6/+jKXzZBU1/vvJ+xfj823uloMXMRHn0PmI+Uu5GPMAMl58FgUaHC6OoBFRClbJttabBQa1DvsNNF23lUZduACZB8cbNQBIlTmh81cpx/PD2fE/M8rwSZddtllNhftyhNOOMFmzJjhGPo1tOcEMPfZ9TAhdZgpXln6c+E5U1ZMPGfCNnlKjeFIyvkxbsvgPTTtbwiPFwOyIpml35AtV9OE5K523BkJIoayYbL/Yc64oZAdaofHU7qDsZiYOzXAkrWQ1ABDTYLfZbsU00tpsC2RgtbFB+DoPidIbhSkyALX2gjWcgfd4U2U4SOFJEfIChexF6KU7HsbQ2EOPs5iyZUss8yWpMEpWwe4KP4n5T9lzWQGI1pFdfOsCYB94vU3rbRqNR0jGE689kQZrv+IMotM3d/i0oTbbDJF8xJnVF3SB+PZ5poEPhRP8tGnW7ZpMK2IF2Vpr6f195FHHnHEiz8690dk2IKIVa+TCe704P/B6qZ0WDecpTCjtHcQw5/p4yxM/9ieSq4yQBuFU9ZNAOjvt7opr/jdtbZw0Ry78trLiTZpJW/KRyJpqeMcmzhhWzCP1Wsz7bnnDJEZiKL2kWxrtrr6WrjHSmwU1DlNdE62tFaTHYsDW9gO6gvptIkxDKfMHXsR13ek/dieIMPiOtPgg5LN4HcC8d8Ci/98MmRf+dpX7SjKX94Rk1Ov4ddYJobMYznV+afOTHV6n3LKKc6hkxMmhy0s9TNEpnHYXoZY6dWc4zun9UH+yTlw5513um7Yb37rZHcmaIR5ycKO8/r98GpwcycagUG+rVrdbvf+/VmcL2Sh2mKcrjV0isetBN7M4pJ2O/jQqTZu0xgZWR2SeGtPPGSdOKF5OKTCZRvcneA2VF5yeO3O03/Q68fLOWTr9+6v83d3TMOycZJDApsh/UmZPvJaziFrNuQcVq6wwoUfmC1djoPGAsMwpkZXWGzcWLMtd0EKAsK/GF69npUGXelbC+DrkjSyUYavWxwZb3vnnXfYwwisFrMBxTM1esxoB/BXF5QvF4Y/uJNUwlkbaPyY3lMYB4F4g1bkgPPMO2E6EPoL7FeHoQ4BRe1y+mTs9V0abirBbPADh1xA7SL4xJQpdU5oGlS9cMEC+95Z59knd98OdvRzEQ/nsH2vhUNyGYdtLV13m5LEBX+UG8N4BqQ3SsYdeENRYQlflRxkHdbW3mCF+E777F1pW249lgOMAA8Maxxwqhps2gA8F8TBG6ZpU1z6jDGH4PHmm292FBXf+MY3HAY1rOAR7uoOZ13C9kl78YEHHrDbbrvNMckffvjhXa/htQZ9ln4wbNAwvrnZL53750h60+TQzvFi/p988km77trrbOp223Zh9zIbugKh+fWNIQWFTVAQB8vYAaC/BYXBG298hUyvONXK6fZupIMYPkTO105bYZ87blvON611AP74X7FZ91ndq89bQW01HeHIcQnwT5AiLHcRAUrnabAc9DLW/+fPfovDj8hlyLLMF0vACtQZIg+KjFanBHrTTprA+zQxORC/pSQBQZlSnrzwGzG8+ALyaFG+eAwUrU5mJJYWxUzigCVZXKJ89F2WYWV6GUAZxBeff8FJmqiNXJmx0dQq9LMfcljCxK/6/WBqVF580a8cx5GY4sUNI7I+p5mXLqvJmPRndEYjrmNLh4CcPg05nCrbHnfccbbFFlv05+WH/nNZSl4yJGyYa2tq7O9kKJ54fKad8u2TbAbdbe8varWXXqyyZUvJ0ipsiJBFVOdvbgzbGYhFaVrJa6IjDUanBIoMtQnKOxFivLgzLxPHtds+++1AcwvmxXU4a7/pcHMqtw6v6mks3oY6QQLW2qvHHHOM05wULlElSWHEvPyM9pmyL2HZNl+CFMWGKDSef/55FxRJFUJNRd1xJQ4HWoYhvzDSXdayq4HtC7rt18DmL9LYBx560OleivZoyy23HHofh6RByhGqA/lhRdYRH1537X9YuxOtuTFmpRUNlOOpJKG129a+xL72jZ1sk5EoUSjCAAtrc+616pdfsOLa1ZyV1DglOk75shW7VlQCZvbki3MO2dC76wN3RU04FCWqMQrMqqI39WtHTabklLC0sn9UHVv5augUSB99QFLMmDeL8TdVOh2prBwlyT3oF3LkCF2TgBvVLeIdMg/u90KqwmP8+LzzXQeNDKg2nSJdB/RMZ0nCxIuZBjBTjHwgZuk1WuaXLVuG3McaB9Ys5JTwJVMfLffnfRPpDJmAq9qkOiiUkRNu5cADD+zPSw+P52KQ5XyJ6DNcRhKW8IKf/swOPuQw+8ZJXyeLX24vPb8MeoMqlmmxjRxVjPD0IiuJ9Y6xGB6TsPFeZSwKgSblyYJCcKKQUjc3Q65ZTPY9nmdV6FkWdlbZcV84xCZOwgapA9yBpDvIIGAjFAxS2vEHuDjCtI/Unb3X3nsF8mppf10lLtkZ7dmw3cjEgolyR12Zylqfc845LiAKZzsV/LsAACAASURBVGbCDQAb711bd59cndRehso5vXJU0kO4skvgj3vllVdcllJNXjpDPI5v3V1FP16pC9RPYMl52ERMfcst/7Ky0sm2eg10FrGVOGNjOBcrWZtkbU/cleBC0mBweyrz8cYjtuK1l6wS+bBCIflhkm0TdCiFM1eJRvSJ8Jb1MnIZsn7cu43yqRkRkNjXHa0BaV4BZhe+v8hFoXLGwpkv7/T4duehPne+AzTzen1pY6hff0/Xlym2nNmJ1v/PJRZvnbHCCakkbPYi/HfXX3+llZYX2PnnnkXpdkdbtsjsgQfnWUNz0uJFUTjw6Iwjqs4D/JobG/AMdBbZZhONUg//J84nypbFMbCEDcAhSnHIcLgEvld3nnisVKbca6+9XDasT1ivUMnzlVdetj/d8idnh+TU7bLrrl0OXU8zLOdOeyITbB7OvvXl7sjp84Sgg1kGDTukmc5puOGoL59hIB6zjHv7+xt+75q9TkY67zNoXypod6S8IYc7HKwP7tlBjQk1kXxoo1SybKBkeellj9nIEVtTRaGTt63BykvLCDpXWmVlqx16yFTbeutybB70UTJ2L820mjdftbxVS6yyhPSFY35ppxpTQrKj2DpPPTfnkA3EwtpYX9ODNv3n9xpyV1x+uRP7/dKXT3DcQGLh1ghHo8OhkylMTuudL98tOZj0HAO1vpRZ0OfQZ9LwoOcwPUB/3jswTIBfSbPKQK1cscpuvOk6+2DJfA7XE22PPXbBea+0116qs1fAjiVx2lQdb4YPz5W26RDOjQ13BjpSRRYvrrUDD55kUznIgNVbeysCbTR4aDQD5v/Vr37lJLdO+NIJ9qlPf6qLt7Av1BTuMGXdiXLh9ttvd9CBr6EMIpvkDnlJyWUZHpem5yog+zhBpPaVd8DC2Q45Z33ByfY3Q6L395yOPvgaTEhItrlVD4eylncB8l8ET+HZZ59t203frotEPNURXL+Gvw/6ebDwZam2JtdqEo9TOXEdloYD+aTFCzeFXw/6J2yn8LGNddJETVH12NomTyJz68jIuO4X/tfa57xhtvJ9YNgC+kN2TNIiQoOcReEo+24gPN7T6O/9zzr/6/gBOQzZOp7Qj/1yIdBmPUSnYmC+A8N39113OZHo7/6/M1iogS5dmN7iY7/PenpCJqlk5mUM9wxZTxv+42YAero9HTR+6FCMUn5qh/rgjjvutIceegDG7oNI73+Dp+XZO2/X2HNPL7OaGkhU4L5LdsJNRak3DjFxHni73NhwZ6AtQZkxWm+Tt4zasZ+bzhlGo1FTvZWWlFnV6hq7/8EHTFxhwhmdfvrpzhnqroOypxmSUogwZ7/97W9dKVPcV0cfFWRh+uKQZXZD+/f2gWW2/e/3lw94vGPRF2dsXd51H/x6WqLBfv8eP0u6JHg/91iNFuU0kJ111lk2loYyV+rMgJAqgJRD6TpwuZ8+gFyXc/XR1/IBIVWfFBmyhk6UHf4N1rGckmUzWbM8KyF4SJDRHzM6BhZ2K5syudI5ZE7+79W7rGb+XIuAISuBvaAl2WKt2LR4vNSKosUWO+minEM2cDdv43tlnyGrqVGEMNLJmIiVffTo0fbDc35oE9OgdU8tEc7ADGbqvj93pqdM3uAYhP5cefbnhg83/Sxjt66yY8G7p8hKIHFTXGqvvvwamoOX2S677GInnfQNGwnvkwqSjz7yjr3xWhUkkSP4QnCa5hMsLkaNEqc6cXNjg52BluZ88IJkH8CS7b/f1jZ12xLyECLnMbvv3gft6muuc2VKYYy8oofPLvUl0yOH7OGHH3a0O2dy0EsaSaVw32iSTTw8nA0P7wufecp2Y/ye0uO6K7Vlc+iy/T3b+8vGZtqvMM3PerfBPqDnOiV/pfNj0qRJ6N1+x0olX5XmqfT3OvxZBoUaQ7RNjk1f3ffuGzQ9NVzbCAIHMNXwkUkqqZV+uCiUFpuOpxnFUxGomY4mlSRaqdGEtJYEyCZAxabl02lpkh2sDJIVPY1chizbCs/9/SMz0NTQCNYD8VVS4+pWvPrqq51MxreQxxAbvxafB9qGecWGizMTdiR1EKxatcpFZgLna+jn4TyEw9Fn0GdT15k/9DKxM2v/GQOHSgfgJZdcYm++8Zb9FDD/tO2mOTLF2e812UsvzKOUicZbYQVRKI0ldCIVkh1rFJ5sIxc6WPt5Hx7PZNnZqFEjweCsAntTZEd9ZgqZhTZ79NGH4Xv6B0Sbo+zMM8/kMaNcuVEUF5nA/V4/KWfpNdgkdVX++te/tjFjxwTM8b6k1IcmXp8t9rQ+kltSp6Y4zLJlaDzVjZ6r56njWnZRQYnsodd0Hci75aXbwvZ3yJBve9A890Mdsz8+/8cuo6mGp+NRT1BJWaVpOY5heaVuKZYGchLlULuzDAfXv49+cM6XFCWCvrn8SIvTeOaj8B26I+gyWM5WzjoTiWybus55Bn6ciaTf083mHLIBvnkbzcuzqMQnpoV6HiD+WbNmOVkMbaZwSSDMD+QdmWzGbKjNoURy//a3vzk9Nhk3fabh9hky51TGWl9yxFQWOuyww9YtP5rjmjN75umncNavsgMPOMxOPPFk5k5yI2Z33bfAli0Bp9FewOFUCGaozhmuAkqXikCj0l3NjQ12BvLIPuQZznd9wiZsnm9HH7OlrVjxll1z1ZU48RE778fnuGy7CF8zbUifaCnIcMih0wEuh0xyXdq7sldu9MEhy9RUXIFKgMqoM2fO7BOezCuQ1NbWOu1a0d2oyUlORriU2d1N7m+GJJwB03tpDvuSWRy0Bcf5ESaNVcD7kx//2L39F77wBbCFdOanh7/fslfe0R206+SNgiA1gN74MyxO93A0QlaXbJfaKjth9gycLBGmx0FEwrsIDi7fZcsC301FUKdHwdrslORNL6O/938w58dtJxZYl8M62G+eez9mgNkXjcW9991rD//jYSc/IsOnyMYZvZDBCwP6wz8P5XmUEfCp8TVr1tjzzz1va6rWuGxSNmM6lD+XvzYdFh7YL/HmHcD9KWrXIaTP3qdOtl4+qPBjtQBer7ryctfOftppp9vkyVu5FH9NTYP9/eFlCIkL+FzgALKpZCNLCq3LfGleSic1hyEbDutoba8xEktYoqXEkmDJxo7ptAmTmyB/fQSx8HftqCO+ZIcftd9HpJAcRyBffQXWV1dV26kw8aszUyVLDQWQyroIjN0Xh8w7AtoTGnpv0WP0quGbnhAd4jpUnSwY+0kOh7LrypSJ/kbfB3Jkvkd3fGsD+f7ZXruliawnc+O55vT4Z5991v4I+a9s7KnfPs2mTp3qXiZ87YNFjSHh+yDw9ql6p06Z/nIEnvxTzljg4CfoupSkYEQs/DD4FxRSqpR+mP4u3CKcZghLO5Y9HmydcUjXexk5hyzbCsr9/SMzIGds7ntz7cILL6Ttt9Iuu/wyl+lIUDOXwWvluw71TPxEn6LbITLX4fbwME+RDMSQijbXYr58BB3GZnzcQ6/Xt8UWPfvsTLv44l85zcHjj/8ydikQ4H3g/pm2aMV4DqYkhipCyRLMWBTqA6R2OjtiLvIMEvy5saHOQH6sHq7MsXBWl1lBrAHuuWegt3jUZuy1B5JGp5JBIIOW7rLzHYv+333BEM15a7ZdeumlTjj88CMO7xK69t3gfXHIfPCYKa8Wpozp6f7oWmU/9F3OkSoIypTJQdPvs0EewmTba7MGNttsM0rBW7sso65X7xcu/Q0FDJn/XGLwl3OmjNnjjz8OofjNTolBagpqDFPJV6D/TOdsbealr89xrpdKkaLfFP8mDlUUoXDRs0TjZMEoS6YSuFdSJpS+s1wv+WaucoTPxc/i6hQMTf+mJ8DBY/UQ5caiWfJJOYesr3dqI3pcOFWsBSInJJw1UquyIk4RLW6//fbO+RrupbyN6Pb2+6OGI9fwzzrA2uGT+tmPL7I1NH1ceu3PMajSfyuxNavy7c83v2mREFFkvy8k9wLDbgY6KNm0c7jlcepVlJfitNSTYVhNdzYlvViTFeQhL5M+tMK4J39QedvkA4o6Or1VFtSQTZJ+7l//+lcCgou7qHfkhIixXxmYgR7++uRMPPHEE+5r5UrIRNOyQAPtEMn5kn3edtttnV3WPDlJuJB270DPQbbXD9sMOasK7FfD5C+Q/9uz59hnP/tZO/LII52z5pn++9qUke29c39ftzOQK1mu2/ns9tXCFAjaPJ5/S0bmjjvucKR+Eog9/vjju0DheqGhtOkHYZo2yrcIZzr9/Q5zt/3rocfs3r89ZPscsK994aufhVaMVvGOUsTDl9m8t1utVR2VubHxzkAk5rrS1IAm3coU66G0LGEnfHlnaE9S6IUEpaKekCn+996x8WvPH/LiMFu4cKHL4I8bN65rntcHKWoNihVSBfH2dDBuuuZh5513/j80ET67P9AOYV8+Y/hehLnllE286fc3OpD/BRdegLTSVu7lfCatGQhEcWnQXJUbQ2MGcg7ZIN0H3xUZ3jBKK0uGRGBwSSNNmTLlI1cTPpgH6TJzbzPIMxAuG/mf/XdlKy75+S/Qf0vYzy/6hVWMhMGa6/vggxa79+7XKGVPsuZETjx8kG/ZkHo7ZcgikU7KlXA81TU5rM7oMTH79D6TbPOJsbQ79uElh8lB9VuvLKHvghN4oXFlwJQJOgX8mBwSffed0Xqep60YaIdE9lJZqoF+n2w3VQ6YPrOfn8xGhWzPH8i/hzu6M8+Mhx540BH6igrjvPPPc465oDBd8nt9aMoYyGvPvfZHZyDnkA3wiggbuTAPj1iVr7jiCpf6F0/QjBkz3JVoQ3nSweHAxD/A07fBv3zYgHpHzGfNnnrqKfvzTTfYbnvshYD4/3BgIgJdGLNH/7kALE0NnZSjLdUZCK7nxkY6A2TIklCdlBUXWU11A3CICKXLCESxhbbvfpOEf/7ICDtkvvzmH+BtlcdKSSNR+LFvf/vbtv/++7uHZeLQBmPWPRlrd+zyA92T5ku74YaE7vjQBmMe+voe4WuVFuYfbvqDPY4Q+QFQYZzGvdTw+rh9wQD29X1zj+v/DOQcsv7PYa+vEI6k/OGrzMctt9xiTz75pH0L/TGRLXrcxlBKhQ/w1ORePj0DYdCzDgA55FoHv4EgeMWSD+z7Z3/PJm+1Nb/Lt/o6s//964t0UY6k9ADAVQr2ubHRzkAMvcKmZsDapUV0WnYCmlYzGg5aeZsdd/yuMPZ/COr3k+SdMg+q15oLB38+my92/nfeecckSi4RcY31FTCGr28wG5q643scSmz94esLz0vXz5SxF1FyFr/lBx98YL+86CJTN7grW8JJB9/ERrt3huIHzzlkg3BXwp2FertHH33UbqYtedq0aXbGGWc4EKZGLiM2CDdjCL5FuIzt8SDibBNuZ+cdP2E//NFZDmAdh1vsySeW2/MvLAXcP8raxOvjWa2H4OfKXdLAz0B+rMA5ZEXo/Em/MtHagVOWYF3U2lGf3ROKlICHLrPk550yz4T//9v7DgC5ynr73/SZ7X2TkEZCIIRQAoEgz9A0QSwPG38B21NQEbDB86kUQSmioChNfOrjYS8UQSwoPgmEoBCQEISQhJCym+xusn16ufM/57t7k2FddsPObLIz8/t02CQ7997vnu/O3HN/5ZzhHrnsaLzkkktk5syZctFFF+22b8s9o31R4zqSXZwjl8OImZN1mCikh+uQOQ9MPN7edKlO1Lyc/TqEjOvpzOdVnp/xBGoJA/IgbJUoxks8L7/8clgrDdUDKh+b6CV6XftXQva64Brfm/lB4ZcHuytfeOEFueuuu8yHh+3IVJweLtg4vqPoVsWKQO5TLsk7ZU4YQWVH2Sc//jk5YenRkoR09a6dWUhdvAjNoRDEX5Gqkph4YI+ko3wRYA1ZOhODMHBCamlHE4bGGOrI0qlemX/YVHiethjJiJE0v/i9Q3IzkvTMn5Hi+tGPfmREqqn6PryMgjf2fWFQPdpD6r56gOW5Oscilo6UxmSq8XUcUYbPz0c/S4wMzuGOO+6Q1XBHOHrxMbBW+rT9oVFCNqm+PJSQTfBy5IaRmYZigeWvfvUrOeuss8yXHUduFyY/+M4Hvdg0VCYYypLevfOFz7UnWb8Urg384v/G174FLSl00cEr5MEHV8umjdCw9kxBcb8lkVSP+F1IO+goWwQYIfP6LOlHB2JNZR3IekbqaqsgHNwtvkBCPnb+EbvJ1EiF8bnfT7mR2quuuspYuF199dVCLS6HgDk6XPuqqH349ycX2tEB21eEbHhakFg4nfKT4cIbbS0cr2Ra87W3t8tNN91kSPi5555rMjS+AFW+dEwWBJSQFXglhn+B+OH75uTraRXydajwLz72WPnsZz8rdRDr0yeUAi9Ake3Osuynb5KvJKIcvF4efWyF/OxnP5Xjj3+DfOiDb0f6aYq8sl7koYc2SCINfQOY8A5Ew1IBxXJ3Ro02imzJ9+l0Fx1bgYahGei2hCEziL2VRrQM1xqct/AvuO7wB0ZPPIjeO51320DEvvCFL+D6Ox56Zp/Zp/PVgxUWAed+5DRjsFGI9WSLFy+G68cnjVCsQzidQECx+CQXFqnJsTclZAVYB6fOgREt/tl5guOHwEpnxIe27Y0bNsiPf/xjozb97ne/W964dKl9ZA0ZF2AFinkXFJtk9xo83WhOiZvkd77zbVmz5lm0qV8hc+dMQV1QjTz1RK889WQbpKn9+L9XoskYIiOQA1BCVsyLP+Fzn3OwC7I6BwuCZviqwfdRxk5fkpAZQxojpU6XZpuY8QZ+9913C1OW559/vhx3/JIJn6MeYGIRyC2J6enpkTvvvNOUzvA+tGzZMnO/yq2Hc0hcbsR0Ymeoe3cQUEJWgGvhtTqPeGHHozETyfju7bebL7oLL7xQ3vq2t6JOCKkmfhFql0sBVqCYd2EXXSeTtGTxGb9KNnrwyZWSA7xtbnipHx6gHbK9PSG+YIX4YZGUQjtdGjfXsaxDihkZnXv+CFTXReSEE+bJoQdX4+sGkjpUJqMn4NDDoCFkOaNjxw658sorpaWlxehWBdmJp6OoERjelUoyxtQlHwDZuMGuS8ehIfe9uZJNRQ1AEU1eCVkBFmt4iNeptzBt5TDpevnljXLll680KvysDZoydepuCwuNkBVgAYp6F5StsMOkjKg+//wLxqbmrW99u3Fu8EBX6pH/2yTPPdsNS5wq23QeQqA0dEvEEX1VQl/Uqz/Rk7eyA3LUomly6kmsA6NLiF0zZA35Nbv5ByN7YeGHWx555BH57+99T84880x5FyIo+v000Ss0sfvP7YTNtbtiHTMtsd75zneaFx8AmeHJjYrtqxq9iUWguPauhCzP9RouEpgrysddRwbDxlNs9erVRu166YknIm2QMXUcZmjKMs8VKPbNM0MWWV5jvvu//3uXrHzsCbn0sstkOoqpO7u9suKvz8u2rXF00TVIOgvFcNjj+LwBicM33Dc8xFHscOj8C4pAIhGHA0iFnPHv8/BwyPIJ+3vHIWTZlF0/RkLW29snN0OsmjflCy68wHhXuozTs45iRSA34sXAAUkWU5RMXd5yyy2yceNGOeecc+T00083pziZOkeLFfN85q2ELB/0hrZ1LvqRFJz/+Ps/GJmLJUuWyMc//nEYvIZAyCzzJWiMXn12W7KOckUgLWGkKasqmVJyyec++3kQNEu+ddO3QLpEfv+nDtR7bEfq2yv10KtLpiJIVbL4n9cRrx31sizXK2dvzjuT8ktdQ0xOPmmGzJpRia46m2AxZZmF+aWVALnHDZoPiY+tfEy+8+3vmMjsWWefbe9eHxj3BuZJ/Z7c+xIjZnyRlD3++OPyzW9+UxYsWCCX4QGQcju5zgROLfSkPrkSm5wSsglc0La2NrnlOzfjybPX2I8cBc0xx9iVhzXdTbzr6ihjBNISh8xFMBCSvr6wfPL8i0Dej0cd2QWwwonJz+/dAkmDFDKUIQkFeeOMIkKWFK8nIFkLr2yijLHTUx8LAXe2AQ9/O+XgeSKnnLoA5MvegkX9rEP0gpkxWt+9a5d897vflR2oIWNd0Zy5c5WQjQVukfw+17KPU2YWx2lAo94hLbLe8Y53mAL/iooKc1aTQfS2SOAt6DSVkOUJJy9udi0NF1fs6uoyF/qvfv4LOeLII02hNr/4TFQMYn27iZk+gea5AsW+edqEIVjUv3lzm1x+2ZXwNj0XYpynytOrn5WVq2EQ7aoAAcPTK6JhmWxUXFlExbI+kDM/vlg1QlbsV8BEzt8rrZLKtEljfbec8/6TECGze4lQ3W/EhgPwwgyj87utvU0u/dKlJnX1MUTyGTEzciz6wDiRyzPh+85NWQ4MwGILtWIcfX19xiFmA7r/aZHFcc0115hmjlwnhAmfoB7gVQgoIcv3ghjqUnKIlol6gXi1b98u10BUkU+iFFmcAquK3NQmL3qKC+pA+gTFU7m+ermYlLrKVjIbEZ8LemKIVPz8Z3fL7//4a7nqq1+HuOdsueeetei6zE+40euBzlB6EDfWhMTw8OD3VoP/+WUwMiCNjVWSTfZBf8qH+qJKmJfHpLLSAwNzrwwOJBGRq8VNO5znJeoFacygFimLflGmS9Lm6dyHlD3dBqx0HP/mhrK8HxFjD0inzzyw+LzoB/Tx3/vNtcEx9ONV88m4B0ednzeL8x0audtjCmZ4vTU4LqJFkKfh3MTFmipEjnxJ/IQPpJvzSuH3jl4czgedilmLhfCsxyKhnsTDn5L+HpE5M2fIoQsr5ehjUCqR3Wlfc1k7GsJxGZqNNm/eLLfedivMyWt317jy+6ukBy4K1lbxgZrXJb+LHCX+femZud8wxkfrlxArv++++4wjA8m4cy8z548mIh37DgElZPliPQJjGMSTyL333iu///3v5X1nnyVnnHGGLfzJGyLrNXiha7GsQX4kPzynzoG/L/U6hsHYgFQG8NSK6+h/fvh9eWr1Krnm2u/IhvUJWfXEDlw3CGnkMWJRRG/9SaQiEIVLZSQQws0WhKyzqwM3oYw01HkkFnGhhq0J5CdtatRc7gwKu0WqKmoRXYnlcXQ28LnsFwiZU8vCGjnn3zMWpDyQR/MjdOMBCSMhylgR3CRZKxfHz3p8VqidBRrk9uAG4ezT/uC5Eq2jzi/pbh861tCNJQsNLtyEzf+wi2y2CzgEQVSrgHUl/sELAmYZkV7emIiVo+Pkwc2JN26j42UIGfcxuR8ZLC+IZbYWUVa3tE5NybvfcyDOiSQW55kImRTmH//wB2Or8573vEc++KEPGTyTSKPTA7Hcash4jTrXZl4XfpFsTCX/zo5Oufnmm430xUfP/agcc8xiiaKulXJN5bb++3vZlJDluQKONQV343RP/uOZZ+TGG2+U1tZW+dJll0pzc7M5Su6TWJ6HLbnNWbPgdACVE1nl7ZxBiFgkKtddf6X09w0ionqbPPDbdRIJ14MYjB4BGutCcLuDOEDcRKn6B2IwoK6V6uoqiUb7parWIw31EC1e34UatnoQHqTS4/24EUPXnRpnIChpKz9Cls6w/s2DfdsRCDYuODUtJiLhTg4RG/4OaTITcUL7KIgZX1lrHrZDpIohLZwDP290N7DQFWihBqoG+x1tREEEKecASoefiGzhzVnLbebArlaf/xVDwsxLkAIGUeH7eEwOT5Z1evafbT9Im4RlMAfjA4mU32QeGQ/IeKBR+roHpLo2go66o2EUbkcDfbg2+J1F3bGOjg659rrrTMqKTDWO7kyjlVjqETJ+b+NCcLQkndITrrFjEzWZ1zffubHBjNf1/8F4nAr+Rx99tFx+xRV7lADKYP3zxbCQ2yshyxdN5wF5SO16cHDAGEOvenyV6ao8BbVAHCNpuowUHcp3OsW2/WthwC9DvkLo/CnlkUa6jmm7vt5Ouf7rX5WDDl4spy3/iNx73wuIGE1HNKM3r9NPWTC1d6fwtOuXeAxt72mk4EBkUvDBPGBGrSw5fj5U2Z+TwT6XBAIVuDnFpLomhBs2JFtgVO315RcBSiMC5gbBccME22VetuzCbpIFG6gM3AYysPRh6pLEyOOBG4GHP/Fy92FO9rWQTMURZU4gfWhHr0iKsil4So0yApWHmRSjvS87HerF/vl3zsnrb8Vx7ZRkJgNyiDo9tydDmTfcqKhkDxJnImH2ncmQRgisWuYFfNz5RTDzWty92DjjAbHNBCQVT8OqzYKN0lSZPadKqit9hms9cP8DeN0vy09bju7K95naVh8Eit0MRZp12ouDFPFbzAOReVCAZmQ5Zi3oEkKHBjzg3PCNG+QZ1D1f9KlPycmnnGLXOVfuSWsX8TIXzdSVkOW7VEP3KycN+Ri8wm677TaEfY8xhfz+YADRiKhpKR7+gdfUpbxKiJBL8S8pg/z4QL6ru4+2t+SlF9fI1792g5y67BxEqA6STZtJhhqRaurPaw6gFoh2hKUqBGKLNJuAoPmRqpwyzSdv+Lc5qOoS+fOf1qGTkxGxEEhP1JC3VDINmy/UkVXkFwFye/FlT9KJl11zxTsgQlMgM4x4uVHj5fHg2AhQmTozNCwwWsoITQqkK9r3ByNomgIRIyFjZIekiO/liCPaNdrwgOyaQ5KUwXbK7w9IwB9CnRzSdUiT+quWmXq2gB9pS5QTuFxslqAFGmJ1SF2KJ4K5Mk+KF9KdNkmhjiDra+gNObm7pBn4i0bSSIsHkbbOSmNDWt60bD4KukXa2jbL1VddK7W1taazkoLVud9J5fD9xIcTp2bMuY5Y/M4HxXp6DZf6wMcoEg5LZVWVMLNDdxBmdm640S70Vx26fXsBKCHLF++cCBlz8PQJe3zlSrno05+Ch9zJJuSfa0HhRITK4cvu9UDL+jpiQuLKQakQplEoXFnKI5VEZMpvycrHV8iDv10J1ewLpX2HR6oaD2DFuVgxEII8hhfEIxJBGpIdvik0TyRJZgaQmkrL8SBk616OIGXZIwFvi+nmjMbDOCzrvbJGClkpxwAAIABJREFUWiODSFA+I+tC1ImkCczAjQgVyZfLzXRkwkS+BD6dadSMJVK7JJrYIZFYO+bQIbFELwgR0mZx+6boRDDYCOMP+BHNC5h6rox/xqjTy0ZfMTfXZAKkDlE2/tmJVjMyEvHtAjELSdBfJxXBKVIZOkBC/in4e7PBwxUaNPMWpi4tHA/RPDNvpFNd6MRwZfNrusgH273a1gdHB0RGSchT8D8Vq1/O+9gbpApli3fe+V1Zu+YlmT17trzlLW8xtl0hWCU5XXbcf6mXdIPemwgZrw2WllAMt5xGZkgYOIGiUeJwB1wannrySTkbOnRvfdvbyiJlPZnWWwlZvqsxZD3CsO/DyMPfe889ctjChfKBD3zAWCWRkDnWSrl6ME6Bf76HL4XtiQ9rOHgz4OCN4aGHHpL7kUrxMEpRwiOF8ztgerNs2dwuNVXzQeLfL1296IRDFCthhaVC4Aqdx4gh0gMvcqj8V0gaIqD4j/T1b0IE7q/iDXTKrIPeCdHZCqmrmmOEZpmK8yFNyTWpRrddFF6s+QxTg8Y0JLoVmS50g8RYEgUB67dFbvt7EQ0bhDjuTolEu0xXpxvRL9axkXxNbX6XnVpk5yUq0OlQwJ8kSSz0TyBtOdqoROSLnzumOVMg/QmQEqrXp1J0PEjLzt4HzLWXTMB4GxFEFvdXVrRIdVUL6uoQvaupQ8NBpQR8aIbAalggl3a61K5z87onudcjZCssXGPsX83ifBPRPnnnu5eA8G7Azfcr0rUjgo7eWlPAzYcgPzDPAC9GKSmRYIHIlvLI4sPhEPWlS5fK+973PnQfN5oGrJHkjEoNC34m+HmKxZCeDFXIc2vWyE9+8hNz/ldedaXU8x6mY58hoIQsT6idon52pdCDcA0u6CtQFHk0UpZmgJCN1F1J0lHJLpYyHyNFCvkFuXXrVniAvizpmJ1yKtXBWqkEolKPPfIk0gYLITlwpLhQhD2Y6ZGK+pS4wrZu0HhHFlEu6pdlkeqLDERk1vR6mT0rIP3R53Aj2iTdg/MlGQfpyzRJNJxFhNKLNLsLNW395guaN+d8hgv1WCbVB1LGOiySsEGQr97+NvzslkT3CtQsBUGCGtDV2QpSOktqKmcgWtUK8lUjXZBoYD0T67/cLKDHftIpphTZoZuV0BgZ1Sj4BOvBfD4kLRH5c6ODlBEu2k+xKaDRgh1VZhBRuU4ZjHJOWyUc7ZRorBcELi6++qWot2qU+trp+NlsyJkp+DdNB4g4ZobSsPmANJHbIk2bARm1IOvRUFePWsFumX1ghbTtWCmPP/FLOe1N75Wm5iZcgwk0e1QbIsYHI6btGImkbWopjyiIeRXSdYyaMjq2EA/THLmejqV8/uxyMVJNiDbzIYV/p6XSk4iSUcz8xJNPKunTn2wnp4SsACtCUvEoasfYOn7yySebYn6OsiwSLQCe5bWLKJ5OvejGvR1pxCORSpqOL0g30kxI18F3EAmVUeFIJdkJx0J0pBwQVSN5sYxoLK2V2D3IlFoYX7jd2L8li448QA4/bKrZpwUZjHDckl/+5GXp3FkhNU1ZSaR7wTOq8XKDxETxJf3q4m7HWsX5iVuXiVZRwiuNCJMXKUlGt1LpGCJeiPC5D8R+wvjdVunp+6d07lgv4f4BpEhrpSbUJO7mBYb4VVVUIyJVg31BZgIkJwvixe7IiXYiYBeqbayNFCTIo4XIVzwxAFI2aKIG6Y6XZDAO4pjpl8qaoLRMOQh1WAsk6JuN99ZIEk4JTMOy0J/n7AaD8YP8ZUH24ojGed3NpjmAw3RnEigMbmOkcOLoQnVHZNGi2agj3CDdPVmpbZiBUj8/ond9UuNjN+hQxydlN9iMgMigvS/8jvMeVnnv6PrxPdVNdfLKK10yY+oM6dreiWiYG+Q3Ko88eo8cvXiOfP6Sj5XXx03P9lUI5EYCWetMpX76W1IlgCUB16Hz1kRKc+RA1O9y4i4iJWQFwJbRHBqId3d3Q2X9P+S4444rr6esAmBYvrtIGlHUq6/7MZ7U50AMtdF0HLLNsbo6CFIwuhJ/CF+g1IxKoBifDoVeFK17EEmyQGpYmF4JotPesUmmtHgg/LhQDjnIrtFLIRUVgBXTQDgtv/4pCVlIqhqGEzKm9mxC6NzzhxOywWgaES2kEcH7EskoCIZt6xSCFRT1vXp61kEVvAs/NyNN1o1/94IUtEotUoIBPwiNv95IR/gQyTFkDE0H4DIYNiGbaK9ON7okGSmjwCvJlAtRLwsG7owWULLDm+gH0QrLQKRTevt2gKhFTfqyoWk2Ik5TcLNagHmjQ1NiJi3KImgf0p5ZdDbGUR/ogw7YHsJka7EZwsRQBAhTZQACrZ4+efs7jpDB/og89fTLsn5jrwSrGqWxBR2gIIZ0+ODasrYrgXXjT5Iy3jBtrF49bHkOW/9tAKlhn7cSKekmCUNSpQ5SJ1u3rpZt7X+Xy688H4KxB5bvR0/P3CAwvHyGUVL6L7Ns5LzzzpPTTjvtVcEFEn2HlA1/GFBI80NACVl++Jkvx9WrV8u1114ry5cvN9ExR8y0bMLeeWJYzptnUWifRETs1tsfxI13KiJJlbixw7IGBKEK3Y4o5Rh1xHHDpQ8mzcYZfImjTozyDX6YSAcQYYsn+2XOnClyxOEzZdYsyj+YezvFuMyXbCSakrt/vll2dKILrw6+mqk+dD4iUoXOQo8HvpmUosgZw7+AU4zQQF2f3ZseRtW8VPyKo/asB+SlV7q6HsU5UOwVjQpI+TU2z5T6mimImlWBmIB0IV3J0nEn4sNzMM4+IGOUpjAF9BM4GN0jIbMjAMAd86cArJFCIAlClIyGGpYVld6B7dIFcts30GHSwAFEAmtqF0hTwzRISkzD+6uQ6kJ0kDjgvHyo30oneoaaCKhtZpMk+1yhBYZjVgUrse8O+ejHjoPEi8jLm/pl9dNbZfuOlAxEEeFCWYNd42SZ/QVBohlZo3tAEv/uthDhMyK39ov7J3a2mK5b+hIdaOiA6G22Cmvqlppq6NENvihTp8fknLOW4zzyS4lP4NLorvcRAk5kLPdwa9euNWKxdJj5z//8T9OJ69RCj0Ti9tFUS/4wSsjyXGI+TWzbtk0efPBBOfXUU+WII44wMheOSWueu9fNSxwByHShs1Dkxz9bgRtsHay2qkxtVBYq9WgxRRRkdB0gTwgyEREUYccgZwFSVlVVgRt2CsQAhCjSKwfNDyGNvgSkwY5yhcODqNOyaxepzJ8GebjnF5tBACD7UEV7pV4IhkId34JOFWyJLBCp0QiZOwRNsCTqlBI4LtKjblhBhaMvS9eup2Xnrn8aUdLauqnS3DQHc5uGXB2sipBKRTUXSstA5RgVA5FgWo5aSEbSgjKu1ADDf5xGmIm6DJxjsFSOxIY6aQ6hIbmJQ2rDa6yeOCcWriGdGW6XXd2bIOK7QwYTG9EwcQiiZccg6rcA9XeNIJmI8rnCOB+kMNEo4ZAl6rGRTDGt7JxzFoQ3FNop/3HeEqSo0XiAyOZAn1fWvdQrKx57TpKZGpCoKnyfhFDTlkKUbgCRyCQinyEj8JuJ2yGy1zIM8FaC2PWmJehhU4ILvpXtctgRlfLm5XOxX7L92omCVvdbBAjkfr74WaAiADuYGQH7Axwcenp65MwzzzR1doykOcK5Wo4zMYurhKxAuDJdSd0a56LmhatelQUCt5R3AyIwAKmxe+77u3Sju9IFwkKtLBfSZlH4TfpQRD7ayCLN5kNq0G0hRYZoF6UNKissaZ0SQDSqSo49oQERNHYpOh3siASZGBQL40EwQA7u+8UWadsOMhWKobi9DxGVBkOuslm4BAwJuTpzGG4V5HHXYt+MgPWhe7NNunZugAXLVnQrpjAPv7Q2nojIWB30zJrhoRjEwwo6/dDt58ecgiG3mEyrIRQkQ05qlCE82990olMizvkYFX8j/soIlj0n47kZomo9ZDNQaxeAK0AoiDouNzs2+3Dz6pdd/X+TXbv6TOq3pmaqTJ9xKLqrp4NIBiHEiwgY0s6m1ou9DdivEck1rgXcP9OzWZl6QELee+Z8vKEX+0xLRagZUVJB6jIma54bkIFBpEyRzkwhmkqLqQAiooxDGgkOSIPwO8d+OeK1NsE15uDoImQtoZcWUIiqxdG8sHTpDEietIA0gjDKJO8SLeXP/iQ4N0eGaaToFxs7eA0xOuYMkjLzuUDYWJUCCr+ASsjyxDT3Qs7dlRY+5glsuWyOmzSCVvLQw88hXUU5CGhiQTG/Ajf+WDQyppdlz2AMERpEUJDKSsYjiKxFZNrUKggTHywHzUWYaagonIXrjgAmv4SNbRC+VGn4/Zu72xDlRerONygRRNZCvhYQvL0jZNXug6U/sk52Dj4qvdG/mTqxoHemtNS/Aa/DcIwGUCvaJYFigfAwDYhEoNHwMrIR2ZBdT8V3mZSeTSSMtASIjNc7sV2MVOinRyZlNUiSSMpskmanLS03ZT9AfmB8nkVtH4mV29yQGDUDoUojlTm4AVIlq9C5+qKRxagJHS7TGk6U5rrDJOndNZSmpBMBXwzFkTxRkw1pWUh8TJthybvOmGcIsCFR7hpD3jiLHZ0im7f0w9t0CzofE0hDVxsCPjgQl57eQZBaitnaaVandswhuPwZQ9qzsjIIktwtTbDJWnzsXDloTrU0Ng1x7RJX4i+Xr5HxnqfT5Z4rXj783uVo9xndvyEdP/5ZlQLGi/prb6eELE9MGeK1Fb5tjz7e9Cb6qT7PKevmkwoBFJAn/PL8i72y9vmduAEj+oKyqWoUFKVhcG2kHkYZgcpqfDF2gySEZcb0Slm4YIYceGC9qUfioPK+XdNI6QloaJEQYHhp5I1B78jf3tsmW9v8koZgbBhyDxV+iMRCCDWD9JwbdVy5N3gnouREkbyxXdLW8aJ09K4VfyguTVNmo0ZsIQrfDxEfauJikO9gGpLkCyVQKODH5wNMI5lAuhQRn4oqvyE5TheXnQqxiUsaBWWeoajPRC1ZGlEmzomEhmg4HYom4gQuGEU+2R9A0wHTkJgX9cpQEoe3glCBVIVQMO/2wt5JtiDC+U9p27wO0hJpmdJ0qBw4Y4FEUL/FFFAAkS0XImPJhG0VZdtD0Z8pgfUKyttPPxDfGyCqhiDB8B0yFEFoejC1y0GMtm6JyboXEc3cStFcD9YYQr5pCtZin3zliN7uJmdYc/R9mE7XmbOq5A3HHYIUKfBHqtMfpAPBRCGr+y0GBBxC5qQj96Tw+TBkf/b5O5I0pwzHiaqp9V/hV1gJWYEwzS3g1+hYgUAth91A/FVQNzYIQf7nn0/Kiy/tQgpsAGrx1I+it+Lod0yX341i/To5ZH4dCJlPKoMo1hcUpu0ekKRA7REULuxU19DujJ6xudcn5Xf3d8jWbSALMgAtrh50/rWCkFGxHQX+YxCyjS9cgBqyZkRuDpeW5mOlpmEeWBcK0Wn+jeiP5QGpo6I9FPsF6bV0El/0IHsBH1KzSMcms/12gTsaGdhVyY5LPtSQ8Jm6rjydAsa+hCgjYSiPIWPsrDRRKtyLOA9G8DIWLJvQqGDBqNvg4aHYLF8kl6j1S8M9AKr+Acw/hvqyrs6nEb16ErVem6Rh5iVIZVZBX67OiNoaS6aUTch8iMpZwPy4JbNkyeJqYIP0c4BhOkS24rgGKqjdhsYK1tshJU2zdzZk4LKQnV0i3bti8kp70hT3xyCTEgeJ43ePmb+pVXNLU1M9jp+RY49rRqSMmlNwbUBHK43UhRpqExuAHBt+fcd+RWC4DiQzPs6145AxPtDllg+8VlZov55IiRxcCVmJLKSeRnEiQC1G28fRJiDdO2Oy9ZVe2bRxF27sUK33ofMSd2B2TtL0urrGh3RTNcQ861FoG5LDjxj9jjpWtJbE7Mm/t8ljK7dJIDQFtVAxFJcjOoTISsAXlIF0BJRwELaTKOhFp14VUpACq6POrpWIqv1RBj1HSnPjDJnaMgcdgVPwXpIO1n8xWmxHnEp5kJ+RYGURMWN61YM1YqdqT287ujG7oCt2u9QF3yLTW94JwnqoZBDJ7I1uMQS1shoK+SjqP/6EA+XwoyHOiyiiHXWwDcv3xl5teE3fcKzHWv9SXhs9N0Wg2BBQQlZsK6bzLTEEjAYFXuyWY6gGitkIukBtAekt/EQBPCuvXCA3XnT6Ue8rgEJtnx91Q+xEfK32uiGUxroh88jPPL1DVjy6DSmsVkTqkKLDcWBiaVJyLshnmIJ2RFyCAaj992+RTa88g7RcmxFArZ25BKKuiMJUteLv6KBMQerBGIDbul5oGy2x9Xr16fAcjVTHECEjuWZ3ZRS1eIxydbb9DdFJxLhcQRRHo7auZR6iZdNMdyujWpVIEy9/65EyZx6K7xGjJCHzefe+0F4JWUlfXnpyZYaAErIyW3A93cmFgAWl9yzkLUwkCaKk9G30uFH0Y+yGEH1xIzK2e8p28btN4DhA1LKjy2LsDSFbh/q1P/15EzryWkAScDxqiyHChTY8zMlW/fcy/Whtlvadf5HtbWulwjtLZk47TmqmzcZ80eXpQnE+pDJs821baNWFbcx5lPAw6v4o1mdNGbsmPRRlhTwGJHaNwGyyHzIWiadle8+fYccURqPDKRBjXYboYwu6JyPSXNstZ7//GAlVc2VRM4j1Zw2hD6bgezOUkO0NSvoeRaA4EFBCVhzrpLMsWQRsiQdGyNjxaA96NvKGDB2uoQCTEwgbHhDzskJ+lDE2IbOg3J6U3/xmLQ5HuQVWrSE9SQJGY3fULtGaaXBwm7R3roZm2iapr2uRAxqPg/r7wZICcaDEgulCRDTMlnNAzRg8I4vC6zHf6wrkC7FKQ0btDk0uGEV3bQxC7lYIxaILs3+lbNmxFtIkQWmEX+kBrYdDNb9ZqoM98qGPHG9quSzUo7H70iFkSXhp+o1w7msPJWT5LqBurwhMHgSUkE2etdCZlCECDESZQvvdmT2Ss1fHxHbDMiL3ypeQJdFE4Jef/vRJpNEa7WI2RO2CSFVmUOBW4w/Jzp4X5JXtK1EX1QESMV9mHXA8REVngHwgmoOUqpG0GPK6o0cjyYghY1CzF8hnlPKwkJo1qV2jY0ZtMNbP8YxtDTUXop8eI36bkL7wy9LW+ZSEB3ZIK2ruZs9YKNNbm+XMs5YgQ5xCqheinP5KQ25NbRpqB72mAF8JWSlfQ3puioCDgBIyvRYUgf2IAIv6zTCEDCIHRovL/jt/ZKGWT+V6CoE6bzROiPRExI3f6x5dOHbsCBnqnaIh+cEPnoIGGgRh0fmXQSE/hOAhbBqVoNUr6195DDpbz0tz66EyY8oy1DjNRQyIpCFuuq/sKA0JCOZvVCvsjkWSFTe7+Up4MDpIQVbaPNmkzLExshl2yuoUd3oaGiWm4j2D0t3/hGzZ/gjqy/rQEdsk737H2bLstCNMmjMDY3Omf9PwD/XSoJxpX9QUKiEr4QtIT00RyEFACZleDorAfkVgT0Rsj0K8MyH+LtfLcU/HouNf6JJ8a8iiIE8V8t07noEiPaQZoJcVi/UhUpOBEGmv9G3/E9KVSJ1BPX727EVSU38g0ppwBYC8gpFoQIehnabkjJB23U3Qhs6hxGvImGJkR+luYVvWzRlubftzeuBYkEmjxi5bCSJL1fxu6enfIO1IX/b2bJU3n/omWXrSUTLvkKmGjKNlA+8HqwWpZZfqWLoUmrLcrx9ePbgiUFAElJAVFE7dmSLw+hCwstQho8sgX0MirMYs2q4rc4Msmfu78Vq0o2ZGP2woimar3L/2GCtCZgmOb1XJf//gOehY1YkXNUsD/Z0gAwnoXO2U9g23yIxpp8FkeCkEUusl6Q5L1gsSx1qndIX4MS8KqBqVe2rws+WQVVX4PSNH5q8lPJhipHZZOo3mDONAYKcqsyj+I1ny+qqBRRhisgNiJQJS4YOxOohZ/8A61OQ9JZs2rZW3/fu/yRnvWi6tzc2GkGXRgUmhV69XCVkJXzp6aorAvyCghEwvCkWgrBFIipXyy6/veUE6doYlFWsEJaiTzs4/yLau6yRY9xlpaToA5tkHgDLS+Ny2FHKjYJ0pyVLvosz30kiBmFVWodYuAS0TCLFaGaRwkeasrMxCAb0XRO0RefbZZ+WNb3yjnHPOOdCZo20ShXQpixGFOGyFnf7Fy/GszHdOur0ioAhMTgSUkE3OddFZKQL7BgGjCm/Jo4+9JJs3uaS/N4vXJkTHnkKUrFMapr8NBfyNsOmph0+mHx2AQ8XqkMYgIWOHoY7XRiCJZtNQBUgs7KOoL+f1UNKE8iZhaWgKyOlvbZT7779fHvrjQ3LKKafIR8/9qPHVDA8OwpgcWhgjyLjtC9N1XVNFQBHY9wgoIdv3mOsRFYHJgwDNzaPdsnlzQlatGJAdHW2ypf0hiNJG5ODpy6V66iwjVOpCcX4atU3sImS9mMsN7TQSMuhv6RiNkLGODJ6dMH8P9yelqrIOEbIUNMk65LjjD5YTT6wH+d0lP/zhD+GY8Hf50Ic/LKeffroxfmce2CJ5s72ddg+nbmysdLSuiyKgCBQXAkrIimu9dLaKQEERsDJh1Cv5pa/HJ3fe+TvZ2v68DMZehj7WPJk/5wxJo7A8C89GSjrwZXdS0ggTno/UTbNG7wIs6GSLcGcppCctSFrUVVdJb3cYKv2wnoLe2MBgm7znvSfKlNZBk6bs7OiQW2+9VTZu3Cif+vSn5YQTToBbQxSNE/7dqUpHWsQhYhopK8ILQqesCIyCgBIyvTwUgTJGwMruQOPAVJAFkWuu/7Ls6HoJJOFYaag/RpoaZsECCLQLUTFTqA+vJr7YbGBBf8wUsWvKctSrh3K/KeiJkZD19cSkuqoa2EXRCLBDPvjhU+ADSr2xFFKZXpjLPy/f//73IQbrl3PPO1fmzz90d8qSZMz2ubRNwzmcv5fx5aunrgiUFAJKyEpqOfVkFIHXiUC2C1GuFvnHU21yxw8uhTG2S+bPez+Mrw+WjDsiHnRgZowVEr00PaagnwSChIxD02Zj4A0Cm0IHZkUgKLEIpEJoRpqNoH4sIe/9f4vx79B7i8M/1B8wROvRFSvktttuk3nz5smll14qFVV7dOaGR8hSkPQ3qU0dioAiUBIIKCEriWXUk1AExo9Ae1u73P3zn8n6ddukoWGRtE5bIml4U8aQWquCUnwammOUc/BCroFyDiQCdBMguaDcg47XRsAFEptBzZgblkpADFEtIAex34VH1Moppx4o/d3d0tCIMNnQSCYS8qMf/UhWrVplivz//Z1nSDWK+52omNNtybfn/lnXQBFQBIofASVkxb+GegaKwLgRgP+1PLH6fvn2TZfLzKYPytw5bxdPZUB6IbmQ9dRBAiM8RMhoeE1hU6TY0Gnp9QYkFAJpiw2O+9hlsaEpsUNyF+2WIX+VJJK0Q4rIG5fOkiOPRucqyC27KknESHQrq6qkq6tL7vyf/5FnnnlGrv3addLa2mpIGQfJscpflMWVoydZhggoISvDRddTLj8EGE3hGB5p2bqxXb5983WSRjffhZ/8ivzlz1shuQBV+SCYhAc6DSVufTTRV4JlfCzhUemFSC703lzoVA2GInLWBxZKbd0ADl8jkUgEumR2atKpC2Nx/0033YTuzJBcfPHFMnXaNJPaDOLvHFFsU8FtRpDFmOhz0v0rAorAxCCghGxicNW9KgKTCgEnvZVLzJKI1vz6p7+W3/7uV/LRj31CFh31ZvnrXzZLTzf0xthAKXF0COodP5+FzLjocZkUPwhZKk4vBo/MPrBCTnnTNKmuZXSxGgKx+D0K+TmcP0fRYfnwww/LfXffI6e+6U1y1llniQ/vySVljKr5gyhC06EIKAIlgYASspJYRj0JRWBkBBwi5kgk8IbPWjBGytatWyc333ij1DdVy5VfvRakoUKeeKJHHn9si9TUtsiunn6Imqrwaz7XVgY4IzYmPthIxaNJGIq75aRTFsicuXAhrYxi17YSP9fD6aR0CvX7+/vl29+6STZs2CCf/dznZPHixa8iZJTFCFWO7mWaz9x1W0VAEdi3CCgh27d469EUgX2KAGuOSMByb/q8+ZOYGTHSlY/IBZ+6UI49/t9Q6uSRtjaR//7eH+WguUfL1rYe1DRphCyfBbM8LhAypIszWUnEI9LSGEB35eEgukgG+xOoCfOY9Xmt8ewz/5Dbb79dpk+fLpdccompMTOWStwB3ehB9HQoAopAaSCghKw01lHPQhEYEYFcQsY3ODVk//znP+UrX/mKHLfocPnM5y4xDuE+XwDeiiK//MXTMtBXCSJRL4l0nyKbDwI+mIwj/ZtKJIF9DES3Ws44Y76xhPfAD5Rdl9QWyx2sI+O6BQJIR+KN9917L0R775RPfOIT8ra3v900AJgoJ7dTPpbP6ui2isCkQkAJ2aRaDp2MIlBYBJwicUbI2MXHm3w4HJa7775bHnjgAfnyl74kRy0+1hyUHYBUhn95Y1ju+t9VMn3aURJL9BZ2QmW2N5cfSv1pryRicUiKCNKOrbLo6GbDo3K5FFPKXCOSs1elLkHIdu3cKVdccYVUITr2+c9/XlrQdcmRAWnzoPNVhyKgCJQGAkrISmMd9SwUgRERyNWqisfj6NILypo1a+QHP/iBzJkzRz5zwafEzZs62EEKgq8eRHTiCa/cdeeTkoy1INmmOmP5XFpZL0KOVkDSyZTMPaga2mPTpbYWNWMIfbmQIs5lZcM7YXncLEiay+WWB3/7W0Og34QC/zPPPNNEx7SoP5+V0W0VgcmHgBKyybcmOiNFoGAI5PodOh18D9z/gNzxvTvk8ssvlxOOP0HgEc4SJ/EGLEPA4km/rH8xLStXdKLbMlawuZTjjjIepCqzFUhbWnLkUc0Qe62TNKyT/B7IYWRIhEeWIyFWRokfGmUWUpjdPd1y4w03oqg/Lpdfcbk0N7eghAxkbZjxeDlirOesCJQKAkrISmUl9TwUgZEQANFnUH8uAAASvUlEQVRyomS0PIrFYnLtNddKX1+f3HLrLUZtP5VGFyAceCzc4JOJrAQhrzCA0rGH//y0rHmpDjd/FJIjdZlIDCC6Ay9GCPVHI1n4XTZJNFreKc0solx+v0uSqaRR5A/4vcZqKg1MA7BLcsk06R1cJ61T+uXDH14GEhaF9IUXRIuEDF6gUPIfbaSHhGP5nhUrHpFbb75Fzjr7bBiTv9dsxjXL7dBkbZljZ6XWSvqVoAgUFwJKyIprvXS2isDrQ4DV4xiMpnCsenyV3HXXXXLSSSfJ+z/wAfNvDiFjBXkyaYmffot4+/PPd8lDfx3EDR7pNbxokp1NWxKCOKkHZtiRaBzEoryryuMo1q+ugnQFKvdJgOj3aVnEkd2t7KBskqS1TZYunSJHLpoqAVhQCeyn3EhDIh/JLotR19NJWbKjcsvWrXI7fC5ZB3jBBRfIlKlTRy3qV/Px1/dR0XcrAvsbASVk+3sF9PiKwEQiMETIGB1zo5PyxhtukPXr18uVV14pM2bO/FdChkiP30dJBZheI1v58Iqdsv6lbeAOQRCxOhnohxF2wAX5Bb/09OySYMBWji/XEQNIJmqIbskY0omMfDFixS5JFuiTFDW3WvKu9xwqXh86J71u1I/BcBxEzhdgQf7eETKTngSJY8flT3/yE7ngwguNYOxIXZYj1aKV6/roeSsCxYSAErJiWi2dqyLwOhHIpEAMqHOFCMvOXTvl8ssul1mzZskXv/hFWzYBI52GN6WPzA0py1TCEDevJ2h+194h8tiKf8qWzTFEgqbDx9IjUfhXBoIZ8eKVhvp8OQ8gJhWhSmiMpRExTBqCGvCj7gsRM5M6zHbL4iWz5bgljYigJSWABgrajKfY0eon/qOnLHev3xDIO7Zvl8suu0zmz58vF19yMdYNx0LhP2sFHfkM/p3HdlKX5bw+eu6KQDEhoISsmFZL56oIvE4ELNQyOcTr//7yF/kJoivnnHOOnHzKycbUmiNjlOIRbEFa0mL9UyaNmjKfIQ4WaqTWvzQgqx7bLDs6siBlLShKT0s8MyB19QFJhBEaKuPhRhelzxsEIaOsCOrvkE6ElThSlnHgnpWDD/IiknWIeCF/4fdZwJoUjN2VTAMTu9FlK3JryBzbpO/dcYesXLnSNGUccig0zUDGGIlT0/EyvhD11EsCASVkJbGMehKKwGsgMJSyZETmBqQrOzs7TXSsrr7OVntHEGdPisveB2iZucmzzolF466sT/72t0559JGN4vY2IF1ZI2FEycRtiW+MCE+prwu7Ur3smLRQqo+fbtTURSL9ppOypiYkb1s2W2bPqUZTRBgp3yCikW4IwsJOiSVkiK65xjBv311DBiB7enqgZdYgTz+9Wr71zW/JsuXL5D8+8pHdEOd21Jp1xBpqlKzUr0A9v1JCQAlZKa2mnosiMBwBEjLcmHt6e+WLX/iCzJ49W74IMVje6E0q08guQBXLuXnz/YakgZDBlieDSJjHUyN9aKZ88u9bZOOmPkTQgpDJ8EEeIy1BRIHKeaStGIirH5B5kcYNAteUDEa7pQEWSYceOltOWFyLtCIxjdvRsKwdEbO7I6E3MgYhM5L+fD/Wi9FLRjV7urvl+9//vmxFkf9Xrv6qIWmOA0NuIX+uBl05r5GeuyJQLAgoISuWldJ5KgLjQQA3dAqI7ureZerHlixZIp84/3yzJ2PBA8kGt9suLsc931gjgouBiGXtDkrXIP6MJJu7AqQORf4Pr5f1G3vgxdgMohZC1yUiZeU8QKqQ4QWhhfyFkRCJQrttUA47/AA5+aSZgiCZhAd7pKomCA0xENhAjY096sl8SGO6BPZIow2sBaObJFckfNQko6zGw39+WO5A6vLqa68xAr9U8edIYE2N5dIQiXOIWjkvkZ67IlAsCCghK5aV0nkqAuNAwELREm/K9EJ89JFH5PP/9V+y4LDD9hhUj1GTz8hZNpsaSrF5Zcf2uDz5t+0o8sd+s/Vi+VMSjg7g9wmpQecluwHDA6ifcgWlproOBCE8jllPnk38/hDIUMpIflBnjHV2lLNgKpCkNYMaMo+rEqwpIKlYSkIwDJ891ytHLWqQmbOqcSJjEK4xTnWPhpxtEu9oi3V1dclFF10kCxculC9/+cumq5O/Z4SMLz+05DRlOXmuI52JIrA3CCgh2xuU9D2KQJEi4NQg3fCNb8imTZtMh970GTOGuvygNzZWkyQiNKamzJW2uwNTXtm2LSMvPNcjGzZ2IwPnsuUdUOhPUuB3+xDJgVVQKiOD4Rg6EO3GgWIdFHglAfMYvTU7f2jXZcE0HGQ1jEhX0BdCUX9KMomIzD2wTo47fqbMnYPzdiXxPmCcxxhOyJyU5MDAgFx//fVoHkjKVVddJZWVlbsFgB1PTNUhywN43VQR2A8IKCHbD6DrIRWBfYUACVk0EpUvoW6strbW/KzgzZtdeZS9GIuQscSMaUwUr7N70O2CDRD+bTMiZM+teUVe2LBDQoFq8bqrEA1zGT0uW84hjbQcLIIoMlvEg0SThNPrsYllGsK4TBlCVMI2AgfhyliQCnFFpLnJL0cdOR0RyBrgwdQvbJPc+Z3/cEKW+/ff/OY38rvf/c6IxB577LGGnDEy5gwnalbE8OvUFYGyQkAJWVktt55suSHAlGV7W5vprDz55JPlYx//uIFgbwlZOo4UHYRgBRGyNHwtPegkZBqO8g27diZk5ePrpWNHGIKxfqkINiOaFIJAahQRtZgEK5DWY5CoiIfbi+gglPX5crm8ps7ObngA8YSmWDoJEdhMv0yb7pNjjpkuc+ZWofaO7gcgYzhvL4Ri8xlOB6yjM5arMfbiiy+a6Nhb3vIW+Qi6LR1CZqeZOcfRRWfzmZduqwgoAoVHQAlZ4THVPSoCkwYB6pA988wzcuONN8p5550nb162zJa54A17LyJkKB+D3yJOx2hmpUA+WE/mMZ2F2IP097llzT82y9o1nRJPVEhFRYMRRU2y+9CD40DmoaiHO4n0K9OUSMXCwYCdp8SAdWVwj5JsIi1NrX5ZeESLLFhYC8LKbkj+3mVU+8cKQO4NNrkkzO62tEy9WH9/v1x88cUyFRZK11xzze6UJVOVTKsqIdsbdPU9isDkQUAJ2eRZC52JIlBwBEjI7r//frkf6a0vXXqpHAKF9wxqvcwNey8ImSmbwj6YrnT5mIqkCCnqpfDTB0biyvoljLr9DRt6Ze3admnbziL+gPhDQaPJ5UoVgpIUHJa93+EQIfO4A1DgD5laOToVWAILKZ9bDplbL4cuQM3YXETCwD3ZZUmLI8fpoBBnnytfkavKT+LFOjLKX1x77bXS2Nho1tX21NxjMr73J6vvVAQUgf2JgBKy/Ym+HlsRmGAEmLK85eab5YUXXpCvfe1rUlOL+qYhhX5GyYzOxWiDRWGIeGUzIFdQ73dGmrlIqs1nWUvFjj6RjS8PynPPt8v2Dkg/pIKIpIGkoLi/mIcX6vrgN0ZnjJZSKVhLiSdmdMZaWmrl2CNrpXUKuykBEzTdYjGkamG+TlKWwHbBAvQ0jKQn5vzbPffcYwj3Zz7zGVm0aJGJimkxfzFfcTr3ckZACVk5r76ee+kjgHqnK664wqi8M23JGzb1rDhMHRk6CEcnZCwCY/E/Xizw58tx+zFcjjpcGaTvAka/LA6+8uxzYVn7fKfs2gWfR0SRinl44PGZhiVS1oIlOPTaXO6UNDQF5PAjZsqCBR4JeHDC7Li0y/xNGteQMyIDfTL/6M5IewXNSPIVDiFbtWqV3HbbbcYOa/ny5cbyyvmdI5GxVwfRNykCisB+R0AJ2X5fAp2AIjBxCKQSSRM9qa6ulq9D+iIWjULUtWKvi/oNyxplQPXKkDwSEgu6XB4PIjQgIp1dPdK2rU2efDqIbks7yiQQT2W3ogeF8qyzyljsQiTD23/DqbMyShZDg9G+3YXx2ekQdB3APAekpdUtB82rl3lzmxAVQwRw/01795F37dxpuizZsHHhhReaf3eK+5WQTYIF0ikoAq8DASVkrwMsfasiUGwI9HT3QKH/Mpk1a5Z8AZ2WzkijUp2yDa6xOvHGckYy1ksctF96tYpGMpmVrTtc0tbWJZs2dsLyB/IQEoI0AzwdQdJi8TgkM+onFaTDvR8T6Z3S2BSCyGudHDi7HgX06CY1FqCQv2CDA4r99+cgoaRAbF1dnZE0oR6ZDkVAEShOBJSQFee66awVgb1C4JWXN5nasUMOOUTOh2VSJSx2TEoLISEk4hyrxNfe1xiEzIX6MteQRlcG3paMLHkDe0iKNeSVycL/La8MyroXtsuO9n7UngWgh1Yl0TBTfvtvkICZaJgj+srUI/6NkT78QY5eUoV6sWqZijox405kzocelEPA4Dz254gn4qawf/v27WadWdjPyJhT2J+rS7Y/56nHVgQUgbERUEI2Nkb6DkWgaBFY+9xzcv3XrkdUpwJ1T0dIJX729fXtVnaHytao5+YeIzGXReV6EMr8bhaxJxOmnszIYjCNCaJjBSPgNSjut2BIDnuhWNQDzbKUDPQlUACflsqa/euF6dRcOSlKm4xBmX/o1doaEo/PJX7IWbCRIZWMG2NwNjL40Rxh7ecu0mzAK6tXr5bBwUFTS9bS0lK016pOXBEodwSUkJX7FaDnX9II/OPpZ4yPJSMmVG4nAeno6IDPZDUkGtJjFvV7QbRGG1YMBuWQWPAMNQewE5FZUBKcNI4Zh0yEo6Plx7EDwQojCUFx1Uw6K5H49v2KvxfSHZwr1fc5SMj4byRkbkTJklEkYxksc9tiqzyXdAYdl0xVZr3ihQDu/hwW2jgbGhpAioMmdUk3BpLvONLBPAeutw5FQBEoDgSUkBXHOuksFYHxIYAA2PqXXjKpShIk1hglEgnYG/lNYX+GPkijDC8I1mjDD/FTCqVmWMmPaJuJLFFKA5IYhrwkQxKEn6UPUaZkOiKRcD+K/OPG4ofyEB5X7fjOq4BbOdExh5CRlFG2giKwGWpXcHgoEUKVEPh2Zt3ADi9Ifvi88QLO5PXvaldvz+6oGMmYM0aSynj9e9ctFAFFYF8ioIRsX6Ktx1IE9jUC4BFJEDB/YIRap73RIRuzqH/PCWVJ7kyNFaNJlok2uSy/6bBMZ6IgYJSN4Pv5HxC0OMzIffu3ButVfHSEtskhH3FTMpamAr/pXEAzBCNohnfu3y5RmzzumXgkEjHRMkZE+VOHIqAIFA8CSsiKZ610porA60eAshVDN2wTEUNqzpdr+D2WdsMYhCzJmjGm+eimvVuojJVpTAHSlRyK/YaEUdsfFuXxFMiaT4Io/DeHhhL+ZB08dVcK9W+7ddcyOAP+jyIfjBzC7WBId2x/nQMxJAlj5DNXxV8jZPtrRfS4isD4EVBCNn7sdEtFQBFQBBQBRUARUAQKgoASsoLAqDtRBBQBRUARUAQUAUVg/AgoIRs/drqlIqAIKAKKgCKgCCgCBUFACVlBYNSdKAKKgCKgCCgCioAiMH4ElJCNHzvdUhFQBBQBRUARUAQUgYIgoISsIDDqThQBRUARUAQUAUVAERg/AkrIxo+dbqkIKAKKgCKgCCgCikBBEFBCVhAYdSeKgCKgCCgCioAioAiMHwElZOPHTrdUBBQBRUARUAQUAUWgIAgoISsIjLoTRUARUAQUAUVAEVAExo+AErLxY6dbKgKKgCKgCCgCioAiUBAElJAVBEbdiSKgCCgCioAioAgoAuNHQAnZ+LHTLRUBRUARUAQUAUVAESgIAkrICgKj7kQRUAQUAUVAEVAEFIHxI6CEbPzY6ZaKgCKgCCgCioAioAgUBAElZAWBUXeiCCgCioAioAgoAorA+BFQQjZ+7HRLRUARUAQUAUVAEVAECoKAErKCwKg7UQQUAUVAEVAEFAFFYPwIKCEbP3a6pSKgCCgCioAioAgoAgVBQAlZQWDUnSgCioAioAgoAoqAIjB+BJSQjR873VIRUAQUAUVAEVAEFIGCIKCErCAw6k4UAUVAEVAEFAFFQBEYPwJKyMaPnW6pCCgCioAioAgoAopAQRBQQlYQGHUnioAioAgoAoqAIqAIjB8BJWTjx063VAQUAUVAEVAEFAFFoCAIKCErCIy6E0VAEVAEFAFFQBFQBMaPgBKy8WOnWyoCioAioAgoAoqAIlAQBJSQFQRG3YkioAgoAoqAIqAIKALjR0AJ2fix0y0VAUVAEVAEFAFFQBEoCAJKyAoCo+5EEVAEFAFFQBFQBBSB8SOghGz82OmWioAioAgoAoqAIqAIFAQBJWQFgVF3oggoAoqAIqAIKAKKwPgRUEI2fux0S0VAEVAEFAFFQBFQBAqCgBKygsCoO1EEFAFFQBFQBBQBRWD8CCghGz92uqUioAgoAoqAIqAIKAIFQUAJWUFg1J0oAoqAIqAIKAKKgCIwfgT+P9fN4VL3aMJkAAAAAElFTkSuQmCC", + "created": 1710768417758, + "lastRetrieved": 1737485739181 + } + } +} \ No newline at end of file diff --git a/docs/diagrams/elotl_genai_stack_enduser.png b/docs/diagrams/elotl_genai_stack_enduser.png new file mode 100644 index 0000000..37db3ce Binary files /dev/null and b/docs/diagrams/elotl_genai_stack_enduser.png differ diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..eddf3ea --- /dev/null +++ b/docs/install.md @@ -0,0 +1,531 @@ +# LLM Chat in a Box POC, v0.1.6, 01/08/25 + +[K8s](https://kubernetes.io/) + [Luna](https://docs.elotl.co/luna/intro/) + [KubeRay](https://docs.ray.io/en/master/cluster/kubernetes/getting-started.html) + [RayService](https://docs.ray.io/en/master/cluster/kubernetes/getting-started/rayservice-quick-start.html) + [vLLM](https://docs.vllm.ai/en/stable/) + Open Source LLM + [RayAutoscaler](https://docs.ray.io/en/latest/cluster/kubernetes/user-guides/configuring-autoscaling.html) + [Retrieval Augmented Generation using FAISS](https://python.langchain.com/docs/integrations/vectorstores/faiss/) + +## Overview +This POC is intended to allow you to easily deploy and use a working state-of-the-art chat serving platform for an open-source LLM model via automatically scaling your EKS, GKE, or AKS cloud Kubernetes cluster with economical compute instances. And to easily tear down the deployed serving platform when desired. + +## Cluster Setup Summary + +Run w/Luna on K8s w/L4 (EKS,GKE) & A10 (AKS) w/GPU quota + specified Nvidia GPU drivers + +* Luna-1.2.8, EKS, us-west-2, K8s v1.30.2, w/K8s Nvidia daemonset from +[https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.17.0/deployments/static/nvidia-device-plugin.yml](https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.17.0/deployments/static/nvidia-device-plugin.yml +) +* Luna-1.2.9, GKE, us-central1, K8s v1.30.5, w/GCP Nvidia daemonset from +[https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/nvidia-driver-installer/cos/daemonset-preloaded.yaml](https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/nvidia-driver-installer/cos/daemonset-preloaded.yaml) +* Luna-1.2.10, AKS, east-us, K8s v1.31.2, w/K8s Nvidia gpu-operator from +```sh +helm repo add nvidia https://helm.ngc.nvidia.com/nvidia && helm repo update +helm install --wait --generate-name -n gpu-operator --create-namespace nvidia/gpu-operator +``` + +## Install Infrastructure Tools + +### Install Luna Cluster Autoscaler on Cloud K8s Cluster + +On existing cloud K8s cluster, install Luna as per cloud K8s in the [Luna docs](https://docs.elotl.co/luna/intro/). +[Download Free trial here](https://www.elotl.co/luna-free-trial.html). + +Please note that running the POC on EKS requires +Luna to be configured to allocate a larger EBS size than the default; this configuration is described in the EKS section below. +And the EKS, GKE, and AKS sections below provide instructions to optionally reduce node startup time on those platforms. + +#### EKS + +For EKS, you need to specify a larger EBS size w/Luna aws.blockDeviceMapping option. And you have the choice of configuring Luna +to use conventional node images (default) or Bottlerocket node images. Bottlerocket images can be configured for faster node startup time. + +##### Conventional Node Images + +To specify a larger EBS size for an EKS cluster on which Luna is configured to use conventional node images (default), +download [block_device_mapping.json](https://raw.githubusercontent.com/elotl/GenAI-infra-stack/refs/heads/main/demo/llm.gpu.service/block_device_mapping.json) +and when deploying Luna, include ```--additional-helm-values``` set to: +``` +--set-file aws.blockDeviceMappings=/block_device_mapping.json +``` + +##### Bottlerocket Node Images + +When Luna uses conventional node images, downloading the ray-ml image introduces substantial startup time. +Configuring Luna to instead use Bottlerocket node images with a snapshot volume that prepopulates the nodes +Luna allocates with the ray-ml image avoids the ray-ml image download time. +Run [get-user-data.sh](https://github.com/elotl/GenAI-infra-stack/blob/main/demo/llm.gpu.service/get-user-data.sh) with your cluster name and region to produce user-data.toml. +Download [block_device_mapping_bottlerocket.json](https://github.com/elotl/GenAI-infra-stack/blob/main/demo/llm.gpu.service/block_device_mapping_bottlerocket.json), +which references the snapshot snap-09946d545033d96f7 in us-west-2, built using the instructions +in https://github.com/aws-samples/bottlerocket-images-cache?tab=readme-ov-file#build-ebs-snapshot-with-cached-container-image; +replace with your image name. When deploying Luna, include ```--additional-helm-values``` set to: +``` +--set aws.isBottlerocketImage=true +--set aws.imageSsmQueryGeneric=/aws/service/bottlerocket/aws-k8s-%s/x86_64/latest/image_id +--set aws.imageSsmQueryGenericArm=/aws/service/bottlerocket/aws-k8s-%s/arm64/latest/image_id +--set aws.imageSsmQueryGpu=/aws/service/bottlerocket/aws-k8s-%s-nvidia/x86_64/latest/image_id +--set-file aws.blockDeviceMappings=/block_device_mapping_bottlerocket.json +--set-file aws.userData=/user-data.toml +``` +Change the images used by the Ray LLM head and workers in the yaml used in the ray-service installation step below +from rayproject/ray-ml:2.33.0.914af0-py311 to your image (in our case, it is +689494258501.dkr.ecr.us-west-2.amazonaws.com/qa-in-a-box:ray-ml-2.33.0-py311-vllm-0.5.4-hfxfr), +remove the line `pip: ["vllm==0.5.4"]`, and add the following lines below the ray-head and ray-worker image lines to speed up model download: +``` +env: + - name: HF_HUB_ENABLE_HF_TRANSFER + value: "1" + - name: HF_HUB_DISABLE_PROGRESS_BARS + value: "1" +``` +An example of this for the ```microsoft/Phi-3-mini-4k-instruct``` model is [here](https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.Phi-3-mini-4k-instruct.fastereks.yaml) + +#### GKE + +For GKE, you can improve the ray-ml image load time by using Image Streaming from the Artifact Registry. To do so, enable the "Image streaming" feature +on your cluster. When deploying Luna, set gcp.nodeServiceAccount to the Luna service account that includes the `artifactregistry.reader` role via +inclusion in ```--additional-helm-values``` as: +``` +--set gcp.nodeServiceAccount=-elotl@.iam.gserviceaccount.com +``` +Change the images used by the Ray LLM head and workers in the yaml used in the ray-service installation step below +from rayproject/ray-ml:2.33.0.914af0-py311 to your image (in our case, it is gcr.io/elotl-dev/rayproject/ray-ml:2.33.0.914af0-py311-vllm-0.5.4-hfxfr), +remove the line `pip: ["vllm==0.5.4"]`, and add the following lines below the ray-head and ray-worker image lines +to speed up model download: +``` +env: + - name: HF_HUB_ENABLE_HF_TRANSFER + value: "1" +``` +An example of this for the ```microsoft/Phi-3-mini-4k-instruct``` model is [here](https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.Phi-3-mini-4k-instruct.fastergke.yaml) + +#### AKS + +For AKS, you can improve the ray-ml image load time by using the Artifact Streaming on AKS preview feature on your cluster. To do so, +follow the instructions [here](https://learn.microsoft.com/en-us/azure/aks/artifact-streaming) to register the ArtifactStreamingPreview +feature in your subscription and to enable Artifact Streaming on your ACR image(s). When deploying Luna, set ```azure.enableArtifactStreaming``` to true +in your ```--additional-helm-values``` parameter. + +Change the images used by the Ray LLM head and workers in the yaml used in the ray-service installation step below +from rayproject/ray-ml:2.33.0.914af0-py311 to your image (in our case, it is elotleastus.azurecr.io/rayproject/ray-ml:2.33.0.914af0-py311-vllm-0.5.4-hfxfr), +remove the line `pip: ["vllm==0.5.4"]`, and add the following lines below the ray-head and ray-worker image lines +to speed up model download: +``` +env: + - name: HF_HUB_ENABLE_HF_TRANSFER + value: "1" + - name: HF_HUB_DISABLE_PROGRESS_BARS + value: "1" +``` +An example of this for the ```microsoft/Phi-3-mini-4k-instruct``` model is [here](https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.Phi-3-mini-4k-instruct.fasteraks.yaml) + +### Install KubeRay Operator to manage Ray on Cloud K8s Cluster +```sh +helm install kuberay-operator kuberay/kuberay-operator --version 1.1.0-rc.0 +``` +```sh +# Small resource footprint; Installed on static, i.e., non-Luna-allocated resources +``` + +## Install Model Serve Stack +You can choose to install the RayService w/vLLM + Open Source Model Serve Stack either without or with the Ray Autoscaler, as described in the 2 subsections for each of the two models below. If you install it w/o the Ray Autoscaler, the model serve stack will come up more quickly, but will have a fixed number of workers, configured as 1. If you install it with the Ray Autoscaler, the model serve stack will start with 0 workers, will scale to 1 worker as the RayService is activated, and will scale to more workers as needed to handle the query load, configured w/a max of 4. + +The instructions below describe installing a MosaicML model and a Microsoft model. +Note that the Microsoft model is of a more recent vintage and loads faster; hence, +that may be the better choice of the two. + +### [MosaicML Open Source Model](https://huggingface.co/mosaicml/mpt-7b-chat) +Install RayService w/vLLM + MosaicML OS Model w/o Ray Autoscaler + +```sh +kubectl apply -f ray-service.llm.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.yaml +# Large resource footprint; Installed on Luna-allocated resources +# Takes 10-15m: add nodes + large image + vLLM update + Ray setup + model download +# Wait for svc/llm-model-serve-serve-svc to be available [This is the last of the 3 services started] +``` + +Install RayService w/vLLM + MosaicML OS Model w/ Ray Autoscaler + +```sh +kubectl apply -f ray-service.llm.autoscale.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.autoscale.yaml +# Large resource footprint; Installed on Luna-allocated resources +# Takes 10-15m: add nodes + large image + vLLM update + Ray setup + model download +# Wait for svc/llm-model-serve-serve-svc to be available [This is the last of the 3 services started] +``` + +### [Microsoft Open Source Model](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct) +Install RayService w/vLLM + Microsoft OS Model w/o Ray Autoscaler + +```sh +kubectl apply -f ray-service.llm.Phi-3-mini-4k-instruct.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.Phi-3-mini-4k-instruct.yaml +# Large resource footprint; Installed on Luna-allocated resources +# Takes 10-15m: add nodes + large image + vLLM update + Ray setup + model download +# Wait for svc/llm-model-serve-serve-svc to be available [This is the last of the 3 services started] +``` + +Install RayService w/vLLM + Microsoft OS Model w/ Ray Autoscaler + +```sh +kubectl apply -f ray-service.llm.Phi-3-mini-4k-instruct.autoscale.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.Phi-3-mini-4k-instruct.autoscale.yaml +# Large resource footprint; Installed on Luna-allocated resources +# Takes 10-15m: add nodes + large image + vLLM update + Ray setup + model download +# Wait for svc/llm-model-serve-serve-svc to be available [This is the last of the 3 services started] +``` + +## Model Serve + +### Run Port-forward for Model Endpoint (in the background) +```sh +kubectl port-forward svc/llm-model-serve-serve-svc 8000:8000 +``` + +### Query Model Endpoint + +MosaicML Open Source Model + +```sh +python query.py +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/query.py +# Run in separate terminal window from port-forward command +# Requires “pip install openai” +# Prompts user for query +``` + +Microsoft Open Source Model + +```sh +python query.py +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/query.py +# Run in separate terminal window from port-forward command +# Requires “pip install openai” +# Requires “export MODEL_ID="microsoft/Phi-3-mini-4k-instruct" +# Prompts user for query +``` + +### Experiment with Scaling + +If you installed the model serve stack with the Ray Autoscaler, you can experiment with scaling by presenting sufficient query load. Running the following shell script presented enough load for the Ray Autoscaler to increase the number of workers from 1 to 2, for which the Luna Autoscaler added an additional GPU node to the cluster. + +```sh +./loadtest.sh +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/loadtest.sh +# Run in clone of repo with question.txt available in the same directory +# Use control-C to stop the script +``` + +After the load script was stopped and a period of time elapsed, the Ray Autoscaler reduced the worker count back to 1, and the Luna Autoscaler removed the additional node from the cluster. + +## Retrieval Augmented Generation (RAG) using FAISS + +In this section, we illustrate how LLM chat can be extended to work with custom datasets using the technique of Retrieval Augmented Generation. If you do not want to incorporate your custom datasets during the LLM chat you can skip this section. + +In order to use RAG, please follow the instructions in all of these prior sections before you follow the instructions in this section: +* Cluster Setup Summary +* Install Infrastructure Tools +* Install Luna Cluster Autoscaler on Cloud K8s Cluster +* Install KubeRay Operator to manage Ray on Cloud K8s Cluster +* Install Model Serve Stack +* Model Serve + +In this section, we provide an example of storing your RAG dataset and the resulting Vector Store on AWS-specific S3 storage. + +### Setup RAG input dataset + +In order to create the RAG dataset, we will run a Kubernetes job that will retrieve text documents from an S3 bucket, convert each text file into a vector embedding and save these embeddings in a Vector store. For the purpose of this POC, we use FAISS (Facebook’s Similarity Search) library to create both the embeddings and the VectorStore file. Please follow the instructions below setup the RAG dataset as well as the configure the parameters needed to run the vector Store creation Kubernetes job. + + +1. Create an S3 bucket and a folder (prefix) within it. Upload all the text documents that you would like to use as your RAG dataset into this folder. +Use can use the instructions here to create an S3 bucket: [Creating a S3 bucket in AWS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html#creating-bucket) and the instructions here to create a folder within this bucket: +[Folder creation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-folders.html#create-folder) + +1. Create a local file with these environment variables exported with suitable values: +AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: These AWS access credentials should have permissions to read from and write to the S3 bucket created in the previous step. +If you would like to use your AWS CLI setup locally, you can use these commands to create these environment variables. Please note that these access credentials will not be limited to the minimal S3 bucket read and write permissions that are needed for setting up RAG. It is only provided here for ease of use (and is not meant for a production use). +```sh +export AWS_ACCESS_KEY_ID=$(grep aws_access_key_id ~/.aws/credentials | awk '{print $3}') +export AWS_SECRET_ACCESS_KEY=$(grep aws_secret_access_key ~/.aws/credentials | awk '{print $3}') +``` + +1. VECTOR_DB_INPUT_TYPE: Set this to a value of “text-docs" if the RAG dataset will be a set of documents in text format. Alternatively, you can set it to a value of “sitemap”, if the RAG dataset will need to be built from documents hosted on a website. For e.g: +```sh +export VECTOR_DB_INPUT_TYPE=text-docs +``` + +1. VECTOR_DB_INPUT_ARG: +If the VECTOR_DB_INPUT_TYPE value is “text-docs”, then this env variable will be set to the value of the folder or prefix name within an S3 bucket where the text documents will be uploaded by the customer. +If the VECTOR_DB_INPUT_TYPE value is “sitemap”, then this env variable will be set to the URL value of sitemap of a website whose pages will be used as the RAG dataset. + +1. VECTOR_DB_S3_BUCKET: Name of the S3 bucket that will contain the input dataset to be used for the RAG as well as RAG vector datastore. Please note that when VECTOR_DB_INPUT_TYPE value is “sitemap”, there is no input dataset that is needed to be uploaded to the S3 bucket. This is because the sitemap URL will be parsed by the Kto retrieve the dataset. + +1. VECTOR_DB_S3_FILE: Name of the vector DB file that will be created by Elotl and saved in the provided S3 bucket. + +1. MODEL_ID = [ microsoft/Phi-3-mini-4k-instruct | mosaicml/mpt-7b-chat ] Select the LLM model that is to be used. You can read about these two models here: +[https://huggingface.co/microsoft/Phi-3-mini-4k-instruct](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct) +[https://huggingface.co/mosaicml/mpt-7b-chat](https://huggingface.co/mosaicml/mpt-7b-chat) + +1. MODEL_LLM_SERVER_URL: Set this env var to the value http://llm-model-serve-serve-svc..svc.cluster.local:8000 +Please replace with “default” if you plan to follow the remaining instructions in this RAG section without any changes. In case you will be deploying the RAG LLM service in a custom namespace, then please replace with the name of the custom namespace. + +Source all these environment variables to your local shell. + +Note: If you chose to work with “text documents” for your RAG dataset, your documents would be made available at this S3 URI: +s3://// + +We will now setup some environment variables that are needed to enable us to customize how the Vector Store creation and RAG querying is performed. + +```sh +# LLM querying configurable parameters: + MODEL_ID (DEFAULT=mosaicml/mpt-7b-chat) + RELEVANT_DOCS (DEFAULT = 2) + MAX_TOKENS (DEFAULT=128) + MODEL_TEMPERATURE (DEFAULT=0.01) +``` + +```sh +# Vector Store creation' configurable parameters: + EMBEDDING_CHUNK_SIZE (DEFAULT=1000) + EMBEDDING_CHUNK_OVERLAP (DEFAULT=100) + EMBEDDING_MODEL_NAME (DEFAULT=sentence-transformers/all-MiniLM-L6-v2) +``` + +If You decide to pass to vector db creation a file created using our process_jira_tickets.py script set the following to "True". +```sh + IS_JSON_MODE (DEFAULT="False") +``` + +### Sample RAG Dataset +As an example of a RAG dataset, you could use this subset of Wikipedia docs: [https://huggingface.co/datasets/rag-datasets/rag-mini-wikipedia](https://huggingface.co/datasets/rag-datasets/rag-mini-wikipedia) +This dataset is accompanied with a number of Questions and Answers that can be used to validate RAG functionality. + +You can use this command to download this dataset: +```sh +git clone https://huggingface.co/datasets/rag-datasets/rag-mini-wikipedia +``` + + +## Creation of the Vector store +The vector Store can be created in your S3 bucket by running this Kubernetes job on your cluster. For the purpose of this POC, the default namespace is being used. Alternate namespaces can be used if desired. You can download createvdb.yaml from here: +[https://github.com/elotl/GenAI-infra-stack/blob/main/demo/llm.vdb.service/createvdb.yaml](https://github.com/elotl/GenAI-infra-stack/blob/main/demo/llm.vdb.service/createvdb.yaml) + +```sh +envsubst < createvdb.yaml | kubectl apply -f - +``` + +Ensure that the k8s job and corresponding pod are running as expected: + +```sh +% kubectl get jobs +NAME STATUS COMPLETIONS DURATION AGE +createvectordb Running 0/1 5s 5s +``` + +```sh +% kubectl get pods +NAME READY STATUS RESTARTS AGE +createvectordb-kzrw6 1/1 Running 0 118s +``` + +This will take a few minutes to complete. The logs in the above pod will end with these messages. + +```sh +...SNIP... +Downloaded file, mini-rag-wikipedia-input/S10_set6_a9.txt.clean successfully to directory, /tmp/selvi-s3-rag-wikipedia +Downloaded file, mini-rag-wikipedia-input/S10_set6_topics.txt successfully to directory, /tmp/selvi-s3-rag-wikipedia +Number of files downloaded is 165, local tmp dir is /tmp/selvi-s3-rag-wikipedia +Number of documents loaded via DirectoryLoader is 165 +Uploaded vectordb to selvi-faiss-vectordbs selvi-s3-rag-wikipedia +``` + +After the job completes, please ensure that the Vector Store file has been created in your S3 bucket. Here is a screenshot of the Vector Store file for the mini RAG dataset: + +You can use this AWS cli command to verify that it was created correctly: +```sh +% aws s3 ls $VECTOR_DB_S3_BUCKET/$VECTOR_DB_S3_FILE +``` +```sh +2024-10-30 12:52:52 104804503 selvi-s3-rag-wikipedia +``` + +## Setup RAG + LLM service + +We will now create a Kubernetes Deployment and a Service that will take in the user’s question, interact with the Vector Store to find relevant documents and then query our hosted LLM service to provide an answer. You can download the manifest rag-chat-serveragllm.yaml from here: [rag-chat-serveragllmpluslb.yaml](https://github.com/elotl/GenAI-infra-stack/blob/main/demo/llm.rag.service/rag-chat-serveragllmpluslb.yaml) + +```sh +envsubst < rag-chat-serveragllmpluslb.yaml | kubectl apply -f - +``` + +Please wait for the deployment and Kubernetes LoadBalancer service to become ready and to also obtain an external IP. This can take a few minutes. The command outputs below specifically show the deployment, pod and services associated with the RAQ LLM service. + +```sh +# View deployments + +% kubectl get deploy +NAME READY UP-TO-DATE AVAILABLE AGE +serveragllm-deployment 1/1 1 1 2m12s +... + +# View pods +% kubectl get pods +NAME READY STATUS RESTARTS AGE +serveragllm-deployment-7bcd47c9dc-nqs2s 1/1 Running 0 2m15s + +# View services +% kubectl get svc +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +serveragllm-service LoadBalancer 10.100.211.63 80:32581/TCP 2m19s +``` + +Please note the IP listed in the EXTERNAL-IP column shown in the output of the `kubectl get svc` command above. + + +## Query the LLM with RAG +You can use the provided scripts/query/rag_query.py script with port forwarding to query the RAG+LLM service endpoint and ask questions about your RAG dataset. + +### Run Port-forward for Model Endpoint (in the background) +```sh +nohup kubectl port-forward svc/serveragllm-service 8000:8000 & +``` + +Use the script to query the RAG+LLM service + +```sh +python rag_query.py +``` +``` +Type your query here: What are the two types of elephants in Africa? +Answer: The two types of elephants in Africa are the African bush elephant (Loxodonta africana) and the African forest elephant (Loxodonta cyclotis). +``` + +## Query the LLM with RAG using a Chat UI + +### Generate and setup a password for your Chat UI + +Run the tool `htpasswd` locally to generate credentials needed for authentication + +```sh +htpasswd -c .htpasswd +``` + +Convert the encrypted password to base64 encoding to be used in a K8s secret: + +```sh +cat .htpasswd | base64 +``` + +Paste the output of the prior command inside the Secret in this manifest: `demo/llm.chatui.service/auth-proxy.yml` + +```sh +--- +# auth-proxy.yaml +apiVersion: v1 +kind: Secret +metadata: + name: auth-proxy-credentials +type: Opaque +data: + # Generated using: htpasswd -c .htpasswd username + # Then base64 encode the file content + # htpasswd -c .htpasswd your_chosen_username + # cat .htpasswd | base64 + # myuser:elotl + + .htpasswd: +--- +``` + + +### Install the authentication proxy for the Chat UI + +```sh +kubectl apply -f demo/llm.chatui.service/auth-proxy.yml +``` + +### Install Chat UI app + +```sh +kubectl apply -f demo/llm.chatui.service/simple-chat.yaml +``` + +Wait for an external IP to be associated with the `auth-proxy-service`. You can now access this external IP type from a browser. You will be asked to enter a username and password before viewing your Chat UI. + +```sh +% kubectl get svc auth-proxy-service +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +auth-proxy-service LoadBalancer 10.100.230.224 80:32497/TCP 85m +``` + +# Uninstall + +## 1. Uninstall Model Serve Stack + +### A. [MosaicML Open Source Model](https://huggingface.co/mosaicml/mpt-7b-chat) + +Uninstall RayService w/vLLM + MosaicML OS Model w/o Ray Autoscaler + +```sh +kubectl delete -f ray-service.llm.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.yaml +# After around 5m, Luna will scale down the nodes that were allocated for the RayService +``` + +Uninstall RayService w/vLLM + MosaicML OS Model w/ Ray Autoscaler + +```sh +kubectl delete -f ray-service.llm.autoscale.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.autoscale.yaml +# After around 5m, Luna will scale down the nodes that were allocated for the RayService +``` + +### B. [Microsoft Open Source Model](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct) + +Uninstall RayService w/vLLM + Microsoft OS Model w/o Ray Autoscaler + +```sh +kubectl delete -f ray-service.llm.Phi-3-mini-4k-instruct.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.Phi-3-mini-4k-instruct.yaml +# After around 5m, Luna will scale down the nodes that were allocated for the RayService +``` + +## 2. Uninstall RayService w/vLLM + Microsoft OS Model w/ Ray Autoscaler +```sh +kubectl delete -f ray-service.llm.Phi-3-mini-4k-instruct.autoscale.yaml +``` +```sh +# From https://github.com/elotl/skyray/blob/main/luna-llm-serve/ray-service.llm.Phi-3-mini-4k-instruct.autoscale.yaml +# After around 5m, Luna will scale down the nodes that were allocated for the RayService +``` + +## 3. Uninstall Infrastructure Tools + +### Uninstall KubeRay +```sh +helm uninstall kuberay-operator +``` + +### Uninstall Luna + +Uninstall Luna as per Installation/Cleanup for cloud K8s type in the [Luna Docs](https://docs.elotl.co/luna/intro/). + +# Potential Development Areas +* Replace one shot question/answer with interactive chat, also provide browser interface +* Provide straightforward mechanism to update models diff --git a/docs/rag-user-docs.md b/docs/rag-user-docs.md new file mode 100644 index 0000000..d0e0337 --- /dev/null +++ b/docs/rag-user-docs.md @@ -0,0 +1,58 @@ +# End-User Overview of Elotl's Question-Answer ChatBot + +Elotl's Question-Answer ChatBot is powered by these technologies: + +1. [Retrieval Augmented Generation (RAG)](https://en.wikipedia.org/wiki/Retrieval-augmented_generation) +2. Open-Source [Large Language Models](https://en.wikipedia.org/wiki/Large_language_model) +3. [Vector Stores](https://en.wikipedia.org/wiki/Vector_database) + +This is a high-level graphic on how these technologies are used to determine an answer to the end-user's question about a specific knowledge base. + +
+elotl_genai_stack_enduser +
+ +## What types of questions can a RAG-based Chatbot answer well? + +- Question-Answering Chatbots powered by RAG & LLMs are good at answering questions about a small subset of relevant data from the given +Knowledge Base. This is because of how Retrieval Augmented Generation works: User questions are first matched to "chunks" of data from +the user's Knowledge Base, that has been stored in a VectorStore or Vector Database. The user's question, along with these retrieved matching +chunks are sent to the LLM with an appropriate the system prompt. This prompt instructs the LLM to answer the question using the context retrieved +from the specialized knowledge base. + +- Let's take for example, a Question-Answer Chatbot working with an engineering team's JIRA ticket Knowledge base. Here are some sample questions +that can be answered well: + +```sh +What was the issue with the Apache Airflow installation? +What type of security issues have been handled? +Were the SQL issues resolved for the ENG team? +Were any Kubernetes upgrades initiated on the cloud? +Can you give the description of any upgrade requests that have been received? +``` + +## What types of questions is a RAG-based ChatBot not designed to answer well? + +The RAG technique is not intended to answer knowledge aggregation questions about a large amount of data from a Knowledge Base. + +```sh +What is the most frequent Kubernetes issue? +Can you summarize all the recent upgrade tasks? +Can you find the ticket that's been unresolved for the longest time? +``` + +Let's look into one of these questions in detail to understand why the RAG technique and input token limits of LLMs make it difficult for a +RAG-based Question-Answering ChatBot to answer well. + +```sh +What is the most frequent Kubernetes issue? +``` + +A question like this requires access to all Kubernetes-related tickets in the JIRA knowledge base to be able to accurately determine which one +is the most frequent. Depending on the specific dataset used to create the Vector store/DB, the number of Kubernetes related tickets could be a +couple to many hundreds. In the former case, i.e there are only 2 Kubernetes related tickets, then a top-K similarity search during the Vector +store retrieval step would be able to pass all the necessary context to the LLM to answer this question accurately. In the latter case, +i.e. if there are many hundreds of Kubernetes-related tickets, then the RAG service would not be able to pass all of this context to the LLM +to answer the question accurately. The reason for not being able to send a large context is because LLMs are limited by the incoming token length +they are able to process. + diff --git a/docs/sql_setup.md b/docs/sql_setup.md new file mode 100644 index 0000000..3edde53 --- /dev/null +++ b/docs/sql_setup.md @@ -0,0 +1,50 @@ +# Text to SQL Setup + +## Preparation + +1. Convert structured data in CSV format to SQL DB and make it available in an S3 bucket within a prefix folder. + +2. Use synthetic questions in CSV format to train a classification model. The result of +this process are two pkl files. Save these files also in the same S3 bucket and prefix folder +as in Step 1. + + +## Setting up SQL + Vector search locally + +1. Export local variables such as + +- LLM server url at 9000 +- Setup location of SQL DB and question classification models + +```sh +source some_location/exports-local.sh +``` + +1. Run the LLM on k8s and portforward: + +```sh + kubectl port-forward svc/llm-model-serve-serve-svc 9000:8000 +``` + +2. Run the local version of the SQL + hybrid search app: + +```sh +llm.rag.service % source .venv/bin/activate + uv run serverragllm_csv_to_weaviate_local.py +``` + +Wait till applicaiton is loaded and you see this message: +```sh +INFO: Application startup complete. +``` + +3. Try a question about your user data: + +```sh +cd /GenAI-infra-stack/scripts/query +``` + +```sh +% python query_private_data.py +Type your query here: How many tickets are there? +``` diff --git a/docs/weaviate_local_setup.md b/docs/weaviate_local_setup.md new file mode 100644 index 0000000..17319c3 --- /dev/null +++ b/docs/weaviate_local_setup.md @@ -0,0 +1,32 @@ +# Running app locally with weaviate + +## Run weaviate +```shell + docker compose up weaviate +``` + +## Run create database +Create an .my-env file and put inside +```shell +LOCAL_DIRECTORY="../../scripts/zendesk_dataprep_output" +OUTPUT_FILENAME="" + +WEAVIATE_URI_WITH_PORT="localhost:8080" +WEAVIATE_GRPC_URI_WITH_PORT="localhost:50051" +WEAVIATE_INDEX_NAME="my_custom_index" + +EMBEDDING_CHUNK_SIZE=4000 +EMBEDDING_CHUNK_OVERLAP=100 + +EMBEDDING_MODEL_NAME=sentence-transformers/multi-qa-mpnet-base-dot-v1 +``` + +run: +```shell +uv run createvectordb.py --env_file .my-env +``` + +## Run serve rag app +```shell +uv run serverragllm_csv_to_weaviate_local.py +``` \ No newline at end of file diff --git a/question_classification/predict/question_classification.py b/question_classification/predict/question_classification.py new file mode 100644 index 0000000..e7acc72 --- /dev/null +++ b/question_classification/predict/question_classification.py @@ -0,0 +1,33 @@ +import os +import joblib + +def predict_question_type(question, model, tfidf, id_to_category): + + # Transform the input question into TF-IDF feature representation + question_tfidf = tfidf.transform([question]).toarray() + + # Predict the category ID + predicted_category_id = model.predict(question_tfidf)[0] + + # Convert category ID back to label + predicted_category = id_to_category[predicted_category_id] + + return predicted_category + +def load_models(): + # Load the saved model + rf_model_loaded = joblib.load('./models/random_forest_model.pkl') + + # Load the saved TF-IDF vectorizer + tfidf_loaded = joblib.load('./models/tfidf_vectorizer.pkl') + + print("Model and vectorizer loaded successfully!") + return rf_model_loaded, tfidf_loaded + +sample_question = "How many tickets are there?" +#sample_question = "What was the last upgrade issue?" +rf_model_loaded, tfidf_loaded = load_models() +id_to_category = {0: 'aggregation', 1: 'pointed'} +predicted_category = predict_question_type(sample_question, rf_model_loaded, tfidf_loaded, id_to_category) + +print("Testing with a sample question: ", sample_question, "\nPredicted Question Type:", predicted_category) diff --git a/question_classification/predict/requirements.txt b/question_classification/predict/requirements.txt new file mode 100644 index 0000000..d5e0602 --- /dev/null +++ b/question_classification/predict/requirements.txt @@ -0,0 +1 @@ +scikit-learn diff --git a/question_classification/train/question_classification_models.py b/question_classification/train/question_classification_models.py new file mode 100644 index 0000000..4a102ab --- /dev/null +++ b/question_classification/train/question_classification_models.py @@ -0,0 +1,69 @@ +import os +import joblib +import pandas as pd +from sklearn.feature_extraction.text import TfidfVectorizer +from sklearn.ensemble import RandomForestClassifier + + +def predict_question_type(question, model, tfidf, id_to_category): + # Transform the input question into TF-IDF feature representation + question_tfidf = tfidf.transform([question]).toarray() + + # Predict the category ID + predicted_category_id = model.predict(question_tfidf)[0] + + # Convert category ID back to label + predicted_category = id_to_category[predicted_category_id] + + return predicted_category + +# Save the trained model +def save_models(rf_model, tfidf): + joblib.dump(rf_model, 'random_forest_model.pkl') + + # Save the TF-IDF vectorizer as well (since it's needed for transforming new data) + joblib.dump(tfidf, 'tfidf_vectorizer.pkl') + + print("Model and vectorizer saved successfully!") + +# loading data, replace with questions specific to your dataset +df = pd.read_csv('syntheticquestions.csv') + +# Create a new dataframe with two columns +df1 = df[['question', 'question_type']].copy() +df1.head(3).T +df2=df1 + +# map categories to numbers +# Create a new column 'category_id' with encoded categories +df2['category_id'] = df2['question_type'].factorize()[0] +category_id_df = df2[['question_type', 'category_id']].drop_duplicates() + +# Dictionaries for future use +category_to_id = dict(category_id_df.values) +id_to_category = dict(category_id_df[['category_id', 'question_type']].values) + +print("ID to category Dict:", id_to_category) + +# New dataframe +df2.head() + +# find features and labels +tfidf = TfidfVectorizer(sublinear_tf=True, min_df=1, + ngram_range=(1, 2), + stop_words='english') + +# transform each question into a vector +features = tfidf.fit_transform(df2.question).toarray() +labels = df2.category_id +print("Each of the %d questions is represented by %d features (TF-IDF score of unigrams and bigrams)" %(features.shape)) + +print("Model training starts...") +rf_model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=0) +rf_model.fit(features, labels) + +sample_question = "What was the last upgrade issue?" +predicted_category = predict_question_type(sample_question, rf_model, tfidf, id_to_category) +print("Testing with a sample question: ", sample_question, "\nPredicted Question Type:", predicted_category) + +save_models(rf_model, tfidf) \ No newline at end of file diff --git a/question_classification/train/requirements.txt b/question_classification/train/requirements.txt new file mode 100644 index 0000000..fda4dd6 --- /dev/null +++ b/question_classification/train/requirements.txt @@ -0,0 +1,2 @@ +pandas +scikit-learn diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..addc2ae --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,13 @@ +# Prepare csv dump of Jira tickets adjusted for embedding + +[Install UV](https://docs.astral.sh/uv/getting-started/installation/) than create venv and install deps +```shell +uv venv +source .venv/bin/activate +uv pip install -r requirements.txt +``` +set jira_url in jira_config.ini + +```shell +uv run process_jira_tickets.py jira_elotl.csv jira_config.ini output_files +``` diff --git a/scripts/eval/elotl_jira_questions.txt b/scripts/eval/elotl_jira_questions.txt new file mode 100644 index 0000000..05ff8d1 --- /dev/null +++ b/scripts/eval/elotl_jira_questions.txt @@ -0,0 +1,25 @@ +What is Luna? +What is Nova? +What was the description of the most recent security ticket in Luna? +Was there a Luna to enable sending user-data to an instance at boot time? +What was the description of the most recent DR ticket in Nova? +What cloud providers does Luna work on? +Can you describe any details about the addition of a promtail sidecar to Luna? +What are recent updates to the Nova JIT feature? +What are the two types of elephants in Africa? +Was there a ticket in Luna to enable sending user-data to an instance at boot time? +Was there any work done in Luna to enable sending user-data to an instance at boot time? +Give me the description of a ticket in Luna that deals with sending user-data to an instance at boot time? +Has the Nova JIT feature been implemented? +Can you tell me what the Nova JIT feature is? +Had a promtail sidecar been added to Luna? +What was the most recent type of testing done in Luna? +When was Nova's DR feature implemented? +Was the last ticket related to Nova's DR feature? +Which was the last ticket related to Nova's DR feature? +Is there a ticket about promtail sidecars in Luna? +Can you give me the description of the of a Luna ticket about promtail sidecars? +Can you describe any details about the addition of a promtail sidecar to Luna? +What type of work has been done to test the scalability of Nova? +What type of cloud providers does Luna not work on? +Are there any recent updates to the Nova JIT feature? diff --git a/scripts/eval/mini_rag_questions.txt b/scripts/eval/mini_rag_questions.txt new file mode 100644 index 0000000..89d049e --- /dev/null +++ b/scripts/eval/mini_rag_questions.txt @@ -0,0 +1,50 @@ +Was Abraham Lincoln the sixteenth President of the United States? +Did Lincoln sign the National Banking Act of 1863? +Did his mother die of pneumonia? +How many long was Lincoln's formal education? +When did Lincoln begin his political career? +What did The Legal Tender Act of 1862 establish? +Who suggested Lincoln grow a beard? +When did the Gettysburg address argue that America was born? +Did Lincoln beat John C. Breckinridge in the 1860 election? +Was Abraham Lincoln the first President of the United States? +When did Lincoln first serve as President? +Who assassinated Lincoln? +Did Lincoln win the election of 1860? +Who was the general in charge at the Battle of Antietam? +Why did Lincoln issue the Emancipation Proclamation? +Do scholars rank lincoln among the top three presidents? +Did lincoln have 18 months of schooling? +When was the first photgraph of lincoln taken? +How long was Lincoln's legal Career? +What trail did Lincoln use a Farmers' Almanac in? +Did Abraham Lincoln live in the Frontier? +Did Lincoln's Wife's Family support slavery? +Who is most noted for his contributions to the theory of molarity and molecular weight? +Who graduated in ecclesiastical law at the early age of 20 and began to practice? +Was Lorenzo Romano Amedeo Carlo Avogadro an Italian savant? +Was Amedeo Avogadro born in Turin August 9th 1776 to a noble ancient family of Piedmont, Italy? +What happened in 1833? +Who determined the dependence of the boiling of water with atmospheric pressure? +Is it true that thermometer had 100 for the freezing point? +Was Celsius born in Uppsala in Sweden? +Was Anders Celsius (November 27, 1701 April 25, 1744) a Swedish astronomer? +Is The Celsius crater on the Moon named after him? +Who was the first to perform and publish careful experiments aiming at the definition of an international temperature scale on scientific grounds ? +Can beetles be found in polar regions? +What are the three sections of a beetle? +Which defense mechanism uses colour or shape to deceive potential enemies? +Which type of beetle is a pest of potato plants? +How can beetle larvae be differentiated from other insect larvae? +What do beetles eat? +What are the similarities between beetles and grasshoppers? +How many species of beetles are there? +What is the study of beetles called? +What are prey of various animals including birds and mammals? +What was given by Aristotle for the hardened shield like forewings? +Who or what vary greatly in form within the coleoptera? +When did Coolidge meet and marry Grace Anna Goodhue? +What period of rapid economic growth did the United States experience during Coolidge's presidency? +What did Coolidge do after graduating from Amherst? +When was Coolidge born? +Where did Coolidge's grandfather had government offices? diff --git a/scripts/eval/test_qa.py b/scripts/eval/test_qa.py new file mode 100644 index 0000000..11f5cb9 --- /dev/null +++ b/scripts/eval/test_qa.py @@ -0,0 +1,90 @@ +import requests +import json +from datetime import datetime +import time +import sys +import os +import urllib + +def read_questions(filename): + """Read questions from a text file, one per line.""" + try: + with open(filename, 'r', encoding='utf-8') as file: + return [line.strip() for line in file if line.strip()] + except FileNotFoundError: + print(f"Error: Could not find file '{filename}'") + sys.exit(1) + +def send_question(user_message, endpoint): + """Send a single question to the API endpoint.""" + + try: + question = urllib.parse.quote(f"{user_message}") + response = requests.get(f"{endpoint}/answer/{question}") + if response.status_code == 200: + return response.json().get("answer", "Could not fetch response.") + else: + return "API Error: Unable to fetch response." + except requests.RequestException: + return "API Error: Failed to connect to the backend service." + + '''try: + response = requests.post( + endpoint, + json={"question": question}, + headers={"Content-Type": "application/json"} + ) + response.raise_for_status() + return response.json().get('answer', 'No answer provided') + except requests.exceptions.RequestException as e: + return f"Error: {str(e)}" + ''' + +def save_results(results, output_filename): + """Save results to a file in a readable format.""" + timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + filename = f"{output_filename}_{timestamp}.txt" + + with open(filename, 'w', encoding='utf-8') as file: + file.write("Q&A Results\n") + file.write("=" * 80 + "\n\n") + + for i, (question, answer) in enumerate(results, 1): + file.write(f"Question {i}:\n") + file.write("-" * 40 + "\n") + file.write(f"{question}\n\n") + file.write("Answer:\n") + file.write("-" * 40 + "\n") + file.write(f"{answer}\n\n") + file.write("=" * 80 + "\n\n") + + return filename + +def main(): + # Configuration + RAG_LLM_QUERY_URL = os.getenv("RAG_LLM_QUERY_URL") + INPUT_FILE = "mini_rag_questions.txt" # Replace with your questions file name + OUTPUT_FILE_PREFIX = "qa_results" + + # Read questions + print("Reading questions from file...") + questions = read_questions(INPUT_FILE) + print(f"Found {len(questions)} questions") + + # Process questions + results = [] + for i, question in enumerate(questions, 1): + print(f"Processing question {i} of {len(questions)}...") + + # Send request and get response + answer = send_question(question, RAG_LLM_QUERY_URL) + results.append((question, answer)) + + time.sleep(0.5) + + # Save results + output_file = save_results(results, OUTPUT_FILE_PREFIX) + print(f"\nResults have been saved to: {output_file}") + +if __name__ == "__main__": + main() diff --git a/scripts/jira_config.ini b/scripts/jira_config.ini new file mode 100644 index 0000000..45e1ced --- /dev/null +++ b/scripts/jira_config.ini @@ -0,0 +1,39 @@ +# Configuration file for Jira embedding preparation. +# Specify prefixes, substrings, list fields, and other mappings for processing Jira data. + +[PrefixFields] +# Fields with column names starting with the specified prefixes. +# Example: Columns named "Comment1", "Comment2" will be grouped into "comments_text". +comments_text = Comment +attachments_text = Attachment + +[SubstringFields] +# Fields with column names containing the specified substrings (case-insensitive). +# Example: Columns containing "issue link" in their name will be grouped into "linked_issues_text". +linked_issues_text = issue link + +[ListFields] +# Fields stored as comma-separated values or lists in a single column. +# These will be split into lists of individual items and included in composite text. +fields = Components, Labels + +[CompositeTextFields] +# Fields to include in the composite text (document body). +# Each key is a user-friendly label, and the value is the corresponding column in the DataFrame. +# NOTE: Fields from [PrefixFields], [SubstringFields], and [ListFields] will also be included in composite text. +Title = Summary +Description = Description +Status = Status +Type = Issue Type +Priority = Priority + +[MetadataFields] +# Fields to include in the document metadata. +# Each key is a metadata field name, and the value is the corresponding column in the DataFrame. +key = Issue key +type = Issue Type +status = Status + +[TicketUrl] +jira_url = https://elotl.atlassian.net/browse/ +metadata_field = key \ No newline at end of file diff --git a/scripts/jira_csv_local_developement.md b/scripts/jira_csv_local_developement.md new file mode 100644 index 0000000..81e2c68 --- /dev/null +++ b/scripts/jira_csv_local_developement.md @@ -0,0 +1,59 @@ +# How to run the full process for jira csv locally + +## Make sure You have +- [UV installed](https://docs.astral.sh/uv/getting-started/installation/) +- [Ollama cli installed](https://ollama.com/download) + +## Prepare data + +Go to `GenAI-infra-stack/scripts` create venv and install deps +```shell +uv venv +source .venv/bin/activate +uv pip install -r requirements.txt +``` +set jira_url in jira_config.ini + +```shell +uv run process_jira_tickets.py jira_elotl.csv jira_config.ini output_files +``` + +## Create vector store +Go to `GenAI-infra-stack/dockers/llm.vdb.service` + +```shell +uv run createvectordb_jira_csv_local.py ../../scripts/output_files pickled.obj +``` + +## Run rag app from local pickled.obj + +Run Ollama Open Api compatible local model +https://ollama.com/blog/openai-compatibility + +Go to `GenAI-infra-stack/dockers/llm.rag.service` + +```shell +export FILE_PATH="../llm.vdb.service/pickled.obj" + +uv run serverragllm_jira_cvs_local.py +``` + +## Test setup +```shell +curl "http://127.0.0.1:8000/answer/How%20to%20install%20luna%20?" +``` + +## Run UI +Go to `GenAI-infra-stack/dockers/llm.chatui.service` create venv and install requirements. + +```shell +uv venv +source .venv/bin/activate +uv pip install -r requirements.txt +``` + +```shell +export RAG_LLM_QUERY_URL="http://127.0.0.1:8000" + +uv run simple_chat.py +``` \ No newline at end of file diff --git a/scripts/json_to_csv/convert_json_to_csv.py b/scripts/json_to_csv/convert_json_to_csv.py new file mode 100644 index 0000000..a318c7c --- /dev/null +++ b/scripts/json_to_csv/convert_json_to_csv.py @@ -0,0 +1,182 @@ +import json +import csv +from datetime import datetime +import io + +def clean_text(text): + """Clean text by removing extra whitespace and newlines""" + if text is None: + return "" + return ' '.join(str(text).split()).strip() + +def safe_get (obj, *keys, default = ""): + """Safely get nested dictionary values with a default if not found""" + try: + for key in keys: + if not isinstance(obj, dict): + return default + newobj = obj.get(key) + if newobj is None: + return default + return newobj + except Exception: + return default + +def format_comment(comment): + """ Format a single comment with proper escaping""" + if not comment: + return "" + try: + parts =[ + f"ID={safe_get(comment, 'id', default='N/A')}", + f"Author={safe_get(comment, 'author_id', default='N/A')}", + f"Time={safe_get(comment, 'created_at', default='N/A')}", + f"Public={safe_get(comment, 'public', default='N/A')}", + f"Body={clean_text(safe_get(comment, 'body', default=''))}" + ] + return "; ".join(parts) + except Exception as e: + print (f"Warning: Error formatting comment: {str(e)}") + return "" + + +def format_comments(comments): + """Format all comments into a single string with proper escaping """ + if not comments: + return "" + + formatted=[] + for comment in comments: + comment_str = format_comment(comment) + if comment_str: + formatted.append(comment_str) + + return " || ".join(formatted) + +def process_ticket(ticket_data): + """ Process a single ticket and return a row dictionary """ + if not ticket_data: + return None + + try: + # Process tags with proper escaping + tags = safe_get(ticket_data, 'tags', default =[]) + tags_str = ';'.join(str(tag) for tag in tags if tag is not None) + + return { + 'ticket_id':safe_get(ticket_data, 'id'), + 'ticket_url':safe_get(ticket_data, 'url'), + 'ticket_type':clean_text(safe_get(ticket_data, 'type')), # renaming for clarity + 'subject':clean_text(safe_get(ticket_data, 'subject')), + 'description':clean_text(safe_get(ticket_data, 'description')), + 'details': format_comments(safe_get(ticket_data, 'comments', default =[])), # renaming for clarity + 'created_at':safe_get(ticket_data, 'created_at'), + 'updated_at':safe_get(ticket_data, 'updated_at'), + 'latest_comment_added_at':safe_get(ticket_data, 'latest_comment_added_at'), + 'status':safe_get(ticket_data, 'status'), + 'priority':safe_get(ticket_data, 'priority'), + 'requester_name':safe_get(ticket_data, 'requester', 'name'), + 'requester_email':safe_get(ticket_data, 'requester', 'email'), + 'assignee_name':safe_get(ticket_data, 'assignee', 'name'), + 'assignee_email':safe_get(ticket_data, 'assignee', 'email'), + 'submitter_name':safe_get(ticket_data, 'submitter', 'name'), + 'submitter_email':safe_get(ticket_data, 'submitter', 'email'), + 'organization_name':safe_get(ticket_data, 'organization', 'name'), + 'group_name':safe_get(ticket_data, 'group', 'name'), + 'collaborator_name':safe_get(ticket_data, 'collaborator', 'name'), + 'collaborator_email':safe_get(ticket_data, 'collaborator', 'email'), + 'tags':tags_str, + 'satisfaction_rating_score':safe_get(ticket_data, 'satisfaction_rating', 'score'), + 'number_of_reopens':safe_get(ticket_data, 'metric_set', 'reopens'), + 'number_of_replies':safe_get(ticket_data, 'metric_set', 'replies'), + 'reply_time_in_minutes':safe_get(ticket_data, 'metric_set', 'reply_time_in_minutes', 'business'), + 'full_resolution_time_in_minutes':safe_get(ticket_data, 'metric_set', 'full_resolution_time_in_minutes', 'business'), + + + } + except Exception as e: + print(f"Warning: Error processing ticket: {str(e)}") + return None + +def convert_zendesk_json_to_csv(json_file_path, csv_file_path): + # Define CSV headers + headers=[ + 'ticket_id', + 'ticket_url', + 'ticket_type', + 'subject', + 'description', + 'details', + 'created_at', + 'updated_at', + 'latest_comment_added_at', + 'status', + 'priority', + 'requester_name', + 'requester_email', + 'assignee_name', + 'assignee_email', + 'submitter_name', + 'submitter_email', + 'collaborator_name', + 'collaborator_email', + 'organization_name', + 'group_name', + 'tags', + 'satisfaction_rating_score', + 'number_of_reopens', + 'number_of_replies', + 'reply_time_in_minutes', + 'full_resolution_time_in_minutes', + ] + # Read and process JSON file + rows =[] + skipped_tickets = 0 + total_tickets = 0 + + with open(json_file_path, 'r', encoding = 'utf-8') as file: + for line in file: + total_tickets += 1 + try: + #Try to parse each line as a separate JSON object + ticket_data = json.loads(line.strip()) + processed_ticket = process_ticket(ticket_data) + if processed_ticket: + rows.append(processed_ticket) + else: + skipped_tickets += 1 + except json.JSONDecodeError as e: + print(f"Warning: Skipping invalid JSON line: {str(e)}") + skipped_tickets += 1 + continue + + if not rows: + print("No valid tickets found in the input file") + return + + # Write to CSV with proper escaping + with open(csv_file_path, 'w', newline = '', encoding = 'utf-8') as file: + writer = csv.DictWriter(file, + fieldnames = headers, + quoting = csv.QUOTE_ALL, + #Quote all fields + escapechar = '\\', + #Use backslash as escape character + doublequote = True) + # Double-quotes within fields + writer.writeheader() + writer.writerows(rows) + print(f"CSV file has been created successfully at: {csv_file_path}") + print(f"Processed {len(rows)} tickets successfully") + print(f"Skipped {skipped_tickets} tickets due to errors") + print(f"Total tickets in input: {total_tickets}") + +if __name__ == "__main__": + + json_file_path = "zendesk_tickets.json" + csv_file_path = "zendesk_tickets.csv" + + try: + convert_zendesk_json_to_csv(json_file_path, csv_file_path) + except Exception as e: + print(f"An error occurred: {str(e)}") diff --git a/scripts/process_jira_tickets.py b/scripts/process_jira_tickets.py new file mode 100644 index 0000000..895ca9f --- /dev/null +++ b/scripts/process_jira_tickets.py @@ -0,0 +1,247 @@ +import os +import json +from configparser import ConfigParser +from typing import Any, Dict, List + +import click +import pandas as pd + + +def preprocess_text(text: Any) -> str: + """Clean and standardize text content.""" + if pd.isna(text): + return "" + return str(text).strip().replace("\n", " ").replace("\r", " ") + + +def get_list_from_field(value: Any) -> List[str]: + """Safely convert a field value to a list of strings.""" + if pd.isna(value): + return [] + if isinstance(value, str): + return [item.strip() for item in value.split(",") if item.strip()] + if isinstance(value, (list, tuple)): + return [str(item) for item in value if pd.notna(item)] + return [str(value)] + + +def extract_prefixed_columns( + row: pd.Series, df_columns: List[str], prefix: str +) -> List[str]: + """Extract and concatenate non-empty values from columns starting with a prefix.""" + return [ + str(row[col]) + for col in df_columns + if col.startswith(prefix) and pd.notna(row[col]) + ] + + +def extract_containing_columns( + row: pd.Series, df_columns: List[str], substring: str +) -> List[str]: + """Extract and concatenate non-empty values from columns containing a specific substring.""" + return [ + str(row[col]) + for col in df_columns + if substring in col.lower() and pd.notna(row[col]) + ] + + +def extract_composite_text( + row: pd.Series, composite_text_fields: Dict[str, str] +) -> str: + """Extract and format fields for composite text.""" + parts = [ + f"{field.lower()}: {preprocess_text(row.get(column, ''))}" + for field, column in composite_text_fields.items() + ] + return "\n".join(parts) + + +def extract_metadata(row: pd.Series, metadata_fields: Dict[str, str]) -> Dict[str, str]: + """Extract fields for metadata.""" + metadata = { + field.lower(): preprocess_text(row.get(column, "")) + for field, column in metadata_fields.items() + } + return metadata + + +def process_row( + row: pd.Series, + df_columns: List[str], + prefix_fields: Dict[str, str], + substring_fields: Dict[str, str], + list_fields: List[str], + composite_text_fields: Dict[str, str], + metadata_fields: Dict[str, str], + jira_url: str, + metadata_field: str, +) -> Dict[str, Any]: + """Process a single row using the provided configuration.""" + # Extract prefixed fields + prefixed_data = { + target_field.lower(): extract_prefixed_columns(row, df_columns, prefix) + for target_field, prefix in prefix_fields.items() + } + + # Extract substring fields + substring_data = { + target_field.lower(): extract_containing_columns(row, df_columns, substring) + for target_field, substring in substring_fields.items() + } + + # Extract list fields + list_data = { + field.lower(): get_list_from_field(row.get(field, "")) for field in list_fields + } + + # Extract composite text + composite_text = extract_composite_text(row, composite_text_fields) + + # Add prefixed and substring fields to composite text + for field, values in prefixed_data.items(): + if values: + composite_text += f"\n{field}: {' '.join(values)}" + for field, values in substring_data.items(): + if values: + composite_text += f"\n{field}: {' '.join(values)}" + + # Add list fields to composite text + for field, values in list_data.items(): + if values: + composite_text += f"\n{field.lower()}: {', '.join(values)}" + + # Extract metadata + metadata = extract_metadata(row, metadata_fields) + + # Add source + metadata["source"] = jira_url + metadata[metadata_field] + + return {"text": composite_text, "metadata": metadata} + + +def prepare_data_for_embedding( + df: pd.DataFrame, + prefix_fields: Dict[str, str], + substring_fields: Dict[str, str], + list_fields: List[str], + composite_text_fields: Dict[str, str], + metadata_fields: Dict[str, str], + jira_url: str, + metadata_field: str, +): + """Prepare documents for embedding using the provided configuration.""" + documents = [] + + for _, row in df.iterrows(): + processed_data = process_row( + row=row, + df_columns=df.columns, + prefix_fields=prefix_fields, + substring_fields=substring_fields, + list_fields=list_fields, + composite_text_fields=composite_text_fields, + metadata_fields=metadata_fields, + jira_url=jira_url, + metadata_field=metadata_field, + ) + documents.append(processed_data) + + return documents + + +def load_config(config_file): + config = ConfigParser() + config.read(config_file) + + # Parse configuration sections into variables + prefix_fields = dict(config["PrefixFields"]) if "PrefixFields" in config else {} + substring_fields = ( + dict(config["SubstringFields"]) if "SubstringFields" in config else {} + ) + + # Handle ListFields specially since we need to split the string + list_fields = [] + if "ListFields" in config and "fields" in config["ListFields"]: + fields_str = config["ListFields"]["fields"] + if fields_str: + list_fields = [f.strip() for f in fields_str.split(",")] + + composite_text_fields = ( + dict(config["CompositeTextFields"]) if "CompositeTextFields" in config else {} + ) + metadata_fields = ( + dict(config["MetadataFields"]) if "MetadataFields" in config else {} + ) + + return { + "prefix_fields": prefix_fields, + "substring_fields": substring_fields, + "list_fields": list_fields, + "composite_text_fields": composite_text_fields, + "metadata_fields": metadata_fields, + "jira_url": config["TicketUrl"]["jira_url"], + "metadata_field": config["TicketUrl"]["metadata_field"], + } + + +def save_to_json_files(data: List[Dict], output_dir: str): + """Save each dictionary in the list as a separate JSON file in the specified directory.""" + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + for i, item in enumerate(data): + output_file = os.path.join(output_dir, f"item_{i}.json") + with open(output_file, "w", encoding="utf-8") as f: + json.dump(item, f, ensure_ascii=False) + + +@click.command() +@click.argument("input_file", type=click.Path(exists=True)) +@click.argument("config_file", type=click.Path(exists=True)) +@click.argument("output_dir", type=click.Path()) +def process_data(input_file: str, config_file: str, output_dir: str): + """ + Process data from INPUT_FILE using CONFIG_FILE and save to OUTPUT_FILE. + + The input file should be a CSV file containing the data to process. + The config file should be an INI file with the processing configuration. + The output file will be saved in JSON format. + """ + # Load configuration + click.echo(f"Loading configuration from {config_file}") + config = load_config(config_file) + + # Get configuration sections into variables + prefix_fields = config["prefix_fields"] + substring_fields = config["substring_fields"] + list_fields = config["list_fields"] + composite_text_fields = config["composite_text_fields"] + metadata_fields = config["metadata_fields"] + + # Load DataFrame + click.echo(f"Reading data from {input_file}") + df = pd.read_csv(input_file) + + # Prepare embedding data + click.echo("Processing data...") + embedding_data = prepare_data_for_embedding( + df=df, + prefix_fields=prefix_fields, + substring_fields=substring_fields, + list_fields=list_fields, + composite_text_fields=composite_text_fields, + metadata_fields=metadata_fields, + jira_url=config["jira_url"], + metadata_field=config["metadata_field"], + ) + + # Save to file + click.echo(f"Saving processed data to {output_dir}") + save_to_json_files(embedding_data, output_dir) + click.echo(f"Processed {len(embedding_data)} documents") + + +if __name__ == "__main__": + process_data() diff --git a/scripts/process_sitemap.py b/scripts/process_sitemap.py new file mode 100644 index 0000000..44e7118 --- /dev/null +++ b/scripts/process_sitemap.py @@ -0,0 +1,50 @@ + +import click +import json +import os + +from langchain_community.document_loaders.sitemap import SitemapLoader +from urllib.parse import urlparse + + +def build_filename_from_url(url: str, extension: str = "json") -> str: + parsed_url = urlparse(url) + # Use the netloc and path as base components + base_name = f"{parsed_url.netloc}{parsed_url.path}".replace("/", "_").strip("_") + # Remove query parameters and fragments if present + base_name = base_name.split("?")[0].split("#")[0] + # Construct the filename + filename = f"{base_name}.{extension}" + # Ensure filename is valid on the filesystem + return "".join(c if c.isalnum() or c in "._-" else "_" for c in filename) + + +@click.command() +@click.argument("input_url") +@click.argument("output_dir", type=click.Path()) +def process_data(input_url: str, output_dir: str): + """ + Process data from INPUT_URL and save to OUTPUT_FILE. + """ + sitemap_loader = SitemapLoader( + web_path=input_url, filter_urls=["^((?!.*/v.*).)*$"] + ) + sitemap_loader.requests_per_second = 1 + docs = sitemap_loader.load() + print("Count of sitemap docs loaded:", len(docs)) + + for doc in docs: + data = { + "text": doc.page_content, + # source key exists in doc metadata + "metadata": doc.metadata, + } + + output_filename = build_filename_from_url(doc.metadata["source"]) + output_path = os.path.join(output_dir, output_filename) + with open(output_path, 'w', encoding='utf-8') as json_file: + json.dump(data, json_file, indent=4) + + +if __name__ == "__main__": + process_data() \ No newline at end of file diff --git a/scripts/process_txt_files.py b/scripts/process_txt_files.py new file mode 100644 index 0000000..0b0fe61 --- /dev/null +++ b/scripts/process_txt_files.py @@ -0,0 +1,35 @@ +import json +import os + +import click + +@click.command() +@click.argument("input_dir", type=click.Path(exists=True)) +@click.argument("output_dir", type=click.Path()) +def process_data(input_dir: str, output_dir: str): + """ + Process data from INPUT_FILE and save to OUTPUT_FILE. + """ + for filename in os.listdir(input_dir): + if not filename.endswith(".txt"): + continue + + file_path = os.path.join(input_dir, filename) + with open(file_path, 'r', encoding='utf-8') as file: + text_content = file.read() + data = { + "text": text_content, + "metadata": { + "source": filename + } + } + + output_filename = os.path.splitext(filename)[0] + ".json" + output_path = os.path.join(output_dir, output_filename) + with open(output_path, 'w', encoding='utf-8') as json_file: + json.dump(data, json_file, indent=4) + + + +if __name__ == "__main__": + process_data() diff --git a/scripts/process_zendesk_tickets.py b/scripts/process_zendesk_tickets.py new file mode 100644 index 0000000..63afc43 --- /dev/null +++ b/scripts/process_zendesk_tickets.py @@ -0,0 +1,287 @@ +import os +import re +import json +from configparser import ConfigParser +from typing import Any, Dict, List, Union + +import click + + +def clean_text(text: Any) -> str: + """Clean and standardize text content.""" + if text is None or text == "": + return "" + result = str(text).strip().replace("\n", " ").replace("\r", " ") + result = re.sub(r'-{3,}', '-', result) + result = re.sub(r'#+', '', result) + result = re.sub(r'\*+', '', result) + result = re.sub(r'>+', '', result) + result = re.sub(r'<+', '', result) + result = re.sub(r'\s+', ' ', result) + result = result.replace("[]", " ") + result = result.replace("[ ]", " ") + return re.sub(r'\s+', ' ', result) + + +def get_nested_value(data: Dict[str, Any], field_path: str) -> Any: + """Extract value from nested dictionary using dot notation. + + Args: + data: Dictionary containing the data + field_path: Path to the field using dot notation (e.g., "submitter.name") + + Returns: + The value at the specified path or None if not found + """ + current = data + parts = field_path.split('.') + + for part in parts: + if isinstance(current, dict): + current = current.get(part) + elif isinstance(current, list) and current: + # If it's a list, try to get the first item's attribute + if isinstance(current[0], dict): + current = current[0].get(part) + else: + return None + else: + return None + + if current is None: + return None + + return current + +def parse_list(value: Any) -> List[str]: + """Convert a field value to a list of strings.""" + if not value: # Handles None, empty string, empty list + return [] + if isinstance(value, str): + return [item.strip() for item in value.split(",") if item.strip()] + if isinstance(value, (list, tuple)): + # Handle cases where list items might be dictionaries + processed_items = [] + for item in value: + if isinstance(item, dict): + # Extract name or id from dictionary + item_str = item.get('name', item.get('id', '')) + if item_str: + processed_items.append(str(item_str)) + elif item: + processed_items.append(str(item)) + return processed_items + return [str(value)] + +def extract_field_values( + data: Dict[str, Any], + keys: List[str], + matcher: callable +) -> List[str]: + """Extract values from fields that match a given condition.""" + return [ + str(data[key]) + for key in keys + if key in data and data[key] and matcher(key) + ] + +def extract_comments_text(comments: List[Dict[str, Any]]) -> str: + """Extract readable text from comments array.""" + if not comments: + return "" + + comment_texts = [] + for comment in comments: + if comment.get("public", True): # Only include public comments + author_name = "" + if comment.get("author_id"): + # You might want to add author mapping here + author_name = f"Comment {comment['id']}" + + body = comment.get("body", "").strip() + if body: + comment_texts.append(f"{author_name}: {body}") + + return "\n".join(comment_texts) + +def process_item( + data: Dict[str, Any], + keys: List[str], + config: Dict[str, Any] +) -> Dict[str, Any]: + """Process a single JSON item using the provided configuration.""" + # Extract data using different matching criteria + result_text = [] + + # First process title and description + priority_fields = ['Title', 'Description', "Ticket"] + for field in priority_fields: + if field in config["composite_text_fields"]: + column = config["composite_text_fields"][field] + value = get_nested_value(data, column) if '.' in column else data.get(column) + if value: + result_text.append(f"{field.lower()}: {clean_text(value)}") + + # Then process other composite text fields + for field, column in config["composite_text_fields"].items(): + if field not in priority_fields: # Skip title and description as they're already processed + value = get_nested_value(data, column) if '.' in column else data.get(column) + if value: + result_text.append(f"{field.lower()}: {clean_text(value)}") + + # Process comments last + # if "comments" in data and isinstance(data["comments"], list): + # comments_text = extract_comments_text(data["comments"]) + # if comments_text: + # result_text.append(f"comments: {comments_text}") + + # Process composite text fields + #for field, column in config["composite_text_fields"].items(): + # value = get_nested_value(data, column) if '.' in column else data.get(column) + # if value: + # result_text.append(f"{field.lower()}: {clean_text(value)}") + + # Process prefix fields + for target_field, prefix in config["prefix_fields"].items(): + if '.' in prefix: + # Handle nested prefix fields + parent, child = prefix.split('.', 1) + if parent in data and isinstance(data[parent], list): + values = [item.get(child, '') for item in data[parent] if item.get(child)] + if values: + result_text.append(f"{target_field.lower()}: {' '.join(map(clean_text, values))}") + else: + values = extract_field_values( + data, keys, + lambda k: k.startswith(prefix) + ) + if values: + result_text.append(f"{target_field.lower()}: {' '.join(values)}") + + # Process substring fields + for target_field, substring in config["substring_fields"].items(): + values = extract_field_values( + data, keys, + lambda k: substring in k.lower() + ) + if values: + result_text.append(f"{target_field.lower()}: {' '.join(values)}") + + # Build metadata + metadata = {} + for field, column in config["metadata_fields"].items(): + if '.' in column: + # Handle nested fields + parent, child = column.split('.', 1) + if parent in data and isinstance(data[parent], dict): + value = data[parent].get(child, "") + else: + value = "" + else: + value = data.get(column, "") + metadata[field.lower()] = clean_text(value) + + # Process list fields + for field in config["list_fields"]: + if field in data: + values = parse_list(data[field]) + if values: + metadata[field.lower()] = ", ".join(values) + + # Add source URL + metadata_unique_id = config["metadata_unique_id"] + metadata["source"] = config["zendesk_url"] + metadata[metadata_unique_id] + ".json" + + return { + "text": ". ".join(result_text), + "metadata": metadata + } + +def prepare_documents( + data: Union[Dict[str, Any], List[Dict[str, Any]]], + config: Dict[str, Any] +) -> List[Dict[str, Any]]: + """Prepare documents for embedding using the provided configuration.""" + # Ensure data is a list + items = [data] if isinstance(data, dict) else data + + # Get all unique keys from the JSON data + keys = sorted({key for item in items for key in item}) + + # Process each item + return [process_item(item, keys, config) for item in items] + +def load_config(config_file: str) -> Dict[str, Any]: + """Load and parse configuration from INI file.""" + config = ConfigParser() + config.read(config_file) + + return { + "prefix_fields": dict(config["PrefixFields"]) if "PrefixFields" in config else {}, + "substring_fields": dict(config["SubstringFields"]) if "SubstringFields" in config else {}, + "list_fields": [ + f.strip() + for f in config.get("ListFields", "fields", fallback="").split(",") + if f.strip() + ], + "composite_text_fields": dict(config["CompositeTextFields"]) if "CompositeTextFields" in config else {}, + "metadata_fields": dict(config["MetadataFields"]) if "MetadataFields" in config else {}, + "zendesk_url": config.get("TicketUrl", "zendesk_url", fallback=""), + "metadata_unique_id": config.get("TicketUrl", "metadata_unique_id", fallback=""), + } + +def save_documents(documents: List[Dict], output_dir: str) -> None: + """Save documents as individual JSON files.""" + os.makedirs(output_dir, exist_ok=True) + + for i, doc in enumerate(documents): + output_file = os.path.join(output_dir, f"item_{i}.json") + with open(output_file, "w", encoding="utf-8") as f: + json.dump(doc, f, ensure_ascii=False, indent=2) + +@click.command() +@click.argument("input_file", type=click.Path(exists=True)) +@click.argument("config_file", type=click.Path(exists=True)) +@click.argument("output_dir", type=click.Path()) +def main(input_file: str, config_file: str, output_dir: str) -> None: + """ + Process JSON data using the provided configuration and save results. + + INPUT_FILE: JSON file containing the data to process + CONFIG_FILE: INI configuration file + OUTPUT_DIR: Directory to save the processed documents + """ + # Load configuration + click.echo(f"Loading configuration from {config_file}") + config = load_config(config_file) + + # Load JSON data + click.echo(f"Reading data from {input_file}") + data = [] + with open(input_file, encoding="utf-8") as f: + for line in f: + line = line.strip() + if line: # Skip empty lines + try: + json_obj = json.loads(line) + data.append(json_obj) + except json.JSONDecodeError as e: + click.echo(f"Warning: Skipping invalid JSON line: {str(e)}") + continue + + if not data: + click.echo("Error: No valid JSON objects found in the input file") + return + + click.echo(f"Found {len(data)} JSON objects") + + # Process documents + click.echo("Processing documents...") + documents = prepare_documents(data, config) + + # Save results + click.echo(f"Saving {len(documents)} documents to {output_dir}") + save_documents(documents, output_dir) + +if __name__ == "__main__": + main() diff --git a/scripts/query/query.py b/scripts/query/query.py new file mode 100644 index 0000000..2176aa2 --- /dev/null +++ b/scripts/query/query.py @@ -0,0 +1,31 @@ +from openai import OpenAI +import os + +query = input("Type your query here: ") +modelid = os.getenv("MODEL_ID", "microsoft/Phi-3-mini-4k-instruct") +modelurl = os.getenv("MODEL_URL", "http://localhost:9000/v1") + +# Note: Ray Serve doesn't support all OpenAI client arguments and may ignore some. +client = OpenAI( + # Replace the URL if deploying your app remotely + # (e.g., on Anyscale or KubeRay). + base_url=modelurl, + api_key="NOT A REAL KEY", +) +chat_completion = client.chat.completions.create( + model=modelid, + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + { + "role": "user", + "content": query, + }, + ], + temperature=0.01, + stream=True, +) + +for chat in chat_completion: + if chat.choices[0].delta.content is not None: + print(chat.choices[0].delta.content, end="") +print("") diff --git a/scripts/query/query_private_data.py b/scripts/query/query_private_data.py new file mode 100644 index 0000000..2d49df6 --- /dev/null +++ b/scripts/query/query_private_data.py @@ -0,0 +1,25 @@ +import requests +import urllib.parse +import json + +def main(): + hostname = "localhost:8000" + query = input("Type your query here: ") + + # Encode the question using urllib.parse + encoded_question = urllib.parse.quote(query) + + url = f"http://{hostname}/answer/{encoded_question}" + + response = requests.get(url) + + if response.status_code == 200: + response = response.text.strip() + data = json.loads(response) + answer = data['answer'] + print(f"Answer: {answer}") + else: + print(f"Error: {response.status_code} - {response.text}") + +if __name__ == "__main__": + main() diff --git a/scripts/query/requirements.txt b/scripts/query/requirements.txt new file mode 100644 index 0000000..41c6a85 --- /dev/null +++ b/scripts/query/requirements.txt @@ -0,0 +1,2 @@ +requests +openai diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000..867c9b1 --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,3 @@ +click +pandas +langchain_community \ No newline at end of file diff --git a/scripts/zendesk_config.ini b/scripts/zendesk_config.ini new file mode 100644 index 0000000..27eb53e --- /dev/null +++ b/scripts/zendesk_config.ini @@ -0,0 +1,30 @@ +[PrefixFields] +comments_body = comments.body + +[SubstringFields] + +[ListFields] +fields = tags + +[CompositeTextFields] +Description = description + +[MetadataFields] +ticket = id +title = subject +status = status +type = type +priority = priority +collaborator = collaborator.name +requester = requester.name +submitter = submitter.name +assignee = assignee.name +organization = organization.name +group = group.name +created_at = created_at +updated_at = updated_at +url = url + +[TicketUrl] +zendesk_url = https://zendesk.com/api/v2/tickets/ +metadata_unique_id = ticket