├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── notebooks ├── baseline.ipynb ├── gaussian_process.ipynb └── test_models.ipynb ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests └── test_dataset.py └── time_series ├── __init__.py ├── dataset ├── __init__.py ├── time_series.py └── utils.py ├── models ├── LSTM │ └── __init__.py ├── TCN │ └── __init__.py ├── __init__.py ├── deepar │ ├── README.md │ ├── __init__.py │ ├── imgs │ │ ├── gaussian.png │ │ └── prediction.png │ ├── layers.py │ └── loss.py ├── gaussian_process │ └── __init__.py ├── nbeats │ └── __init__.py └── transformer │ └── __init__.py ├── settings.py └── utils └── __init__.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.py linguist-detectable=true 2 | *.ipynb linguist-detectable=false 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | ipynb 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | 108 | # mac 109 | .DS_Store 110 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | # command to install dependencies 5 | install: 6 | - pip install -r requirements.txt 7 | - python setup.py install 8 | # command to run tests 9 | script: 10 | - pytest 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alberto Arrigoni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Time-Series 2 | 3 | **time-series** is a Python module for machine learning for time-series built on top of tensorflow and is distributed under the MIT license. 4 | 5 | This repository was created as a companion repository for chapter 12, **Multivariate Forecasting**, of the book [Machine Learning for Time-Series with Python](https://amzn.to/3Eb62VH). 6 | 7 | Tensorflow implementations of Time-Series models including these: 8 | * Amazon DeepAR 9 | * Gaussian Processes 10 | * LSTM 11 | * TCN 12 | * Transformer, and 13 | * NBEATS 14 | 15 | The `time_series.dataset` package, part of this library, includes utility functions for loading datasets. 16 | 17 | Please see the example notebook for usage and training results. 18 | 19 | ## Installation 20 | 21 | ```python 22 | pip install git+https://github.com/benman1/time-series.git 23 | ``` 24 | 25 | ## Contribute 26 | 27 | Pull requests welcome! 28 | 29 | ## List of Contributors 30 | 31 | Contributions from various people have found their way into this repository. Thanks to everyone for their hard work! 32 | 33 | * [Alberto Arrigoni](https://github.com/arrigonialberto86) 34 | * [ketan-b](https://github.com/ketan-b) 35 | * [Philippe Remy](https://github.com/philipperemy) 36 | * [Theodoros Ntakouris](https://github.com/ntakouris) 37 | -------------------------------------------------------------------------------- /notebooks/baseline.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "660fcbda-4d8e-4d4e-b186-d901d50f860d", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 4, 17 | "id": "6a83e95d-402c-4284-b1b6-256d1350fd90", 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "from time_series.dataset.utils import get_energy_demand\n", 22 | "from time_series.dataset.time_series import TrainingDataSet\n", 23 | "from time_series.utils import evaluate_model\n", 24 | "\n", 25 | "train_df = get_energy_demand()\n", 26 | "tds = TrainingDataSet(train_df)" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 16, 32 | "id": "4c6818d4-1394-4e53-b129-9990c2c8d894", 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "from statsmodels.tsa.api import ExponentialSmoothing\n", 37 | "from joblib import Parallel, delayed\n", 38 | "\n", 39 | "\n", 40 | "def univariate_forecast(column):\n", 41 | " fit1 = ExponentialSmoothing(\n", 42 | " train_df[column].values[:split_point],\n", 43 | " seasonal_periods=4,\n", 44 | " trend=\"add\",\n", 45 | " seasonal=\"add\",\n", 46 | " use_boxcox=False,\n", 47 | " initialization_method=\"estimated\",\n", 48 | " ).fit()\n", 49 | " return fit1.forecast(10)\n", 50 | "\n", 51 | "\n", 52 | "split_point = int(len(train_df) * tds.train_split)\n", 53 | "forecasts = Parallel(n_jobs=10)(\n", 54 | " delayed(univariate_forecast)(column) for column in train_df.columns\n", 55 | ")" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 30, 61 | "id": "a7d8248d-52e1-4877-8421-3708f539b710", 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "data": { 66 | "text/plain": [ 67 | "11.28" 68 | ] 69 | }, 70 | "execution_count": 30, 71 | "metadata": {}, 72 | "output_type": "execute_result" 73 | } 74 | ], 75 | "source": [ 76 | "import numpy as np\n", 77 | "from sklearn.metrics import mean_squared_error\n", 78 | "\n", 79 | "actual = train_df.values[split_point:][:10, :]\n", 80 | "round(mean_squared_error(actual, np.transpose(np.array(forecasts))), 2)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "id": "40b7b17c-e9b8-4431-84bd-fd1127f4d724", 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [] 90 | } 91 | ], 92 | "metadata": { 93 | "kernelspec": { 94 | "display_name": "Python 3", 95 | "language": "python", 96 | "name": "python3" 97 | }, 98 | "language_info": { 99 | "codemirror_mode": { 100 | "name": "ipython", 101 | "version": 3 102 | }, 103 | "file_extension": ".py", 104 | "mimetype": "text/x-python", 105 | "name": "python", 106 | "nbconvert_exporter": "python", 107 | "pygments_lexer": "ipython3", 108 | "version": "3.8.8" 109 | } 110 | }, 111 | "nbformat": 4, 112 | "nbformat_minor": 5 113 | } 114 | -------------------------------------------------------------------------------- /notebooks/gaussian_process.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "fd7a4738-8476-45aa-951a-b79bed1ed9a6", 6 | "metadata": {}, 7 | "source": [ 8 | "\"Open" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "01b7dada", 15 | "metadata": { 16 | "collapsed": false, 17 | "jupyter": { 18 | "outputs_hidden": false 19 | }, 20 | "pycharm": { 21 | "name": "#%%\n" 22 | } 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "%load_ext autoreload\n", 27 | "%autoreload 2" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "id": "73a9d219-67c2-41f9-af0e-71f264a70d56", 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "# pip install git+https://github.com/benman1/time-series.git" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 2, 43 | "id": "892bb0e5", 44 | "metadata": { 45 | "collapsed": false, 46 | "jupyter": { 47 | "outputs_hidden": false 48 | }, 49 | "pycharm": { 50 | "name": "#%%\n" 51 | } 52 | }, 53 | "outputs": [], 54 | "source": [ 55 | "from time_series.dataset.utils import get_energy_demand\n", 56 | "from time_series.dataset.time_series import TrainingDataSet\n", 57 | "from time_series.models.gaussian_process import GaussianProcess\n", 58 | "\n", 59 | "train_df = get_energy_demand()\n", 60 | "tds2d = TrainingDataSet(train_df.head(500), train_split=0.1, two_dim=True)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 8, 66 | "id": "7fc55cf8", 67 | "metadata": { 68 | "collapsed": false, 69 | "jupyter": { 70 | "outputs_hidden": false 71 | }, 72 | "pycharm": { 73 | "name": "#%%\n" 74 | } 75 | }, 76 | "outputs": [ 77 | { 78 | "data": { 79 | "text/html": [ 80 | "
\n", 81 | "\n", 94 | "\n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | "
zoneCTMASSMENEMASSBOSTNHRISEMASSTOTALVTWCMASS
ts
2003-03-01 00:00:00-0.318751-0.574674-0.966582-0.615339-1.003294-0.727829-0.624415-0.591646-0.543866-0.446601
2003-03-01 01:00:00-0.480297-0.731304-1.193852-0.786355-1.145521-0.879446-0.758124-0.756747-0.728727-0.599752
2003-03-01 02:00:00-0.567380-0.813741-1.241200-0.876271-1.209149-0.972373-0.822407-0.838081-0.807953-0.686537
2003-03-01 03:00:00-0.607767-0.838472-1.255404-0.886850-1.220378-0.991936-0.837835-0.866931-0.878376-0.740140
2003-03-01 04:00:00-0.596408-0.812991-1.127565-0.876271-1.171721-0.977264-0.791551-0.835648-0.843165-0.714615
\n", 191 | "
" 192 | ], 193 | "text/plain": [ 194 | "zone CT MASS ME NEMASSBOST NH \\\n", 195 | "ts \n", 196 | "2003-03-01 00:00:00 -0.318751 -0.574674 -0.966582 -0.615339 -1.003294 \n", 197 | "2003-03-01 01:00:00 -0.480297 -0.731304 -1.193852 -0.786355 -1.145521 \n", 198 | "2003-03-01 02:00:00 -0.567380 -0.813741 -1.241200 -0.876271 -1.209149 \n", 199 | "2003-03-01 03:00:00 -0.607767 -0.838472 -1.255404 -0.886850 -1.220378 \n", 200 | "2003-03-01 04:00:00 -0.596408 -0.812991 -1.127565 -0.876271 -1.171721 \n", 201 | "\n", 202 | "zone RI SEMASS TOTAL VT WCMASS \n", 203 | "ts \n", 204 | "2003-03-01 00:00:00 -0.727829 -0.624415 -0.591646 -0.543866 -0.446601 \n", 205 | "2003-03-01 01:00:00 -0.879446 -0.758124 -0.756747 -0.728727 -0.599752 \n", 206 | "2003-03-01 02:00:00 -0.972373 -0.822407 -0.838081 -0.807953 -0.686537 \n", 207 | "2003-03-01 03:00:00 -0.991936 -0.837835 -0.866931 -0.878376 -0.740140 \n", 208 | "2003-03-01 04:00:00 -0.977264 -0.791551 -0.835648 -0.843165 -0.714615 " 209 | ] 210 | }, 211 | "execution_count": 8, 212 | "metadata": {}, 213 | "output_type": "execute_result" 214 | } 215 | ], 216 | "source": [ 217 | "train_df.head()" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 9, 223 | "id": "39c9aff1", 224 | "metadata": { 225 | "collapsed": false, 226 | "jupyter": { 227 | "outputs_hidden": false 228 | }, 229 | "pycharm": { 230 | "name": "#%%\n" 231 | } 232 | }, 233 | "outputs": [ 234 | { 235 | "data": { 236 | "text/plain": [ 237 | "Index(['CT', 'MASS', 'ME', 'NEMASSBOST', 'NH', 'RI', 'SEMASS', 'TOTAL', 'VT',\n", 238 | " 'WCMASS'],\n", 239 | " dtype='object', name='zone')" 240 | ] 241 | }, 242 | "execution_count": 9, 243 | "metadata": {}, 244 | "output_type": "execute_result" 245 | } 246 | ], 247 | "source": [ 248 | "train_df.columns" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": 10, 254 | "id": "8c133d78", 255 | "metadata": { 256 | "collapsed": false, 257 | "jupyter": { 258 | "outputs_hidden": false 259 | }, 260 | "pycharm": { 261 | "name": "#%%\n" 262 | } 263 | }, 264 | "outputs": [ 265 | { 266 | "data": { 267 | "text/html": [ 268 | "
\n", 269 | "\n", 282 | "\n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | "
zoneCTMASSMENEMASSBOSTNHRISEMASSTOTALVTWCMASS
count1.241710e+051.241710e+051.241710e+051.241710e+051.241710e+051.241710e+051.241710e+051.241710e+051.241710e+051.241710e+05
mean1.111590e-14-5.550440e-145.267530e-141.964806e-14-1.417297e-14-5.904490e-152.278479e-14-4.836024e-154.680011e-144.847010e-15
std1.000004e+001.000004e+001.000004e+001.000004e+001.000004e+001.000004e+001.000004e+001.000004e+001.000004e+001.000004e+00
min-2.860578e+00-2.078022e+00-2.462776e+00-2.025786e+00-3.001960e+00-2.811343e+00-2.190355e+00-2.353885e+00-2.645718e+00-3.295209e+00
25%-7.251402e-01-7.402971e-01-8.150690e-01-7.387527e-01-7.712391e-01-7.327200e-01-7.269825e-01-7.449296e-01-7.815442e-01-7.549140e-01
50%-3.099671e-025.381168e-031.327082e-011.231042e-028.586684e-02-1.865201e-02-2.786611e-021.592663e-028.114064e-028.986296e-05
75%5.937324e-015.952086e-017.142682e-015.923568e-016.435471e-015.437988e-015.378266e-016.151574e-017.325557e-016.509817e-01
max4.705586e+004.776973e+003.389425e+004.821935e+004.225427e+005.023842e+005.104509e+004.537964e+003.223778e+004.415944e+00
\n", 405 | "
" 406 | ], 407 | "text/plain": [ 408 | "zone CT MASS ME NEMASSBOST NH \\\n", 409 | "count 1.241710e+05 1.241710e+05 1.241710e+05 1.241710e+05 1.241710e+05 \n", 410 | "mean 1.111590e-14 -5.550440e-14 5.267530e-14 1.964806e-14 -1.417297e-14 \n", 411 | "std 1.000004e+00 1.000004e+00 1.000004e+00 1.000004e+00 1.000004e+00 \n", 412 | "min -2.860578e+00 -2.078022e+00 -2.462776e+00 -2.025786e+00 -3.001960e+00 \n", 413 | "25% -7.251402e-01 -7.402971e-01 -8.150690e-01 -7.387527e-01 -7.712391e-01 \n", 414 | "50% -3.099671e-02 5.381168e-03 1.327082e-01 1.231042e-02 8.586684e-02 \n", 415 | "75% 5.937324e-01 5.952086e-01 7.142682e-01 5.923568e-01 6.435471e-01 \n", 416 | "max 4.705586e+00 4.776973e+00 3.389425e+00 4.821935e+00 4.225427e+00 \n", 417 | "\n", 418 | "zone RI SEMASS TOTAL VT WCMASS \n", 419 | "count 1.241710e+05 1.241710e+05 1.241710e+05 1.241710e+05 1.241710e+05 \n", 420 | "mean -5.904490e-15 2.278479e-14 -4.836024e-15 4.680011e-14 4.847010e-15 \n", 421 | "std 1.000004e+00 1.000004e+00 1.000004e+00 1.000004e+00 1.000004e+00 \n", 422 | "min -2.811343e+00 -2.190355e+00 -2.353885e+00 -2.645718e+00 -3.295209e+00 \n", 423 | "25% -7.327200e-01 -7.269825e-01 -7.449296e-01 -7.815442e-01 -7.549140e-01 \n", 424 | "50% -1.865201e-02 -2.786611e-02 1.592663e-02 8.114064e-02 8.986296e-05 \n", 425 | "75% 5.437988e-01 5.378266e-01 6.151574e-01 7.325557e-01 6.509817e-01 \n", 426 | "max 5.023842e+00 5.104509e+00 4.537964e+00 3.223778e+00 4.415944e+00 " 427 | ] 428 | }, 429 | "execution_count": 10, 430 | "metadata": {}, 431 | "output_type": "execute_result" 432 | } 433 | ], 434 | "source": [ 435 | "train_df.describe()" 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": null, 441 | "id": "28222b80", 442 | "metadata": { 443 | "collapsed": false, 444 | "jupyter": { 445 | "outputs_hidden": false 446 | }, 447 | "pycharm": { 448 | "name": "#%%\n" 449 | } 450 | }, 451 | "outputs": [], 452 | "source": [ 453 | "# please note this only works in tensorflow Eager mode!\n", 454 | "N_EPOCHS = 100\n", 455 | "gp = GaussianProcess(tds2d)\n", 456 | "gp.instantiate_and_fit(maxiter=N_EPOCHS)" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 6, 462 | "id": "9965f68c", 463 | "metadata": { 464 | "collapsed": false, 465 | "jupyter": { 466 | "outputs_hidden": false 467 | }, 468 | "pycharm": { 469 | "name": "#%%\n" 470 | } 471 | }, 472 | "outputs": [ 473 | { 474 | "name": "stdout", 475 | "output_type": "stream", 476 | "text": [ 477 | "MSE: 0.4221\n", 478 | "----------\n", 479 | "CT: 1.68\n", 480 | "MASS: 1.12\n", 481 | "ME: 1.51\n", 482 | "NEMASSBOST: 0.86\n", 483 | "NH: 1.62\n", 484 | "RI: 1.02\n", 485 | "SEMASS: 1.0\n", 486 | "TOTAL: 1.38\n", 487 | "VT: 1.75\n", 488 | "WCMASS: 1.59\n" 489 | ] 490 | }, 491 | { 492 | "data": { 493 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAArkAAAQBCAYAAAAuF5KWAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAAEAAElEQVR4nOzdd3hU1dYG8HenUELvPQkCUqU3pahgAURBRUWwXQvXgmLvXsArNq4FuygWFBsgKogdkSZVOoIivXcIoaTt7493znfOTCYFSDKTyft7nvMkc87MZGcymVmzztprG2stREREREQiSVSoByAiIiIiktcU5IqIiIhIxFGQKyIiIiIRR0GuiIiIiEQcBbkiIiIiEnEU5IqIiIhIxFGQKyIiIiIRR0GuiEgBM8YMMMYsNMYcNsZsN8Z8Z4z52Xf5sDEmxRiT6rn8XajHLCJS2MSEegAiIkWJMeZeAA8DuBXADwBSAPQA0NVae57vOsMA1LfWXhOqcYqIFHYKckVECogxphyAJwH8y1r7pefQZN8mIiJ5ROUKIiIF50wAJQBMCvVAREQinYJcEZGCUwnAHmttWqgHIiIS6RTkiogUnL0AKhtjVComIpLPFOSKiBSc3wEcB9A3xOMQEYl4CnJFRAqItfYggP8AeN0Y09cYE2eMiTXG9DTGPB/q8YmIRBKdMhMRKUDW2heMMTsAPA5gHIAkAIsAjAjpwEREIoyx1oZ6DCIiIiIieUrlCiIiIiIScRTkioiIiEjEyTHINcaUMMbMN8YsNcasNMYML4iBiYiIiIicrBxrco0xBkApa+1hY0wsgFkAhlhr5xbEAEVERERETlSO3RUso+DDvouxvi3byLhy5co2MTHxlAcnIiIiIpKVRYsW7bHWVgl2LFctxIwx0WCLm/oAXrfWzgtynUEABgFAfHw8Fi5cePIjFhEREZHCY9gwbgXMGLMxy2Mn0kLMGFMewCQAd1prV2R1vbZt21oFuSIiIiJFhDFACNrSGmMWWWvbBjt2QotBWGsPGGN+BdADQJZBroiIiIhEuNRU4JdfgKSkUI8kqNx0V6jiy+DCGFMSwPkAVufzuEREREQkXI0ZA5QrB/TsCVx5JfcZwy0EZQvB5KZPbg0AvxpjlgFYAOAna+2U/B2WiIiIiIQFa4EFC4B77wX++Yf7qlYFLrkE+Oor4Ngx93rWhk2Qm5vuCssAtCqAsYiIiIhIuFixAvjsM27//APExgLt2wP16gEXX8wtjJ1QTa6IiIiIRLCjR4GSJYEDB4DWrYH0dKB7d+Cxx4C+fYEKFYLfbujQghxlrijIFRERESnKtm4FvvgC+PRToHhxYOZMoHx5YOJEZm6rVcv5PsKkRMFLQa6IiIhIUfTNN8ALLzCotZaZ2759+b0xYV+OkBMFuSIiIiJFwcGDnCh28cVAxYrA5s3A7t3A8OHAVVcBp58e6hHmKQW5IiIiIpHqyBFgyhROHps6FTh+HBg7Frj2WuDWW4Hbb2fWNgIpyBURERGJRHv2AHXrAocPAzVqMKi9+mrW2QJAdHRox5fPFOSKiIiIFHZpacD06czYGgO88w5QuTLw0ENAp05A164RH9QGUpArIiIiUlgtWgR88AG7I+zaBZQuzWytM3ns8cdDPcKQyc2KZyIiIiISDqwFliwBUlJ4edIk4N13mamdMIGB7ujREVtneyIU5IqIiIiEuzVr2Iu2cWOgVSvgp5+4/957gZ07gfHjgcsv50IOAkDlCiIiIiLha/t2oFcvZm+NAc45h4HtmWfyeMWKoRxdWFOQKyIiIhIuduxgfa21wJAhXG0sIQG44QbgiiuAmjVDPcJCQ0GuiIiISCjt28cldD/7jB0SMjKACy5gkBsVxQUc5ISpJldERESkoB0+zGwtwDZfgwZxBbLHHwdWrgR++CG044sACnJFRERECsKxY8CXXwJXXglUrQr88Qf3338/W4GtWcMldps0Ce04I4TKFURERETy044dzNZOmgQkJQFVqgD/+hdQrhyPN2wY2vFFKAW5IiIiInkpIwOYORM4cgTo2ZPB7LRpQL9+QP/+QLduQIxCsPyW4yNsjKkDYCyAagAsgNHW2lH5PTARERGRsDVsGDeHtcCCBZw89vnnwLZtQLt2DHJLlgQ2buQkMikwuXm00wDcZ61tAqAjgDuMMSoWERERkaJr+HD/y7fcAnToALz+OoPbzz4Dfv3VPa4At8DlmMm11m4HsN33fZIx5k8AtQCsyuexiYiIiISX9HR2PwCA5s2Bb78F6tQBBgwAOnUCLr0UKF8+pEMUOqGCEGNMIoBWAOYFOTYIwCAAiI+Pz4uxiYiIiISHNWuAiy4C/vnH3bd8ORAfDwwd6l+6IGEh10GuMaY0gIkA7rbWHgo8bq0dDWA0ALRt29bm2QhFRERECsquXcDs2dxmzQIGDgTuvJOTx0qUAP79b6BzZ+Daa90+txKWchXkGmNiwQB3nLX2y/wd0ikILAIXERERyYq1wMGDLC9ITwdatHBLEYoXZ21txYq8XL06sGKFe9trry3w4cqJyU13BQNgDIA/rbUv5v+QTsHw4cDllwOlSwOlSnErWVLF3iIiIgKkpACLFzNDO2sWs7VNmnAp3ehooEcP4LrrmKlt04aBblaGDi2wYcvJMTaHVLsxpjOAmQCWA8jw7X7UWjs1q9u0bdvWLly4MM8GmSvWBg9m77oLGDWKq4w0aeIGv6VKMRgeMAC46iour/fMM/4BcqlSfJI3aAAcPw6sXeverlQpnrYwpmB/zxOhzLaIiBRlBw4Ay5YBXbvy8sUXA1Om8Pt69RjMduvGwFYKJWPMImtt22DHctNdYRaA8I3khg3L3MYDYF+6c84BWrfm5fR0zno8fBhITua2dy+wbx+P798PPPssGzh7vfQScPfdLDRv1sz/WFQU8O67XLVk+XLgmmv8g+BSpRhkt28PbNjAvnmBQXT79kDlyhzPvn3u/mLFTj2AHj5cQa6IiBQdO3awbZeTqV2+nPv37WNJwp13Atdfz3igRo2QDlXyX+FfbsObrTQm6yLwUqWAjz7K+n7q1AHS0pixTU52g+EqVXi8Zk32vHMCZGdr3pzHY2KAunV5u6Qk/qMlJzPwBYBVq4CHH878c3/6CTjvPGDqVK5l7YiJ4Zh//hlo2xaYPBl4/nn/ALlUKeA//+E/6uLFwO+/u0F06dK8n5QUBswikjWd9XDpsZDCIj2dNbKzZ7NtV40awJdfAnfcwffAM89kCWPnzkBcHG9zwQWhHbMUqBzLFU5GSMoVgOyD3FCzFjh61A2OnSC6USPO2Fy3jkv+BR6//34G4FOmAC++6H8sORmYNw847TTgueeCB9GOHj2A3bsZrNeowa1mTeCGGxgEHz7M8gstMyhFUTi/dhQ0PRYSznbu5BnUWbOAOXOAQ75mT599xtLDXbuArVuBM87Q+1kRkV25QmQFuUU5A3HsGGuPvEFyp05Aair/0T/6CPjkE2D7dm67drHIPiWFZReDBvGFo2pVNwBOSADeeIP3v3gx76tGDc4wjY0N6a8rcsJSUvi837kTaNiQmZ7584FPPwVefpnZH2u5Pf44/wemTeObp7UsZXKOjxzJMqNvvgHGj3f3O9s77/D+x40DJk3KfHzCBP5fvvkmP8B6j8XG8swNwA+vP/7of7xsWf5cAHj0UU6Y8R6vUQP46isev/12nuFxjmVkcI7Bl74mOf37A0uW+N9+7Vo3yH3zTZ7hqlnT3apXz34yjkhecVp5zZrFrGy/fsCWLUz8NGvGDG2nTvyakBDec2Qk35xSTW6hUlQDXIBZ2OrVM+93Pslee61/u5PUVGZ2ncl6l17K22/fzvW2t29nMOB47DHgu+/cy1WqsLXKt9/y8gcfMLB2AmQnW6w3Q8lP6el8nu7Y4f/18ssZzP36K4PXnTvd+nsAmDED+OUX/3r+11/n15IlWbdXsybPsEyezP8TY9zt2DFed/Nmvgl7jxnDwBDgm/Sff2Y+7gSRhw5xbN5j3vKi1FSWUAXe3lGsGMuTnP1RUUCFCu7xypUZEDjHjGEw4Khfn2NZudJtmwS4P6NCBc5X8OrVy/2/v+Ya/o97g+DGjYHTT8/d308kkLVMusyYAfz1F/cVL87neb9+QO3afE5qRTHJhcjK5Iq/vMxsr1rFN3xvEFyuHDNNANCyJbB0qf9tOncGZs7k97fcwkyaNwBu1IinlES80tK4lSjB/pVffeUfwO7cycmgvXszwOzcOfN9fP45a9wXLwaeeoof4KpVc7927gxUqsQ31MDAs6jzPhYZGZygu22bu1WrxsfeWmbRNm7k38aZtPvvfwNvvcUPIAkJvL43CO7enY9/RgY/aFepojaPRVFKCvDHH26mtmRJnm0EgPPPZw2tk6nNqZWXFGlFJ5Mr/vIys92kCbesLFoE7NnjBsDbt/t/0t60iRmtHTuYnQKAq692X9QSEvii5g2Cu3VjlwyAp1Br1OCn+ZNRlEtZwkF6Op8fTqBavTonbSYlAbfd5h/E7tkDjBgBPPIIS3BuuIH3UaoUA6Zq1dxMacOGPKXuDWCrVXOfJ61aARMnZj0und7MXlQUg9AqVdgk38sY1kQC/Pvu2sX//zJluO/YMf7/btvGU8zz5/M6MTEMXrZvZ1YuOto9A1SzJnDrrcCFF/K5MWeOu79iRf29CrPkZPf/8r77WArnnBGpX59/c8dPPxX8+CQiRUSQO24cz6Zv2sQlpEeM4Cp8UoCio90Ao1WrzMd/+IFfMzJ42njbNveTeXo6cMklboA8axa/GsM3yaQknnoG+AbqvCH++9+sKTxyhNk+b4Bcpoz/G6LaqfnLy6B/5Ur+7Zws644dQNOmDE4zMni63JvpA1hC8NprzNbOmcPnTb16zNpUqwZ06cLr1arFDzjVqrkdQ7wqV2ZQdKrU1N11Mo+FE6h6WzKVKsXaZK/UVPdDbqlSfA54s8Rr1/KDDcDnVY8e7m2LFeP9v/kmXxfWrQO++MI/S1yzJs8wKRgOvU2b3CztrFlMcuzdy9fmxo35f+tkaoOV2onkgUJfrjBuHMt3jhxx98XFAaNHK9At1Kzlm2GxYvzjTpjgZoidYHjQINYZr1rFoMorLo6nTK+9li+2CQnAPfcwixQby6/9+/PFdsMGTuTxHouN5WnVGjX485YsyXy8WTMGXgcOMEPlPRYTw3rG6Gg3uAunU7JZnZ53PoQcOcJPjADw/vvA6tX+2dbmzYEPP+Tx2rU5m9lRvDgfdyfAue8+/yxs9ersCFKzZv7+jlK4HT7MEignAHb+9+++m/3PJ00CLrss8+1+/ZU90n/9FXj77cxBcPv2bjup3NBZIFdWj4XTyisxkR8yRo9mEgJwW3l17gwMHuwukSuSRyK6u0JiIkvCAiUkMHaRIiAlhYt1eAPg7duZAX733czXdwK8r74C+vRhj+KLLsp8vZ9/ZqD72WcsrQg0dy7QoQN/xi23ZD6+ciVLPF5+mQF2VJR/ELxiBQPEV1/ldQKD6GnTmPV45x13Nr73+McfM4j+9FNmSrzHSpZkD2WAv9+aNf7Hb77ZDXIffJC/686dDNbT0nhqeskSHu/YkbWt3iC1Qwd2IAB4arF4cbdcoGxZZdKkYCQnu//3W7fy6zXX8Hk4fjxP8W3d6p8F+ftvnh5/4w3glVfc4Nd7higujpMCY2P5veq1yXntPH6cXTucLO3vv/Pxcmrh//4b+P57BrZq5SX5LKKD3KiorF9/+vXje3GHDqxbP5EP7xJhAjOXzvfGMGOclMSvaWnu15o1+aTZu5enUb3HUlOZnShfnsfmzct8/JprmM2dO5flGoH3/+STzHpMmsSWToHHx4/nz3/tNQa0gfe/Zg3H/8ADzLZ6jzuTtgAuXf3pp1k/Np068ffwBrF163L5S4ABQsmSClylcLKW/99ORrhzZ54h+uorzgnwlkscP+4+3++5hx8+AX6YjIrih7mkJO679VbePirK3SpV4v8lwDNN33/vdraIimLpzvTpPH7LLQwOvcdPP52BonN85Ur/482auV1A/v1vZnicrhlRUcxwOx1Dbr+dr13e4+3bcxVOABgyhL+rc9/G8DXN6cLzwAM8s+P9/Z59lo/nunUsLwLcVl6dO3Nho2rV8ukPKRJcRE88i48PnsmNi+NcqAkTeDk6mh8on3iCZ7jC8QyyFCBvwBYbm/0ptEqVuGWlfn1uWenYkVtWLr2UW1YGD+aWlZEjuWXlvfeYtXKC4NRUnurI7QdcfTqUwswYnl0oW5YdXRx9+3JzWMvSo5IleUreCXABno5PT2fGxHH22byu0384I8P/f6VFC/7POf2VMzL8X0fi49kKy3u8alX3uLNypXPfgf+vhw9zvN7jdeq4x9esYeDuPV6unHv8l1/cn+9s0dFukPveezxLduyYO9HTeTwB1gO++qp/yzqRMFPoM7k51eTu2sVJvfPmcRsyhGem58zh3IVvv+UH0L17+d6v+vcIpbo6f2qZJZI7+l9x6bGQIEI9+T+iM7nOA5nVA1y1Kls69u7tf7vy5XkdZ9L+++/z7ExCglvi0KEDz/6ULFlgv47kFwW4/tRNQERETlFgonHjRl4GwmPyf6HP5OaVVau4oJeT8d20iftjYjiR3Al6BwzQirYiIkWGzgK59FiIx4EDTBTu2ZP5WEFO/o/oiWf5ZccON+CdNw9YsID1u/v28evLL7O84YEHQj1SERERkfyVkcFF6r7/ntvcuSxVD8YY/9bo+SmiyxXyS/Xq7C7Vpw8vZ2Rw0R5notrvvzM97wS5PXuypr9jR2Z8W7XiBHcRERGRwmjnTuDHHxnU/vgjs7bGAG3bAo8+yvlPO3dmvp3TZj3UFOTmUlSU/x/t88/dTykZGWxnOnu22/0lNpaTa731vQ0aqAuTiIiIhKfUVJZr1qvHOYYtWjCIrVqVybwePYDzz+dK3wBXVg82+X/EiNCMP5DKFfLYtm1uicP8+SxzOHyYxx58EHjuOXZl+fln4KyzOAFOREREJBS2bXMXoOzfn2UI69czKff11+xM17Jl1i1Xw7m7Qo5BrjHmPQC9Aeyy1jbLzQ8sykFuoPR0Ltk9bx779LZvDyxcCLRrx2XXr7iCi8N89x2zvS1bst+4iIiISF47ehSYOdOtrf3zT5Zj1qoFzJjBkoS+fQvPOgKnWpP7AYDXAIzNy0EVFdHRXBCmmefjQdOmXPTmjDN4edo09u8FuBBPy5b+ZQ716qnMQURERE6ctcBff7lB7fTpXOOjeHHgnHNYbuDMIeraNZQjzXu5KlcwxiQCmKJMbv7ZssW/m8PChW6NS6VKzACPHQtUruyutCgiIiKSlWXLOIHeaefVsCHranv0YEAbCQtaZpfJzbNQyRgzyBiz0BizcPfu3Xl1t0VG7drA5ZcDzz8P/PYbcPAgsGQJZy727cvTB87qiXfeyZmNzueTTZtY5+sYNw5ITGQgnJjIyyIiIhLZUlOBbt0YSwBA3bo8O/zmm8C6dcDq1WyB2qNHZAS4Ocmz7grW2tEARgPM5ObV/RZVMTGc1diiBXDLLf7HOnRgRtcpYejThzU1rVqxjdmvv7pBb7itPiIiIiKnbs8e4KefgB9+YDzw/vvs7FSzpjupvUwZYNKkkA4zpFSuEAG+/BKYM4dlDrNnB19avHJlQAl2ERGRwiktjR2bnNraBQv4fl+xIpNd770X6hGGhhaDiHCXXcYNyLpW11l2Ly2NLT4efxy4/XYWn//+OyfGOX3vREREJDz8/DPwzjvM2u7fz/f5Dh2AoUNZdtC2LSe5S2Y5BrnGmE8BnAOgsjFmC4Ch1tox+T0wOTnx8SxRCLYfAJKTgUsuAU47jZdXrmT9DsAgt1kzdn9wvjZt6tYCi4iISP5av541tPfey9VX16xhy6++fRnUnnces7eSMy0GEWHGjQu++sjo0cFrcpOSmMlduRJYsYJfV650F7AAWN/z/vvABRew5GHdOvXzFRERyQv//MO62pYtuUjUkiXsqPT111xlLCWFtbZqJRqcyhWKECeQze3qI2XKMHi94AJ3n7W8rTfwrV2bx777Drj+emDVKqBxY16ePt3N/DZuDJQsma+/ooiISKGVnMz3Tae2du1a7n/wQQa5LVoAe/fy/Rlg/3w5OcrkygnZtYuZ3169+MlyxAjgySfdbg7GcPEKb8mD81W9fUVEpChasYIB7Q8/cFWxlBQmhLp1YwnChRcC9esrW3syTmlZ35OhILdoSU3lJ1Fv5nfFCi5XnJ7OT6HJyWyL9t57wL59wP3387bW6p9aREQiy4EDfB/s3JmXW7YEli5lwsdZjKFzZ3elMTl5KleQfBUbyzKFxo2Bfv3c/cePs2B+82YGuADwyy+cGOcEuV26sC44MPNbt64yvyIiEn7GjctcEnj11cAff7BffXQ0j3/wAZM6xYuzO0L16kCdOqEefdGiTK4UuPR0t93JsGHs9bdypX9XiJIlgSZNGPReeCEwYAD3K/MrIiKhEmxyd3Q037MOHwbmzwfateMCTfv2AR07qr1XfiuQZX1Fcsv7Dz9sGPDtt1xX+9AhYO5c4N13gVtvZYuUn3/mCm4AkJEB1KoFvPgiL6eksG/g9u3BF8AAtMSxiIicut27gbffBm67zT/ABZi4sRb4+GOgQQPua9wY6NRJAW6oqVxBwkaZMmxw3aGD//60NH49ehS4/HKgUSNeXrPG7QpRoULmkofVq4H77nNfkLTEsYiIBHPsGOeRFC8OnH46F1248ELgzjuBa68Fduxg8iUrR47ofSUcKciVsOfU85YqBbz6qru/Xj1g2jT/CW+ffcaC/6wcOQI8/DBfjFT6ICJSdFgLbNvGBMmaNUyEON9v3MjjN93Es4nlygGVK7PPPMDkyoYNQNeurMUN5Cy4JOFFNbkSUaxl+cKKFfwUHowxLH146y3gP//hRIDatTN/dTYteiEiUvhMmcJSgj59+N5QqxbfHxylSgENG/pvrVrxa1ZOdMElyX/qriBFhjFcoa1mTSAhIfgSx87s1oYNgUsvBbZs4Sf0mTN5iirQ2rXMGn/9NfDjj8DLL7OjxPr1fAGtXVttYEREClJGBl+7nUyssxnDXrQA8MIL7PLTpw/33303ULq0G9DWqnXiZ/NOdMElCS1lciVincwn7uRkvnA62+bNbHdWogTw/PPAa68xcDYGuO464KOPeLvKlTNngevU4XbOOfn+q4qIRKQjR9ySgY8+YnZ2zRrgr784T8NRpgxLCpo3Z7kBwNKEChW0Cmek02IQUmQF62eYV5+4//gDWL6cgbA3KN6yha1jAAa/u3fz+5tv5v4vv3THlpHhXxqhF2MRKWrS0/ka7WRjb7qJGdenn2YHnuRknj0bMoRBbsOGDGi9ZQbVq2uORVGlIFekgCUnA1u3svzB6Rbx/PNsk/bUU7xcvz7wzz/+t6tUyT8b3L498K9/8diWLTyuQFhECqPkZE4Q9pYXrF7NrgbHj7vXW7gQaNMGmDWLS+DefTezuZosLMEoyBUJQ0eOZM4Ae7/fvJnLPn79Na9fqxaXghwzhi/2l13mrqDjLZHILiOcn5ltEYlMJ/K6kZbGrUQJYN064LnngLvuYmvHjz9mOy6A/WNPO80/G+tkZ6tUUTAruaeJZyJhKC6O/RhPPz3r6zg9ggG+WThtag4f5mS5WbOAPXsy365SJQa7//43m5enpgKDB7OmzaljU99gEclJ4NwG53UjORk444zME7/WrgX+9z/2l01NBSZOBC65hEFut27ApEkMZOvVA4oVC+3vJpFPmVyRQu7oUZZGBMsGX3YZcMMNfGNKTAx+e2PcfpCB2733csGNjRuBl15i0Ny4MTM0P/8c/DberUIF1tKFK2W2pShKT2fQmpzMzft9cjJQrZpbZlWhQva9xwH+j9er52Zj+/QBzjxT5QVSMJTJFYlgJUuyvrd+/ayv47TKCfaZ1lqgXz++0TlvdkeOsJ44JYXX2b4deP99vnk1bgwsWMCANyc//gicfz7w1VecTDJrFm//+edc2CO7ALlUKQbolSoxO+SscBcbC+zaxWy29/oxJ/hqllWGCiiaga4Cfn+hfDy8weH27axXdT6kTp/OyazeoNS7HTnC9olPPsnrX345LzvLoVerxv+f7PTrB4wfz++zC3AnT2ZgW7du8P8/BbgSarl6WzDG9AAwCkA0gHettc/m66hEJE/FxPCNOljf4IQE4I03sr99x47AwYPu5T59mD0ODIwDt8aNef06dYABA4CKFd3xlCjBQHXnzsy3c4LrPn0Y5E6aBDz4IJCUxCD3uefcN21HbKx/gBwXB8yZw+/ffx/47Tfggw943U8/Be64I/Ma9EeOcJJLbCw/PFx8MfcvX86Mefv2vLxmDZcBjYnhdWNiMn9frBh/dmGggN9fTo+HtQw8s8qEHj3KsygA8N13fL7cfTcvv/ACMH9+8ODU+b5yZfd/9cYb2ZVl3jxeHjIEWLYs85i9z/127dz9tWszsHUMHsyuLqVKuf8nzvfO5apV3evXqcMzQ4ESEoDevU/4oRUpUDmWKxhjogH8BeB8AFsALABwtbV2VVa3UbmCSPgpTCv1pKUxUIiL4wSVnTuZUWvTBoiKctu3BQusvdv48Qw4n32WE/h+/533f9VVwBdfZD+GChXcVnD9+nEW+IoVvNypEwPo7DRt6l6/c2cGEE6T+lat+CEhuyD5zDPdZayvu479P++/n5evuYaBVuDtvLdv25ZZPIAfCNq1A7p0YXD20Uf+tx08OHhtd0ICA7SpU/nz69VjZu/77xkoWcuvzua9fNZZQLNmvN9PPgEuuoi3X7+edZre2wW7r/79+RiuWcPJloMH84PavHm8v6xu5+x7/HH+vN9+A95+G3jlFQaPEyf63z7YfcycyQ8xwR6PPn3YLzsjI/u/f1oan7u33QZMmOC2Ehw0iPcfLLh0vq9SBXjoIV5/2jTWtjorOK5axfF6b1OyJP8v8kNhet2QoulUyxXaA1hrrV3nu7PPAPQBkGWQKyLhpzCt1BMTw+bujmrV/LNRrVtzy62HH+bm+PxzYO7c4GvQ16zJMgvv5/8nn2SGzfHsswzeUlPd2eTO987XChXc6193nf8km4svZtDj3NZ7O2crW9a9flKSf+P7JUsYrAb7+c73117rBrkPPcRMeJcubGN3yy25e9w2beL1L7uMAffgwdx39dU53/bVVxnkbtvG7GPt2gw616wBHngg+9tGRfGDQNOm/HmvvsrfJT6e7abGjuV1jOFX7+bsu/NO3tfu3WxJ5bSo2ruXCwkEXt+7BQtwncejWzc+N7MKUJ3Lzqn6l19mUOwYPTrnx86rWzf/y02anNjtT1Vhet0QCZSbTG4/AD2stTf7Ll8LoIO1dnDA9QYBGAQA8fHxbTYGOy8qIhImilKGKjmZHxyKF+eko+3b/QPj7t25L1BCAuuhV65kXXflygwAN2zIHBwGXi5XjgFfWhpLXcqUYaCflsb7yOq24VDHmZiYdWnPhg0FPRoRyU6BTDyz1o4GMBpguUJe3a+ISH4oShkqb21wdDSzql4jRwYP+EeMYHDcooW7v0QJzqDPrZgY1lV7L5cufWLjL2gjRmT9eIhI4ZGbKp6tAOp4Ltf27RMRKdQGDmRmLiODXyMxwM2NgQOZwU5IYCY1ISEyM9q5pcdDJDLkplwhBpx41h0MbhcAGGCtXZnNbXYDCEW9QmUAQaZPiOi5IdnS80OyoueGZEXPjfCQYK2tEuxAjuUK1to0Y8xgAD+ALcTeyy7A9d0m6A/Lb8aYhVnVZUjRpueGZEfPD8mKnhuSFT03wl+uanKttVMBTM3nsYiIiIiI5Il86qwnIlL0GGM2GGNSjDGVA/YvNsZYY0yiZ98w374OAdctZox5wRizxRhz2HefL3uOdzbGzDHGHDTG7DPGzDbGtIOIiPiJtCD3BDsQShGi54ZkJy+fH+sB/H8nWWPMGQDivFcwxhgA1wHY5/vq9QiAtmCP8jIAzgHwh+92ZQFMAfAqgIoAagEYDuB4Ho5f/Om1Q7Ki50aYy3HimYiI5I4xZgOAdwH0sda28+37H4D9AJ4CUNdau8EY0xWc53AzgFcA1LDWpviuPwXAz9bal4Pcf1vfsfL5/9uIiBRukZbJFREJtbkAyhpjGvuWRe8P4OOA61wPYDIAZ3HhiwNuf68x5nZjzBm+rK/jLwDpxpgPjTE9jTGeddVERMRLQa6ISN77CCxDOB/An/D0FjfGxAG4AsAn1tpUABPgX7LwDIDnAAwEsBDAVmPM9QBgrT0EoDMAC+AdALuNMd8YYzyLHouICBBBQa4xpocxZo0xZq0x5uGcbyFFgTGmjjHmV2PMKmPMSmPMkFCPScKLMSbaNzFsSh7e7UcABgC4AcDYgGOXAkiD27FmHICexpgqAGCtTbfWvm6t7QSgPIARAN4zxjT2Hf/TWnuDtbY2gGYAagJ4OQ/HLgCMMeWNMROMMauNMX8aY84M9ZgkfBhj7vG9p6wwxnxqjCkR6jFJZhER5PpOCb4OoCeAJgCuNsY0Ce2oJEykAbjPWtsEQEcAd+i5IQGGgNnWPGOt3QhOQOsF4MuAw9cDKA1gkzFmB4DxAGLBoDjwfo5aa18Ha3ozPW+ttasBfAAGu5K3RgH43lrbCEAL5PFzRAovY0wtAHcBaGutbQauIdA/tKOSYCIiyAVnIa+11q7zTd74DECfEI9JwoC1dru19g/f90ngG1Wt0I5KwoUxpjaAi8DJYnntJgDdrLXJnn21wNUjewNo6dtagOUJ1/nGdLcx5hxjTEljTIyvVKEMgMXGmEbGmPt844Yxpg7YyWFuPoy/yDLGlAPQFcAYALDWplhrD4R0UBJuYgCU9K0KGwdgW4jHI0FESpBbC8Bmz+UtUCAjAXw9SlsBmBfioUj4eBnAgwAy8vqOrbX/WGsXBuzuAmCJtfZHa+0OZwM7LDQ3xjQDcATACwB2gEuG3gHgcmvtOgBJADoAmGeMSQaD2xUA7svr8RdxdQHsBvC+r5TlXWNMqVAPSsKDtXYrgP8B2ARgO4CD1tofQzsqCSZXK56JFHbGmNIAJgK42zd5R4o4Y0xvALustYuMMefkxX1aaxOz2J8GwOmS8GyQ49vAkgWAQWvQ/pu+N9crT3mgkpMYAK0B3GmtnWeMGQXgYQBPhHZYEg58XU36gB+GDgAYb4y5xlob2EVFQixSMrlbAdTxXK4Nz2xmKdqMMbFggDvOWhtYHylFVycAl/h6234GoJsxRm9SAvBs4BZrrXPWZwIY9IoAwHkA1ltrd/s6pHwJ4KwQj0mCiJQgdwGABsaYusaYYmAB+DchHpOEAV+P0TEA/rTWvhjq8Uj4sNY+Yq2t7cu+9gcwzVp7TYiHJWHAV0Ky2RjT0LerO4BVIRyShJdNADoaY+J87zHdoYmJYSkiyhWstWnGmMHgCkLRAN6z1q4M8bAkPHQCcC2A5caYJb59j1prp2Z9ExER3AlgnC9xsg7Av0I8HgkTvhKWCeBy22kAFkNL/IYlLesrIiIiIhEnXzK5lStXtomJiflx1yIiIiIiAIBFixbtsdZWCXYsX4LcxMRELFwY2DlHRERERCLSsGHcCpgxZmNWxyJl4pmIiIiIhEJqKjB8eKhHkUlETDwTERERkQK0dy8wfjwwbhxQvnyoRxNUjkGub9nIsQCqAbAARltrR+X3wEREREQkDM2eDZx7LjO4Xsa35s3QoSEpXQiUm0xuGoD7rLV/GGPKAFhkjPnJWquegSIiIiKRLD0dmD6dGdszzgDuuQdo0wa4917gqquAli0Z3BoDhFnHrhyDXGvtdnBtZlhrk4wxfwKoBTXGFhEREYlMS5cCH30EfPopsG0bUKYMUMe3uGyJEsCzmVYoDzsnVJNrjEkE0ArAvCDHBgEYBADx8fF5MTYRERERKSg7dwLVqvH7J54AvvsO6NULGDgQuPhioGTJrG87dGjBjPEE5HoxCGNMaQC/ARhhrf0yu+u2bdvWqoWYiIiISJjbt8+dQDZ7NrBuHZCQAKxdC1SoAFSqFOoRZssYs8ha2zbYsVxlco0xsQAmAhiXU4ArIiIiImFu7Vrg/vuBqVM5gaxRI+DJJ4G4OB6vXz+048sDuemuYACMAfCntfbF/B+SiIiIiOSp9HTgt9+AmBiga1egXDngjz+AwYOBa64BWrVyuyNEiNxkcjsBuBbAcmPMEt++R621U/NtVCIiIiJyaqwFli0DPv6YE8i2bgV69mSQW6UKsHFjxAW2XrnprjALQOQ+AiIiIiKR6JprgE8+Yfa2Rw/ghRc4gcwRwQEuoBXPRERERAq//fs5gWz8eGDiRKBsWeDSS4FOnYArrwQqVw71CAucglwRERGRwujYMeDbb1mOMHUqkJICNGwIrF8PtGgB9OsX6hGGlIJcERERkcIiIwM4eJDtvdatYyBbvTpw++0sT2jdOuLLEHJLQa6IiIhIuPNOIOvShbW2TZoAM2cCHTuy7lb86BERERERCVfvvguMGgWsWMFA9sIL/csQOncO3djCnIJcERERkXCxfz/w1VfAtdcyqF27FihTBnj99SI7gexkKcgVERERCaVjxzhx7OOPOZEsJYVL63brBowYAURHh3qEhZKCXBEREZFQWbMG6NCBk8mqVQNuu40TyNq04XEFuCdNQa6IiIhIQVm+HBg3jt0RHnoIqF8fGDgQuOQSoHt3TSDLQ3okRURERPLT5s3sivDxxwxyo6OB66/nseho1ttKnosK9QDy1LBhoR6BiIiICHDokPv9Y48xa1uqFPDaa8D27cCYMaEbWxFhrLV5fqdt27a1CxcuzPP7zZExQD78PiIiIiI5On7cfwLZ/PlA8+bA338zRqlfP9QjjDjGmEXW2rbBjkVOuYIT3LZtC1Spwq1qVeC884AePbhCyPz57v7SpbUiiIiIiJycYcPcM8g7dwJPPAGMHw8cOMA449//ZusvAGjQIESDLNoKf5A7bBgwfLh7edEifi1XDkhNBYoVY5B74ABw5pnu9YoXZ8D72GPArbeyL91//8snphMkV6kCnH46UKlSQf5GIiIiEu6GD+dEsS5dWIbw9ddA796cRHbeeZpAFgYiv1whPZ1F3ceOAdOmAbt2Abt3c9u1i6uG9O7NFh5t2gDJyf63Hz0auOUWYMkSXs8bAFetCtx4I3DGGcC+fcDq1e7+smWVKRYREYkUa9cCv/3GZXRnzQL++YdxgxPvpKUpsA2BolGukBWnv1yJEkCvXllfr2FD4PBh4MgRNwjevRto2pTH4+KACy5wg+S1a/n1/PMZ5M6aBfTp495fbCwD3gkTmEGeP5/rTHsD5CpVgFateN/5wXsqRURERHInNZXJrSVLmOgCgPvvZ7bWa9EiJrSGDtX7bRiKrExuKII6a/kE372bT3Zvlnj3bpZDnHYai9Bvvx1ISvK//cqVQJMmwBtvAE89lTlTPHw4UL48i9Z37nSPlS8PROXQHEMT8URERHJn5UrW1M6aBcyd657Z3bWL77vLlzOB1bAh31/1HhsWssvkRlaQWxgcO+afKe7aFShZEvjxR+CLLzIHyZs2sb74wQeBkSPd+4mO5vrVGzeyvnjsWGDBAjdDXLUqcPnlnHCnsgkRERHXrl3A7NksPRg8mMmo995j1rZFC6BzZ3erWTP4fSjIDQsRX64wbhwTpps2AfHxXOZ54MBQjyoLJUoAdepw87rgAm5ZueMOFrJ7A+T9+xngAjyl8tFHXBbQy8n2dunCwPe009ytfn1+FRERiXSbN/Ps6KxZnIcD8D303HP5XnjllUwOlSuXu/sbOjT/xip5otBncseNAwYNYimtIy6O88XCNtDNTykpwJ49LG1o3dr9lHnvvezdt349rwOwlnjZMn4/ZAhPzXiD4Hr11FlCREQKl/R0vrc5E8TOP58Z2t27gUaNgE6dmKHt0oXvk06ySAqliC5XSEzkGftACQnAhg0FMoTwFexUSkYGsG0bsG4dC+u7d+f+vn1Zg7Rzp3vdXr3YzBrgJ4myZf2D4IQEvTiIRBpNWJXCxpkbYy0ngE+f7s5/iY9nEufee/2vKxEjooPcqKisS2Leeosf2Jo0yXmOVkQ6mTer5GRme9etYxPrc89lYNyiBTtKHDvmXnfQIODtt/mpedAgoG5d/yC4ShW9mEjhoMDOpTpDCXd797KedtYsbnFxwM8/89j117NnrVNPGx8f2rFKvjulINcY8x6A3gB2WWub5eYHhkMmNyqKsRnARgRnncXn+2WXcWKknISMDGDHDgbA69YxkO3c2S2N2LbN//rPPAM8/DBPET3zjH8AnJjI+mSRcJCbwM5a/g9Y6/bCTEpib8z0dHcrUQKoWJHH167NfLxSJb7xZmTwjdp7LD2dHxYbNWJZ0ddf83re4y1bcktKAj78kPu81+nWDWjXjhNrXn3V/7YZGcBVVwEdOvDD7DPPZP75n37K33H3bo6vVi1u1aq5LRlFCoq1fG+pVYuXb7uNGSyAiz21a8czkt5FoaRIOdWJZx8AeA3A2LwcVF4ZMSLrmtyOHfkhz/nAN3Uq53s1bMiOXKNHA3feqQ96uRYVxVmmNWsyuHVUqwZs3QocPcoaEScIPussHt+0iRlf7x8JAD77jG+4a9eyuNobBFevriywnLqdO4Ht2/l11y5upUtzuU2Ai8E4qyRWr84gr3NnYNIk7mvalC8WToAI8HToV1/x+3r1GAx6XXMNJ4ECrHv3nv0AuMLim2/yzbtr18xjfuAB4Pnn+f9y5ZWZjw8fziD34EG+gAV68UW+8e/dyxfI6Gh3i4ribTt04CqQkydzf1IScOiQex/B/veio/kYff45T5GtWAFMmeIGwc5WunTm24rkVkYGn1uzZrk1tVu2cK5JpUr8EFenDutp27VTskSylWOQa62dYYxJLICxnBRncllW3RXq1ePZC4Cv+U4J6fLlwCuv8EMhwNZ433/v1qM3aKAY64SVLAk0bszNq00bLrSxa5cbAK9bxzdbgBMEhg/3z6SVLMkV6jp2ZOeI335zA+C6dU98AQ2dji78rGUgtm8fnwMAa8YXLfIPYuPigO++4/GrrwZ+/dX/fpo3Z5A7bBgwcaK736lH9wal11/PLibeQNF7KujJJ3l97/HTT3ePv/8+x+097ow9Kgr46Sf/Y9HRbruiMmX4QuUEp87xChV4vEYN/r7eADY6mtktgP+HTmAeTKtW/AAQyMlqHz7MGehbt/pv1avzenPnAo88kvn2y5YxuJ8yhYvhBAbBLVqw16gIABw/zvaXjRqxLeaYMcxcAfxf6NKFb8rOWYQrrgjdWKXQyVVNri/InZJduYIxZhCAQQAQHx/fZmOwGoIwc+wYg15jmPwYMYLvnwDLSTt1coPe1q3d9w7JB8eOse7EGwQ/8ADfUF94gSvNeFWvzhfG2rWB338H/vrLDYJr1MhchK06w/CUkuIGp97tvvv4N3v5ZWZFnf0pKfwAlJzM49ddx+MVK7JFntMm7/33ef8//cQsZdWqPOOQ1ZLben64TuSxOHyYp5K9QfBtt/Exfustvqhu385MuGPfPgbq//sfswuBQfDAgfz/dZZkl8hy9Cg/eDqZ2gULGOh++CH/nzdt4sSxLl1Y1qZsk+TglCee5SbI9Sqsi0FkZDBx4ZQ4zJ7NM+kAz4h07cpsrzFsTKBkRAGxlqeqvAHwP//wTbRYMZ6yfe019/rFizPQWbaMtZMzZgBnn83MUsmS3MqUAZr5ns6HDvGPWqKE/qinylqeBi9dmo/ln3/yDS0wiP3yS2Zthg5lNjSQEwi98QbrjJwA1tkGDGAglJSUN383BbmuvD7rkZ7Ov7kTBF9yCR/vd99l6YOz/9AhZuEPH3Y/wHzzDQPfmjX5tUEDnrYDWBpVvDifDwqGw9fWrQxmq1XjRGanvjYmhtkjJ1N79tnuWQqRE6Ag9xTs2AHMmcPA9+hRltIBwJln8qzlBx/w8vbtKiMNmePH+enfGwTv3cvalawmI9SuzcbgANCzJz+9AHyzLFmSp7Rnz+a+m24CVq3i/hIl+LVpUzc4GzWKQZlzrGRJnpJ2FveYM4cBlPf25cq5k5NC0dLmRAKZ48f9A9SdO/m71azJDxBPP+1fLpCWBvzxB0+Hjx7N0gBjWE/nBKljx7KubuFCXtfZ72RbS5cu2MdE5Syh55Q0OQvUfP45/we9WeLy5VnCAbA289dfGSxVr87/6Y4dgZde4vGffuIxJ0NcqtTJjUvPjRP33nvMxs6c6fby7N+fkxoBvqG2anXyfxMJK6FekEtBbj54/nm+H19/PYPfcuWYmHK6lnTqxNKzmByrniXf7d/PgHLuXJZFHD3KLKAThH71FVP2R49yO3aMAZmTMbrvPr6xOseOHmUW+LPPeLxVK9YNe/Xo4daF1qnDiRNel1/OekWAT5yjR/2D5KuuAp56isd792am0hskd+/OViFpaSwu9x5zaqNPP53HV6/OfDwuzs26/vSTfwC7axf7Sp59NvDLL1xpL9A33wAXX8wg46GH/APUqlX5hlajBrNzR47wd9Q/g5wq7wfCX37hc9sbBCcksKYTYNbXORUH8EX6iiuAd97h5Rde4P+Bt1SialWVOmUnMOBPSeGH1FmzOBHyv//l/rZt+ZrnLLjQubPeECNUOCzIdaotxD4FcA6AygB2AhhqrR2T3W2KQpDrlZzMxJTTsm/TJu4vVYqJBSfoPfNMTTwOmfx+o7LWDYCPHeMbpXeCzqFD/kFynTruQhz//S/fILzHu3blLPyMDD5xvAH40aPMjo4YwfsNtgTl0KF8M/K23gk25pUr3bINY1iMXrUq7/uSS/hG9eGH/gFs1aq8Ty0EIuHs7795tsYbBDdsCAwezOd+2bLMHnvdeCODZGv5Qa16dX6IfPxx/k+ffTYzyMnJ3B8V5b917swZ/wcP8t0/8PiZZ/ID6P79/BAceLxtW6bC9u3j60ZUFP8vnePNm/N/dP9+nl0KvH2DBvy9Dh7k/27g8Vq1+GH38GF+wA08Xr48A9GUFG6Bx2Nj+diMHcu693nz+HoEMIhdvJjjdcqNdGoz4oXDglzZBbmw1ub51qZNG1uUbdpk7aefWjt4sLUtW1obFWUtYO2YMTy+ZYu1n31m7cGDoR1nkTJ0aKhHkD8yMqw9cMDa7dutXbfO2lWrrF20yNrNm3k8OdnaL76w9sMPre3dm0/EwO2226zdudPatLTQ/i4iBSktzdqtW62dP9/aSZOsfe01a3/8kccefjj4/0qXLjy+Y0fw4889x+Nr1wY//sYbPL54cfDjH33E4zNmBD8+aRKPf/tt8OM//8zjn30W/Pi8eTz+zjvBj69axeMvvhj8OMDjTz1lbZs21g4ZYu2ECXz9kSJj715rP/7Y2rffttaY4E8TYwpuPAAW2izi0UK/4llhcOgQP5Q3b87EwJgxwM0380xbw4bsjrVqFZMATZsW0dXZpGDpFKxI7nj/V7zLx6ak8EyLdytenJnS9HTOCwg8XqECJ70eO8ZTfoHH69ThdZKSOGnT2e8sRNK4MUt/9uxh1jTw9h07MtO7eTPfdAKP9+zJ269ezdOOgccHDGBp18KFrKnNyAB+/JGlIYGcs0US8azlU2bKFLbWnj2bT43mzXnSIJwzuQpyQyA1FVi6lO1jjQHuuosLEwE883zWWW7rsnbtTrwlrEiOFOSK5I7+V1x6LIqMlBTOG3QC23/+4f4WLTgdo3dvxieffhreNbmqAg+B2FiWXjlGjQLuvttdmW32bHfOUkwMg+Fu3TiJXSRPDB0a6hGIFA76X5EiYs8elnQXK8Z4Y/hwnpzo3p3zr3v35skGr5wW5Ao1ZXLD1L59XOPACXpjYrgAGMBVQ5s0AR59lJcDO1CFup2HiIhEILVTiyjW8sxysWLsdNmlCxeR7NGD8zZXrWJznXDv9KZMbiFUsSJw0UXcAP+SsLQ0dwGh48eB+vXZU7tzZ076HTnSPXWwcaO7QqICXREROWkKcAu948fZ3nzyZJYiDBzIBj+tWwNPPOGuSt6gAbfCTpncQm7PHuDBB5nx/fvvrK9XkEXgIiIiEh527+bCkZMnAz/8wA5yJUowS3vjjcCll4Z6hKdGmdwIVrkyF5cB2MO/evXg8wKc2Y979gB//cU6X7U5FRERiUxvvgl89BEbbVjLRSoHDODEsW7disakdjWriiBVq7IGN5iaNfn1u+/YucHJ+s6bB3zyCVfC1aRZERGRwumPPzhXx3kvX7iQNbdDhwKLFnF9kLff5gSyohDgAsrkRpwRI4K383j+eX7fqxdXsW3cmJfHjgXeeIPfV6nCNovO1q4dWzqKiIhIeNm5k2UIPXpwFfXFi4GXX2YMkJjINl7R0aEeZWipJjcCnUh3hbQ0ruw6d667rV7NY8ZwxdcuXYDXXtMKjSIiIqFiLbBsmTtpbP587nv3XeCmm5jcMgYoWTLUIy1YWgxCTsj+/fzncYLetDTgp594rG9fBs6vvMLLSUnK9oqIiOSHY8eAX391A9vNm7m/fXt3UYYWLYp2EkoTz+SEVKgAXHght0ANGgDVqvH7tDROdKtZk+UNHTrwa4sWXPBCRERETszx45wYnpoK1KrFvvmlSgHnn88ubr168b1XcqZMrpy05GTg9deZ7f39d2DHDu4vUYLdG5za3i5d3MBYREREgrvxRpYMzpnDy6+/DtSrB5xzDt9bJTOVK0i+s5anUby1vYsWcf3r114D7rgD2LaNnRyuvpqfTkVERIqio0eBX35hCcLPPwNLlgClS3My+LZtwEMPFe0ShBOhcgXJd8awVjc+HrjySu47fhxYutRd63r+fOCBB7gOdq1abEo9ebKb8a1XT//UIiISmbZtY1A7eTID3KNHGdheeCHnwpQuDVx3XahHGVkU5Eq+KV6cxfGOvn3Z8qRiRV5eswb48EOejgGASpX8a3vbtwfKlSvwYYuIiOSJHTuAt95iYPvHH9yXmAjcfDMnjZ19thZmyk8qV5CQSk8HVq3yL3P480+WPzgtzBYt4kS23bsZIBf1vn8iIhI62bXpzMhgtrZiRaBzZ642etppTNw43RCaNtVZy7ykmlwpVA4eBBYsYMC7fbub6b3gAq657RTkz5kD1K/Pld5ERETy27hxmRdcKlkSuPde4KmnmKBJSADOOgv47DMe37fPPYMpeU9BrkSEiRPZUqV/f2aAK1Rgn966df1XamvZEihWjLc5kYUxREREspOYyOxsoKgovj9FRbEUr25d931I8pcmnklEuPxy/8tTp7olDr/9Bnz6KfcXLw60bs163l9/5QQ4gC9MgwbxewW6IiKSG9OmAV9/DSxfHjzABdwSOwBo2LDgxibZiwr1AERORnQ0653uvx+YMAHYupUtzMaPB+68k8e//94NcB1HjjCzu2sXZ7euW8dP3yIiUjSlpgIrVvDMIAB89RW7/ezZw8uzZwNjxrA3fKlSwe8jPl51tuEoV5lcY0wPAKMARAN411r7bL6OSuQk1K4N9OvHDeBpo2DVOJs2McPbvz8vR0ezzVnduu522mn82rx51i9qIiJSeFjL1//ly7mtWMGvq1cz0J0yBbjoIs7zaNvWrbu9/34mR6KigtfkxsWxFE7CT441ucaYaAB/ATgfwBYACwBcba1dldVtVJMr4SCr2qmEBLZyWbqUmdz16/03Z+U2gKUQHToA33wDjB4NfPABULky8M8/nARXty5QtmxB/UYiIpIb+/Yxs1qhAjv43Hyzf7YWYPa1WTPgjDP49bzzcrdcruZ6hJdTrcltD2CttXad784+A9AHQJZBrkg4GDEi60/cFSsC557LLdCRI8CGDQx4mzZ1923dCpQpw8ujRgGvvsrvK1Z0M7+BW/36/PQvIiJ578gRBrErVgA1anBhhQMH2Hf9ueeABx8EypdnG8rrrnMD2mbNTr4P+8CBCmoLi9xkcvsB6GGtvdl3+VoAHay1g7O6jTK5Ei7y6xP3338zE7x+vX82eMMGLmUM8EX16FGWQ7z4IrPKo0bx2OrVXN2mZk0FwSIiOUlL4xk0p9TAKTdYu9YtS+vXj/MyAOCNNzhvo3nz0I1ZCkaBdFcwxgwCMAgA4uPj8+puRU5Jfn3ibtCAW6CMDPb2XbeOk9uchSu2bWMQ7Lj2WmDhQraYSUjIXAvsfK/eiiJSFM2axZKDSy7h5YYN+boKMDFQvz6zsgMGuNnZ+vXd299+e8GPWcJPbjK5ZwIYZq290Hf5EQCw1j6T1W2UyRXJ3vTpzOZ6a4HXreOLuqNzZ2DmTH4/ZAjQogVw4428vGYNJ8vFxRX40EVE8sSBA+7krxUrOB9i4kQeu/RSrn65ejUvjx7N9pBnnAE0bswFGESAU8/kLgDQwBhTF8BWAP0BDMjD8YkUOeecwy3QoUNu0FuihLt/7lw3oD1+nC/y1gLVqmWuA3aywXXqADEB/+GaMCEiobBuHVep9JYabN7sHi9bltnY48cZzL78Mku6HE6Pc5ETkasVz4wxvQC8DLYQe89am22zDGVyRfLPsWPApEmZO0Ns2sSV4BwPPsiJF4cPA3ffzRZrI0dmnog3erQCXRHJWm4/HGdk8MN3dDR7y77yCl9fypUDnniCy97GxvJDulNicMYZ3OrUUZ9ZOTla1lekCEhLY2bECXrPOANo356TNTp1YgDsNDf3MoYBcPny3MqV49fBg9k+betWri538cVsr7N/P+uNnet6M84iElmy6gs7ciRw+uluucHy5cDKlVxI4fzzgW+/Be66i18bNeJrU1IS5zLExobs15EIpCBXRLJcHAMAbriB9XEHD7pfX3sN6NmTAe5FF7k9g997D7jpJve2xYu7gbE3SB4xgm9oy5dz2eXrr2cLtq1bgb173euXLq0OEyKhYC27wRw+7G7lywO1avGM0YQJPCO0fXv291O1qpuVvekmfhUpKAXSXUFEwlt8fNaLY7z/fta3O+88ZmGqVOHlc85hdufAgcyBsbNv82ZmlgEGuHfeCVx1FYPct97iaUuHMW5g7A2W33+fjdynT+fiHffcw+uuWcOMkHP9cuXYpeJkqEZZshKOz42UFD73vUHp4cP++xITgR49eP3Bg4GuXYErr2S9/3nnZb6d83/qcMqcjh1jF5js/PILg9uqVfPl1xU5ZQpyRYqI7BbHyE6xYixncJx2Grfc+ve/GeBWqsTLAweyU0SwANn5un69G7hOngy8/TZw7728/OSTwCef+P+MuDj/QLlKFa5SB7Bv5p49wG238fKCBVzCc+ZMYPhw9jIG+AHAmdwS6mAmFMIxqAuVwFP0J/vcOHiQE6mcIHDOHJb6BAtOvUGq8z/ZqxdQr5678Ezlyv4rdgVzxRVukPvTT+7PLl6c/4MJCTx74mxlyvhfbtKE1y9blv3Au3XznyDmSEjgMZFwpnIFkSKkMAYyGRl883eWT16xgkFwYCbZ+70xwI8/8vr9+gF//QUsW8bLZ57J0ovsdOjgXqdPHwbOY8fycs+ewO7d7FwRG+u/OftatwYeeYTXf/xx1iRecw0vDx3K8QW7nbM1aMBxAsB337H/Z4MGDM4XLQp+G+++uLgTq5XOqu6yICYlWsu/sXcrVoyTl44f55jKlWNJy+HDzEh6r5uenvn2p5/Ox2H7dvaobtOGP+uff7gv8PqB2+DBwc96VKrEspvsMqkVKwJLlvD6F17I56TzXGrRwn0eepUq5QaZHTrw7wEwq1qzJieOAlxMxpjMgak3WC1bNm9bC4byuSGSG6rJFZEiLSXFzQwvXcp+nD17Zl2jPHIkcP/9/P6pp/im7mSSb7qJt09LY9Dp3Zx9nToB77zD67dsCZx9trvaXbFivE52brwRGDOG30dF8YPJf/8L7NzJyX85eewxjnvXLl7/9deZyV6xgktZBwbG69YFH1PNmswEvvQSy1R++w249dacg8wPPmA28YcfmMWfNo2B/3vvsUm/93bBzJ8PtGvHx3DQIGDLFtaJPvkkPyTkxLn+8OHAsGH8WVFRHPvbb+d8e2Oyfm54A9Jg2dDq1YFnfF3kp05loH7ppby8ZAnv13u7uLjwr0kvjB+OpehQTa6IFGnemt0WLbhlV6PsBLgAM7FeTvCZW05Wz5GSwuDOCYiDBculSrnXnzsXqFGD35cvz8Apq9s5+9q14/VLlmRw4mQyy5bl6ezA26xZE3zs27cDbdu6jffLlOEyqVFR/lt0tP9lJxCvXZuTGp1SlWbNmJXM6nbOVqsWr3/mmeyX6mTxL7qIj0Ww23i3ChV4/f79GVw77rqLmf2cbn/55QzoAmX1nMlKr17+l1u2zP1tw0l+rRwpkt+UyRWRIkmnYV2JiVkH/Bs2FPRoQk/PDZHCI7tMbpifJBERyR8DBzJoSUjg6emEhKIbxIwYkbmOMzeTEiOVnhsikUGZXBERUd2liBRKBT7xzBizG8AJVC7lmcoAgqzpJKLnhmRLzw/Jip4bkhU9N8JDgrW2SrAD+RLkhooxZmFW0bwUbXpuSHb0/JCs6LkhWdFzI/ypJldEpIAYYzYYY1KMMZUD9i82xlhjTKIx5gPfdQ57tqWhGrOISGGlIFdEpGCtB3C1c8EYcwaAwPb9z1trS3u2FgU6QhGRCBBpQe7oUA9AwpaeG5Kdgnx+fATgOs/l6wGMLcCfLydGrx2SFT03wlxE1eSKiIQzY8wGADcDeB1AXwB/gZN0OwHYAKAugGEAtlhrHw92HyIikjuRlskVESkMnGzu+QD+BLA14Pj9xpgDnu3DAh+hiEghp2V9RUQK3kcAZoCZ22ClCv9TJldE5NRETCbXGNPDGLPGGLPWGPNwqMcj4cEYU8cY86sxZpUxZqUxZkioxyThxRgT7etuMKWgfqa1diM4Aa0XgC8L6udK7hljyhtjJhhjVhtj/jTGnBnqMUn4MMbc43tPWWGM+dQYUyLUY5LMIiLINcZEgzVuPQE0AXC1MaZJaEclYSINwH3W2iYAOgK4Q88NCTAELBkoaDcB6GatTQ7Bz5acjQLwvbW2EYAWCM1zRMKQMaYWgLsAtLXWNgMQDaB/aEclwUREkAugPYC11tp11toUAJ8B6BPiMUkYsNZut9b+4fs+CXyjqhXaUUm4MMbUBnARgHcL+mdba/+x1ma1/vmDAX1ytapSATLGlAPQFcAYALDWplhrD4R0UBJuYgCUNMbEgC0At4V4PBJEpAS5tQBs9lzeAgUyEsAYkwigFYB5IR6KhI+XATwIIKMgfpi1NtFa+3OQ/WnWWmOt3WCtvcFaWyygT27lYPcn+aYugN0A3veVsrxrjCkV6kFJeLDWbgXwPwCbAGwHcNBa+2NoRyXBREqQK5ItY0xpABMB3G2tPRTq8UjoGWN6A9hlrV0U6rFI2IkB0BrAm9baVgCSAWiuhwAAjDEVwLPFdQHUBFDKGHNNaEclwURKkLsVQB3P5drI3JJHiihjTCwY4I6z1mqSjzg6AbjE17v2MwDdjDEfh3ZIEia2gL2KnbM+E8CgVwQAzgOw3lq721qbCk4ePSvEY5IgIiXIXQCggTGmrjGmGFgA/k2IxyRhwBhjwLq6P621L4Z6PBI+rLWPWGtrW2sTwdeMadZaZWME1todADYbYxr6dnUHsCqEQ5LwsglAR2NMnO89pjs0MTEs5dgn19cWYwaA4r7rT7DWDs3vgZ0Ia22aMWYwgB/AWY7vWWtXhnhYEh46AbgWwHJjzBLfvkettVNDNyQRKQTuBDDOlzhZB+BfIR6PhAlr7TxjzAQAf4AdfBZDS/yGpRyX9fV9SillrT3sO+07C8AQa+3crG5TuXJlm5iYmKcDFRERERHxWrRo0R5rbZVgx3LM5FpGwYd9F2N9W7aRcWJiIhYuzKozjoiIiIhElGHDuBUwY8zGrI7lqibXtyLQEgC7APzkKcYXERERkaJu+PBQjyCTHDO5AGCtTQfQ0hhTHsAkY0wza+0K73WMMYMADAKA+Pj4vB6niIiIiISTI0eAn38Gjh0L9UiCOqHuCr4VX34F0CPIsdHW2rbW2rZVqgQtjRARERGRwm7+fKBPH6BcOX696iruN4ZbCMoWgskxyDXGVPFlcGGMKQngfACr83lcIiIiIhIO/voLGDkScOZbpaQAixcDt94K/PgjcPw491vLLUyC3NyUK9QA8KExJhoMir+w1k7J32GJiIiISEhkZABz5wJff81tzRruf+YZoG1boFMnYONGZm3DWG66KywD0KoAxiIiIiIioXD0KLB+PdCkCYPcSy4BDh4EzjkHGDyYl505V8GC26FhtYQCgFxOPBMRERGRCLN7NzBlCrO1P/4IVK8O/PMPEBPD/Y0aAeXL5+6+wqREwUtBroiIiEhR89//MjDNyADq1AFuvJGTyBwdO4ZsaHlFQa6IiIhIpMrIAObNc+trJ05kScJZZwFPPMEyhFatwr6+9mQoyBURERGJNNu2sU528mRg506WIJx9NnvbAkD37twimIJcERERkcJuzx7W0ZYvD/TtC5QqBUyaxEC2Tx+gZ0+gQoVQj7JAKcgVERERKYzWrnXLEGbPZmlC377cypVjBjc6OtSjDJkTWvFMREREREIkIwNYudK9fOutwP33s9XXY49xsYYvv3SPF+EAF1AmV0RERCR8HTsG/PILs7WTJwO7djFDW7ky8MILQNmyQN26oR5lWFKQKyIiIhKOJk8Grr4aSE4GSpdmXW2fPkBcHI+3aBHa8YU5BbkiIiIiofbPP8A33zBjO2gQMGAAcMYZwLXXMrA991ygePFQj7JQUZArIiIiEgppaWzz9fXXbq3tGWe4tbSJicCbb4ZseIWdglwRERGRgnDsGDBtGrB1K3DLLe7yuVWqAC+9xIUZTjst1KOMGApyRURERPLLvn3At98yW/v996yvrVkTuOkmICqKHRFiY0M9yoikFmIiIiIieWndOiA1ld8/9xxw3XXAnDnANdcAU6ey/jbKF4IpwM03CnJFRERETtSwYe73GRnAggXA44+zprZePeC333js1luB+fOBLVuAt95ih4QSJUIy5KLGWGuzv4IxdQCMBVANgAUw2lo7KrvbtG3b1i5cuDDPBikiIiISVowBrGXWtksXYNs2Zme7dGE3hP79gRo1Qj3KiGeMWWStbRvsWG5qctMA3Get/cMYUwbAImPMT9baVXk6ShEREZFwlZ4OLFnCDK2TpQWAhATgvPO49eoFVKoUsiGKvxyDXGvtdgDbfd8nGWP+BFALgIJcERERiUzWMlsLADffDIwfDxw65H8d5/jQoexnK2HlhLorGGMSAbQCMC/IsUEABgFAfHx8XoxNREREpGCkprLTgZOp3biRvWuN4RK6V18NnH02t5o13XIFCVu5DnKNMaUBTARwt7X2UOBxa+1oAKMB1uTm2QhFRERE8lpKCvvURkUBr7wCPPIIcOQIjzVpApxzDnD8OCeJPftsSIcqJydXQa4xJhYMcMdZa7/M3yGJiIiI5LHjx4F585ilnT4d+P13YPZsoFUroFEj4MYbmaXt2hWoWjXn+xs6NN+HLKcmxyDXGGMAjAHwp7X2xfwfkoiIiMgpOnqUgW358gxuzz6bl40BWrQABg0CypThdS+4gNuJ8LYQk7CUm0xuJwDXAlhujFni2/eotXZqvo1KRERE5EQkJzM762Rq588HHn2UGdcmTYA77mCg26ULUKFCqEcrBSA33RVmATAFMBYRERGR3Dl8mL1pTz+d7b1q1wYOHACio4HWrYEhQ4ALL+R1y5QBXnghpMOVgndC3RVEREREQuLQIWDWLGZpf/sNWLSIZQeLFjGwff55oE4doFMntwxBijQFuSIiIhJ+DhzgUrnnn8/LN90ETJgAxMYC7dsDDz8MnHuue/1bbgnJMCV8KcgVERGR0DtwwK2n/e03ri5mLbBlC1CrFnDffcBttwEdOwJxcSEerBQGCnJFRESk4O3eDcyYAXTowHrayZOB665jX9ozz+SEsbPPBqpU4fU7dgzteKXQUZArIiIi+e/IEeDbb91M7cqV3P/GG8zQ9uoFzJwJtGsHFC8e0qFKZFCQKyIiInlv+3YGsxUqsMvBsWPAVVex1KBzZ2DgQGZq27bl9StV4n6RPKIgV0RERPLG+PHATz8xuP3rL+7r25dBbsWKwOLF7FkbGxvSYUrREBXqAYiISBjRKk6SlcDnxsaNwNixwIgR7r5XX2Wg27Ah8L//sTvC+PHu8RYtFOBKgTHW2jy/07Zt29qFCxfm+f2KiEg+M4Yz2kUcGRl8XkRFAT//DHz0ETO1GzbweNWq7IAQGwvs2sWyg+jokA5Zig5jzCJrbdtgx1SuICJSWKWmcilTZ6tRg03wt28HZs/mvsOH3eM33ggkJHBG+8svZz4+eTLv9/PPgaef5n15t6efBqpVAxYu5JKpgcdbtWKgk5LCIEeBTuhYy4leSUlcRKFmTaB0aWDTJv79Dx1yjx06BNx/P58bkyczM+vsT0ritmYN73fxYk4e69oVuOce1tSecQYDYIABr0iYUJArIjJsWP6dps/IYLCRnAyULAmULcvLc+b4B5iHDwPdugEtW/I08LBhmYPQJ5/kDPQZM4DzzmOQ6/X118AllzAIveKKzGM55xwGMklJwNq1QKlS3Pbu5eXGjXm9/v35NTGR7Zs2bOBtnnyS+7/7DvjPfzLf/969rLscOhR49llOMPIGwXPnAsWK8RT3rFn+x8qVA26+mffz11/8vZ1jpUtznCYEK8zn53MjmPR0Po7eADQpCWjWjH+PLVuAt992jztfH3uMgeevvwKXXsr9GRnu/X7/PetiFy4Err3W3R8VxefkNdfwuVGsGFC+PFcOK1uWHRDmzePSuQDwwAP8esYZwF13FdSjInJSIqtcoaBfjEQkMhjDmd/p6W6T+YULMwehDRoAXbowuHzwwcxB6BVXAP/+N4OUxo2578gR9+c8/TTwyCPA+vXAaadlHseoUQwcVq8GLrjADUKdIO+++xgIb9gAvPWWu9+5Tteu7Dd68CAzdt7blizpZttyeixyel84doyN+50sn7P16AHExADTpjEQP3zYPZacDHz1Fe//kUeADz5w9wMMqA4e5Pf9+zOb7FWzJrB1K7+//XbWenqD5NNOc4PwiROB/fv9j1etyjpRgJnm2NjcBc25eTyOH3efO+npwO+/+weohw6xg8DZZ3Nct96a+fhDD7GN1po1QKNGmX/Gm2/ydosXA23a8HcqW5ZbmTLAf//LlcH++gt47TX/Y2XLcmWwmjX5s3budPeXLJn7Dw8qZZEwlF25QmQFucbwBaN06YL/2SIS/jIy3EDv3nsZKK1aBezbx30DBwIff8zvS5XyD1ABLhs6ejTvp0IFBjXeQPLaaxmIHDsG3H135iD0zDN5Sv/4cZ7u997W+T4mxCfYCjqQychgMHzkCFC9OvctW+Zmj50tNpaPKcBgdu5c/+MJCQyuAS75umCB/8856yyWcADMiq5e7R8Ed+vGSVMAT90fO8b9zz7Lv3ubNvwAAzBg3bfPDVJTUoA77mBwmZISvMfrAw8Azz/P23To4AaZzterrmKWPimJmW7vsbJlgbp1WevqZGdz84ElrynIlSDGjeOJhE2bgPh4VrsMHFhwP79oBLnp6e6bQ9Wq/FR/2mlAv348deMsDVizpurERIqCrVuZ9Vq6lNuyZVwa9JdfeDw+Hti8OfPthg7lGaEff2Rg5Q1AK1RgwBHJIuGM2MGD3LxBcMmSbg/WN9/k+4H3ePPmbglGjRrAjh2Z79d5bgwY4J7mdwLR9u2ZSQX4HCtd2v946dKhCUzzUiQ8NyRPjRsHDBrknw+Ii2MuoKAC3cgOcocNA4YPz7y/bl1+/fe/eRpo2za+wRUrxromJwgeOJCf8FNTgaNHI/8NTCTSHDvGusGlSxnYPvEE9/fqxdpRgP/rLVoAnTrxlH8gZagkK3puiARlLaujtm3LfCwhwW2+kd9OKcg1xrwHoDeAXdbaZrn5gSEtV8jq9zlwgDVe69b5b6NGca3sefO4Lnblym4AfNppPNawIYPgqChlgUVCxVp2Dahenf+L77zDDgFr1vBMDsBs2Z49PF08dy73n3FGzh9eFchIVvTcEPl/mzezKuiXX/jVKZMPZIz/vMf8dKotxD4A8BqAsXk5qAJXvrxbT+Xl/BVq1ACee84NfhcsACZM4Azmhg05a3nAAH48CQyCq1Xzr/UTkVO3dSt7ci5b5pYc7NkD/PMP//dKlADq1QMuu4xZ2hYtuN/5INqxY+5/1tCh+fM7SOGn54YUcUeOsFvctGlswgIwH9itG1+inSkNXvHxBTvGrOSqXMEYkwhgSthncvO6XigtjV9jYvgGG5gJ3rsX+PtvoH594KWXOLvVGwCfdhrbsjiztUUks1273CB26VJOCGvVih8yr7iCwWyzZm4g278/21qJiEi+ePJJ5u6GDeOJjCZN2FymWzduzZoxrxcRNbm5CXKNMYMADAKA+Pj4Nhs3bjy50Z6EkM3sO3iQp0ejo5m7nzjRDYA3bGCJw5EjnPDw4IN80w4Mgq+4IjS9H0UKWmoqSwvKlOEZkT//5Kuld4JPrVqcFHTxxfz/2raNr6yh7jggIhKBjh5l05Fp05izc1Zgvu46fh3rO4dvbdahSqHvrhDOmdxw+BQRVHo66wdr1+blceO4SowTBO/ezWzUrl08ftNNnAnuDYAbNWLz9pOlmbASSikpwOuvuxnaVau479FH+SqYlATceaeboW3Rgi2SREQkX6SmshrTqamdM4cvyzExbBDy449sJpNdUBtuIjrITUzk4kCBatZkoqhMmTD9QyUlMYPVoAEvjxzJlWrWrWOj+JQUoHVrYNEiHr/8cjYR9wbBTZtyUk1WNGFC8ltaGj/+Oy26li7leaznnuNzr0IFlht4A9mzznK7n4iISL777Te+LM+YwfVXjOHiik75QZcujJcKo1OdeBbWNm0Kvn/bNq4SGRfHydg1anC75BL2a7eWn1iaNeMZ0gLnNCB3PPCAu1xiRgZ/gUOH3OM1azIo/vZb9/Runz5cQQhgf8bSpf2DYJG8dOAAA9l9+4C+fbmvbVsGtgBTAY0bcx/AV9ENGzjpU0RECsz69VzT5MEHufbIkSPcd/31DGrPOadonDjLMcg1xnwK4BwAlY0xWwAMtdaOye+B5VZ8fPBMbqVKwMMPs2Jg+3bGhStWMKgF3BUoR47kE2HdOn6SqVHDPyh2Nu++YsXy+ZeKinLLHBzOSjwAP4Z5G9BlZLDu96+/gMmT3XZKgJvGrlCBmd+aNRnV16zJZR7btOHtjx7lOQopGrIrZfGep/rwQ9aaL13qfqKsVs0Ncu+7j9dv0YLlNYErPSnAFRHJVxs3um29unRhI6ny5VkB6eTEevQAevYM6TBDotAvBnGyNblOXUrt2gyUN23ie74TFG/fzrLZwIfn9de5bPpff3EVxxEjWMeyaRNrW7yBcenSISiVsJbP6nXruLqPtZzAc/vtzA5v3crtyBGeu3jwQX68O+009hJ1AuCaNYGbbwa6duUngpUrua969QKI8iXfOaUsSUn+LbqWLuXksB07GLA+8AAwdapbatC8Ob/WrBnq30BEpEjatYtBrRPYrlvH/VWrcuXrRx4J6fAKXESXKziB7InO7IuNZWmgIz4eeO89/+ukpfHJ5A18nVUhjxzhcutOEDtjBssgvOLiMmeE77qLrT1372bM2bhxHseMxrg/zFGuHD8NOJzgxlG6NNdn37qVg9q2jQU8vXvz+JIlDHYdVaowGB41ivvXrwd++sk/S1ylivoGh8rRoywdiI3lE3jRIpYaONv+/e5133qLH3QAfvRv0YLTao8cYZD7/PM83SEiIiE1dCjw5Zc8Kw3wrf2cc4AhQ1iC0LRpmM5BCqFCn8kNF8nJPGXgDYi9pRLO99OnswXo6NE8pbBpE1CnDvDGG8D772dfJlGtWu4C4v9v57HRIj7BnHo7j337uHqUEwA7wfCTT/KX+eSTzD8gJoZ9Sdq3Z8A8frx/lrhmTeD00xmIib/jx/m1eHHWZf/+u3+QeuAAcOWVfOwXLgQGD/Y/dvw4l7Pt0QOYNImLJWTn6quZ1a9dW6+QIiJhYuxYViB623rt3OlOFmvdWouwAhGeyQ0XpUqxWXKTJtlfz/lMceGFwBdfMIgF+ImsUiUGyvPmBS+VALj+RMWKwAcfADNnAmN81dELFzL5tmAB8MQTTOYBBhs3spwDOIVAt2JFoFevrI9fcQUzut5M8LZt7IUKcPb9uHEMwLw2bOB13nkH+Phj/0xwrVrApZcyqs+L1eQKsp1aaipPA5QsyS4Zv/2WOUjt1g3o3p2P2RVX+B87etSti1m/nsGqV2wsu2q0asXOBeXK8XEsX55bhQpcoATg3+X3391j5cvzNuq8ISISFlJSgPnz3bZe48ez9ODgQSbJjhzhmWGnZ63knjK5YSo11b9UYscObo8/zvjkqaf4CW/ePF7/4ouBKVOyvr/KldmhrFmumsDlkyNH+Ms4wfDllzNg++ADprGdeuFjx/hLHj/O43fcwWyxNwCuU4eZZICfDGJimOrOatGAEwnq0tL4s52JeL/+ymz2/v1uINq8ObOp6enA2Wf7B6nJyZzNOHIkM7Hlyvnff0wMMHw4+8Xu28cVvLxBaPny/BTUpg0fs6VLgwepp0JBrohISKSnswrQCWpnzuRLvTHMzo4Zw8oxyZ1T7pN7ohTkFry1a5n0u+CCrK/ToAEnzAHM9lauzFoegLFY2bL5P84cWctAcccOFiwDnN3/66/+pRJRUW5bjT59gG++4StEtWoMhFu2dNPcP/7IoHHMGDcQrVEDuO02Hr/iCk62coLYw4d5in/iRB6vXJkpdEdUFBfvGD2al3v14sdsbyB61lnM1lrLso0KFdxjcXGhLwvQQiEiIgVq3To2pJk+3T2x2bgxT+p168Z8ScWKoRxh4aQgtwjJanGMGjWAzz5z549ddBFjwXfeYRxWqRJjt0aN3K1xY35NTAzDuh9vm6uZM7malrdmuGJFnrIfPjz47c8/n8EvANx4IwNc51R/+fJMeTu1rPPns/TACVJD0jZDREQKk7Q04IYb+L47aBBP3LVrx8li3buzi6d3jricHAW5RcjJtFRLSwNee40rxK1ezc1ZbRhgWezpp/N+77yT8eWSJUDDhrzvsLd5M9tnOAsTlCmjzg8iInJS/n9yt6ejU/fuPOH4yy+cM/z667xu9+48keg0sZG8p4lnRcjJtFSLiWFvPa99+9yAd/VqBsBOn/8dO1g39OqrnNi/aRPwwgtu5rdRI1YNhE2ys04dfnUmwomIiJyEwETSxo3uKqoA8yjOWjkAg14JHWVy5YQdPgz88AMn9592GuuLevfmfCtHuXKZyx46dw7hMoKqQRURkRO0ciUb5Nx2GxM35cpxDkug8uWBn3/mdJCwK++LcCpXkHxnLUthnayvNwu8bRuv88svLK7/5ReWR7z+OuuC9+5lNjmwCYGIiEh+OXgQ+Oef4Nu0aVy46aWXgHvvZVvPypVZ6RYsbDKG3S6l4KlcQfKdMVxLoHZt4Lzz/I8dOsTmBU6zhP37GfyWKcPL//sfF1yrWTP4xLdatcKo9EFERAqN5GRg8WL2sK9YkYtzPv44A1lv0xyAC3XWr++/wOcNN3C9HOcsZHx88Mnd8fH59ivIKVCQK/mubFnOKHX068fN0bcvs7hO5vfjj/1PB5UuzWB3zhy2zV2xgqeDnKA5O8EmCJzS6m8iIhI2UlP5+r52rX8m9uabWUa3YgXQpQvw9dfAJZewzXiZMmzTXq+e/+YkXrwqVPC/PGJE8MndI0bk7+8pJ0dBroRchw7cHNZycpu35GHPHncF4Mce44uYs373Y4/xhc6bAa5QIfgEgVNe/U1ERApUcjJf48uX53vBE0+4wezGjVxcwVGiBOeKHDzIy82acZVzJ9HSpQtrZ0/WyUzultBRTa4UOitXsvtDly68fN55wKxZXKTM4SyJ6N3nqFOHL4wqgRARCT1rGbx6M7EJCcD11zOAjYtjXewzzwBJSezdHpiFdbYaNdQhsqhRTa5ElKZN/S///DNfCDds8M/+vvtu8Ntv3syWaaNGcc3wXr1Yi1WlCicWeL8631eqlPWKwSIikj3v+j1jx3L9Hm+JQVKSe11jgKuuYpAbHc3JXy1b8liZMplraUWykqtMrjGmB4BRAKIBvGutfTa76yuTK+Egq9XfypcHxo9nBnj/fq7+tns3MwnOUouBHnwQeO45Zod79eLlPn24aMZHHwUPjkuVUrZYRAq/3M5tOHqUy8v/8w9fS6+9lvsHDODrq7PIZLNmXGL+tNOCZ2Pr1mXZgUhunFIm1xgTDeB1AOcD2AJggTHmG2vtqrwdpkjeymqCwGuvuR0gKlTghDZHaiqzBE7Q63x1sgjHjnGFX6cP4t9/A/ffH/znFy/uBr5PPcVgevNm4IMPgGuu4Qv5/v3A9u1utlj9FUUknASb23DzzcDvv7M0wFtisHWre7tSpfg6ZwzQqZN/H/Xp0/naq9c7yW85ZnKNMWcCGGatvdB3+REAsNY+k9VtlMmVcJHf3RWsZScIb0C8e3fmIPm++7he+S+/MMCeOZOLY3z8sZvtMIYv/MHKJv79b9ao7dzJQPmMM9wV6MLp8RCR8LdvH19HDh7k61dWX19+GWjTJvgZMUf16pkzsfXr82ulSjqbJfnvlBaDMMb0A9DDWnuz7/K1ADpYawcHXG8QgEEAEB8f32Zjdv8VIkXY8ePMYMTEMNicM8c/IA4MmPfsAebP5wpzb78N3HorsGUL+wc//zwz04E1xIFfO3ZkiUawzPbo0Qp0RcKZtcyEFi/OLjO7dgFLluQcpI4cCbRoAUyYwPrWpUsZgI4cyZKrYJyFecqW5YfyevWyXvwgKYkZW5FQKpCJZ9ba0QBGA8zk5tX9ikQabwY2Pj7nJuLWum8yvXqx32PVqrzcsCFXkXMC4r//5vfeSRwAMzePPeYf4AK8fMstbLFTtiy3MmX8vy9fnj8DYJ1ddHTwfpIikSQvz3qkpTEbWrEiz9bs2cP/uWDBqff74cOBSy/lB+HOnVnTev75PBPk7TXucP53nSDV6S5Tvz6XpXUC0ksuYT1suXLudZ2vJUv6Z1+zW/xAAa6Eu9wEuVsB1PFcru3bJyIFwBj3TadOHW6OPn24BTp2zK0t3r2bgeqmTcHv/+hR1tcdOsTgOLDtWoUKDJIBBsQrV3JmNMCG6qtWuUFxsEA5Ph7o35/XX7KE2ePTT+fl48eBYsVCd0pT5Ruuov5YWMtlWdPSgE8+Ae64g/8bAIO8m25iBvWee5hVfeON7LOoBw8CQ4YAjz7K29WvD7z1FkuPNm0CrrvO/dnFi/sHmuXK+S9OUK8ez9rUq8fLZ5/Ntone25Qpk3WNa8uW7rwCgB+OGzbM3eOixQ+kMMtNuUIMgL8AdAeD2wUABlhrV2Z1G9XkioSfrLpNJCSw/Zrj+HEGu07Qe+yYu1jHd98x4HWCn2HDGPQ613du43yfkQG0bg0sWsTrt20LVKsGfPstL9euzTrjYBlk5/t27dxFPD79lBkoZzzLlvlf31kwJDcCJ9QARa98w1q23xs3jpk+J6gDmNF7/nng9tvZd3TnTk6UbNSIx//+mx+g0tLcLT3d/3JUFD8IATz1vWcPW0MB/Ftu2uR//cD7qlKFjf8B4L//ZRD36KO8fMstnOiU1c9OS+PzbcwYXr9rV5b8jBrFy9WrM1j1Xj8nZcsyeD1wgB/+oqLc52lgRrRcOU42vfhitir87DPgzDOBBg34P7Vli3u9k6mvL0hF/QOQhLdTqsn13UEvAC+DLcTes9Zm+xlOQa5I+CnooM5aBk3HjvE0LQDMncuav7a+l6OXX2agFCxAdr7v3p19NQHez8CBwKuvMhgPbDPkLNnpDZavuoqBWno6A6SLLmLAEx/PyTeBqlQB3nyT109PZwascWMGNuPHA+eey6zc5s2sdXSul9V29dW8j7/+YoB1990MdH7/nZm9nG4/ciTQvDlPVQ8fzmCpTh3gww+BF1/M+fa//84PBqNGuVnFUqXYXP+ll3L+Ox4+zOvfcw/w3nvuSlL9+wOff579bcuUcZfo7t+fmfzVq3m5a1eedndERfG54WzR0cw2/v67e/uYGE7WBHgGY/t2t7492Na8OR8zAHjoIWZCnQ9MDzzAD2GBPzMmhgFdMMbwNk6NrNoEioTeKdfkWmunApiap6MSkQJV0MtRGsMgOi7O3dexo/917r77xO5z2TKWNwAMiiZOdIPiwCDZ2VJTef3kZOCVV5hJ7tqVmbRgdu/2r3ccOZJB7q5dDJDGjWOQ+88/DBSz+t2jo7m1bs0gd/du4Isv2DO0QQPe34wZ7vWy2pwMY0wMs6uOcuXYhi6n2zt1ky1aMFvrnNK+4AJ+EIiOBv7zn6wfcyc7ft117iqDAPDww8CNN2YOEL2bN7P+xhv+y6/+8AMfJ+e2OQWLn33mf/nrr7O/fqDnnvO/PHJk1tcdPTrrOlSAYy1d+sR+vogUPC3rKyJFirPyUkJC8DrlGjWA7793g8SqVZlBTk3lKfuKFRm4p6YycA4WWBa27F5uS1mKCpWyiBQe2WVytcKziBQpTgD69NP+WWaAl53ygKZNWX/qlFrExrKG2LlNbCwn9JUpw33FizMrWdgCXIAZ/WCPRVGdXDRwIAPahAT3A5ECXJHCR0GuiBRJCmRceiwyGziQWeyMDH4tyo+FSGGVL+UKxpjdAEKxGkRlAHtC8HMl/Om5IdnR80OyoueGZEXPjfCQYK2tEuxAvgS5oWKMWZhVXYYUbXpuSHb0/JCs6LkhWdFzI/ypXEFEREREIo6CXBEpEowxG4wxu4wxpTz7bjbGTPd9b40xycaYw57tQd+xYb7jQwLuc4hv/7CA/XWNMRnGmDeDjKOPMWaJMeaQMWaPMWaaMaau71h5Y8x7xpgdxpgkY8xfxpiHPbf1jnGPMeZTY0x5z3FjjHnAGPO3MeaoMWaTMeYZY0xxz3VqG2Mm+m5/0BizwhhzgzGmi+f3Tvb9LO9jkcMC1CIi4SXSgtzRoR6AhC09NwTggjZDgux3nh8trLWlPdvznuv8BeC6gNtd79sf6DoA+wFcFRBg1gcwFsB9AMoBqAvgdQBOB9mXAJQG0Nh3/BIAawPuu4W1tjSA0wBUADDMc+wVAIN8P78MgJ7gapVfeK7zEYDNABIAVAJwLYCd1tqZzu8NoKnvuuU9j0UWC0NHPL12SFb03AhzERXkWmv1hJOg9NwQn5EA7vdmP4FcPz8WAIgzxjQFAN/XEr79/88YY8Ag83EAqQAu9hxuCWC9tfYXS0nW2omeALIdgE+stfuttRnW2tXW2gnBBmOtPQTgGwBNfD+3AYDbAQy01v5urU3zLb9+OYAexphunp/xgbU22Xedxdba73Lx+xdJeu2QrOi5Ef4iKsgVEcnBQgDTAdx/krf/CG4293rf5UCdAdQG8BmYQb3ec+wPAI2MMS8ZY841xgSumzUXwAhjzL98QWuWjDEVAPT13QZgxnaLtXa+93rW2s2+65zv+RmvG2P6qwRBRCKZglwRKWr+A+BOY0ywljN/GGMOeLYLA45/DOBqY0wsgP6+y4GuB/CdtXY/gE/ALGpVALDWrgNwDoBaYAC8xxjzgSfYvRPAOACDAawyxqw1xvQMNkawdVE8gLd9+ysD2J7F77zddxwArgAwE8ATANb76oPbZXE7EZFCK2KCXGNMD2PMGt+bwsM530KKAmNMHWPMr8aYVcaYlYETh6TosdauADAFgHdC12Lft62tteU92w8Bt90E1sg+DeBvX5YUnvspCQaR43zX/x3AJgADPPcx11p7pa+vYxcAXQE85jt21Fr7tLW2DVgv+wWA8caYip4f09paWx4slXgTwExjTAkw6K2Rxa9dw3ccvlKIh621TQFUA7AEwFe+Mgvx8U0CnGCMWW2M+dMYc2aoxyThwxhzj+89ZYVvAmiJUI9JMouIINcYEw1O3ugJ1qddbYxpEtpRSZhIA3CftbYJgI4A7tBzQwAMBXALmFGtDeDPE7itM3FsbJBjlwIoC+ANX4eEHb6fcX2Q68JauwDAlwCaBTl2CAymS4ET1AKPpwJ413esGYBpAOoYY9p7r2eMqQM+938Jch97APwPQE0AFQOPF3GjAHxvrW0EoAVO7DkiEcwYUwvAXQDaWmubgRNa+4d2VBJMRAS5ANoDWGutXWetTQFr4fqEeEwSBqy12621f/i+TwLfqGqFdlQSatbatQA+B3A3mDF99wRu/jmAC+DfscBxPYD3AJwBTjJrCaATgBbGmDOMMZ2NMbc45QvGmEZgB4W5vstPGGPaGWOK+TJDQwAcALAm8Af5Ptz/C8BRAOustX8BeAvAOGNMR2NMtG9y3EQAP1trf/bd7jljTDNjTIwxpgyA28DXz70n8BhENGNMOTDDPgYArLUp1toDIR2UhJsYACWNMTEA4gBsC/F4JIhICXJrgS1xHFugQEYCGGMSAbQCMC/EQ5Hw8CSYdf0HQIZv39KA3rAvB97IV1Lws7X2qHe/L7vTHcDL1todnm0RgO/BAPgAGNQuN8Yc9u2fBMBpVWYBvA+WFmwDJ4tdZK097PlRS3233e+7z0uttft8xwaDAfvHAJz7nw52WHDE+X7mAQDrwFZil+TqESs66gLYDeB9Y8xiY8y7xtNfWYo2a+1W8AzIJrDe/aC19sfQjkqCiYhlfY0x/QD0sNbe7Lt8LYAO1trBoR2ZhAvfxJ7fAIyw1n4Z6vFI6BljegPoZa293RhzDoD7rbW9QzsqCQfGmLZgdr2TtXaeMWYUgEPW2idCPDQJA77OJhMBXAV+WBwPYIK1NthEVAmhSMnkbgVQx3O5tm+fCHwz4ScCGKcAVzw6AbjEGLMBLHHqZozRm5QAPBu4xVrrnPWZAKB1CMcj4eU8sN/1bl9t/JcAzgrxmCSISAlyFwBoYLiUZjGwAPybEI9JwoBvxvgYAH9aa18M9XgkfFhrH7HW1rbWJoKvGdOstdeEeFgSBqy1OwBsNsY09O3qDmBVCIck4WUTgI7GmDjfe0x3aGJiWIoJ9QDygrU2zRgzGMAP4CzH93wr/Yh0ApctXW6MWeLb96i1dmrohiQihcCd4CS+YmDt8r9CPB4JE74Slgng4i5pABZDS/yGpYioyRURERER8cqXTG7lypVtYmJifty1iIiIiAgAYNGiRXt8i+tkki9BbmJiIhYuXJgfdy0iIiIi4WbYMG4FzBizMatjkTLxTERERERCIS0NGD481KPIJCImnomIiIhIAUpKAiZOBMaOBcqUCfVoglKQKyIiIiK598ADwOuvA0eP+u83hl+HDg1J6UIglSuIiIiISNaWLweeeAJITeXlypWB668H5swBMjIAp1OXtdzCIMAFlMkVERERkUA7dgCffMJyhKVLgZgY4JJLgHbtgIceCvXockVBroiIiIi4liwB2rRhlrZ9e+DVV4H+/ZnBzcrQoQU2vNxSkCsiIiJSVGVkADNmMGNbuzbw5JNA8+b8evnlQKNGubufMClR8FKQKyIiIlLUrFnDwPbjj4FNm4DSpYHbbuOxqCjgscdCO748oCBXREREpCjYvx8oX55dEJ55BvjoI+CCC4BnnwX69AHi4kI9wjyl7goiIiIiker4ceDLL4G+fYFq1TiJDGB5wZYtwHffAVdfHXEBLqBMroiIiEjk2bWLk8E+/5wZ3OrVgSFDgIoVeTwxMaTDKwgKckVEREQiwbp1bP111llAqVLM4PbsCVx3HdC9O9uAFSFF67cVERERiSQHDgDjx3MS2axZ7IywdCmD3C1bgNjYUI8wZFSTKyIiIlIYPfMMyxAGDQL27AGefhqYPNk9XoQDXECZXBEREZHwZy2weDEztg8+CNSsyR62gwaxHKFNG3ZNkP+nIFdEREQkXG3ZAowbx+B21SqgWDHgnHPYLeHSS7lJUApyRURERMKJtczK7tsH1K0LpKVxMtlbbwFXXglUqBDqERYKCnJFREREQi09HZg2jRnbI0eAiRPZ7uvdd4FOnYD69UM9wkInsiaeheG6ySIiIiJZWrMGeOghID6eq49NnsxFG6zl8euvV4B7kiIryB0+PNQjEBEREcnezp3A0aP8/uuvgRdf5MSx8ePZ5/aNNzSJLA9ETpDrfOIpXRpo0ADo0oV1K599xv0ZGcAPP7B33K5dvCwiIiJSEI4e5epjF10E1KoFTJrE/YMGAdu2Ad98A/TrB5QoEdpxRpDCX5M7bJh/Bjc5GVi7FkhNBXbvBlq35v49e4AePdzrRUfzdMCwYcAtt3DJu5deYr+5GjW4Va/OFh3FihXkbyQiIiKR4uhR4M47maU9dAioXZstwDp04PHy5UM6vEhmrJMBzUNt27a1CxcuzPP7zZExbkY30PHjwMKFPA2wfTu3HTuAK65g8LtsGdCqVeYM75gxwI03AitWcM1nJ/h1AuFzz+XXjAz+fJ1eEBERKdr++otxw2WXMS456yz2tL32Wrb/ioqcE+mhZoxZZK1tG+xY4c/k5lbx4pydmJXmzRkI797tHwR37crjx45xmzOHx44d4/4ffmCQ+/XXwIABbgDsfH3gASAx0b3PGjWAKlWK3PrRIiIiEWXYMP8J73v3shzho4+AuXOZoe3dm2eD58xREiwEIiKTO24c8NhjwKaNFvEJBiNGAAMH5uMPtBY4eJBBcO3arANesoQDcYJjJ6idNQto0gR49VXgrrt4+6goBro1agBTprA2Z/Zs4I8//EslatQA4uJOfpyB/4AiIiKSN7xnj8eMAW67jaWSZ5zBFcgGDGDJo+Sr7DK5OQa5xpj3APQGsMta2yw3P7Agg9xx41izfeSIuy8uDhg9Op8D3RO1aROD2MAg+JNPgDJl2D7k+ecz3+7wYaBUKeDNN4HffstcL3zeeVn/zOzKN0REROTEJSUxU3vBBUxkderESe1jxzK4bdEi1CMsUk41yO0K4DCAseEY5CYmAhs3Zt6fkABs2FAgQ8gbGRk81eEtldi9G7j/fh5/8kmeAtmxg4EvwBVP9u3j99deC0yf7gbBNWoAb7/tBrlpaSqREBERORlHjgCPPgp88QXfowMNHaozpyFySkGu7w4SAUwJxyA3KirrZOWMGUC7dhHYjePwYQa7Bw4AbX1/19GjWfMzcyawbl3m29Spw0C3QQM2lW7QgJ0nLrigQIcuIiIStqzl4gyzZvH9tHZtYMQI7q9bFzjtNKBzZ24XXqizpWEgoieexccHz+QCnDNWvDjQvj2/79KFExzLlCnYMea50qUzr34yaBA3L2+5wquvslxi7Vrg22/ZiLp3bzfIbd+eD5YTANevz24TDRrk/+8jIiISChkZbqeDu+5iCeHevbxcpQrQvz+/N4YJJHVFKFTyLMg1xgwCMAgA4uPj8+puczRiRPCa3Bde4Bn7GTP4YezZZ3nd0aPZFnfHDuD331nSWuiD3ty4807/y0lJ3AAGwi1asOXJDz8AH3zA/bfeylrgtDSge3d+iq1f390aNiwiD55IEaIJqxLJDh7km/+sWdzWr2dtozFA2bLAxRczI9a5M5M83o4IgQHu0KEFOnQ5cYW+XAHwdFfYxMxusO4Khw/zeX3GGZyz9f77bH+7ciWbH/z+Oz+kdenC+4gIJ/tmlZwM/PMPULIk/8n37QMuvxz4+29g61b3eiNGsEZp1y7WDnuzwPXrs2ZYRAoXTViVSLJlCxd+io0FRo7kJG9ruSBU69YMZp966tQ6GUlIRXRN7sk6fhxYtAjo2JEfzm6/nUlLgEGuU97QpQv7N6u9nc+RIwyA164FGjfmg7NsGUsfNm/2v+64cWyh8vffPAXkzQJXqhSa8YsEU1iyl87rtTFASgpXT0pN5fepqdwSEviGvXMnsHq1u9/Zzj+fGasVK3iay3ssJYXZKWs58fXYMXZxiY4O7e8tkhsZGcCqVW6WdtYs1jPOmQOceSZbdf7yCwPb9u1Z+ieF3ql2V/gUwDkAKgPYCWCotXZMdrcpDEFuoPR0YPlyvuY7JQ47d/JYlSr8n+jWDRg8OLTjDGtHjzIdvnYtA9u+fRnQfvkl1+P2PtcqVAB+/JET51au5CcOJwtcubI+VUjBSElh/V3NmpxskpLCD2A1ajDomzXLPwBMTQWaNuWWlMSWQYFBZo8e/PS8bRvPdniPpaayl+a55/J5f8cdmYPMl15irfz06cCVV2b++d9/zwkvEyZwxcZAzhv6Bx8A//pX5uPLlvGUlrd3d3ZiYjj5Jj6eAfSbb7Kt4fr1HFN8PM/6iBS0Y8e4kmn16nzvmDEDOPtsHqte3S076NdP/Woj2ClNPLPWXp33Qwo/0dFAy5bc7ryT8djff/sHvVu3ukHuQw+xzOH660M56jBTsqQbAHhddhkzwOvXMwB2tjp1eHzKFODhh93rlyvHF6ypU4GqVfnJfP9+7qtaVQGwZC893c08TpnCdj87d7KsZudOnqa54w6+QXqDs4YN+fWBB9iz+vBhfrIN9OSTfI4fOBD8U2+FCgxyDx9mu6HYWP/NafvnlAWULMnMamwsV0ZyskvVqrFMyLldsWL8etppPN6qFfDKK+5+Z3MmpV5wAbNWgT/fOf6vfzFIDrz/mBiOa/ly1nFt3Ohus2e7j9nTTwPvvsvvq1ZlANygAc/gAOwbai2D4AoV9H8rpy4tjR/ynCztggX8oPXII3w+tmvHD3edO/P/RM+5Ii8iVjwrKIcP8/3HWqBDB75X/u9//B/r1Yv7nA4OZcuGerSFyPHjLPx3MsBr17Ik4ptv+KY7eDDw+uu8bpkyfJM+/XSWQERF8bYlSjAoyOpFrbCcjpbMrOU/386d3KKimKkE+Oa2Zo17bNcuZjnHj+fxSpXcoLJcOQZjAwa4z4fhwzP/PO+Ey1mzMgeB1arxbEN6OrPAgQFiYZ99ndua3GXLGMhu2uQGwRkZwE8/8XiPHpzICvCFMyGBwb8TGP/2Gx+zhARmzgv74yZ5y1o+t2bN4nNywAA+vypWZNKkbVsuwtC5M79WrhzqEUuInHJN7omK1CA3kNN5ZPNmng1ZtIjve1FRzAh763qrVAn1aAuxzZtZP+gEwGvXcnLcb7/xeJ8+DIid1mr16wNt2rjZ4YMHuYa4JtOEnw0buDlB6s6dzBQ+/jiPX3wxs5FHj7q36diRGUYAOOcc1o5Wq+ZubdsC11zD48uXu8Ftdg2zNdnKlVcfCFes4AcQJwDetImByDvv8HjTpjxLAzDYrV2bf+9Ro7hv4kT+7RISeNYn4hqeS1CffAJMnszgdssW7mvfHpg3j98vX87XeJXIiI+C3AJy+DBX+nPKG+bO5RlRgPOz3niDpXjetnySB2bPBhYv9s8E16jBmkaAM2gXL+Yp07g41hOeey7w1ls8Pngw6ytLlXK3Fi2ASy/l8cmTmaHzHq9cWZmDQOnpwJ493JySlSlT3AJ3ZzOGnwgB9wOKw6kbcl4//vc/9vvzBrF16rBWKC8pyC14q1ezhMlbDtGoEfCf//B4uXKcWOeoXp39H598kpffeot1lgkJ3MqV0+npwuTIEZYbzJzJD0Sffsq/3zXX8LXbqaft3Blo1kyTHyVLEb0YRDgpXZp9d887j5edDg4zZ3KrWpX7P/mEnbdmzWK5WnIyYy+9Pp+kTp24eVmb+XT0/v3cmjQBatVy969YwWxicjK3o0fZANwJcgcMcJdSdtx8MzNS1jLDVKKEfxB8ww3A3XfzSXDTTe7+0qX5tWtXnnI/dgz4+Wf/25YqxdR/qVJ5/1g5cputS011a1m92913c/GQN95gsLFzJ4PbjAx35n9MDOuqx4zhk79aNX748D72jz3GyU9OAFupkv8nQGdZ6/ymfpcFr1EjbsFYCyxZ4l8KsXEj13EHeHbmttv8b1OmDP+O993H/+PXX2fw60yYq1795LILKnXKW19/zcb1ixbx9QVgEHvgABMR77zD11O9IRYauWnjGirK5IbAr78C773H+vjoaLYvGz/eLW3o2pWJxBh9BMk7J5Kpy8hgPWaxYry8YgWDXCcITk7mm+2ZZzJ7+Z//+B87fJgZyptu4gt3mzb+x61lX8bHHvN/4/Z66SUGkn/+yVN1gUHw448DF13EbhbPPON/rHRpHqtXj4Hn0qWZg+xq1TiOdeuAadMyB7Fjx/LVauRI4MEHM49v40YeHzuW3TOqVXMD2WrV2FkjNpbBbmys3rAkb1nLD1/eUoiNG4GePbmtWpV5AmxsLFcDuuEGziJ+5x3/ILhOHX5wC6Qs/4lzXlu8rbw+/pivhZMmAS++6GZpzzpLPdULsXHjgi/INXp0wQW6yuSGmXPP5ea48EImD2fM4P8/wFjkrLPcut727VWSVmCiotwAF2CWISvR0fzYmpXy5TmJzmEts7dO0Fe9OmvNvEFwcjJnMQKcwXjLLZmDaGd8u3dzmWbnWHo69yckMMidN489jLMydy7vH3BrV6tVc2tgL7iAYwgMYp0OANddxy0r3sdRJK8Y4z4X27fPfLxJE2Z7AzPBzv/ymjUsewgMXqdOZZC8dCnfvRMSuH/CBAbAXbvy/2TXLgbKxYv7bxUrRvZp9ayy2mlpfF0rXZo1sxdcwDIjgAFsp07uY33ppe5ZMin0HnrIP8AFePmxx8Ijm6tMbpjZutUtb5g5k68XADujzJ/P7xcsYLcjdXA4AUXhlKO1zJw69S8lSrCzwPLl3Pf++3yzDnT33cwG61OUFCUpKZzY5A2Cb7iBge3ll/MMRaB//5vlOa+/Hrx93Nq1/HA5ciTPtgQGwfPmsZZ/9GhmNgOPf/ABPxhOmsTJld5jJUq4y7PPn8+xe4/HxTFTCvDDb2qq//GYmFM/o+JktQ8f5u/iZGl//51lIsOHc37D7be7mdrGjTUJJYKsWQN89RUr6m68kX/aYGGkMTwpWhA08awQ27eP86oyMngGPC2NH4yvu46vs+npLHHq3Nmt+Q3n+hgJEzoFK5K9jAxmbGvUYLu048eZIY6L44S5pUu5z7tddx2zD7/9Bnz3HfelpLjH33yT2c4xYxjkBt7+zz8ZjN53H+vdjx93/0+LF3dnMl93HfDRR/7jrVSJ5UkAe5M7pwUdiYkcNwBcdVXmILpxY44JYHpu3Tr/4w0b8gNxejp/1oEDfB1p0YJvQJdd5n+KUiJCRgY/U339NYPb1au5f+BAPl0SE/n5MFBCAqe6FAQFuREkLY2vn1WrctGiP/5wP7w3bMjX4zlz+LrqKOj6GCkEFOSK5E4o/1es5Yu+EyxXrMj9W7cyoPUGyIA76/mXX1gm5Rw7dowT84YM4fEXX+QZHu/t4+MZWAN8s1i8mPt372Z2NtDAgcy0lCuXv4+BFLjjxzlV4+uvue3Ywc9e55zD6RaXXOKu5RTuNbkKcgu5lBS3g8OMGSwpC/YnrV2b7WZFABSN8g2RvKD/FZc+HEesgwd5EsIYVuWMHs2TDj17MrDt1YtTTIIJ9dljBblFSE71MfPn88zbNdeoBFNERE6AgtyIYi3/pFOnMpBdsIDVJ0uWANu2cVXzwhAnZBfkqho8wsTHB9/vnFqYMIFlVc4E4HHj+Intr7/02iUiItlQP+lCzVp213v6aTYleftt7m/TBrjnHrfypGVLZm4LQ4CbE2VyI0xO9THWclKuE/RecIG71Hz16qy5Oftsfm3YUO1NRURECqv0dHaK/OorbmvXcn+HDsC99wJXXhnK0eUN9cktQpw6mKzqY4xxA1wA+OEHroQ7fTontE2fDnz2GY9Vq8aA9/LLI+MfQUREpCj4/nueuZ08mU1CYmOB7t25iOTFF3NF7KJAmVzxYy0n5XqD3t692fkmI4NtJP/1L3WKERERCRf797Oz0kUX8XKvXmw/etFFbD/as2fk9tZXJldyzRigfn1uN9/MoNfpTrN1KwPfCy7g5dWrmTF2yhuaNVPPbxERkYKwaRMXZShZkt3c/vMfvk/XqAG8+y7XHSnqi04qyJVsGeMWn9epw6bPziomW7eyT6+zMFDFilz10gl6mzdX0CsiIpIXrGV7Y6e+dvFirvnRty/PsF54IcsMgaJTjpATBblywpzAtXt3LqCzcSMzvE55w1df8Xj58gx6336bk9pEREQk99LSWHbgBLYbNjD5dNZZwPPPu4tB1arFTfypJlfy3ObNbtA7fz577xUrxmXNV6wAvvhCXRtERESykprKTkmTJwN793Jl5fPPZ33txRe7GVtRTa4UsDp1uNjENdf47y9eHChVyg1wL7yQSwWefTa3Nm14WUREpKj58kt2O3roIXZD2LTJXXHswgu5ApmcGIUUUmAeftj93lrg9NOBn3/maisA/4E7dXJ79bZty390ERGRSLNuHfDdd8Btt7EM8JdfgGnTgAcecC/LqVG5goTcjh3AjBluTe+qVdxfqhSXjL//fgbFqamaKSoiIoWTtZws9tVXwNdfA8uWcf8ffwCtWgHJyVy8SeV8J0blChLWqlfnYhPOghO7drlBb6NG3Ld6NcsZxo9n37+jR/lJt3jx0I1bREQkO6mpfD/7+msGt5s3872rc2fgxRdZY3vaabxuqVIhHWpEUoMnCTtVqwL9+gGvvsqFKAAGs7fcAjRtysvjxrF7Q/fuwJNP8kXk2LHM9zVuHJCYyBeVxEReFhERyW/r1vH97LzzgHfeAVq3Bt57j2cvf/sNuOceN8CV/KFyBSmUFi1iwDp9OrBkCU8DFS8OdOzo1vRu2AAMHgwcOeLeLi4OGD3aXeZYRETkRIwbx4WQNm0C4uOBESPc95T+/dnK64UX2FP+rrsY5J5/vjK1+SW7cgUFuVLo7d8PzJrlLkW8eDFfXIxh8BsoIYEBsIiIyIkYN46tvbzJk+ho4MMPGejedRfbez32WOjGWNSccpBrjOkBYBSAaADvWmufze76CnIllA4eZNDrlDoEMoaT2eLigIYNuZ1+utqziIhIZunpbO21dCkD3EOHMl+nTh1mdqXgndLEM2NMNIDXAZwPYAuABcaYb6y1q/J2mCJ5o1w5Tk5LSOBqbIHq1AGmTOFkNu9nvNq1GfA2asSvZ57JNmYiIlI0HD7Mbgdt2zIR8sYbwH33BZ/z4bVlS8GMT05MbiaetQew1lq7zlqbAuAzAH3yd1gip27ECL5IecXFAU8/zTZlycls4TJ+PPDUU6zlPXQI+OgjnnJ67z3eJi0NaN8e+OQTXj5+nC+Chw8X6K8jIiJ5JD0d+Osvvv4/8QTL3ACWvZ19tnu5WTP2sf3gA+6Ljw9+f1ntl9DKTQuxWgA2ey5vAdAh8ErGmEEABgFAvP7aEgaciQBZTRAoWRI44wxuXtZy9mtGBi8fOgRUqQKUKMHLK1f6rxfuZH69WeA6ddjRQUREQuvgQWD5cpYbLF3K5Mby5W5dbXQ0u++0asUFib77zn1f6NqVm+PppzPX5MbF8b1Fwk+ONbnGmH4Aelhrb/ZdvhZAB2vt4Kxuo5pciWT793NVmtWrgTVruK1e7V+nVbIk8Omn7IG4eTMwcyaXZ6xQIXTjFhGJdBkZ7Edbsya77WzaxNI1R4UKQIsW7ta8OVtTOkmM3Miuu4IUvFNdDGIrgDqey7V9+0SKpAoVgMsv999nLbBzpxvwrlnDjC7A01/XXQf8+Sdv+/HHwPvvZ87+xscr+ysikhtJSW52dtkydjQYNoyvobfdxnkZHTvyrNqzz7LsoEULnn071RXFBg5UUFtY5CbIXQCggTGmLhjc9gcwIF9HJVLIGMOV26pXZz2X15VXsgl4/frudY8cYab3wAH3eiVKsMuDE/w+/DD7KmZkKPgVkaJr40bWwzoB7dKlwD//uMfLlQP69nUvz5jB4Bbg6+1DDxXocCWM5LaFWC8AL4MtxN6z1mZbfaJyBZGcWQvs3u2f/XW+37qV5Q8xMVzQYto0TpYDgB9+4G2d7G90dGh/DxGRvPT998DChcDjj/PyuefyjJgxQIMGLDHwlhzUqXPq2VkpvE61XAHW2qkApubpqESKOGO45GPVqkCXLv7HUlMZ4AJc47xKFffYE08ACxbw+xIl+KLvLXtwtrJlM/9M1ZKJSKhZy9cgZyLY0qUsPVi0iP3Kf/uNy+A+/DBfB59+mmezmjXTqmFyYrTimUghs3t35szvmjVcJz09ndc580xgzhx+P2IEJ1YkJwefFaxljkUkP23cCPz0k393g4MH3eP16jEj++qrnDB29Cg/wCs7K7lxyplcEQkfVapwC8z+pqSwTm3NGqBYMe6zlkFs377A11/7B7gALw8aBMyezUlxFStya9UKaNnSbadWqZJ7nyJStOTmDJC1/JAdE8PXoP/8h2edmjVjd5lbbmEWtnlz4Oqr3VKDZs2AMmX876tkyYL73SSyKcgViRDFigGNG3NzGANs2MDyh1dfDX67I0fYEH3fPrc38COPMMjdv5+ZlZdfBoYMAdauZWcJJxjOamvShLOdnRNFysiIFE7jxvmfAdq4kZfXr+drgzMRbNkyYPhwziGIiWFJ1Y4dDGJ79+ZrR926mkQrBUtBrkiEM4YBcHx88GWOExIYCGdksC3Pvn3uSnHFigFvvskG6Y66dXmdNWv4de9eZpG9Ro9m5mbRIuCss4BvvgF69GAJxTPP5Bwk164NFC+ebw+JSJFnLZeqTUri6o1JScygnn46j3/0ERdIeOyx4GeAnniC38fFceGEfv0Y0AIsP1i3zr1++fLcRAqaglyRImLEiOxX6omKYiuecuXc46VLA7fe6l6uX5+N1r2sZQ3dvn3c9u9326VVqQLcfz/f9ADWBW/dykkm+/bxjTWYn38GundncHzvvewoUa8e8OOP3JddgFyhAhAbm7vHRBPxpLBw/s+cgNQbnDpb6dLAVVfx+iNG8PKQIbzcowef597bOjX8jksuYVkTADzwABez2bQp+HiM4QfdevWUnZXwpSBXpIjIaZnjk2UMg+W4OGZgvRISODPacf753BypqQyKnQDZ2ZyMUMWKQPv2buC9Zg37C+/f75ZCBLNuHTPOH34IjB3LZTqLFQOmTGErtooVgRUrgLfeAo4f5202bmT2+eBB4PbbuS8tjW/gReFNXAG/v1N9PKzlc8tZSWvdOmDbNv+gNDBQjYoCXnuN17//fpYETJzIy2eeCcybl/3PbNbMDXJnz2YtvaNaNQa9Zcpk3pz93pXB/viD+374IfgZoPh4dnYRCWfqriAihU5GBoPRwODYCZjvuYeTXD78kKvL/forg/Ebb+TlnDgvizfeyFnhmzfzcr9+7NcZG+tuxYr5f1+rFjBhAq//5JPMXj/3nHt5+/bgt3O+r12bC4gAwNSpbAXXuTMvT5/OsWX1s2NjGbA4p4ZTUlgfmVOQHlh3CYS+84Yzkcn5kJGWxtPrGRncrHW/916uWJGBZXIyVyGsXZuPzb59vOy9TbDbN28OTJrEDzxHj7rjKV6cf/9GjTIHqt5g9Y8/+PPvvhv44AN3wZerrgK++CL471q8OAPK6tV5lgPgc2bLFreWfswYdlYJDEy9W9myeV8WEI7PDRGv7LorKMgVkSLDWr5Z79vHrFVWL3/O/m++YTbNOeX75pvAypUMHlNTuQV+X7ky8MknvP5ttzHw+fhjXu7WjRnkwNt5edu/NWvGoMoJmitWZCCfnUsvBb78kt9XqsSZ7K+9xp9XpUrm4Dg2ll05AscB8HqjRwPXX8+Aq3fvrINMZ3vwQf7eGzdyWdVXXgGuuIJZyJ49gweW3u2DD4BrrwVmzWIHkR9/ZPZ//Hg3+M9O4PVXrGALvVGjGHjmZMUKLgkbLHvpcILSYMHm++9z308/AUuW8LQ/wOB3797gWdTclteEirL8Es7UQkxEBMzmlirFLbuJeI5LLvE/dtttJ/bz3nzT//K0aZmvYy2zlMEC3ilT3EVBAJZdHD3qXjdYkO0d/6OPMsBz/OtfwW+3enXw8aekuKfbY2M5EckYN7sabHOWUy1Vio+fU8JSpQpwzTXu9bK6H6dUJSEB+O9/3Xru5s2BkSOD39Z72eku0q4dM/k1a/Jyz57MlAb7md7bx8dnX4d6/HjugtLA0pzWrXO+TbgaOFBBrRROyuSKSJGk07CuxMTsO28UNXo8RAqP7DK5RWA6hYhIZgMHMqBNSGCGLiGhaAa4AE8/O23jHN7OG0WNHg+RyKAgV0SKrIED3R7BGzYUzQAXUMAfSI+HSGTIl3IFY8xuANmU7eebygD2hODnSvjTc0Oyo+eHZEXPDcmKnhvhIcFaWyXYgXwJckPFGLMwq7oMKdr03JDs6PkhWdFzQ7Ki50b4U7mCiEgBMcZsMMbsMsaU8uy72Rgz3fe9NcbUD7jNMGPMxwU8VBGRQk9BrohIwYoGMCTUgxARiXSRFuSODvUAJGzpuSHZKcjnx0gA9xtjyhfgz5STp9cOyYqeG2EuooJca62ecBKUnhuSnQJ+fiwEMB3A/QX4M+Uk6bVDsqLnRvjTimciIgXvPwBmG2NGBTn2hzEmw3O5BIAJBTMsEZHIEVGZXBGRwsBauwLAFAAPBznc2lpb3tkAPFuggxMRiRARE+QaY3oYY9YYY9YaY4K9cUgRZIypY4z51Rizyhiz0hijCT/ixxgTbYxZbIyZUsA/eiiAWwDUKuCfK7lgjClvjJlgjFltjPnTGHNmqMck4cMYc4/vPWWFMeZTY0yJUI9JMouIINcYEw3gdQA9ATQBcLUxpkloRyVhIg3AfdbaJgA6ArhDzw0JMATAnwX9Q621awF8DuCugv7ZkiujAHxvrW0EoAVC8ByR8GSMqQX+37a11jYDO6b0D+2oJJiICHIBtAew1lq7zlqbAuAzAH1CPCYJA9ba7dbaP3zfJ4FvVMqcCQDAGFMbwEUA3g3REJ4EUCrHa0mBMsaUA9AVwBgAsNamWGsPhHRQEm5iAJQ0xsQAiAOwLcTjkSAiZeJZLQCbPZe3AOgQorFImDLGJAJoBWBeiIci4eNlAA8CKFMQP8xamxhweTM4scy5bILcZli+D0wC1QWwG8D7xpgWABYBGGKtTQ7tsCQcWGu3GmP+B2ATgKMAfrTW/hjiYUkQkZLJFcmWMaY0gIkA7rbWHgr1eCT0jDG9Aeyy1i4K9Vgk7MQAaA3gTWttKwDJCD5JUIogY0wF8GxxXQA1AZQyxlwT2lFJMJES5G4FUMdzubZvnwiMMbFggDvOWvtlqMcjYaMTgEuMMRvAEqduWj5XfLYA2GKtdc76TACDXhEAOA/AemvtbmttKoAvAZwV4jFJEJES5C4A0MAYU9cYUwwsAP8mxGOSMGCMMWBd3Z/W2hdDPR4JH9baR6y1tX0lBP0BTLPWKhsjsNbuALDZGNPQt6s7gFUhHJKEl00AOhpj4nzvMd2hiYlhKSJqcq21acaYwQB+AGc5vmetXRniYUl46ATgWgDLjTFLfPsetdZODd2QRKQQuBPAOF/iZB2Af4V4PBImrLXzjDETAPwBdvBZDC3xG5aMtTbP77Ry5co2MTExz+9XRERERMSxaNGiPdbaKsGO5UsmNzExEQsXLsyPuxYRERGRcDNsGLcCZozZmNWxSKnJFREREZFQGT481CPIJCJqckVERESkgKWkANOmAcnh2UJamVwREREROTFTpwJlygA9ewL9+nGfMdxCULYQjIJcEREREcna3r3ABx8AffoA48dzX+PGwDXXAFOmAEePcp+13MIkyFW5goiIiIj4y8gA3ngDmDQJ+O03ID0dqFMH6NuXx+vWBcaMCekQc6IgV0RERESAv/4C/vyTGduoKAa5APDQQ8CllwJt2rAcIZihQwtunLmUY5BrjKkDYCyAagAsgNHW2lH5PTARERERyUfWAosXM1v75ZfAqlVA2bLAnj1AbCwwezZQoULu7itMShS8clOTmwbgPmttEwAdAdxhjGmSv8MSERERkTyXns4NAP77X2Znn34aqFoVeOUVYMUKBrhA7gPcMJVjJtdaux3Adt/3ScaYPwHUgtbxFhEREQl/x48Dv/zCjO3XXwOffgp07w5cfjlQuzZwySVA5cqhHmWeO6GaXGNMIoBWAOYFOTYIwCAAiI+Pz4uxiYiIiMjJ2rsXGDwY+PZbICmJLb8uuggoX57HmzblFqFyHeQaY0oDmAjgbmvtocDj1trRAEYDQNu2bW2ejVBEREREcrZnD/DNN/z+xhsZzC5fDlx1FSeOde8OFC8e0iEWpFwFucaYWDDAHWet/TJ/hyQiIiIiubJ5M8sQJk0CZsxg66+uXRnkRkczyM2qI0KEy3HimTHGABgD4E9r7Yv5PyQRERERydLff7MzAgA8+igwZAiwaxe/X7QImD7dvW4RDXABwFibfWWBMaYzgJkAlgPI8O1+1Fo7NavbtG3b1i5cuDDPBikiIiJSZFnL4NVp9bV6NbsgNG0KrFnD6zRsGNoxhogxZpG1tm2wY7nprjALQNH9GCAiIiISKosXc3GGzZtZfnD22cAddwDVq/N4EQ1uc0MrnomIiIiEg2PHgJ9/Zsa2XTvg1luB+vWBtm3Z07Z3b6BSpVCPstBQkCsiIiISShMmAOPHA1OnAocPc9WxhAQeK1OGJQpywhTkioiIiBSk3buB33/nIgwA8Pbb7IIwcCBbfZ17LlCsWGjHGAEU5IqIiIjkt40bga++YlZ21ixOJtu5E6hSBRg3jmUI0dGhHmVEybGFmIiIiIicIGvZsxYAPv4YSEwE7r4b2LcPePxx4I8/3KV0q1ZVgJsPlMkVERERyQsZGcCCBe7iDI89Blx3HXDOOcDzz7MUoX79UI+yyFAmV0REROREDRvmfp+aCgweDMTHAx07Ai+8wIljVavyeO3awAMPKMAtYMrkioiIiJyII0eA4cOBatWA224DYmOZwW3fntna3r2BChVCPcoiL8cVz06GVjwTERGRiDJnDieOzZwJLFwIpKUxQ7txIxAVxRrcIryEbqhkt+KZyhVEREREvLZuBT77jCUIR45w35QpLEOYO5cBLgBs2cIJY8OGKcANQ8rkioiIiCxbxiB25kxg/XruK12al1u2BPbvB0qWBEqU4DFjmL2VkFImV0RERAQA0tPZvmvUKKBfP+CHH7j/yBHgu++AVq2Al15iScL+/QxwAdbYOgGuFAqaeCYiIiKR7+BB4KqrWFublMR9iYnAZZfx+/btuThDbssOhg7Nl2FK3lGQKyIiIpHj0CEGsjNnAjNmAM2aAW++CZQty2ztwIFAly7c6tRxbxd1gie3vS3EJCwpyBUREZHCK+n/2Dvv+Ciq9oufm0IghN5bEhAEKVKkSEdBEERRARtib9g7dkBfXnvv2AuivIiFoiCKCgpIlSpFeifUQAhp9/fHyfxmd7MpQJLd7J7v5zOf3Zk7u7nZnZ0589xznycZKFeOzwcNYhGGrCwgKgpo0wY45RS2GUPRK8IGiVwhhBBClAys5aSwWbPcSO2+fcDu3YzEduoENGsGdOvGogxlywa6xyKA5CtyjTEfAugPYLe1tnnRd0kIIYQQAozIrlgBNG4MlCpFi8CTT7KtUiWgSxfaDtLTgZgY4N57A9pdEVwUJJL7MYA3AHxatF0pBEaOlEdGCCGEKKmkpTHzwe+/M1L7xx/McDB7NtC5M3D++awy1rUrI7bH66MVYUWB8uQaYxIBTC5oJDdgeXKVs04IIU4OBQtEcXLkCDBnDieANW5Mcdu9O9tOPZVitls3oF8/oGrVwPZVBCV55cktNJFrjLkJwE0AEB8ff8amTZtOrLcngzHAxo28y1MuOyGEOD4yMzlZR8ECUVRkZABTprh+2kWLeNwNHw488wyQmsr2Ll14LRciH4pF5HpSrJHckSOBUaNybo+JARISgFtuAe65hz+sZ58FatbkD8d5rFGDPh8hRPgSDNFLazlUay1v0rOygLVredE/doyPqanM63nqqYyAffGFu91Z+vRh5Gv7duCBB3K2P/ggMGAA8PffwHnnebdlZrp9WbwYuPVWoFo1RtCcZdAgoEED5hzdtYvbKlbUsLHwz5YtFLQAcMUVPK6rVuXx2769m8qrUyegQoXA9lWUSPISuSU/u4JzccrIAKKjgQ8+AHbu5Ml3506gShXut2cP8NhjOV8/ejTwyCO8IAwZklMAd+/O9CMZGfxxShALEXqMGgU8/jjFZGamm45o3Trm3HRE4LFjFHQdO7L900+BvXu9hWjTpsBVV7H92mvpJ/QUkv36AU88QSFZpw5w9KjbBgB3381qS6mpQJMmOfv62GPAU09RJNx0k3dbVBRQuTJFbno6MG8eBbOzlCkDREZy30qVgHPP5faFC4G5c933cZLhN2jA/2vxYp5Djx0DzjiD26dPBy65hPtFRvJcW7UqMG4ccPrp/NuTJnkL5GrVmLM0JuZkvzERzPzvf8D331PcOqO6bdtS5EZEcPspp2jEVRQ5JV/kOkRl/yvXXee/vVYtXkx27/YWwe3asf3oUQrZRYu43amG8skn/DHOn887zSpVvIXw/fczD9/OncCSJe72atXcPgkhAoe1QFISrUwbN1L8XXEF2wYNAmbM4HPn99qxIxPJA8BFFwHLl3u/3znnUOABrHi0caPbFhMDDBzoitx//uG5pXRptlWo4ApoY/j+UVHeQrR9e7aXLs1IbUyMd3t8PNurVAG2bnW3x8R4n3MSEijScyM+Hnj//Zzbc5vbYC0T6UdHc71DB+Czz/jZOsuePUy4D/B8+MwzbnTY4d9/KZJfeQV48UVX/DpC+L//BeLigFWreF51tlepoiBDoPEd8cjI4Pc8axawdCnw4Yc8fiZN4m+ka1eOpHbtyhsfh2bNirnjIlzJ165gjBkHoAeAqgB2ARhhrf0gr9cU98SzsWOBRx8FNm+yiE8wGD2aQdmTIiWFQrhSJUZuNmxg1MYRx87jRx8xajJ+PMsFOhjDE/PUqbyDnTMH+Pprb4FcsyYjNTpxC3HiZGXx97hxI6NGSUnA7bezbdgw3qgePeruX6cOxWFuVqeLL+ZvFQB++skVqc5SuTLQsCHbk5Io+kqX5u+4oOVAg5nCnMCblUVbw549rhDu3Zuf15QpwIQJOUXyzp1sv/tu4NVXvd+vUiXuExkJvPUW8Ndf3iK5Rg2gf3/um5LC9zlZG0UwWFmCBefYmD6dNyh//gkcPsy2+vUZva9WjdvKlg2N34MIek7ak3u8FKfIHTuWI3YpKe622FhgzJhCELrHw/79wMqVvNh6CuHHHgPq1mXE5I473CFJByeq8c47XHxF8LBhHGJMSuL+lSsX/KStk7MIBTIzaSfatMkVssOHM2r5xBPAc89xGN0hIoK/s+ho/u5WrWJUMzGRjwkJvHH1RJlZXILlvLF5M7B+vSt+k5Ionp59lu0PPgh89RXbnJuYWrV4rADABRdQSDs2iqpVGUF8+222T5xIy4evnSIuzrsfJ3NsWMtoZ3o6l6goij9r3VEFz6VGDUbY09KAn3/2fm1GBqOhLVrQQvPRR96vzcgA+vblSMT27bTi+b7/sGFAjx7MO3vXXd7vn54OPP880KsXJ4QNGZLz76eksO8TJ3IUo1s311Nbp86JfUZCnCQhLXITE13LjycJCd6jiEGBtbRBeIrg88/nMOP//sehP882x+MXE8MT0muv8SRZvTpPhrVrc1jIGGDmTL7GUyRXqaILtwh+MjIYWXUE7MaNjMRWqQK88YY7cdSTLVt48/jdd8yj6QhY59FXqOSHRG7JJiXFFcFNm3Lb+PHAsmXekeLq1SmMAXqLFy3yfp9OnXg8AcDgwQxe/PwzcPbZFHqdOwNPP832Ll1of/MUgRdfzAgzQFuKE+V0GDaM7enp/kfwHnyQIn7/fgY0fHnqKQZONm/mce7Lq68Cd94JrF7NvkZHey+jR9NOs2IFcOONOduHD+frVqxgpDYqin5sf9fzESOC42ZIBJz/H03fzHu0QhlNPw5CWuRGROR+bRo4kHbZNm2A1q1LWDYSaznM50Sc5syhL9hTBKemun7CgQN5d+3vfQAOy27Y4Ark6tUpCLp2ZXtWlmZHi6Lh2DGKUs9I7NChQKNGHK6+7DJv36YxnATVvj0Fx5QproBNTORZtEyZwu1jsEQvRfFx4IC3TSIpiZ7piy/O3crSrRvw2298fsMNjAQ7AjEqimVkr72W7SNH8vzrKSJbtaJgtpb2N8+2qCjaYJo0oWBesIDbPNurVaNlIzOT1wfP10dEFL09QDeDwodgGE0PaZGbWyQ3NpaBTs95F3XqsBrgddfxRnrXLm4LCdvQgQPAjh2c6PGpn+J0LVuyvveuXRwKA+gVnj+fz9u1owBxRHCNGjxh33UX23/7jcLCadOsWOFw9Chv4T0jsQMGcGLS7NkUBp7nmYgI3pANGMCJWWPHugI2IYFJ4TX7XgQLEnYu+ixENkeP0tUyeLA7T9+T4hxND+kUYqNH530XcfAgJ38uWsSldm3us3gxr8Hffstr7fr1zKLTpg0tsiVO+FasyOWTT7gA/k9I1tLPtWsXlb7DkCEc4tq1i0NwCxa4qYYApgravdtdL18euPpqWigA4L77ODznRIlr1GBWilq1iuK/FSfL8UQuDx+mePWMxHbrxhyr69fze/YkKopnuA4dGK0dMcI7ElunjjtDv0kTDsEKIYKfESMC3QMRIKzltKNp07j8/nvOKUaebN5cfH3LixIfyQVOzA+yYweDSQMH0sL62mtu0LJCBdobHKtDmzbMve6p+UoEhXnXPX++a5PYvZuPrVpxaC4jg3cPe/Z4v+aee4CXXuItX5MmbhTYEcF9+9IukZ5Oge34iGWbKHo8j42DB70F7MaNnNxyzTU8i/laA0qV4g/uiSdoRXj+ee9IbO3aJfDHIkQuyMoiwhRrOV3oxx+ZUGPbNm4/7TTWnOndG7j5ZrrRfAmWSG5IiNzC4NgxpsN0Ir6LFrEgkDNpOzaWmu7bb2mLOnCAk2SdgFRQUtwn54wMCl0nw0SdOkz8fuAA7yA8BfLu3ZzAcf/99JQ0asT3iIzkB1y9Ovt+0UUU159+6i2QnceCfgEl4UJlLQV/Who/S8ePvW0bhWhamrtERzNSCnDS4c6d3O68vkoVN6XdW2/xLOS89tgx4L33XJFbvbr3DUqZMsD11wOvv871V1/lZ+1EY2vU0I2IEEKEIAsW0EV25ZVcP/10zgvu1YuitndvN1U3IE9uiSY9nV+2I3pXrGCYPjKS1YK/+46ZWowBfv2VE7qbN5ddtUA4qXWioyngfvzRFcCOCL79dibe/+03pr3xZcIEhuLnzAEefjinCL7gAobpjx7lr87xIztL/fr0fm7bRqHtbHeE4vnnU/D99RcnQnm+Ni2NQ3dOZowff/RuS093M188/TTzrnq2xcRw7Aeg7WPcOG/7SO3a7m3zeecx37Inp57K6DfAqny//+7dfsYZ7ozodu04yzwry/tvOFx4IYsjOJHYatVKoF9HCCHE8bJhAxOIXHcdYxfDhvFylJRE59nWrXQd5jU4F8zZFUq8J7coiY7mqG2LFtQhnlx8MedyOVrgrrtY8CUqiqkYPa0OLVsy6is8MMaNwlao4F1Iw5fu3d3Ua55CuG32Me2UXF6yhO0HDnB7y5YUuV9+yXXf9BorV3LcZfx44N57c/5dJ03VtGkcmvckKorpfmJimId1+nQO40dH87FUKfYpMpJ3PzVruttLlfI+IPr25d/xfL1nDfcHHuAB6PneTlUpAPj4Yze667R73mk5kws90QQSIYQIOw4fZlDO8dauXcvtZ5xBm+bjj7PooFM8sW7d/N9zyJBirktwHCiSW0hs2OBtdVi40B0BNoaW1Msv5wEEMPOMhG8RcewYxe677/KW0pcBAyiq+/alJWDzZhbl8BShpUoxnU90NL+sY8fc7VFRJX+4XiJXCCFCnqwsWi8dUfvHHxzQi43lAKnjrW3cuOQO4MmuEACspZXBU/i2bk1baHo6A3WPPMK83seOcUS+TRsW3RFFgESdNyXBoyyEEOK4cZIn1a3Lgbz27bn99NMpavv0YS2TUMnUKJEbZBw5wmwOXbowucCCBbRNAkwR6ml1aNOGfpiSeocVNEjkCiGECEGOHWNQrX59itvKlelwe+MN1g0ZNw7o2TN0M3rKkxtklC3LeVIOp50G/PKLd9T3++9dTVajBsXuU0/RN5ORQatnbsI30CbwoET5HYUQQoQA1gJr1nAqyLRp9Ng2asT8/9HRwPvvu9WtIyPdTAnhiERuEFC2LHDWWVwcDh+mj8ZT+DpG8C++AO6+m/Os4uNpJ83MpIV03DjvdB6bNnEdCHOhq6F5IYQQJZQDB5gFwRG2TqXXhg2Z0rxPH3ffvOZxhxuyK5RA/vgD+PxzDkVERjLlxzvvcBJ/erqb29eT4kzMLIQQQogTJzOT85uNAZ55hvN3MjNZWPTss11vbYMGge5p4JEnN8RZvRr4809Ge994I/f9nK96yxagUiWKYiGEEEIEHmvdvPsXX0wbY6tWrPfz888UtWeeGeRFqAKAPLkhTuPGXK69lvUHnGEMT2Jj3edDhvCO8I8/uP7SSxS9TZvSH+yZglUIIYQQhU9KCuv4TJtGG8KttwK33cbr+YABzFgJ5LQzioJTIJFrjDkXwKsAIgG8b619pkh7JU6Y0aNzL7Hn8PDDzJ0H8M7xP/8B9u932+vWZUGLpk29F6fKrBBCCCGOD2uB5ctdUfv777QXxsSw5pFTeKFWLeCjjwLb11AhX7uCMSYSwBoA5wDYCmA+gMuttStze43sCoHleLMrZGaymMWKFSwC5iyrVrEiLkDh/O673PeBB4DBg4GOHYvn/xFCCCFKMrfdBnz7LVN9AQwk9e5NC0K3bqwgL06Mk7UrtAewzlq7PvvNvgQwAECuIlcEluMtsRcZyRmaDRtyiMQhM5PWh5UrWZUWAHbsoNht1owi9++/gXPP9Y74OlFgFbYQQggRahQkkPThh4zUfvwx1w8cYG58p8JYQcrlipOnICK3DoAtHutbAXTw3ckYcxOAmwAgPj6+UDonAktkJGdues7erFsXSE6mAAY4zNK3L6PAH3/M1GcO1aq5wveOO+j3zcqisV7FLYQQQpQ0xo7NmabzxhuBGTOAgweBTz5hBoTdu5neMy2N3tqxYwPb73ClIHaFQQDOtdbekL0+FEAHa+3tub1GdoXwxFpg61bX7uBpf/jxR84K/eIL4M47WWqwfn36k3btohCuWVPiVwghRPCSkMAIbm5t33/P8rmi+DhZu8I2APU81utmbxPCC2NYlrhePe/E1Na66csSE5kapU4drr/3HkscA26GB1/rQ+3aEr9CCCGKj6wsRmIrVACqVwcWLgSuuip3gWsM57boWhVcFCSSGwVOPOsJitv5AK6w1q7I7TWK5IqCsmcPsGyZd9R3xQpg7153nypVGO2NjGS+QGuZDFsIIYQ4GZwRyOXLuTRuDFxwAe0GNWoAL7/MCqObN3MUcuZM4NChnO+jgkuB46QiudbaDGPM7QCmgSnEPsxL4ApxPFSrRsHqK1p373ZF7549FLgADf4HD9LuAAA33EB/sGfkNz6elWKA4880IYQQIjTZs8cVs56Lp2i96SaK3OrVgU8/BTp14vb4eGZH8PXkAkzTOXp0sf4rooCo4pkoUezdy+XUU7l+8cXAnDnAzp3uPrGxnORWujTw118sdezZNmaMhK4QQoQ648cDqam0GQAMqiQl8XnlykDz5t5Ls2bcnh8KngQXKusrQp59+5jX13PC2y+/uFkgPKlUifunpvJOPTGRS3w8hbEQQojg5+hR4J9/vKOyhw8Dv/3G9v79Gb2dN4/r48fz/N+8uSY6hxISuSIsiYhwJ7x5YgwnFaxeDTRp4t1WsyYFb0KCK37POos+LSGEEIFj9mzgp59cQbtunVu9s1Qpns9btmQ6y4gIWtvKlXPtayI0OdnsCkKUSOLjmcPQ33aAxS82b+ZkgY0bua/zfMECYOJEWh3efZcid8kSJvH+4gugVy9g7VqWZnTEcEICEBdXTP+cEEKEGFlZPCfXrMlRtYkTgaeeorgtWxaYNAl44QWeu5s3By67zLUaNGwIREd7v1+FCoH5P0TwIJErQpbRo/OeIBAZ6aY869o15+uzsljhrWxZ97UXXsj9AZ54b/fJFl2lSs5I8CWXcJZuVpYiCkIIYS0z5vhOAFuxgnaDX38Funfnubd2bVYLK1sWePhhYORIlcAVBUd2BRHSFOUEgawsZoFwor++0eBNm+gZW7aMkYa33gIee4w2iWrVOOy2YoV3JLhiRfnEhBChw/79DCiUL8/RsLvvpqD1TBNZvbr3BLDzzqO4FaIgyK4gwpYhQ4pu1mtEBIfVatZkNTdfrOWkB2e2brNmwBVXMNoLAN98A7z9tvdrypfPGQm+804gKgo4doy+M4lgIURxUpBgwZEjnPC7fDmrWfboAWzfzsI/b70FDBvGaGxaGrPiNG8OtGjB82L16gH5t0QYoEiuEAHCWmZ58BcFdhaAkyeMAYYOBRYtYvQX4IXjyBHvSHC1anmLYKW+EUIcD/7ywpYuDVx/PUeeli/naNWGDe5E32uvBT78kOsvvcS5DC1aBKT7IgxQdgUhSiDWAsnJjO4CwP/+R4/wnXdyvWNHYO5c79eUKeMdCW7blhcjgPmB77knp0dZeYOFCC+spff14EH/S5kybm7ZihW5zR+RkZyU65tvtkEDt4CPEEWNRK4QIcrBgzl9wJ7P27YFfvyR+0ZF+c8bHBvLWcqxse5SpgwfTz8d6NaN+/38M3DKKRTPmZkcinT2L11aNgohigNreaOam0A9eJCWgEce4f5PPw2sWQN89BHXzz2X8wGc1Fv+OPVUzh0Acv9dG8M5BzExhfe/CXEiyJMrRIhSoQKF6Omn+2/3rPbmT+ACvGBOn87HlBQWyXC45RaK3MxMpk178kng8ccZUXZSsTl4imRHKN92GyPJ+/bx+c0306u3dSvwwQf+hbXnkpDA5O2ZmfxfYmIKV0zLvuGiz8Kbovo8jh3j7yE3gXrgAB+feYa/gbffBj77DPjzT77+6qu5nhcxMcxEYAz/3pEjbtsFFwBnnMFzR8WKfPRdKlZ0909IyD0VowSuCHYkcoUIYTzzRuZ2sUpIcP2/AAVlaioFb1T2GcIYYNYsoG5drleoALz3Hvc5etQVyM7ibHPyBh85Aixc6M6o3rSJqYDy4+OPeVGfNw/o3JlR6T59gMmTgVtvzSmKfZfbbuNwqpPT+PLLORFw40baNF56iSLA6dMNN3Cy4MUX83+vXt2d9JeWxv/HGEbTQily7eu73LSJ60B4Ct28Po8BA4Bt2/IWqAcPAiNGcALW+PGMqs6ZQ8/8f/7DJTeMoUXp4Yd5DJcty8mqTgrCQYM4WSs3gVqhgnucAjl/Z7feenyfRX6pGIUIZmRXECJM8DeBJJCe3KysvAVySgrQujVF+JYt7P/ll3P9r78Y4fJ9re97fPcdcyB/8QX/x3/+oeh98UXg/vvz76Pv/ocOsYLSffcBL79M32FUFBfnuefjypUUHM89R0/1/Pl83yeeAGbM8P8653lcHPDJJ9z/3XcZUXSExRtvcKKPv7/pPFatyglAAJPoG8MypwAwZYp7ExMZ6Yp7X5wboF9+oahq04bbnUIpWVm5L6ecApx9Nvd/6y1OPOralTdQr76a92uzsrhvv378Lh9+mDmqe/TgKMKoUe5+mZk5X5uZSU9p//48dm67DXjgAb7nkiXAgw/6f43zfMkS3tT4+zxGjACuuy73Y6ZcOX5WEyfSLvTrr7whfPll3jT99Rdv+HITqXFxwZdPW1F+EczIriCE+P+LUrBcrCIiGKVyim3kRb16wEMPuevt23MpKIMGAeecQ+sDQLH8wAP+yz4DwPvvU/TUrMn1bt2A55+n9xjgbPGyZYGMDC6Zmf4fnUh6jRrAaae571+mDF/v7JeWlvO9PBPeL13KWewOU6cysu65v+//0rixK3Kff56C1hG5d90F/Ptv/p/b5s18vPlmft5jx3J96FDvmyV/XHGFK3IfeIARxK5dGRX3/C49iYhwF2spctPTGdFv1Igi9/Bh4NtvvfeNjPRej4hwRw0yMmiPcfqbmcn38Nw3Korp+Zz38Sdwnc+je3faBfyJ1HLlck646tGDi8PxHrvBQFGmYhSiKFEkVwgRliQmFsy+UVJwopGeordcObYlJXG9WjWu//svI6TOvv37Azt35nxP57NYtowRxvr1uX3lSj76E5fOEhvr3lTs28cbhNhY9iM1Nef+wWT/CLVjQ4hQRpFcIYTwIdS8ho5Y9PRhO1St6r1+yine6y+8kPdn4ZvjtGnT4+ubUxAFoJgN9rKsoXZsCBGuBJnzRwghiochQ+hHTkig8EpICN+cwfosvNHnIURoUCR2BWPMHgB+BnuKnKoAkgLwd0Xwo2ND5IWOD5EbOjZEbujYCA4SrLXV/DUUicgNFMaYBbn5MkR4o2ND5IWOD5EbOjZEbujYCH5kVxBCCCGEECGHRK4QQgQAY8xGY8xRY8xhY8xOY8zHxpi47LaPjTF5lAwQQgiRH6EmcscEugMiaNGxIfIiUMfH+dbaOACtALQG8HCA+iFyR+cOkRs6NoKckBK51lodcMIvOjZEXgT6+LDW7gQwDRS7IogI9LEhghcdG8FPSIlcIYQoiRhj6gLoC2BdoPsihBChgkSuEEIEjm+NMckAtgDYDWBEgPsjhBAhQ8iIXGPMucaY1caYdcaYXCqji3DDGFPPGDPTGLPSGLPCGHNXoPskggtjTKQxZrExZnIA/vyF1tpyAHoAaALm3RRBgDGmojFmgjHmH2PMKmNMx0D3SQQPxph7sq8py40x44wxpQPdJ5GTkBC5xphIAG+Cw31NAVxujDnOwpMiRMkAcJ+1timAMwHcpmND+HAXgFWB7IC19jcAHwN4IZD9EF68CuBHa20TAC0R4GNEBA/GmDoA7gTQ1lrbHEAkgMsC2yvhj5AQuQDaA1hnrV1vrU0D8CWAAQHukwgCrLU7rLWLsp8ngxeqOoHtlQgWsr2w5wF4P9B9AfAKgHOMMS0D3ZFwxxhTAUA3AB8AgLU2zVp7IKCdEsFGFIAyxpgoALEAtge4P8IPoSJy64CeNoetkJARPhhjEsE0TfMC3BURPLwC4EEAWQHuB6y1ewB8CuCJQPdFoD6APQA+yrayvG+MKRvoTongwFq7DRx12QxgB4CD1trpge2V8EeoiFwh8iQ7yf7XAO621h4KdH9E4DHG9Aew21q7MBB/31qbaK2d4bNtmLV2oLX2GmvtY4HolwDAKF0bAG9ba1sDOAJAcz0EAMAYUwkcLa4PoDaAssaYKwPbK+GPUBG52wDU81ivm71NCBhjokGBO9ZaOzHQ/RFBQ2cAFxhjNoIWp7ONMZ8HtksiSNgKYKu11hn1mQCKXiEAoBeADdbaPdbadAATAXQKcJ+EH0JF5M4H0MgYU98YUwo0gH8f4D6JIMAYY0Bf3Spr7UuB7o8IHqy1D1tr61prE8Fzxi/WWkVjhFOcY4sxpnH2pp4AVgawSyK42AzgTGNMbPY1pic0MTEoiQp0BwoDa22GMeZ2sGJQJIAPrbUrAtwtERx0BjAUwDJjzJLsbY9Ya6cGrktCiBLAHQDGZgdO1gO4NsD9EUGCtXaeMWYCgEVgBp/FUInfoMRYawPdByGEEEIIIQqVIonkVq1a1SYmJhbFWwshhBBCCAEAWLhwYZK1tpq/tiIRuYmJiViwYEFRvLUQQgghhAg2Ro7kUswYYzbl1hYqE8+EEEIIIUQgyMgARo0KdC9yEBITz4QQQgghRDGSlAR8/TXw5ZdAxYqB7o1fJHKFEEIIIUTBGTYMeP99RnA9MYaPI0YExLrgi+wKQgghhBDCP0ePMmJ71VVAWhq3nXYacO+9wOLFQFYW4GTqspZLEAhcQJFcIYQQQgjhSXo68NNPtCJ8+y2QnAzUqAGsWwc0bQrceWege1ggJHKFEEIIIcKdzEwgJQUoVw6YNw847zx6bS+5BLjsMqBHDyAqD9k4YkRx9bTASOQKIYQQQoQj1gLz5wPjxgHjxwMDBwKvvQZ06gRMngz06gXExBTsvYLEouCJRK4QQgghRLjx7LPAmDHA+vVAqVJA375A795si4hgJLeEo4lnQgghhBChzrp1jNI6k8Q2bAAaNgQ++gjYtYve2/79A9rFwkaRXCGEEEKIUGTbNuCrrziBbP58buvdG2jSBHj7bTflV4gSWpHcIPSDCCGEEEIUOz//DNSrB9x3H9N8Pf88sHkzBS4Q8gIXAIx1wtaFSNu2be2CBQsK/X3zxRimvchr9p8QQgghRChx6BDwzTeM2J51FvDgg8yU8NJLzI5w6qmB7mGRYYxZaK1t668tdNSgI9ajo5nyompVLkOHArfeyqocL7zgbq9ShY916gDlywe060IIIYQQx8033wCffw5MmQIcOwYkJrq+2thY4LHHAtq9QFPyRe7IkcCoUd7bDhygeC1Xzg3H79sHPPxwztc/9RQPgu3bmQPOEcGOEB40COjQATh8GFiyxG2rVAmIjCza/00IIYQQwiEtjd7azp25/v77wKJFwM03A5dfTr0SBjaEghIaItfx4hrjRnR9qV4dOHIESEoC9u7lY1IS0Lw5260FzjiD27ZsYam6PXtY2aNDB2DFCqBrV/f9jAEqVwY+/BC44AK2v/RSzkhxp058zMhgSo6I0LJBCyGEEKIIycwEfv+duWy//hrYv5/e2rp1mRmhShUF3XKh5Ivc4yE2FoiP5+JLnTo8gDxxajADNGpPm+aKY0coJySwfedOt/3YMfc9Zs5khPh//wOuvJLC2FMEv/gicMopFMnz57vbnX0qVTrxuzLPGwAhhBBClCx+/53VxnbsAMqWBQYMYMS2enW2O4/CL6Elcgu7pJwxrsCsUMFNkuyPnj2BrVspio8ccUWwY/Zu2hR45BHvKPKGDe77T5vGGZC+bNxIIf3++8Cnn3oL5KpVgdtuA0qX5t9OTeW2ChX4vqNGSeQKIYQQJQFrgWXLGHBr1w64+GJqiDPPpLA97zwG60SByTe7gjHmQwD9Aey21jYvyJsGLLtCSSYlhcmYfSPFt9xCEfvJJ8DHH3u3pafTnxMdDdx+O/Dmm3yvyEiK3V273GwTu3czKhwdHdB/UwghhBAerFtHYTtuHLBqFa/hw4cDo0cHumclgryyKxRE5HYDcBjApxK5QYS1QHKymxli8WJg+XLOspw+Pef+jRoBmzYBzZoBrVsDrVoB7dvTbyyEEEKI4uPAAWaCAjgfaNEizvu5/HJOeK9WLZC9K1GclMjNfoNEAJMlcksYnhPxJk0CZs1ihojFixkJ7tKF2wDeNZYvT/HbujVQq5ZmaAohhBCFxZ49wIQJzGW7cCHn8sTFcT5OzZos3CCOm2LJk2uMuQnATQAQ729ilwgs55/PBaDw3b6dd5LO+uTJwMqV7v7VqgF33QU8+ijX164FGjTQDE4hhBDieFi4kNfSGTOYKeG00xhYyshge7t2ge1fCFNo+aystWOstW2ttW2rFXOYfexY5j+OiODj2LHF+ueDl9wm4hnDbBLNmrnrK1ZQ9P7+O/Dqq0wmXbs223fvpvm9fHmgY0dg2DBgzBhOnBNCCCGES0oKMH484IxolyoFrFnDKmRLl/J6+/jjrl1BFBkl3q4wdixw0008phxiY6nBhgwpli6EPocOARMnulaHJUu47aOPgGuuAf75B/jPf1yrQ6tWzAAhhBBChCqeaTrT0jgfZtw44LvvmGXp1ls5IdzRWbIAFgkh7clNTOR8Kl8SEph9SxQB1vLDrViRGRt+/plid+tWd5969YBvvwXatGGWh5QUfln6kQshhAgFPOe9tGzJKG3lypw4dtllQLdusvgVAyflyTXGjAPQA0BVY8xWACOstR8UbhdPnM2b/W/ftIlZtfr0YS2GcuWKtVuhjTFA/frues+erBKXlMQor7PUrcv2jz5iSeUKFRjldSK+l17K9GhCCCFEsGMtbXqzZrmTtjMzKWQfeojXuF69aE8QQUGBIrnHSzBEckuXpkc3JYWpYTt1Yi2HK67ga0QxsnYtK785doelS2m4P3yYX86LL9Kj5FgdWrZ0U6MJIYQQgSAri8I2MpI2hPvv56Rtf4wYoeJLAaJYsisEitGjc/fkDhoE/PEHbTLTp3NyY9u2FLkrVwJ//QUMHsxKeaIIadSIi0NmJiO/TmGKnTuZ3eGjj9x9PNObLVoE1KjBiXCyOwghhCgK0tI4WcyJ1P7xB/213boxrWa3bly6dmUV08hI164ggpISH8kFOPns0UdpXYiPp/D1N+ls1y7aSGNigKeeYtXbvXs5wvDzzwwudu2qqnkBwVqKXWdimzG0OAAUyOvWMa2ZY3fo1SvvMstCHA+eE0iEEOFBcjKQmspry/LlTOWVmsq2xo0pCO64Azj9dP+v9/TkioBx0hPPjpeSUAzCWuomJ8B4zjlMYRcTw+O6Tx9qqBYtFDwMOH/8QfHrCODly4GrrgLee4/DST178oTk2B1atMh5pyIRI/JCFysX/VZEqLJnDzB7NlNlOsWR7r4beOEFRnEffhjo3JkjidWr5/9++q0EBRK5BSAlhcf8tGm0NqxYwe01a1Ls9u5NIVyQ414UMenp9PNWqgTs2wcMHMiTlVPcIiICeOklFrM4cgT4809+gUeOAGXK6K4lnEhP53Fx4ACwfz+Xdu04A3rJEuCLL5gO7913me4nMpK+u/h4+pm++47bIiOBqCg+3ngjU+QtWcIbMM+2yEj6pGJjWYP+n3+82yIjORM2KooZSnbuzNnetCmP0b17eWLybIuKcnNrZmRwv4iIwj2mJfhFqLBpEz20HTvymK5Rg0I3JoYl7bt2ZU74M88MdE/FSRDSntzCIjaW0ds+fbi+dSvw008UvFOmAJ9+Sv/u+PFs/+MP+ntjYgLX57AlOpoCF6BYmTmTJ7BNm9zJbe3bs33hQtfWULYsRUL58sDnnwN9+9J/9dRT9Kw4S/nyTP8SH89CGOvWebfHxUkoFyepqa5A3b+flfdq1eKP9MMPvQXs/v3Ak08C3bsDU6cC552X8/1++ol2l9GjWWLT4a23+JiWBrzzDvD338Czz9JD7snFF1PkzpgBPPBAzvc/5xyeUL78kn3xJTmZx9Drr/NmzJesLD4+/DBHKzyJi+PrAWDoUP4NgEI3KopFXtav57YhQ9hHT4HcsCH/fwC47jr63T1FetOm7t/66ise54mJzKZStaqOexHcrFkD/PKL66ndsoXDtWvW8Nh9+21GrnTxDhskcnOhbl3g2mu5ZGZSN0Vlf1obNnA049VXgTvvZCBo+3aOmOsaECCci3FiInDhhdw2ciSN155kZFAkOenNkpMZUTt4kMuhQxQZnTtT5P74I3D11d7vERFB8dyqFUXSq696i+AKFSh+KldmJM9XJDtLuBws1jLyvn8/BWmlSsyjfPgwRZynQN2/nzmXBw8GVq9mpo1jx7zf7913Odt01y7OaI6L43tWqsQopyMSmzTh9++0OYtT6W/8ePc78Be9vPFGLgDfMyODJwPn4njbbbTNZGZycdqdio/DhgEXXeTdlpnJ0QTn/Xv18m7LzHT7dPXVjDZ5tnvm3LzkEpYH9Xyt5yzazp2ZO9GzfzVquO21azNanJnJ4/Tff4E5c9zPw5fYWP5NZ4LoBx/w83ZEcKVK4XNMi8CTns6btLlzeSE2BnjmGR6ftWoxSvvgg3x0GDgwcP0VAUF2hRPg6FEGSFq3plYaOxa48kpqIsfa0KuXG2wUQUBBh2AdQVa6NCPGO3YwoueIYGe56y6KmW++AV57LWf75s080Y4a5d+ztX8/BcIzzwCffZZTAL/1FgXN778zQu0ZZa5Y0TtP8YlwvF6yzEzeAOzfz7u9+Hhu/+ADCiVHoB44wOH4W25hBLZePW5zarQDzCf59NO0mlSpwu+mYkW3uMhdd1E87t/P/TwFbKVK9FzXrs0+ZWW5WTpOBg3RuzifxaFDPPY2bOCN4MaNvEG8/Xa2x8a6k3QACuo77mCE3FpGquPjeawmJvL4FeJkWL2aqbxmzaK4ddIqrV3LUYp163j8NmigG64wQp7cImb7dmDSJFobfv6ZGicigtY/ZwJbhw5uJFgEgOIUMZ4lHHftolDwFMCHDjHyEBlJH8x333m3p6a6VU6uvpr7eFKxIgWg0/7TT94iuEEDDrcDvCDs3u22VajAaF6LFuzn5Mn8W55D/o0aAcOH8/Vnnsko36FD7v91ySUcynb6cvAgD25HjA4ZAjzxBPe/4w7+TUegOpHUJk3YfvAg+xURUTTfRUHRBBKXgv5WDhyg8HVE8IYNPF6uuIK+R98JDJUq0b5x++28kfz4YzcKnJioXI7Cm337OEls1ixac04/nUGFgQM5wtO1K5cuXRhQEGGLRG4xkpHB+SpObt558xhsqlQJ2LaNI5VHjuh8XuyUVBFz4AAryXkK5IwMTm4CONz/11/e7VWqULwCzOno5Bt2aNOGw3zW8vnixdxepgwP1J49XWE9fDiHLjyH+089lRM5AEa6y5dnVE+Rk9CgMH4r1jLC7yuCBwzgnf+iRcAZZ3i/plo1eiYHDmTk4LvvXAGcmKjqiOFAUhLw+OM8Zzmzv0uV4ojRlVfyXJSWplEB4YVEbgDZv58++NWrgUce4bZevagnJk3i+rFj8sCLIiIjg8L34EFOonr33Zz73Hcfh5h1EIriwlqOMHgK4I0b6VNu25YC1/HWO9SsSQ98584cXZg1y40Ex8erlGpJwlpeFJ0JYq1aAffeSxEbH88bICdS2769bnBEnii7QgCpVCmn133wYPd8nJ7OkZbmzWlr6NOHwTXP+SVCnDBRUZwAV7kyLQyOjUEeVBFIjKFtpkYN/+mbzj+f2TN8I8G1a7N9xgxaYTzfr04d4NdfgVNOAebPZz5tRwTXrSu/WCCx1h3pue46jjTt2cP16tX5PQGM/uzaFXj7kggZFMkNMIcOce7RtGkcwQOoR845x53E5iQCEKLQkMgVJZmMDPq/fEXwG29wAtzw4cBzz7n7R0ZyEuTy5fSKzZzJ9FKOHaJ27dwjCyXV6hRIjh6lV8+J1CYnu5k7nEmpTqS2USNZncRJIbtCCWHPHgYonIIUO3Zwe9OmwJtvctK6EIWCLtwilElLYyTYUwDv2EFvJ8DJkV984e4fHc0JkY4/fdIkCrP69YFOnThrPzbWneCUmurmFw4ngZbbeePAATct4qhRtD+lp3O9eXPmrX71VUVoRZEgkVsCsZa+e0fwvvwyxe7XXzO71BdfeKe8FEIIUUCcDCaekeD0dJZ3BfxP2GzVyhXB7dqxkAxA71mpUnzNlCnc1qcPI8VOW6lSzALwzDNsv/lmZpjwbG/XjqnzAOD553kRcNpiYpgTuVMntk+ZQmHu+fpatWjZyMqioPdsK1WqcDxwzgjQ9u1ulHbWLGDZMqbxOuUU4PvvWS2pa1f6p5VLM+QZOxZ49FH+pOLjeY8zZEjx/X15cksgzg1w8+acF+SQkcEgRdWqXB85kjnc+/ShxaFGjcAfcEIIEdSULs0sIaee6r+9a9ecInfJEjeSefvtjBSnpbmL4ysFGJEoX95tS0/3jmKuXMmSzp6vT011Re7IkW4OWIdbbqHIzcxkKVpf7r+f4vjwYf8et5EjWTxlxw6mEPQUwDExLJwwdCgvHNddl1Mk33AD3+f775klA6D1o1Mnt5Q1AFxwARcRFowdy9o8zuG6aRPXgeDQHYrklnAeeogjcElJXK9Xj+cwz9z7sbHAmDHBccAJIUSJIhD+9fR0bwGclsZJWVWrMlK7cKG3gE5Lo7WiWTOm6/n005yv796d0eZ9+5jH2rf9mmsontevp9h13nvbNvcC48kNNzDlmyb0hTUJCW5ad9/tGzcWTx9kVwhxsrIYZJg2jXYo3yqoQPEecEIIETJokqaLPgsBpkZ1ClVGRPg/JIxxK6wXNXmJXLnAQ4CICKYde/hh3nz7Y/Nm3pRfdBEntwkhhCgAI0YEugdCBBRrgaVLaSnv1o11W5w5nHXq+H+NU/k90Ejkhhi5HVjx8ZwrsHo1J8ICPGgvuogl5les0A26EELkQFlIXCT4w4bDh1mT5eabqR9atmQg7fBh2iR79+Z+zzzj2rEdYmM5FygYkJkmxBg92tsEDrgHXEIC5zs4YnbHDuDvv4Fvv+V69erA2WdzOessTpQNp+w4Qggh8kCCPywYNIhZ9NLSmHb6nHP41fft69ZjcXDm+gTrZHd5ckOQ482usGEDc6PPnAn8/LObn7dePQrel19WFhghhBAiFHn7beCHH5g4A2BGJ2OAfv2Y+S7YK2YrhViYMWTI8d1F1a/P5brrGOVdswb45Rcus2czEw4APP00C1a89FLR9FsIIYQQRcemTRS0U6cCH3/MCqtZWcxMl5rK7HovvhjoXhYeErnCC2OAxo25DBvmXXJ8504uDpdeyry8Z5/N7DSK9gohhBDBQ3o6a3NMncplxQpuT0zkKG7lysBtt3EJRSRyRZ54enJffdV9npEBHDpE387rr3O/1q1dT2+XLvTyCCGEEKL4SE4GJkygqJ0+ndfq6GhmRrjuOtoQGjcOjzk38uSKkyItDZg3j37eX34B5szhtqgoVqkcPtwtjiOEEEKIwsVaXoetBTp2BHbvBmrWZKXnfv2A884DevYM3cCTPLmiyChVihUwu3ZlEZ2UFODPPyl4Z850K68tWwbcdRejwS1aBLbPQgghRElm715g1SqOmhrDgnWJicCPPzJT0qpVrFodDtHavCiQyDXGnAvgVQCRAN631j5TpL0SJZbYWKBXLy6e7NvHxfHtfvQRMH68a29o1QqIjCz27gohhBBBj7WsbOp4a+fO5aTwPXs4cjpuHNOEOjRuHLCuBhX5ilxjTCSANwGcA2ArgPnGmO+ttSuLunMidOjenT9Qh6wszvJ88EGuV6wI9Ojh5uht1kx3oEIIIcKX5GTgp59cYeuk92zbFnjsMVoRIrJLerVuHbh+BjMFieS2B7DOWrseAIwxXwIYAEAiV5ww11/PZccO18/7yy/ehSnOOw/48MOAdlMIIYQoFqzlEhHBfPfXXsvsCOXLA336UNT27cusRqJgFETk1gGwxWN9K4AOvjsZY24CcBMAxAdL0WIR9NSqBVxxBRcA2LjRFb2Onxfg5LVOnTiRTQghhAgl1q+nze/554GBA4EzzgDuuYfCtlMnZkcQx0+hTTyz1o4BMAZgdoXCel8RXiQm8u712mvdbVlZQJkyQEwM15OTgQ4dONnNsTdUrx6Q7gohhBDHxYYNtB9MmUKbwejRrE7arh3z1gJAkybAs88Gtp+hQEFE7jYA9TzW62ZvE6JYiIgAvvzSXd+3D2jYkNvGjOG25s0pdn0LUxxviWMhhBCiMElLA2bNcr21//zD7Q0bci4KwMljX30VsC6GLPnmyTXGRAFYA6AnKG7nA7jCWrsit9coT64oDjIygEWL3HRls2YBR49ywlqbNsCFF7IUcUqK+5rYWApjCV0hhBBFyddfA59/DsyYARw+zJSbPXrQgtCvH9CoUaB7GBrklSc3Ir8XW2szANwOYBqAVQDG5yVwhSguoqKA9u2Bhx4Cpk0D9u8Hfv8dGDECKFsWeOcdb4ELcP2BByiOk5MD028hhBAll7Fjaa2LiODj2LHc/s8/wMiRQGYm13/9FVi4kEGV77/nKOS0acwZL4FbPKjimQhZIiI4UzUvatZkwuxGjbwfTzkFKF26ePophBCiZDB2LHDTTd4BlDJlgPfeY673K68E/v6baTBTUtimdJhFiyqeibAkPp65eH2pXRt47TVgzRpg7Vo+TprEUogONWu6OQk/+IDDTEOHcj0ry81NKIQQIrSxFti6FVi6FLj99pwjhEePcu7HqlVAUhLzvgO0x4nAIpErQpbRo3PeccfGAs89xxQtvhw8SNG7dq33az7+mHkKHZHbpAkfPaO/zvN69SSAhRCipHL4MEVrtWoUrAMHUtweOJD36zZvZtS2TJli6aYoIBK5ImRxJpcVNLtChQqsJNPWZ9Dj99+B1FQ+txa47DJ6r9asoefKUxDHxHDG7JVX0isMAH/+yRKLVaoU6r8nhBDiBMnMZG7apUtpJ7j4Yp7fExKASy4B3n6bWXoiInjOP/10oEUL5nTfsiXn+6k8QHAiT64QJ4G1wPbtru3BeezeHbj3XuDIESAuDnjqKZZh3LOHw12+PmAJYCGEKBr27gWWLaOgXbqUz5cvdwMUrVtzMjLAkbtTTmEedn/48+Qqa09gycuTK5ErRBGSlsZob/36FLPLlzO12caN7gxcgAnAPW0PgwYBp50WoE4LIUQJZf16jrT168f1iy5yy8UDQNWqjMp6Lk2bHp/NQPnXgwuJXCGCjLQ0Vr3xjQCvXcuhsK+/5vDZL79weGzKFJZ5XL0aWLHCzQAh/5cQItywFti5043MLl3K7AalSwP33Qe89Ra9tZGRwCefcATNEbQ1aijbQaih7ApCBBmlStGn27hxzraUFHfyWqVKwHnnAXXqcP3bb12vrzGc6OZvAtwpp/AE74siEEKIksTRo7yx9xS0y5ZxUphDnTrMftCwIe1g117rCtmrrw5Mv0VwoEiuECWIw4cZzfWNAK9Z4z37d8cOpkH76itgzhzgpZeAceOAG2/kRcNBXjIhRDCxbRvw4Yc8JzVoAHz0EXDddWyLjWUJd0+rQYsWtHuJ8EWRXCFChLg42hbOOMN7u7WcXOGkQKtRg9uXL2eFnYgIRnA9BS7AqPFNNzGDRKVKvFjUrs3sEAAtFaVKuZFkIYQ4WQ4e9J4ItnQpcMstwFVXsW3ECPpkGzQAzjmH9q3TT+e6UjSK40GRXCHChLwqwNWowZKT6ekc8lu7ltt79gSOHQNmz+Z6167Arl2uIPZ8dJ43aAB068b9k5IozFU9Tojw5Ngx2qw8Be3mzW57pUoUsLfdBgwezAm5R4/yvCFEQVAkVwiRawW4hARme7CWkd0jR9y2xx4DMjLc9W7dOHt53z43crx/P60SWVncp3dvV+S2awd06QJ89hnX27blZLncBHKlShTZTl33jAwgqgjPUvIoC5E/BfmdHDkClC3L57fdxpvd++6jN3boUJ5fmjQBOncGhg1z7QZ16nhPBIuMlMAVhYdErhBhQm4V4EaP5nNjeJFyLlQAcNZZOd/DH1lZwKFDFLyejBjhWh0yMymo9+2j2F6yhM8PH/Z+zc03A++8w/1jYoBRoyi2k5KYpN2fMPZ8TEgoWN5h33yXmzZxHZDQFcLB3+/khhtocYqLc6OziYnAvHncZ+tWV6iWKsX2+vX5exaiOJFdQYgwIhgjl+npFMf791P0Vq7MrBNpacAzz7CwRvfuvHBedpn3vseO5Xy/Z58FHnyQfuJ27YB332VpzpUrgf/8xxXIb72VU5QDzFixeTP7lZbGyLN8gKIkc+AAfy/OSE1KSs7nKSlu5HXcOOCvv4CXX6Z49TcCBNCG1Lw5J3+1b09frRDFjewKQggAFLSBFrW+REcD1atz8aRUKeCJJ9z1unVdb7DD0aO8eDuid/9+t4hGTAxw6aWM7AJsmz/fFcmOvcKXrVv5OHMm0KcP/2bnzsCXXwL330/RGxvLxd/zBx7gUO3y5cCMGZwZXr48M2D8+2/ur4+N5WcRKILxBiiQFPXnkZXF49dTaCYkcCRlyxaKzD59GBGdOxf46Sf/4tTz+aRJ/J289BLwyCOcxBUTAzz+OPDGG/n36bbbeEwuXw5Mn85tnv5ZT4xxc9EKEaxI5AohSixlytAO4S/7Q+3awJtvuuudO7sT6rKyGKHyV4O+dm0+NmzIqHD9+lyvWxc491zvyNfRo0zX5jx3slUAwJ9/AvfcQ4tF+fKMjo0cmff/ExlJf3TduhQlb73FWeiRkVz/5ZecwthXNF99NSPPq1dT5LRvz/feu9f9zEqX9o5Oh6J1wxmkNIbP09P56CxZWTnXS5fm8tlntM042UicIfrVq3kc5SY0Bw1iidiVK2mxGTWKUc6pU1nm23P/1NScfZ45E+jRgzdWV1wBrFpFH+uff/KGLzqa33PZsu537jz3tOi0aQPcfbf7GQwZQj+85/6+j7Gx7gTR0aNda1JuXv74eAlcEfzIriCECEuKugZ9RgYjXeXLU1Du2MGomKdA9nx0nj/0EKN3//sfUyd9+SXf78kngQkTcu7vadmIjKSYMwa4/npG4xwh37cv8OOP7r6lS7viZudO7wmGDnFxQHIyn195JW8S/IlE5/nppwOff879BwygEHr9da63bctoe15Cs29f4IMPuP+pp7Lq3zPP8H+sXj3vv20tJzS99hr3L12aQu2RR3jj4Nys5IWzf926zNd6PEREAB9/zElWS5YwHda77wIdOzJX9Suv5BSmvs979GCmk/37OaJw6qmMxKan828EItJf1L8TIU4W2RWEEMIH5wJdVEPSUVFAxYrueq1aXArK4MFcHJ54wtu+4eCkXHIWZ6b6Aw8A11zj7nf77UC/fjlFckoKk+/7w3NSYFwcvczGUNAZk/N53bru/qeeyoIkDu3a8f38vc5Zb93a3f/CCxmRBCjenSpWuf3tiAjgzDO5f1QUJz127cr1SpXox87rbxvDKC0AbN/u//MwhlFWf0I1Otr97Fu14mQrh44duRQUxzfuEEgbS1H/ToQoShTJFUKIMCe3yUVOerlwQ5+HECWHvCK5mjMshBBhzujRjEh64pleLtzQ5yFEaCCRK4QQYc6QIfRYJiRwyD0hIbw9l/o8hAgNisSuYIzZAyCXzHpFSlUASQH4uyL40bEh8kLHh8gNHRsiN3RsBAcJ1tpq/hqKROQGCmPMgtx8GSK80bEh8kLHh8gNHRsiN3RsBD+yKwghxHFgjOlijPnTGHPQGLPPGPOHMaadMeYaY0ymMeawz1I7+3UbjTFpxpiqPu+32BhjjTGJPttHZm/v4LO9lDHmRWPM1uz332iMeSW//hXdJyKEEMGJRK4QQhQQY0x5AJMBvA6gMoA6AEYBcLLVzrHWxvksngmpNgC43OP9WgDwmeIEGGMMgKsA7Mt+9ORhAG0BtAdQDkAPAIsK2D8hhAgbQk3kjgl0B0TQomND5EVBj49TAcBaO85am2mtPWqtnW6tXZrfC7P5DN6i9WoAn/rZryuAWgDuBHCZMaaUR1s7AN9Ya7dbstFa67zHyfZP5ETnDpEbOjaCnJASudZaHXDCLzo2RF4cx/GxBkCmMeYTY0xfY0ylfF/hzVwA5Y0xpxljIgFcBuBzP/tdDWASgPHZ6+f7vMe9xphbjTEtsqO+hdU/4YPOHSI3dGwEPyElcoUQoiix1h4C0AWABfAegD3GmO+NMTWydznTGHPAY/nXz9s40dxzAKwC4FVA1hgTC2AwgC+stekAJsA7+vs0gGcBDAGwAMA2Y8zVBeyfEEKEDSGVXUEIIYoTY0wTMBK7FsA0ADdYa7vksu9GADdk7/s7gDkApgAYByAdQH1r7UZjzBDQU1vTWptmjOkGYAaAOtbaPT7vWQbAdQBeA9DcWrsqt/5Zay+HEEKEESETyTXGnGuMWW2MWWeMeSjQ/RHBgTGmnjFmpjFmpTFmhTHmrkD3SQQXxpjI7AwHk4/3tdbafwB8DKD5cbxmEzgBrR+AiX52uRpAHIDNxpidAP4HIBrAFX7e66i19k0A+wE0LYz+CWKMqWiMmWCM+ccYs8oY0zHQfRLBgzHmnuxrynJjzDhjTOlA90nkJCREbra37U0AfcET/eXGmBwnfBGWZAC4z1rbFMCZAG7TsSF8uAu0DeSLMaaJMeY+Y0zd7PV6YLaEucf5N68HcLa19ojP+9cB0BNAfwCtspeWoD3hqux97jbG9DDGlDHGRGVbFcoBWFyI/RPAqwB+tNY2Ab+DAh0jIvTJ/p3eCaCttbY5AMdfL4KMkBC5YCqdddba9dbaNABfAhgQ4D6JIMBau8Nauyj7eTJ4oaoT2F6JYCFbDJ4H4P0CviQZQAcA84wxR0DxuBzAfdntHf3kyc2Ro9Za+6+1doGf9x8KYEl2RoSdzgLaEU43xjQHkALgRQA7wWpLtwEYaK1dX4D+iQJgjKkAoBuADwDAWptmrT0Q0E6JYCMKQBljTBSYBnB7PvuLABASnlxjzCAA51prb8heHwqgg7X29sD2TAQT2cn2fwe9i4cC3B0RBBhjJoATucoBuN9a2z/AXRJBgDGmFZgeaiUYxV0I4C7fyLsIX7Ktb6MBHAUw3Vo7JMBdEn4IlUiuEHlijIkD8DWAuyVwBQAYY/oD2G2tXRjovoigIwpAGwBvW2tbAzgCQHM9BAAgOzXfAAD1AdQGUNYYc2VgeyX8ESoidxuAeh7rdeGTlkeEL8aYaFDgjrXW+pvoI8KTzgAuyM568CWAs40x/nLWivBjK4Ct1tp52esTQNErBAD0ArDBWrsnO83fRACdAtwn4YdQEbnzATQyxtTPrgx0GYDvA9wnEQRkJ8r/AMAqa+1Lge6PCB6stQ9ba+taaxPBc8Yv1lpFYwSyfdBbjDGNszf1BK0LQgDAZjAndmz2NaYnNDExKMlX5JaEFEzW2gwAt4N5KlcBGG+tXRHYXokgoTM4medsY8yS7KVfoDslhAh67gAw1hizFMxy8d/AdkcEC9kR/gkAFgFYBmopVT8LQvKdeGaMqQWglrV2kTGmHGjAv9Bam+tdbdWqVW1iYmKhdlQIIYQQQghPFi5cmGStreavLSq/F1trdwDYkf082RjjpGDKVeQmJiZiwQJ/2XGEEEIIIUTIMXIkl2LGGLMpt7bj8uRmp2BqDWCen7abjDELjDEL9uzZk+O1QgghhBAiRBk1KtA9yEG+kVyH/FIwWWvHINuT0rZt25KffFcIIYQQQuROVhYwezZw4ECge+KXAkVylYJJCCGEEEIAAJYsAe69F6hYEejeHRiQXWTWGC4BsC34I99IrlIwCSGEEEKEMdYCy5YBp50GREcDX30FvPkm0LcvcNllwPnnA3Fx3C+IKEgkVymYhBBCCCHCjdWrgSefBJo1A1q2BH75hdvvuw/YtQv49luK3LJlA9rN3ChIdoXZAEwx9EUIIYQQQgSabdsYnV28mPaDrl2BO+4A2rZle9WqOV8zYkTx9rEAFHjimRBCCCGECEF27AD+9z8gIgK4/XagZk2gdm1g6FBg8GCgbt383yNIfLieSOQKIYQQQoQbe/cCX38NfPkl8Ouv9NP27UuRGxkJTJ4c6B6eNMeVJ1cIIYQQQpRQkpPdyWH33w/cfDOtCU88AaxcCUydGtj+FTISuUIIIYQQocqRI8D48cDFFwPVqgFLl3L78OH03P7zD60Gp50W0G4WBbIrCCGEEEKEGtu2MVo7aRKFbq1awC23AOXKsb1Jk8D2rxiQyBVCCCGEKOlkZDDFV0YG0K8fCzX8+Sdw5ZVM89W1K722YYRErhBCCCFEScQpq/vll8yOkJQEdOlCkVu2LLBxI1OAhSkSuUIIIYQQJQVrXeE6dCjwxRdAbCzz2l52GXDuue6+YSxwAYlcIYQQQojgximr60Rsf/uNeWyvv57i9vzzg7bqWCCRyBVCCCGECEb27AHefpvidtUqemp79QIOHKDIPfvsQPcwqFEKMSGEEEKIYGHjRkZtASA9HXjySaB6dYrdHTuAH38EmjYNaBdLCorkCiGEEEIEku3baUP48ktg7lygTx+K2dq1gZ07gapVA93DEolErhBCCCFEoBg2DHj3XfpuW7UCnnkGuOQSt10C94QJLZE7ciQXIYQQQohg4+BB4LvvgIkTgbFjOVmsfXugRg1mRgiDAg3FibFODeNCpG3btnbBggWF/r75Yoxbk1kIIYQQItCkpACTJ9OKMHUqcOwYkJAAfP89cPrpge5diccYs9Ba29ZfW2hFcgHWZ65dm+XratVi7jghhBBCiOLi2DFGbatXB9asAS691C2re9llQIcOYZ/Dtjgo+SJ35Ehg1Ch3/dJLvdsfeAB47jkecDfcwIPMUwQ3acKDUAghhBCioPhaJNPTWVb3yy+Bb74BLroI+OgjoGVLViU788ywK6sbaELHrmAtEBEBLF3KFBvbt/OxbVvgnHM4O7FjR25PS3Nf99xzFMIbN7JKiCOAa9fm0rcvhXB6Ol+nZMtCCCGE8LRIPvEEU3wlJQHlywMXXwxcdRVw1lmB7WMYEB52BSfs36IFF19q1gQ2bOABuX8/xe727UDDhmzPyuLrduwA5szhY2qqG+3980+gRw+gXDlvITx8OD01u3YB//zjtsXFFdu/LoQQQohiIjWVab4AIDOT0dnMTBZpuOwypv8qXTqwfRQAQknkAsCIEfnvYwxQuTKX5s3d7Q0aMEedg7WsKBITw/X4eKb1cCLE27fzID9yhO0zZgBXXum+vlw5it2JE4FmzYCFC4Fff/UWyLVqcT8hhBBCBC/r1wOff077wcaN7vaobBk1YgQwblxAuiZyJ3TsCoFmzx5aJTxF8I4dwMsvU9A+/zzw4IM5X7dpEwX02LHApEmuTcIRwl27uj+i40Up1YQQQojjIz0dWLAAmDmTNsY2bYCffmKEtmVLWhDOOgu44AJldAoCwsOuEGiqVQN69sy9/f77gZtucm0SjhCuWZPtu3fzR7VjB9ONAIw6O/7h++5juhHPKHB8PHDPPWzftYvDI+XLu9aNUaMkcoUQQoj8OHIEeOMNCtvZs91R2rJlKXK7d6fftnLlwPZTHBf5RnKNMR8C6A9gt7W2eZ47ZxOWkdzCwlogOZkCeM8eRnIBDpFMn+4tkCtUALZtY/v55zMPX2ysK4RnzXLvMg8dojVCKUuEEEKEM5mZwN9/U9CWLw/ceCOQkQFUqQLUretGart3z7vamEZLg4K8IrkFEbndABwG8GmwityxY4FHHwU2b2Zwc/RoYMiQYvvzgSMlxc0DPG0asHw5MGGCa4j3pEYNplFr1sxdzjyTlVaEEEKIUOejj4BvvwV+/51zbgBaDr77js8PHaLoFSWKkxK52W+QCGByMIrcsWPpAnBG+AHqvjFjwkTo5oVnepP33gMWL6YQXrEC2LePOYW//JLt55/PO1hPEaz8wUIIIUoa1gIrVzJSu2wZ8O673H7ZZbQFOpHaHj046ilKNMUico0xNwG4CQDi4+PP2LRp04n19jhJTOTcLV8SErwnQIYluZU5tpYe3tRUfoApKUDv3hS/zt0twPD4f/7D/T76yBW/VaoU138ghBBCFIzffgPefJOZjPbs4baEBAZ4KlXiaKaTMUmEDMUy8cxaOwbAGICR3MJ63/zYvNn/9k2bgCef5I1a+/ZhmrIut5RqxrgT3gCGvmfPpvjdvp1id8UK18qwahVw663u/jVqUOw+/jg/4KNHKYQrVSqyf0UIIYQAwGvVunUUszNn8lp02mmcozJnDjMi9OjBaG39+u7rJHDDjhJvV8gtkhsdTR+5tTyuO3akh/zyy4HGjYula6GDtcDWra74dSwPzz3HE8nkybQ71K7tbXe48MK8TftCCCFEQdm6FXj4YQpbZ9J1zZocaTz3XE4oi4jQBOswI6RTiI0enbsnt18/Bih//ZWjGE89xaJmjRszODluHAOUnkFN4QdjgHr1uJx7bs72Jk2AZ591RfCYMfxCOnWiyB03DvjkEwrf5s352LSpqsIJIYTwz+bNFLMzZ3KS9C23MEPQzz8z65Djqz31VFfURkYGts8i6MhX5BpjxgHoAaCqMWYrgBHW2g+KumMFxZlcllt2hfPP5wIABw8CpUrx+V9/Af/9LzBsGNe//pq2ne7dqc3Kli3e/6NE07Chd6GLrCwaouPjuZ6ezjzAv/1GWwPAk9KBA5zJOn0606I1a8YhJ334orhRKiAXfRYikNxxBzB1KiuMAZwD0qABnztpMxWpFQUkrCueJSe7VXUffBB46SWOdkRFAe3acSS+e3egc2cFHQuFzExgwwZGezdsAO6+m9sHD2bqM4Anr8RE3rl/8QW3bdnCiHCZMoHotQgHcpukGY7osxDFwc6drqf24EE308+gQfQaOpHa5s1pQRAiF046u8LxUlJEri/JycAffzDg+OuvzDSSkcERkLZtgfPOo79dFDIZGcC//7p2hxUr+KGPHcv2Dh2A+fN5N+/4fTt35hdSUBSdEr7s28fhm337gEsuAT77jKMQ55zDgir//suh0cxMbneWSy+lx2npUkacPNuysoDbbmMFxNmzWarbt33kSE7SnDqVOTt92995h56rsWP9t3/3HS/6r73GKoiebdHR7DMAPPEEMGWKd3ulSswRCnAYa/p0ty0zk6Mvc+ZQ5D71FEdkatRwF+cGFOA+iqiJ4+W991juftUqrpcvD5x9NodTJWbFCRDSntzCpFw5Wk4d2+nhwzzfO55eT91+5ZW8ybz++oB0NbSIiqJRunFj4OKLc7Y/+iiwaJErgKdMAQYMcEVujx4steg56a1xY9ebAqjEcajiVAhMSmLKoKQk+r3r16dIHT3au23PHvrDBwzgsKgzWgAAQ4e6j59+CixcCNx8c86/eeaZFLkLFnASjC+DB1PkLlkCvPoqb9giItzlgQcoNtes4aRNz7aICJbyjo1lpGvlypztWVnufqmp7vaoKO9jvkIFTgb1fG2FCm77qafyJBcRwepPf//tTuZxxGtcHLOnZGZyvV07er2cz2HrVorf6tX5eOaZrgdszhxaj2rU4EiM/JLhxb59vHDOnMmL6LRpvHkEeLN0zTW8iLZuzWNXiCJAkdzjwAlcpKcz2HPeebxeHTzI9W7daG/o2hWoWDHQvQ1h0tL4oVerxijwpZcy48O6dRQAAAXMa6/xyxo9miJ30iTeyZQrx5Ns5crusKwiUsFBZia/s9KlKeCmTKEw9RSpgwcDF10ErF3Locy0NO/3eOMNRlOXLwf69OFx4rlccw0vrPv2MVF85crA6afz/SIieCEuU4aTJw8c4DZPoVquHC/KGRnsq2e7MSX/WPK1K2RlAfv3M7d2RgY/K4DZVVav5nZn6dyZE00BCl8nV2lEBIXuVVcBzz/PbY8+StHtGSmOj1ce7pLOX3/x5vDvv3kclSkDdOkCvPIKb0BFyBHoqrOyKxQxa9ey9PXcucw1bQzQqhUFb48eFL2VKwe6l2FAaiovuitWcDLc1KkUt/547TUK4WXL+GU54tdZRo4E+vZlNPCllzik5tnesyezTRw6xF92uXLuPopKuBw96i1Q9+xhZb0ePSieBg70btu/H7jvPgqh5GTvEpsVKlCk3ncfZ1ofOsSzadWqroCtWpXf/fEKJflQXQrrs5g9m9FoTxHcrh1www28OahUCThyxPs1994LvPgibzDatvUWwNWrs2hNu3YU29u2cXtYJkEPEJ62r0OHgFmzXF/tsGEc2ty4kY9Ontr27b1HGERIEQxVZ2VXKGIaNeLvPDUVmDfPtTe88w5vXo1h8OODD4AzzpCVrcgoXRpo2ZILwJPryJEUWrGxjDAkJ3Npnp3yuUoV4JFHeMJ22pKT3aThO3ZwQkRyMi/MDpMnU+T++iuHvn37MX06726mTWPVOE+BXL48hVqdOsA//9CK4Suy69env7KoOBGPclYWI5tHj7LvAKN2Gzd6R1tbtgSeeYbt9eoBe/d6v8/ll/MCGBHB4iOxsfyBOCK1a1fuFxfHaFC1avyefC+U5cszdV1hkFvhlHCksD6LLl1yb4uOplXi8GFvEewk7k9NZaaVXbtoG9m1i7/BMmUoctevdxOely/vCuFHHuHN6a5dwDffeIvkGjVObAZxKPv5rXVHJKx1M9ts20bVkp7OJS2Nn92oUbTodO9Ou05mJn+XZ57pDl8mJrq+cBGypKfzJ/noo94CF+D6o48WbzQ3NxTJLUKOHaOuckTvJ59QG7zxBktpz57N4FRGhoJ/RU5hRKeOHXNFcPXqvCBs28bZip4COTmZw+UJCRS7Tz+ds33ePA7dvfIKcM89Of/Whg28WDz3HKOVviJ44kQePJMn80Dybb/oIg6h79zJfjvbHeHsfB67d1PIe0Zbo6JcX+WwYYzW7NlDsZqZyQvanDlsb9mSE7DKlnUjqmef7YrPMWPYD89oa40a3hFaIQrC0aM8ZmNjaTWZOJHHr6dIfughWlR++YWjLb5MnMjfxrx5/F36iuCzzuINlWdRgbzOHVlZ3kKwYkW+bt8+/p7S0ty29HTmp4yIoJVm7Vrv12ZmMsoNcBRq8WLv10ZHM+8lwIvInDlumxMZ//xztt95J3+3nn+/YUNgxgy2n3022zMy3P/F83fdvDlHxDzp1Yuvt5a2k/h4fl4dO/I7ESHPvn3ADz/Q+ffjj5yv+/77/n8exrjuwaJGkdwAERPDoFTXrt5ZGWrXpjZw5oAMHcpRc8fe0K0bz7eiECmM6FRMDBfPKm516vCXnhu9e3PJjeuu40xHXxHsHACtWwPXXpt7pPmPPzhT2dOXaow7Uejxx3kWcihd2s05CXCc6bvvvPsUH++K3HLlGDHr3NmNtJ5yirvvjBmM8OSW3u2mm3L/34U4HjyPscqVXUHoj27dOCnOUwDv2uWO8hw6xGjwnDm8gXOu0vPmUeR++il/A87vMD6eYnHOHN58vvgiMHy4+ztz2LmTr3n5ZY7g+HLkCAXh++9zUqInxrj/09dfAx9+yOdRURS4Vaq4Inf1akZQoqMZSY2O9o6UVK7MPjtt0dEcVXEYNIii1mmLjqaNyGH0aJ5noqN5YzB+vCuQnWHIESP830iIkMFaHmqTJ1PY/vEHD/nq1ek0GzSIcRx/VWedNPmBRpHcIODtt6kzZs92LWqnneaK3u7dVZVN5INnlPnIEdeOMXcuZ+gnJ/Mgmzkz52sHDwZuv92NtlaurJnwInzIzGTUddcuRjtjYznJwvPm0OG++4AXXmAatmnTXBHpPN5wA0c1/v6bkVBHQDrtZ51FMbplC0dGPNuioxkBMca9aY2ODi5vm7zrYcPLLwNvvcX53ADvD/v3Z3Gtdu3cbG/B7smVyA0i0tNpz3TsDbNm0bIGcHTok0/4PCnJO5goxHGji5UQBUO/FRd9FiHLX38Bb75JcRoTQ/v13LkUtf375x2ZDebsCsq8HERER7PuwfDhtGTt388D77nnOJcC4ChbzZoMJgAM4G3d6v0+Y8dyRC0igo9OTQUhhBDihNEEzZBh9WrqCKcmx+7d9Nk6kdsRI+i/vfXW/K0HQ4Zw/nFWFh+DYcKZgzy5QYxTXrhdO3ebtRS9Z5/N9VmzmKO3QQNaG6KjaSc7epTtmza5tshgOvBEgNHFSoiCod+KS6hmmQgD0tNpiXT8tWvXcntMDO2RfftyDnKoFZ2TXaGEs3UrMGECLQ6//87orz/i4/2bw4UQQggReuzf750N4cABWsDPOsu1ISQkBLqXJ488uWFCVhajv3ml8/jjD04Avuii0LtjE0IIIcKZffvcYp516jA6W706K7T278+R33LlAt3LwkUpxMKEiIjcI7ZO9ph332We7osv5vprrzFV4plnAm3aqHiQEEIIUVLIzHST4dx8M1N6rV/PwNbrr1Potm8fvkEtidwQY/Ro/+k8nPSK773HGZBOVpqvv6bNAaCft1UrCt6OHfmYmBhcGWyEEEKIcGb/ftoPHBvCsmUUsxddxOKRmZkc1R04MNA9DTwSuSGGM7kst3QeMTEsQ+zw228czpg3j+lC5s5l+eHXX2d79eoUzU89xfXUVEV7hRBCiOJkzRqK2smTOeE8M5NpzQcMYJYlgHWFhDfy5IocZGSw6qQjetu0YZXII0dYdOe557iemkprRKNG4TsUIoQQQhQFe/awQvqkSRS5ANCiBSeNnX9+eNsQPNHEM1Eo7N8PvPIKU42ceSajwD16sGR6hw6uzaF9e5ZwF0IIIUTB+eorVrC+4ALmxa9bF+jUKbSyIRQ2ErmiSNi1C5gyxY34Ll/uZnY47TSK3jPPZH3rypUD21chhBAi2FizhtdOZzJ427a0CU6dyvVjx2gzFLkjkSuKhUOHgPnzXdE7dy5LEK9dy5LwU6YAf/4JPP64fL1CCCHCj4wMpvKcNMm1IZQpw9RfpUsD27cDNWq4GRNE/iiFmCgWypcHevbkAjCq+++/wCmncH3+fOCjj4D//IfrDz8MbNniRnxbtmSGByGEECJUcLIhTJ7M4gz79/Nad9ZZwB130IbgBH5q1w5sX0MNRXJFsZKe7grZu+8Gxo9ndgeAP/K2bV3R27GjfvBCCCFKHllZnBT29dfApZcyG0LVqizKcP75QO/eoVeUIVDkFckt0Lw8Y8y5xpjVxph1xpiHCrd7IpzwjNS+8gqwbRszNHz1FTBsGE8Er71GH2+dOq5PCQD+/psZHYQQQohAMXYsc8hHRPBx7Fi3bccOoEkT4LPPuN6uHfDAA2610Y8/Zv5aCdziId9IrjEmEsAaAOcA2ApgPoDLrbUrc3uNIrniZDh2DFiyhJ7eqlWZ4/fYMdoh7r6bKVVSU4GJExntVcEKIYQQxcHYsTkLLkVGMuvQpEm06V1+OXD11dwmip6T9eS2B7DOWrs++82+BDAAQK4iV4iTISaGKck6dPDe/tVXrr930SK3wEX16t4Wh7Ztgbg4to0dm3thDCGEECI/rAW2bmUWhNtv9xa4AEcgncqhxgBffln8fRT+KYjIrQNgi8f6VgAdfHcyxtwE4CYAiI+PL5TOCeEQEwNceKG73r49sHgxMGeOm8nh++/ZFhHBhNlVqjCbg2Nx2LSJd+CAhK4QQgj//P03l6uu4voFF3DSWF4kJxd9v8TxUxC7wiAA51prb8heHwqgg7X29txeI7uCCAR79wJ//eWK3p9+cvP2epKQwDvtbdv4PDGRgliWByGECA/27wdWrGB01nn87jva4h59lLa4I0cYYJkwgdXHmjcHrriCUV1fEhKAjRuL/d8QOHm7wjYA9TzW62ZvEyKoqFKFHijHB5VbucPNm4H33wc++MDdFhtLsZuY6ArfhARg8GCVTRRCiJLM5s3Azz9TyDqidpuHiilXjgJ2716K3Dvv5FKqFNsHDXL3feaZnJ7c2Fha4UTwUZBIbhQ48awnKG7nA7jCWrsit9cokiuCgcREWhR8SUjgUNSGDWzfuJGL83zTJibmLl8eOHCAEd4bb+T+M2bwPcaPZ1JvRxjXrCkxLIQQgSI1ld7YsmWBVauA4cOBJ58EWrUCvviCFrUyZViNs3lzd2nWDKhX7/hG8jTXI7g4qUiutTbDGHM7gGkAIgF8mJfAFSJYGD069zvuChV48mvVyv9rk5OZ7sU58bVp452zd9QoYKXH1MtSpXiyc6LAiYksbnH++Wy3VnYIIYQ4WdLTgXXrvKOyy5ezsubLLzMCGxPDoMT+/XxNv35sr1+/cCqJDRkiUVtSUDEIEdIU1R33kSOM+HpGfz0jwjt3An36sMoNwLyJvXszBzDAftSu7QriunVV7U0IIRyyshigiIsDDh5kHvXly4HVq4G0NO5jDEvGO1HZ889nXloRXuQVyZXIFaIISE0FDh1iejMAGDkSaNoUuOQSRokrVPCeFBcRweIXnpHgc84BunXjfmlpjE4IIUQoYS39scuXU9j268ft8fGcX/Huu7QhNGvGFJKeVoMmTWhBEOHNyU48E0IcJ6VLu7XIAYpch3LlKIK3bPEfCZ41Cxg3jpHdbt2A3bvp+X3vPeCGGziz9623vCfIJSQU7GQvL5kQIlDs2ePaDDytBgcPsr1VK1fk3n8/0KABn0dGAv/8E5AuixKORK4QAaBUKUYlnOIWvmRkuENyUVH0ALfNvk9dtw54/nnu40n16t7C99prGT1OTeW+333n7VFW3mAhREE4kZvjFSs4b2HwYK5fcAErgjlUrsxo7JAhjNI6k8Ac7ryz8P8PEX7IriBECSQzE9i+PWck2PNxyhSgVy/gm2+Aiy8GatViXXVfKlcG3nyTFoqKFd3HGjUKZ5KGEKLk4q+MbWwsMGYMC/SsXOmdb3biRI4q3X8/8MYbnL8QGQl8/jmQlOSK2Zo1NRlXFA7y5AoRZmRl0evmDPN9+y3wyCP+i2Pkxr//crjwzTeBV14Bli2jBePjj1nC0lMQez46z+vX10VMiJJKSgqtUb160VrlS1QUb7adc0pMDEeOvvmGI0lbt3IEKSFB5wFRtMiTK0SY4Zmzt0kT4KGHgHfe8Z83uE4dYPp0+uIOHHAfa9Rge926QIcO7sS3f/9lNbkDB4DDh3PvQ0YGRfY99wC//soyzADwn/8w+pOXSK5SBWjc+CQ/hAIgj7IoqVgLHD3K36DnEh/P3+y+fcBXX3ECa8OGzB376qs59z9yxHt97FiO/MyaBZx7bu4CNSODeWidyOwpp3iP/NStWzyfgxB5IZErRJiQW97gZ59lBCY3Bgzg4vDUU1wAXugOHfIWxwcP8mLpXPDatPGehLd1KzB/vrtvenrOv5mYyDyXACeiZGYC06Zx/cYbWZkoL5FcuzbFPcDX+rNd+A7DyqMsPCnsG6CsLGYR8BWZvmLzjDOYVSAlBbj6av7NCy8E1q9nWkJPUepvZOaFF4D77qM14NZb+X80bEjR++23TMnlLBUr8ibXc1vDhnyfli352uHDcy9j+/jjJ/55CFEcSOQKESY4F+jCvHBHRdHTW7ly7vsMHeq9/s477nMnGnXwoLdI9rx49+9PgeBw4AATuzv7Jifn/Jtnn80yngDFbufOtFkAQM+enPj3++/egh/g+u23A9WqMa8xAHz2GdCiBWd+p6VRbEdHu0tUlPd6dDRQtSo/k6ws9jMujn/T+b+Ccfg2HKLax47xxsxZMjNzPo+OBv74w/8N0J9/smKWvwios3TsCPz3v3xd/frAwIEUnmlp/Fzz4447KHKjoznisW8ft8fFAe3bs6KXpyj1XT/tNO7foAHzdVeqxPXOnbleUGrWBK64gsesytiKkoo8uUKIEk1mpnc0+eBBTnxp357tL73EqNPAgbxg9+7NffM6RV13HfDBB3weEUHx99RTwK5dvPjnx6OP0pbh7P/mm4yqLVkCtG6dUxT7CuWRI4HLLqOYv+Ya4OmnmU5uwQL2Iz+RffXVHEL+919gwgTgqqs48fCff4DZs3PuP3s2JwkdO+b+D7GxFGsNGnDIu3RpYOlSLnmJxIwMzoyPi2MZ7J9/Zv8BlledNSv312VmcnFm4b/wAoXlxIlcv/NO4Lff8v7bNWpwEhTACOjOncDcuVxv3ZrfQV60bctUV/6sPRER7g1XREROgRkXB/TowWF8AHjsMf5N59j78MPcxamzLRiLwoTDDZAouciTK4QIWSIjGa1yIla+3Huv+9wY+okBWiL8CZl69dzKdACFYvnyfF65Mq0W6enukpHhvZ6e7qZCiovjpL0uXbhevTqHePN7fZUq3N9aCvao7DN1Sgr7nN/ru3VjH1aupB+7Vy+K3N9+A265pWCfa0oKxdq+fRyurlOHYnPUqPxfe801/N///JM3GaNHUxQuWAB8/TX/n6gofne+zyMjvctge0a9q1Wj6Pb3Oue553Fw0UXevvE77+Qwfm5/OyqKUfi+ff3/X9Yyb3VcHEV/fhH5//zHfW4McP31+X92wYjK2IqSiiK5QoiwJK/USKFyQc/MZHQ2JoZC7sgRilZfUXzGGf79ncZQ1LdoQbtFUhKj4HkJ1Kgo7huMloyCktsNUEICU/QJIYIHRXKFEMKHovAoBxuRkRTuDmXLcvElPt6/qIuPpwB2qFqVS6iT2yRN+VCFKFlE5L+LEEKEJkOGMDKXlcXHUBK4x8Po8gNv/QAAedNJREFU0d5iGAhvUTdkCCP6To7XhITQivALES4UiV3BGLMHgJ+4QJFTFUBSAP6uCH50bIi80PGBqpWB2nWA6FJAehqwfRuQtC/QvQoCdGyI3NCxERwkWGur+WsoEpEbKIwxC3LzZYjwRseGyAsdHyI3dGyI3NCxEfzIriCEEEIIIUIOiVwhhDhBjDGHPZYsY8xRj/Uh2fs0NcZ8b4w5aIxJNsbMNMZ0ym7r6rH/EWOM9XnP+Oz9Rma3dfD5+9cYY2YX/38uhBDBT6iJ3DGB7oAIWnRsiLw4oePDWhvnLAA2AzjfY9tYY8wpAP4AsAxAfQC1AXwDYLoxpqO1dpbH67Oz66Kix3tsNsYYAFcB2Jf9KIoXnTtEbujYCHJCypMrhBCBwhizEcAN1toZHts+A1DFWtvPZ9+3ATSz1nbz2JYIYAOAaGtthsf2bgCmAbgBwGsAallr07Lbrsn+m12K6N8SQogSS6hFcoUQIpg4B8D//GwfD6CzMaZMAd7jagCTsl8DAOcXUt+EECKkkcgVQoiioyqAHX627wDPv5XzerExJhbAYABfWGvTAUyALAtCCFEgQkbkGmPONcasNsasM8Y8FOj+iODAGFMve6LPSmPMCmPMXYHukwgujDGRxpjFxpjJRfD2SQBq+dleC0AWgP35vP4iABkApmavjwXQ1xjjNyekKDyMMRWNMROMMf8YY1YZYzoGuk8ieDDG3JN9TVlujBlnjCkd6D6JnISEyDXGRAJ4E0BfAE0BXG6MaRrYXokgIQPAfdbapgDOBHCbjg3hw10AVhXRe88AI7G+XAJgjrU2xU+bJ1cDiAOw2RizE7Q+RAO4olB7KfzxKoAfrbVNALRE0R0jooRhjKkD4E4Aba21zQFEArgssL0S/ggJkQugPYB11tr12RMyvgQwIMB9EkGAtXaHtXZR9vNk8EJVJ7C9EsGCMaYugPMAvF9Ef2IUgE7GmNHGmMrGmHLGmDtAy8HwfPpWB0BPAP0BtMpeWgJ4Ft6WBWOMKe25FMH/EVYYYyoA6AbgAwCw1qZZaw8EtFMi2IgCUMYYEwUgFsD2APdH+CFURG4dAFs81rdCQkb4kD17vTWAeQHuiggeXgHwIGgdKHSstWsBdAHF6UbQizsQQB9r7R/5vHwogCXW2unW2p3OAmZYON0Y0zx7v04Ajnou2RdeceLUB7AHwEfZVpb3jTFlA90pERxYa7cBeAFMG7gDwEFr7fTA9kr4I1RErhB5YoyJA/A1gLuttYcC3R8ReIwx/QHsttYuLIz3s9YmeqYP89i+3Frb31pbPjv3bQ9rbY4CDtbajdZa46QPs9Y+Y609w89+26210dnv+3H2a3yXDN/XieMiCkAbAG9ba1sDOAJAcz0EAMAYUwkcLXZyX5c1xlwZ2F4Jf4SKyN0GoJ7Het3sbULAGBMNCtyx1tqJge6PCBo6A7ggO7/tlwDONsZ8HtguiSBhK4Ct1lpn1GcCKHqFAIBeADZYa/dkZz2ZCI6oiCAjVETufACNjDH1jTGlQAP49wHukwgCsqtFfQBglbX2pUD3RwQP1tqHrbV1rbWJ4DnjF2utojEC2baQLcaYxtmbegJYGcAuieBiM4AzjTGx2deYntDExKAkJHxb1toMY8ztYFWgSAAfWmtXBLhbIjjoDHoblxljlmRve8RaOzX3lwghBO4AMDY7cLIewLUB7o8IEqy184wxEwAsAjP4LIZK/AYlKusrhBBCCCFCjiKJ5FatWtUmJiYWxVsLIYQQQggBAFi4cGGStdZvgZwiEbmJiYlYsGBBUby1EEIIIYQINkaO5FLMGGM25dYWKhPPhBBCCCFEIMjMBEaNCnQvchASE8+EEEIIIUQxsm8fMHEi8OWXQPnyge6NXyRyhRBCCCFE/mRmApGRgLVAq1bAli3e7cbwccSIgFgXfJHIFUIIIYQQ/jl6FPjhB2DcOGDpUmDVKiAiAnjtNaBePaBNG4pbYyh+gwiJXCGEEEII4c3ixcDLLwPffgskJwPVqwODBwNHjgDlygEXXhjoHuaLRK4QQgghRLiTmQn8/jvQoAGQkABs3w5MmgRccglw2WVAjx5AVB6yccSIYutqQSmSYhBt27a1SiEmhBBCCBHEWAvMm0crwvjxwM6dwGOPAU89BWRkUPjGxAS6l3lijFlorW3rr02RXCGEEEKIcCMzE2jRgh7bmBigXz9GbPv3Z3tUVN6R2xJAye69EEIIIYTInzVrmO5r1SpGbiMjgaFDgdq16a+tUCHQPSx0JHKFEEIIIUKRrVuBL76guF28mBkQunUDUlKA2Fjg4YcD3cMiJd+KZ8aYesaYmcaYlcaYFcaYu4qjY0IIIYQQ4jjZuRM4eJDPp00Dhg8HSpVipoQtW4Bff6XADQMKUtY3A8B91tqmAM4EcJsxpmnRdksIIYQQQhSIffuA998HevYE6tQBPvuM2y+5BPj3X2DuXODuu9kWRuRrV7DW7gCwI/t5sjFmFYA6AFYWcd+EEEIIIURupKcDAweyWENGBtCoEfDoo0CfPmwvV45LmHJcnlxjTCKA1gDm+Wm7CcBNABAfH18YfRNCCCGEEA5O9bG1a2lDiI4GypYF7rmHmRFat3ZL64qC58k1xsQB+A3AaGvtxLz2VZ5cIYQQQohCID0dmDGDGRGc6mN16gDr19NrG+bklSe3IJ5cGGOiAXwNYGx+AlcIIYQQQpwEmZm0HwDASy8xh+2kSSyrO306sHGjBG4ByNeuYIwxAD4AsMpa+1LRd0kIIYQQIszwrT72+uvAoEHAFVcATZsCvXsHffWxYKMgntzOAIYCWGaMWZK97RFr7dQi65UQQgghRDiQmgqMHAl89RUjtE71sVq12F6vHhdx3BQku8JsAHIxCyGEEEIUBmvWAKtXA+efT1H7/fdAkybAqFHAgAEhWX0sEKjimRBCCCFEUbNpE20I48ax+liVKizcEBUF/P03MyWIQqVAE8+EEEIIIcQJ8uKLQGIi8OCDFLMvv0xhG5Uda5TALRIkcoUQQgghCgun+livXsCff3LbWWcB//0vq4/NmxeW1ccCgewKQgghhBDHy8iRXAAgLQ343/9oRZg2jem/GjYE9u9ne5s2XESxUuBiEMeDikEIIYQQIqQxBli5EjjtNIrcmjWBuDhWHlP1sWIjr2IQiuQKIYQQQuRHRgZ9tLNmcQGACy5gpoRSpYD584H69YEIOUGDhdD6JpxhAyGEEEKIk+HoUeD331mkAQBuuw1o2xa45x5gYnbx13XrKGpHjgROOUUCN8gILbuCMcDWrUzLUbp08f99IYQQQpRMDh50o7SzZgELFgDp6cCqVcxhO2cO04B16QLUrUvNUQQaShwf4WFXyMriY926fIyLA6pWBW65BRg+nAfqAw9wm+dy6qlA7dqB67cQQgghip+tWylm27XjJLGZM4GLLmI6Lydi27WrW22sY0cuosRQ8kXuyJGsEOJL8+ZAo0Zuio6DB4EPPwSSk733Gz0aeOQRYPNmoEULbwFcpQpw3XVAjx7AgQPAL794t1eu7Oa4E0IIIUTwkpICjB3rRmo3buT2558H7r+fab5mzgQ6dADKlMn//UaMKNLuipMn9OwK+f0/x44Be/cCSUl8TEgAGjRg1ZFnnuF2z+W//wWuuILDFJ065Xy/8eOBwYOBRYt4wHsK5KpVWX+6dm3+uFJSgEqVgMjIovn/hRBCCMFJYosXU8zWqAEMGUKPbYUKQMWKjNA6S8uWCliVYMLDrlBQYmIoOn0tCjVrAq+8kvvrWrbkD8YRx44IbtGC7YcPA9u2ceblnj1Aaiq3//Yb/9a33/JHZgwjwI4I/ugjWiYWLOAdpK9Irl//xCuheObwE0IIIUKdl18Gpk5lYOrIEW4bPJjX3zJlOFGsXj2l9goTQkvkFuXQQWws0KpV7u3dujGa65CSQhFcrRrXzzgDeO01Vxw7QtmZIPfrryz358vmzfxBvvIK8O67OT3FTzzBH+6aNUw67YjjChVo45DIFUIcD7o5FiWBffuA2bMZqd2+nTYEgLbC3buBa65xI7WeQa34+IB0VwSG0LIrlGSs5V2nr11i8GBGn//3P1ojPAXy3r18TVQUU5u89Zb7flFRHK7JzGRKk8mTaclISOASH68MFEI4SNi5aMa4CGY+/hh44QVgxQqulypFD+2MGXyemSlLYJiRl11BIrckY6075LJ2LZf33qM1wpdGjdjuSZs2wMKFfP7RRxTMjgBOSKBvSYhwoCQKO2uZNSYz03spXZojT5mZnD3ubHduemvU4AhTaiotWL7tffrwvZ3PQ8O6orixlmm7PNN5zZzJ+TOffQZ88QUjtF26AO3bK2AT5oS8J3fsWODRRzmyHx/PhAlDhgS6V8WA58WnUSMu/fp5tzsXqvR0eoY3beIHtWmTt9H+zTddwevQsyfvjgFOyouMdCPBCQlA9epKfC1KHhkZ7rE/YwatPgDw4otsq1oVuP56bnv3Xc7A9hSBiYlMLQQAjz0GbNni3d6yJbcDwNChHEHxFKE9egD/+Q/bu3ShzcizfdAg4Lnn2F6rFsWoZ/uttwIvvcTtsbE5/79HH+X7793LvvryzDNMq7h9u//JtIB7bomK4km1Vi3OW6hVixlnWrcGDh3iZ1OrFm1SOheIEyU9nUtsLPDHH8CAATx+Ad6Ude3KSeMAf1NDhwaur6JEUeJF7tixwE030QILULvddBOfh4XQLSjR0bzg+bvoASxHuHs3P0BnqVLFbR8zBtiwwfs1l1/OO2oAuOMORoc8RXDduic+aU6I4+XoUWDHDl4c27Xjto8/ZgRo+3a2bd/OEYr163OmH7z/fj5Wq+aK3M8+A/76izd4UVF8PPNMV+TOncuJLE5bZCQnljokJ3OExGmLieGQqkP9+hSPTntkJCeiOlxyCW9UnbaoKKBzZ7aVKkUx69kWGen+7+XLAx98kLP99NPZXrMm8MMP3m2RkRTe1vIc8Prr/Nx27ODw8IwZQK9eFLl//OHeVEdFuSL4rbeYY3T1as41qFXLXWrU0DlB8Dcxd64bpZ07l9Gpu+9m1bALLnD9tKecotEEccKUeLtCYiLPxb5UqkQbaps2YTySUdg+w4MHvUVw/frAeecxolS/PiNWntx7L6NjKSnAjTd6C+CEBH55BclFKMKblBRXaDli9fbbKchefhl4/31uP3CA+0dH85iMiGDUc/JkTjypVYuPCQnuJM+tWynQatViZNIRfJ5CNNzIz7rh2KR27uTEH+e7cZaXXwZOO43WKSfi4PneS5cyj/mUKcCECd4iuFYtivRw/vxDEWcOSePG/G1WqsRHYzjy0bUrcNlluY8sCJEHIe3JjYjI+3wcHc3fUIcODMCcdZZbH0IUMqmpHLp1RHCzZvzQt2wBunfnY0aGu/8rrwB33cUhz3vvzSmCGzcGypYN1H8jioO9exkh9BSw27dzOL56deDZZ4GHHsr5uh07GDn88EOKJU8RW6sWo43HM/mkJHpyi4rCujlOT6cQ3rnTWwTffTej6e+8w0j0rl3e54WkJI4i/fe/jKT7iuC77uKNSFISHytUUKQv2Ni8Gfj9dzdSu2oVL76//ML2N95ghLZTJ35/QpwEJyVyjTEfAugPYLe1tnlB/mAwRHLr1uXvaO5cYN48jjgeOULr6a230p76/vvAtdcqo0ixkZnJi9zGjfzS2ralkF20CLjySm5zfCcAozwDB/ILHDmSX7anCG7ZsuAiWLPni4/Dhyk+Spfmd/3NN94Cdvt22lzatKGd4Npr3deWKkWhOnUqo4Fz5rhD3p5CtnLlwhU2Oj4CR1YWBasjgvv04Xf7xRfAxIneAhngOcIY4OqrgU8/5WiQY5Vo2BD45BPuN2sW93XE8Yn6hnVsuPj7LLKygJUrebN66aXc1rs38NNPtMx07sxI7VlnMeghRCFzsiK3G4DDAD4NRpHr68kF6F0fM8bbk5uZyd9hjRoMEE2eTNvPokVMfztlCjN0ORHfFi1kHSt2rGVkz4kEd+zIi9PPPwMPPMBt+/a5+8+bx5m1EyfSO+gbCe7UybVDKFLnzYlcuJOTXbHaoAHvDtetY65mz0hscjLw9dfAxRfTw3nOORS8nkL1iSf4I9u2jT9MZ3ulSorKCf9Yy2OrfHmu//ILT+CeIjgmBvjxR7b37OlGDgHeeHXpQo82wNGC5GTvKHG9erxIeKJzh4vzWaxeDXz/PW8k/viD52VjOImyQgXO8YiO5m9c6bxEEXPSdgVjTCKAycEocoETz65w6BADgZGRFMVPPMGRM4Da6IwzXNHboQOjw7r+BpjDh93sEF27AnFxFFQvvcRt27e7F6Tt23nhev114M47+UVGRbnLDz/w8b33GHXwbCtd2s07PG4csGQJT9pOe7lyHDYF+D6bN3O7s0+FCvQrAzzhHzjg/f7lytGXCLDf6ek5/76Twi0tjQdpREThHYDOxcoRDr52gXbtaDHZuhU4+2xuP3zYfb1jNVmzhpOPHJHgiNUBA4AmTTgjOiWF/4t+PKI42bKFv0tPEVyhAjNLALyJnjvX+zWeGWXOP5+/y2nTgIsu4rauXd1Jh5df7la2dOjdGxg2jL8r5zWeDBjAkYuUFL7el8svpzd1717vEQ6H664DLryQv8thw3K23347I+Fr17r99OSBB/i7XroUePjhnO0jRjBwMHcu8OSTOdt/+IH/2xtvcLLxqae6qby6duXNr37nYUegM1wVSwoxY8xNAG4CgPhiHv8fMuTEPlAnIAAwGnzjjdQb8+ZxmTuXv+UXX+Q+9eoxcFWqlJt8IC6ucP4HUUDi4oCmTbk4DBzIBaAg3LqVX9A773ifqJ0LWnw871icocudO4Hly+kLzMjghc0zjD9jBodOnXaAwwGOyH3nHUY1PDnlFFfkDh/uRo8cWrakcAZY8GP+fO/2Ll0YJXH2/ecfPndEcL9+FPcAbR+7d3uL8H79gOefZ/u55/Ji7NnukJrq3xM3fDgvhhUr0lbgaxdwPv9TT+WPIjdiYrgIUdzUq8clN+bM4W991y5XBDsn9JEjOdzn8M03fNy92xWPGzYwo4cnSUnu840bc/7N/fv5mJVFReDLwYN8dHIc++LcaGZk8GbUF6eMreOH9sUR5Wlp/F98cdJ0paW5/8u2bd5/yxGx993HogwirAn2DFchEcktStLSgL//pujdupUpJgGOwO7d61bynTqVltEmTZQuMigprCFHa92cqE7ajgMH+At3RHBGBg+Chg3ZvnIlL26e7bGxjHwAjBTt2ePdXr06h/sBimjP9vR0epmdNFcPPMALkmd7x468CAGMSB06xLRZ/i6cffow76QjYGvXZqRZERkhZFfwRJ+FyCYtjU6VSy/l5cmXhAT/93lFQcjbFQLBzz9T15x/PjVPpUquXaxdO9fi0KED9YoIMDo5e6PPQ4iCod+Kiz6LsGb7djpWpk6lwy85Ofd9jeGARXEQ8hXPAkHPnu7ziAiONjuZHObOZcQ3M5Pt9etT7F59NUeORQAYMSLQPRBClER07nDRZxFWZGRQ00ydysVx2NWrB1xxBV1xt99O+7svwZK1qiDZFcYB6AGgKoBdAEZYaz/I6zXhEMnNj5QUWhk8he999zFF5NattJA+9xxtj5mZhTunSIh8UVokIYQQPuzZ42bbu+MOzktyCiH268elWTNXrxQ0w1VRclKRXGutnymgIj9iY3lQdOnibnMiuwcOMHuDM8fh66+B225z7Q0dOnCCqzO5XohCRwJXCCHCnqwsTuOIieH86QsvBBYuZOXua65hIK5Xr9z1iCNkA5ldIS9KfMWzUGDOHGaxmjePc5QcmjTxTmF2+ulKOSiEEEKIE2f/fmD6dFoQfviBLpTbbmPCjXffZfa6unUD3cuCE9JlfUONgwe9/b3z5rkzF/fv593UjBk0fPtLwwgEPmedEEIIIYIDa5ka2fHW/vknI7iVK3Oe0A03sCBdSUUitwRjLdMxLl/OCm0AH9ev5zaA5d9jYhjtXbeOPppA+mOEEEIIEXjuuguYMMFNddymjeutbd8+NEaHJXJDjGPHeMDWr8/1du2A/D7u4sxZJ4QQQojiZ/x41h56+22uDxnCnLb9+jFqW6tWYPtXFCiFWIgRE+MKXID2hj17aG04/3z/r9m0yX0+bx4rypYtW7T9FEIIIUTRkJJCQTt1KvD008zTv24dizSkprJe0dixge5lYFFtrhChWjWgf39GbP1RtSofd+3iRLYxY9z1F1/kD+XAgWLpqhBCCCFOgH//BV5/Hejbl57a/v2Bjz8GVqxg+0MP0X/rFOQMdxTJDTFGj/afs+6VV/i8XDngu+8YyQWYKuT++91969enZ6dNG6YQadMGqFGj2LovhBBCiGzS0oDffnMnja1Zw+2nngoMG0YbQrduHOEFmN9WuMiTG4Icb3aFPXuAxYtZvGLRIj5ft85tr1ULmDQJOOMMRn6PHWPFExWvEEIIIQqXLVuAw4eB004Dtm1jOq+YGGZA6NePUdyGDQPdy+BBE8/EcXPwIEv4OeL3xRdpiXj6aeCRR9x0Zj/9BOzbx6hvw4a6ixRCCCGOh/R0Zkxq3JgZlRo0YF78775j+8yZzJ4UGxvYfgYrmngmjpsKFVjppHt37+0XXwzUru1WP3nzTfeHGBdHsevYHNq0YUGL6Ohi7boQQggR1OzcCfz4Iy0I06fzOrlrFwNF770H1Knj7luSc9gGGolccVw0bszFYfx4VmnztDq8/77rCY6JAXr3ZrlAgHertWvLFC+EECJ8yMxkJiTHW7twIbfXqgUMGkQbQlYWRW6vXoHtayghkStOilKlgFatuFx3HbdlZtIc71gdPAVt796M9P7vf1x/7z36jlq25KQ4IYQQIpSYOJETwvfupYjt2JFzZfr147VP81uKDolcUehERlK4nnYacMUV3m3PPce0JwCQlMQfPsAfeaNG3pkdWrcGqlQp3r4LIYQQeZHf5O5Nm4DLLwcee4xCtkEDt8pY797uNVAUPZp4JgKGtcCOHa7NwbE8bN7s7pOQwMlul1/OrA779oVmxRYhhBDBz9ixOdN0lirF/PP9+wMPPMBrVe/efN6/f+D6Gi7kNfFMc+FFwDCG/tz+/YHHHwe++YZ3wElJzNrw7LMc1qlenfvPm8f9p0/n+tq1HAbauJGC2R9jxwKJiRwiSkxU9RchhBAnRmoqhaunwAWYy3bWLAZhAM5F+e03CdxgQJFcUWLYsoWi9soraWN49llWdwGASpXcrA7O4/z5wC235CyMMWZM3nmDhRBChDerVwMbNgDnnsv1iy9mJqGsLP/7G5N7myhalCdXhCRHjwLLl3tndli6lENFAE86/g7vhARGf4UQQoQvhw8Dq1YxQ9DKlRS2EyYAUVHArbcC48YxOmsMS+kmJQFvvcVHX3RdCRwSuSJsSE/nSWvxYuCaa/zv49xx33efO3EgIYGL87xSJc14FUKIUGH9eloIVqxwRe2mTW57qVJMjzl9OlCzJvDvv7QhNGnifS3w58nVCGFgUTEIETZER7NSzOmnAyNGeJ/EHOLj+XjkCLBsGTB5Mr1WnsTFcb8uXYB33+W2n39m1bfTTy/a/0EIIcTxc+AAo7BxcbSrPfYYI6+nnMLctHfcQb9skyZAp07AjTcCTZtyOeUUvtbhlFP8/w1HyOaVXUEEDwUSucaYcwG8CiASwPvW2meKtFdCFAKjR/u/4x49ms/feYeP1nL4adMmLps3u4+e1dpuuQVo25ZDWABzA1eokDMKnJAA1KunEoxCCFEU7N3rRmOdZcUKZuv59FNg6FAK1qQklqAHgMsuo7+2fn2muTwZhgyRqC0p5CtyjTGRAN4EcA6ArQDmG2O+t9auLOrOCXEyFPSO2xhGaKtVo4jNjcmT3WGrjAygeXOK4d9+A7ZtYxEMT6pVo69r5EgK6ddfZ3nGFi1cr7AsEUII4Z89e3iurF6dE4+vvpqCdtcud5+yZRmJ7d2bj845vHVrt6oYAFStykWEFwWJ5LYHsM5aux4AjDFfAhgAQCJXBD2FecftWc44Kgr4/HN3PSMD2L49ZzS4YUO2JyUBd90FvPoqRe7q1TwZO9Ff38eEBKZLi5KhSAgRwlhL0epEZKtVAy69lOfUOnWAe+8FnnmG8yRSUoDzznMtBs2aAXXrMkWkEP4oyCW0DoAtHutbAXTw3ckYcxOAmwAg3jE9ChEmREVRoMbHA1275myvWpVRCcf+EBtLK4UjihcuZLsnkZHAJ59QpG/YALz/PnDDDRxuS0nhxaFs2ePrZ36VeoQQoqjYvt174pezOPllAeaWvfRSnlPHjHHnQMTFAXPnBqbfouRSaHEia+0YAGMAZlcorPcVIhQwxnuoLD4eeOkl731SUig+PT3BLVqwbc0a5gW+6CKK3C+/BK6/nuUh/XmCnefVqrmWCN9ZwZs2uWWVJXSFELlxPDfH1rrnnK++4mseeIDrffow7SPAc1ezZsAll7iR2aZNmdnAIbcMOUIUlHxTiBljOgIYaa3tk73+MABYa5/O7TVKISZE4eN4fiMjmQ94yhRva8SmTcwY4cmyZfQOT57M0siHD+d839q1gb//BsqV48xjIYRwyC1l1rvvMvuM7+SvLVuArVtpIbj+elaqdITttGk8xzRt6n0DLsTJcLIpxOYDaGSMqQ9gG4DLAFxRiP0TQhQAzxnBTpo0T6zlTGJP4Vu/Pts2bPAvcAEOIVarxufR0RwWLFcOKF+e4jcigsOGK1bQUwwAX3/NmczlyuW9yFMsRPBjLXPCHj7MpUYNoHRpCtZ77slZxjYlBbjqKu9iOzVrUrwOHMiUjLGxwNtvM/+sQ58+xfP/COGQ7yXIWpthjLkdwDQwhdiH1toVRd4zIcRxYQyHACtX5sxiT+64A3jxRf95g6tUYU7hw4eB5GR3OXbMndDx77+sKufw9tvMG5wX8fHu37v2Wk4k+ewzrj/wAH14eQlk56IJ8KJaunThTzCRR1mUNKzlb9MRpM6SnOw+79KFN7hr1zJV4m23AQ0aAD/9BDz9tP/XZmS4f+PPP4GOHYFffsk5V8CzH+++S8vBaafxvOOLp8AVIhAUKM5irZ0KYGoR90UIUYTkljf41VfzF3bPPuu9PnmytyD2t5Qu7e5fv773RXTJEg5vOhdmf66p7t2BX3/l81atgDPOcHMUt25NUZ+XSG7ZEujVi/v/8QdFbL16/FspKcC338qj7IkEvzeF8XlYy6imMfw9pKbyZjEvgXr4ML33/frxmBw4EHjySa7//jtw9tk50xX68vnn/M3t3MlRmAEDKHKtZVXIKlWAxESO2vhbnBGg884DatXiqI0vCQnu70WIYEWDiUKECYVZqad0aS6OzSE/nnjCe/2nn9znWVkUms6F3hHJnsU07ruPF1uHpk2Bgwe53/bt3uLaEa3XXkuRay0F84MPAv/9LxPJ59bvlBS+7qWXWA3plltYRWnQIKaAO/981qd/4AFaO6KiuDjPPbdddBHQoQPTI33yCdcbNeIQ8LRpOV/ru3766ZysePAg7SannsrPJDmZUXB/f99ZjtfrGMyTErOy+B06dp2jRynysrJyX6KimFsVYA7ryEh3QtOaNRR6eb1+9mzmt/b9PLKy+H75idTLLgNGjWLENTaW0dOHHqLo7NzZ//9pjCsyW7XittKlXesAQGE5fLi7X7lyOQWqMwoCMNNLcrL7N3r35lJQqlYFnn8+76I6QgQzErlChBHBWKknIsK9QOfGzTd7r48dm/u+mZk5/cfTpzOKC3Diy7PPUiz4Iz2dk/Gc/mRmUlg5keiUFPqTMzK4b0aG/+cNGlDkbtvGv9WkCUXu339TQOfHd98BF1xAwdW/P/DXX0C7dsyskV8ELTKS6ZbatmUFqPvu42TFWrVYlOSNN7wF8rJl9GR6kpLCG6LUVKavmzOH20ePpic7L5GYlQWsX8/9H3wQmDHDtbsMGQL8+GPer61ZkzcDAKOQ27e7if27dPG2zvijTRt3/wsu4Pc5aRLXu3en2MyLMmX4nft+Ho89xn45owi+AtM5bpxIaEwMc7x27871mjV5g+MvelqmTM6bkxo1OMHUISGh+MWlytiKkoxErhAipIiMZLllB2M4xOtQrhyF11tv+fcoJyS4ggjg0O4ff7jrTZvSalFQWrVi1gvHn9irF4WSrzD2XXf8yGecAXzzjVtYpFs34IMPcr7GV2Q7ke8GDYDBg93IeO3aFIGe+3tWhvLEKW1drpybGqpCBTcBf26LMe7+zZtTuDp07Ur/Zl6vL1/e3X/IEO9o5F13Abt35/16z3R9o0Z5jwq8+67rN89t6dvX/+exZQu/y9KlCxYtN8b7Zqp06eOLpAYLwXhzLERByDeF2ImgFGJCiGAnt9RIY8aE3wU9MTF3wb9xY3H3JvDo8xCi5JBXCjEVwxNChCVDhlDQJiQw4paQEJ4CF+Dws2e0Ewhv36U+DyFCA4lcIUTYMmQII3NZWXwMR4ELSPD7os9DiNCgSOwKxpg9APwM9hQ5VQEkBeDviuBHx4bICx0fIjd0bIjc0LERHCRYa/3mzCkSkRsojDELcvNliPBGx4bICx0fIjd0bIjc0LER/MiuIIQQxYQx5kdjzJN+tg8wxlhjzOHsJd0Yk+ax/k4g+iuEECUZiVwhhCg+PgFwpTE5ElANBfCStTbOWhsHYCyA55x1a+0txd5TIYQo4YSayB0T6A6IoEXHhsiL4jo+vgVQBUBXZ4MxphKA/gA+LaY+iOND5w6RGzo2gpyQ8uQKIUSwY4x5Dzz33pC9fjOAYdbaVh77fAxgq7X2sYB0UgghQoBQi+QKIUSw8wmAQcaY0tnrV2VvE0IIUYhI5AohRDFirZ0Nph260BhzCoD2AL4IbK+EECL0CBmRa4w51xiz2hizzhjzUKD7I4IDY0w9Y8xMY8xKY8wKY8xdge6TCC6MMZHGmMXGmMnF+Gc/BSO4VwKYZq3dVYx/WxQAY0xFY8wEY8w/xphVxpiOge6TCB6MMfdkX1OWG2PGeYzMiCAiJESuMSYSwJsA+gJoCuByY0zTwPZKBAkZAO6z1jYFcCaA23RsCB/uArCqmP/mpwB6AbgRsioEK68C+NFa2wRASxT/MSKCFGNMHQB3AmhrrW0OIBLAZYHtlfBHSIhccLhvnbV2vbU2DcCXAAYEuE8iCLDW7rDWLsp+ngxeqOoEtlciWDDG1AVwHoD3i/PvWms3AvgTQFkA3xfn3xb5Y4ypAKAbgA8AwFqbZq09ENBOiWAjCkAZY0wUgFgA2wPcH+GHqEB3oJCoA2CLx/pWAB0C1BcRpBhjEgG0BjAvwF0RwcMrAB4EUK64/7C1tkcebdcUX0+EH+oD2APgI2NMSwALAdxlrT0S2G6JYMBau80Y8wKAzQCOAphurZ0e4G4JP4RKJFeIPDHGxAH4GsDd1tpDge6PCDzGmP4AdltrFwa6LyLoiALQBsDb1trWAI4A0FwPAeD/c1sPAG+GagMoa4y5MrC9Ev4IFZG7DUA9j/W62duEgDEmGhS4Y621EwPdHxE0dAZwgTFmI2hxOtsY83lguySChK1gnmJn1GcCKHqFAOin32Ct3WOtTQcwEUCnAPdJ+CFURO58AI2MMfWNMaVAA7h8bgLZ5VM/ALDKWvtSoPsjggdr7cPW2rrW2kTwnPGLtVbRGAFr7U4AW4wxjbM39QSwMoBdEsHFZgBnGmNis68xPaGJiUFJSHhyrbUZxpjbAUwDZzl+aK1dEeBuieCgM4ChAJYZY5Zkb3vEWjs1cF0SQpQA7gAwNjtwsh7AtQHujwgSrLXzjDETACwCM/gshkr8BiX5lvXNzv32O4AYUBRPsNaOyOs1VatWtYmJiYXVRyGEEEIIIXKwcOHCJGttNX9tBYnkHgNwtrX2cLa3cbYx5gdr7dzcXpCYmIgFCxacYHeFEEIIIUSJYuRILsWMMWZTbm35enItOZy9Gp295B3+FUIIIYQQ4cOoUYHuQQ4K5MnNrii2EEBDAG96zDj13OcmADcBQHx8fGH2UQghhBBCBBtZWcDcucD+/YHuiV8KlF3BWptprW0FpuZqb4xp7mefMdbattbattWq+bVGCCGEEEKIks7ffwPDhwOVKgGdOwP9+3O7MVwCYFvwx3FlV7DWHjDGzARwLoDlRdMlIYQQQggRVGzYANSvz+dPPQV89x3Quzdw2WXAgAFAhQpAPskMipt8I7nGmGrGmIrZz8sAOAfAP0XcLyGEEEIIEUg2bgSefRZo1Qpo0ABYt47bn3sO2LEDmDIFGDoUKF8+kL3MlYJEcmsB+CTblxsBYLy1dnLRdksIIYQQQgSElSuBG24A5szh+plnAq+8AlSuzPUGDXK+ZkSe2WUDQr4i11q7FEDrYuiLEEIIIYQobvbtAyZOBKpXBy64AKhZE0hLA55+Grj0UtemkBdB4sP1JCQqngkhhBBCiOPg8GH6ar/8Epg2DUhPBy6/nCK3cmUgBOodSOQKIYQQQoQDGRlAVLb0GzAA+OUXoF494O67OYGsdWgN3BcohZgQQgghhCiBpKcDP/wAXH01UKsWrQkAPbSzZ3Ny2XPPAW3aMP1XCKFIrhBCCCFEqLFhAzMjTJgA7N3LFF8DBwIpKbQjdOsW6B4WORK5QgghhBAlHWuB+fOBmBigZUsgMxP47DN6bC+/HOjTh21hhESuEEIIIURJxFpg+XJOHvvyS2D9euCSS4CvvgIaNgSSkoAyZQLdy4AhkSuEEEIIURIZMACYNAmIjAR69gQefxy48EK3PYwFLiCRK4QQQggR/Gzdygjt1KmcSFaqFHDRRUDfvvTaVq8e6B4GHRK5QgghhBDByL59FLbjxgGzZnFb27bA9u1AYiJw7bUB7V6woxRiQgghhBDBwoEDwM6dfL5yJXDrrcyO8NRTwJo1nFyWmBjIHpYYFMkVQgghhAgkR44AkyczYvvDD8BNNwGvvw506gQsXQo0bx5yOWyLA4lcIYQQQohAceutwCefMH9t7drAbbcBV17JtogIoEWLwPavBCORK4QQQghRHGRkAL/+Cvz0E/DMM4zOxsUBQ4cyl22XLsyUIAoFiVwhhBBCiKIiKwuYM4dWhP/9D9i9GyhXjhHchASW1BVFQmhNPBs5MtA9EEIIIUS4Yy2Qlsbn33/PCO0HH7CU7tdfA7t2UeCKIiVfkWuMqWeMmWmMWWmMWWGMuas4OnZCjBoV6B4IIYQQIlxZtQoYMQJo0gR4/nlu69OH5XV372Yk9+KLw75IQ3FRELtCBoD7rLWLjDHlACw0xvxkrV1ZxH07PqzlY+fONG7XqcOlSxegY0e2Hz0KxMYGtp9CCCGEKPmMHOmOIL/0EvDpp8Dff9Nne9ZZQNOmbCtTxp1IJoqVfEWutXYHgB3Zz5ONMasA1AEQHCJ35EjvCO6ff/IxOhpITwceeYQid/9+oEoVoEIFVwDXqQMMGQL06gWkprL+c506rBoi47cQQggh/LFxI7WHI3JnzmQQ7dVXgcGDgVq1Atk7kY2xTgS0IDsbkwjgdwDNrbWHfNpuAnATAMTHx5+xadOmQuxmgTvoRnQBIDkZyMwEKlYEDh4E3nqLVUK2bXOXJ55gPrqlS4GWLfm6yEgeoLVrM/ly795MzDx9uneUuHz54v8fhRBCCFG8OBrg118paDdu5Pbt26kX0tJYZlcUO8aYhdbatv7aCpxdwRgTB+BrAHf7ClwAsNaOATAGANq2bVtw5VyUlCvnPq9QAXj44dz3jY8Hvv2WwtcRwtu3AzExbF+4ELj6au/XxMUBkyYBPXoAixez9J4jgB0xXLu2osJCCCFESWLHDorZs86iiJ00iQGx0qU58utQuzYfR4zQ5PcgpEAi1xgTDQrcsdbaiUXbpZNgxIgTf23FisCAAbm39+rFcnq+keD4eLavWEFPTnq69+uWLGGEeOJE4OOPc4rg7t35oxFCCCFEYHAqjjmR2tWruf2DD4DrrgMuugho1w44/XQWaAByjh6LoCNfu4IxxgD4BMA+a+3dBXnTtm3b2gULFpx870oaWVlAUpJ3NPjyyxlR/vRT4MUXuW3vXvc1e/cClSvTFvHJJ95+4dq1gTvvZCT44EGKYSeyXBA8TfFCCCGEIElJwG+/8fp71lm0I9Sqxet1t27c1qMH0KpV7qOxErlBQV52hYKI3C4AZgFYBiAre/Mj1tqpub0mbEVuQUlN5VDItm3MBmEMrQ6OXcJZIiOBw4fZftVVTEFStaorghs1Al55he/59998rFOHE+yM0Q9QCCGEcJg0CZgxg5HaZcu4beBAYMIEPl+yBGjeHIgqoJNTgaSg4KRE7okgkVsIWMvobcWKXP/hB2D+fG+7RGwsMHs2288+mz9cgOb32rVpjHe+37VrgZo1vX3KQgghRChy8CDw++/A5s3AbbdxW4cOFLedO7uR2nbtmI1JlFgkcsOBJUuAf/8F3n8f+PHHnO2VKjGNWnw80KwZ71bPPhs499xi76oQQghR6Pz1F4st/PorsGgRLYQVKwJ79jA6u2kTgz3HY/sTQU9eIjckyvqOHQskJtILnpjI9bCjVSsOu/zwA6O3zs2L8/zjj4H//Id3sNu2MZffxOw5hFlZFL7nnw889BBtEYsWsXiGEEIIEWwcOcKUXg8/DBw4wG0//QS89hpHOR97jKObO3a49oOEBAncMKPER3LHjmVWj5QUd1tsLDBmDOs8hDV5eXIzMniSqFABOHQIuPFGZohYs8bNEDFyJDNW7N8PvPAChXCzZkDjxsoIIYQQonjZvJkX95kzGbXNyKCA/flnThY7eJDWA1U2DStC2q6QmMgRCF/q1gW2bCmWLgQvJ2KKT0+nf3fFCpYkbNaMXuCOHVlYA2DIvGFD4M03mVpt/356hRs1UjJsIUo6mkwjgoHUVGDuXArarl15rVm+nKOWbdvSU3vWWUCnTsxZL8KWkBa5ERG5Byvbt2exsnPOAc48U/rrpDh2jFHeFSvc5YkneMIZNw644greUZ96qhvxveUWoEaNQPdciPyRsHNRVhYRKDIygKefprD9809edyIiOKL4xBM8Lg8f1gRq4UVIi9zcIrkVKlBnzZvHAGRcHG/6HnuM4lcUItu2Md/g8uWuAF6/nl9MvXr0SL3/vit+naVhQzepthCBIC2N+TLr1OExm5XFYaCYGI5Q7NzJbc5iLXDaaWzfvp3DRZ7tWVkc9ShVCli3DtiwIWd7v35MD/j33xw1sdZtA5hbG2DmlDVrvNujo5mYHgCmTmW706+sLJ7ohg1j+xdfsA+e7VWrAnfdxfa33nL/Z6c9Ph64/36uT5/O/WrW5FK1qn6vovBIT+cooZMV6NFH+diwIUWsE6nt2tXNMiSEH/ISubDWFvpyxhln2OLi88+tjY11ZldxiY3ldmutPXDA2m++sXbYMGsbNrR2zhxu/+UXa6+/3trdu4utq+HFkSPWZmXx+fjx1vbvb21iovslRUZam5rK9s8+s/a//7X2u++sXbfO2szMwPVbhA4HDlg7axaPv1dftfahh6y9+mprV69m+6efep84nGXxYra/+ab/9nXr2P7MM/7bd+1i+6OP+m9PSWH7nXfmbIuMdPt/7bU52ytWdNsHDcrZXreu237uud5tERHWNm/utvfsaW3ZstbGxVlbqpT/vvr2bdAg9/XDh1v78MP8bL/6ytrffrN28+bC+OZEKPPJJ9b26cNjzzm2evZ0253rghAFBMACm4seLfGRXICTzx59lJ70+Hhg9OjcJ51Zy9G4Dz7g6Mf69QzKvPMOgzLnnEOLj6wNRcThw8CqVfyyBg7ktiuuoOXBoUwZ5i+cml1vZNEiVqWJj1ckKZyxlpMkIyMZsdy1C/j8c86e9lxefJHR0mnTvFPkRUUxIjl2LCepDBvGH74vDz4IPPsso6yLFvGYi4jgiSMigieJsmWZsm/NGrfd2adLF55ANm0Ctm7N2d66Nf+H7dtZ8dC3/dRT2Y+kJE4O9WyPiHAtQIcPMxrm216mDNszM92iMMYU/HN27Arr17OPO3e6S0ICJ6kCjGivW8chZoerr2YmF2s5WlOxohsJrlmTZcy7d2f7li38XzTbPTTJzGRqS8d68NVXHIm45x6OEjiR2m7dgGrVAt1bcRIcjwYrCkLarnAyOIIX4Hn7o4/4uyxblhqrd28ujRsf3zVCnACHDgErV7p2h5gYerMAXkz/+YdfjDMZ7pxzKI4B7y8yN+S5DF6spdhzRGq9evzOd+9mEndn+86dTKPy+uvA7bfzOGnenKKuVi13uesuCs29ezkcWrs2t1epkvtNknyoLsfzWWRlAfv2uSK4ShWK+LQ02io8BfLevcAjj/AKuG8f9wW8hfCttwKDB1PAT5xIEexpl8itvKoIHubMAZ55hoUYnNRejRszf3tiIi+y+h5DhmDIcCWRW0AOHmQO6enTmW5v7Vpur1uXYnfgQAaIRDEzZw6r1KxY4fp+zz3XjRglJNBT6en3bdUKqF7dfQ+JmMDgCNTt293nzZsDF1/M2dOnnkoB5KStA4Dhw3mRPHSIBnpPAVurFmdZt2rFCOLhwzTgn+xdqI4Pl6K6IUxL43cWG8vv7auvvEXwzp0UuZdfDixdCrRs6f36iAhGIq66iifn//7XO0pcsyZw+uksfCOKBs9jIyuL5+KZM7ncfTej9L/8Atx8MyNFTlWx2rUD1mVRtOQ2LyohgUVXiwOJ3BNkwwaK3Z9+YrnrQYOA997jtfDJJyl6mzcPdC/DlPR0Dn0dPQo88IAbAd6zh+1OxCg5GbjvPn5xr7/OCHFMDD0pDRtSSC1ezG2lS7uPNWrwYpyZyS+8oLXMSwonI2SystyI6A8/8IfiaRdo2ZICBGCU7uBB79ffeCNv853nVaq4ArZ2bX4vNWueWN9OFEX6g4v0dI59+orgQYN4fM2eTTG8c6e3XWLKFEYipk7lKEDNmt7R4BtuYNRi/37+9mvUyD/ndzgcG4471vldJyfzc83MdJfSpflbTUpi5pxff+VzgErnhRd4USzIyJoo8ezZw0tw5cr+4wPGuHNpixqJ3EIgM5O/+4oVacVr0oTX6Wuv5bl44kRGe087Tb/vgLJ7N8VunTqcXT5qlP/93n8fuP56pt8488yc7V99BVxyCe9uzjmHw2ueQvjzz1kW+ddfGXn0FMilS/MuqEkTDpd/+aV3e0wMx3GqVqWncdky77aYGKBFCz4mJ1PIO22lShXOAeYvcnnkCEXq4cOMlAK8MViwwFvENm3KoUiAd3krVvDiWL06hWrv3ozEAvwOYmNdEVuzpgzvovDIynKzYOzcyeO2ShX+rt9801sg79nDCHGLFsDbbzNqDHAkwBHBY8fy3LFgAUeNatSgaJ4+nRcB51zw99/0YztCMCODv6drr+V7zpjB37WnSCxVipkrAP6dJUvc12Zmsh/OzeHzz/Pm23ltRgb79eabbL/zTv4vnkL0tNOATz5h+4ABtH95vn/nzsD48Ww//XReyDxff+GFwDffsL1qVdpLPBk6lNUw09NpSWnTxo3UJiYW+lcrgo+dO3mITJjAS9/zzzN5UjBHckMsPFV0REa6WUxOOYW/f8dW9Ouv9NIDDEQ5Xt5eveSnL3aqV3dtCk4ExolQ7N7NvIupqe4X06QJh9dSU9nmtDt55ho0oGD1bDt2zJ38ExXF4dHUVArSPXvc/QBg9WreDaWmekecevXihWTKFA7z+bJxI88Sr7/uptZxiIlh2rYqVYDnnqNgd8SxI4Z//JG32Z98wgPUU2Q7XkiAJTEnTqR4TU7mNs+z04wZvNg6EdauXb2HL779ll7p6tX9++wc37QQRUFEBI/nKlVoU3Lo0IGLJ84kPQDo2ZOzj30jxU6lrIkT3TkBAE/oAG8EY2Npm3j1Ve/3N8YVuePGAR9+6N1evrwrcqdOpVqIiuLvJjKSEWZH5K5axRvkyEh3H08/uVOYJybG3cfzd92kCX+Xnu/ftKnbfuml9Mt6vv9pp7ntTz3F81hkJM8lU6dS4AI8rwCM2l5zDURos307fw4TJjC2YS0t1o88wvu/GjX8e3JHjw5cnz1RJLeQ2LSJtobp06kL9u/n9tatXdHbrVvojXqXGILBc5mZ6Qrg8uV5MCQlcQa+p4A+dowiuEwZYOFCVv35v/buOzyqMm0D+P0m1JDQQw2QqPQmARQEEUEQFGFBFBBdXRTWXrHvLvqtKH669vZlhVVWFKXZVllUmlIVBKkqHUJHSkJoybzfH/ccz8xkJgmQZCaT+3dd55o5ZSYnycw5z3nP8z5vYBD+5JMMWD/4APj009xB+Lff8nf+y1+AiRO57NAh5kUGatGCP89pbW3QgK3UIqXV44+7Aaevv/6VF707d/Ig7xskxsayaznAuyHZ2e5yZyrpJ4BIOI5KsXjrLd6wXLCA8y1bsk/o4ME8ZfjeUFR1hVImJ4exiRP0LlzIY9tvvzFuWbCArcK+DQ9SxEpDXl1BOS3bOlmJ5E+BnUt/i6i1bRvwzTfuzYirr2ZGy+DBfO7b0B9pzirINcZMANAPwF5rbYG6WZX2IDdQRgbTuzp35nzHjrzL9N13nP/qK/al8C0GIFKkdLISKRh9V1xqLIgqGzYwFT0+nv0GH3yQ5auTknhTML8+mZEiryC3IJX13wHQJ7+NJLSEBDfABYBp05hqCfCu1pVXMq+lXTv2Yfr6azelU6RIjBkT7j0QKRn0XXEpwC3x1q8HnnqKfTQbN2a2G8D06k2bGOACJSfAzU+B0hWMMckAPldLbuHzeDiokpPasGAB+0dUqMCSg716MZ+3VStVbRAREZGCs5aFNqZOBaZMYTEcgFU0Bw9mH8SSXsb4rHNyCxLkGmNGARgFAA0bNmy/NVhNCclXZiYwb547IMW6dVz+zjscMfPwYbbyOp37RURERHxZy5sQU6aw9dYYFscZPJhj8dSvH+49LDxnm65QINbaNGttB2tth0TVzTpj8fFMX3j5ZV59bdvGSjdOBZsPP2QOzebNnN+zh2VUfU2axLKFMTF8nDSpOH8DERERKW6rVrFBDGBQO3cuC+a88QYLgsybB9x1V3QFuPkp4fVMol+DBhwC3nHppcBLL7m1tx98kFdq3boxteHUKebbODXrtm5lDTugeEt6iIiISNGxluWUU1NZwenddznOybXXslbt7Nklv2rd2VJObgk3dy7wySdMb1i7NvR2xTn6iIiIiBQ+j4cD+k2Zwk7sTumvHj04nknZsv7jgpQGZzXimTHmAwDdAdQ0xuwAMMZaO75wd1HOVPfunAAOguX0jAy0bRsf//EPoH179zUiIiISuXJyWG9/6lQGtunpHCW6d2+OTZKayu3q1AnvfkaifINca+2w4tgROXv167PFNlifv4YNOQDPmDEc9rx7d3Zy69KFlRvatOGQ7m3a8H1UyUFERCR8srKYkjh9Oltpy5cH+vYFnn0W6NcPqFIl3HsY+Up5tkb0GTs29DjSZcpwJEqnBu/hw8z5/fZb4P333e2rVXMD3tatebXo5ACLiIhI0Zg/nw1VN9zAEVK//Rbo2pVVEa64gnX3peA0rG8UOpNxpA8dYs/MVauAn37itGoVW3vfe4+v/+kn1gJ/+mmgWTPgxAl32HYRERE5PSdPsj7+pZdy/oYbGNhu3sw7qh4PKyVJaGeVkyslz/Dhp19JoWpV1tC7+GJ3mcfDK8rq1Tm/fz87t5Uty/l//hN46KHc6Q6tWwM1axbKryIiIhJVTpxgHfypU9lx/NAhYPVqoGVL4LnnmIbgpAwqwD07asmVM7ZgAb+kq1YBK1cyCHbUresGvGPGsP6vtcr1FRGR0ufYMVZBmjqVQ+keOcLGpQEDmIrQqxdzbuX0nfWIZ6dLQW7pYy0HpnDSHZzHzZsZ/MbGsgj1ihW8FQMwMK5WjXnBCn5FRCTa7N0L3HMP8PnnTP+rXh0YOJCBbY8erJIgZ0fpClLkjGH5kjp1eEXq8M0natOGLbqOm25i0FulCtf5pjy0aqUEexERKXmmTePATEOHsrV2+XLguusY2Hbv7qb8SdFTS66EzeLFwI8/+nd2y8hw16ek8CDx9NOc37qVdYDV0U1ERMIlsHP3X/7Cx969ub5XL1Yxcu5aKlWvaKklVyJSp06cHNYykPUNeuPiuC4nhxUd7rgDeP55HkDeestt+U1MDP4zzqTShIiISDCTJvmX6dy6FRg5ks/37AFq1eI2vqOOKcANHwW5EjGMYT3e5GTgqqv812VnM6ht0YLzP/8M3Hefu75OHTfgdaaVK4Hbb/c/GI0axecKdEVEpCA2bGBnsbVrgX//m2W/AtWu7VYVqlWrePdPQlO6gpRYTkc335bfNWtYniUvjRq5NQhFRKR0sxbYvp2DLyQmMpgdMYJ3Dbt2BWbMAAYN4rp9+4K/h1PTVoqf0hUkKtWuzemyy9xl2dm86v7pJ2DIkOCv27YNeOMNljZr0IBTUpL73Jnq11dJFxGRaOHxMJhds4aBrPO4di0rH4wbBzz8MKv+xMW5Qevll7NKQmIi7zRu3Zr7vRs2LNZfRQpILbkStUIdjBo1AiZMAD76CNixgwe97ds55HGgLVu4/ccfA7NnAy+8wFHetm7llXu9epwXEZHIsWULg9RzzmFN2ssuYzB79Ki7TZ06TIFr2ZKP3buz70deAnNyAQbEaWlKgwsXteRKqTR2bPCD0dixrE/Yo4f/9pmZbtDrPNaty3Vr1jDQfeUVzj/6KPDBByyPVqdO7lbgpCRe2V94YbH8qiIipY7Hw2DWaZWtWBG4+26u69qVx/iJE1mOsm5doHNn/6DWGc3zdDiBrDo0lwxqyZWoVlTVFZYsYS6w0wrsOzlBde3awO7dfH7rrVw+cSLnp0xhKTQnKK5VS8M3ioiEsmUL09B80wzWreNIYo6LLwbmz+fzL75gylnbtmHZXSlGGvFMpJhYy3HIt2/nLbKuXbn8iSd4MH72Wc6npPCg7ShXjgdk3/zgjh2Bq6/m+sOHgcqV1VlORKJXTo5bB33GDA4d//zznL/8cg6LC/AY6dsi27Il0Lw5B16Q0uesg1xjTB8ALwOIBfC2tXZcXtsryBXJ2759uVuAffOD09NZRm3aNG5fuzZ79775Jm/R3XyzGxT7TlWq5B0Iq26wiIRbTg6wcWPuzl/r1wO7djFY/dvfgPfeA375hf0elixhI0Lz5jzOiTjOKsg1xsQC+AVALwA7AHwPYJi1dm2o1yjIFTk7Hg/TG+LjeWB/9VUe3Hv1An77jbfgdu7MXbKmUiU34P3Tn4Bhwzi85Jw5PKmMHq0OEyJSPLKzedEdGwvMmwf83/8xqP35Z/9Sjw0buq2yDz/MKgYaJUwK6mw7nl0AYIO1dpP3zSYDGAAgZJArImcnJoYBLsADvdOZAmBnie3beQLZtSt3K7AzHTnC7bdv562+GjX8A1yA8yNGsKRauXIsmeY83n03c9w2bGCHu7vuAho35knqs8/8tw98bblyQIcOLMVz6BBbps87j+uysjhinbNdmTLhO5mpZVskf/l9T06dcltm27Thd33ePA5zO2cOcNFFrGu+cCGD2d693aC2eXN2DAukAFcKQ0GC3PoAtvvM7wCQq8+4MWYUgFEA0FAF40SKXJkybqttXurW5Rjq3boFX3/yJHslnzzJgPTECT53guRduzjKz5AhDHJ//JHVJfIzdy5wySXAf/4DXH89W2+aNGFA/eCD7nbGuIGyb5A8ezZzlydN4mu++ootzxMmAF9+GTrIdp7ffz+fL1nCE/B11/HnrVgB7N/Pk7AzRDTgDs954gRw441ubqBIaRZsGNsRI4Dp03kMWruW3+1Tp7j+xReBe+/lseLee90h16+9lpNIcSpIusJgAH2stbd4528AcKG19s5Qr1G6gkjkyatusG8nuPx4PAyCnWA41PNWrZg7t3UrsHQp0LcvW6eXL2fQnd97PP88q0589BHwz38yWC5Xjp333n039Otycrifx44BFSqwRfq995jmAQDXXANMnZr371ilCgN+gENDb9oEzJzJ+XvvBX79lQF3xYp89H1esSLrJw8dyu0XLeJ+tGvH+a1bGRw4rytXLjJardSqXfJ5PAw2nUFsfv6Zz5OTOT91KuvEHj3KoNV59H3epQvv2gBAaiqPDcFqiAOsQeu0yPp2AIuLK+rfVMR1tjm5nQE8Ya293Dv/KABYa58J9RoFuSKRp7QUMc/JcU/0xjC4PXLEPdH/8gtHL+rWjXl/wTz3HPOXAeZD79oFPP0050eOZGt2VhYDaSdIyMpyc6TbtmWLMQBccAFTRb78kvMNGjC9xGFM7iC5Z0/g9de5/pZbgPbtgdtu4/wjj/B3CxZgO8+TkhiAALxNXLkyl4dSWj4bp6Mogv4TJ3IHmIGBZvnywODB3D4tjZ9n539/220ckjzY648e5V2JHj2Ab77h9ueey9qw773H+UqVcqcslS/P/3WlSnwcNAh4xnt2HzYMmDw5+O+iYWwlUpxtkFsG7HjWE0A62PHsOmvtmlCvUZArEpnUWucqrJZth7UMro8dY750jRpcvmoVc6xbtuT81KlsGXMCm8BA+dgx5jU+/ji379mTAfmYMQx4EhL8a4MGM2oUO/l4PEy7GDOGZex27eKIToGB8U8/+XcEciQl8eePGMF92LyZAVBMDCdjgj8OGcLgfvt2VgS58UagaVP2nv/ww9Cvcx4HDWKqysaNrHc6bBhQsybzwRcsyL194Hv07ct88I0bebFx5ZVsTV+3jjnmoV7rPN+6lS34gUH/ffdx8Bff/1dgkOrxcJ8BtvovWcLWfIAB6Jw5ef/vUlJ45wAA+vThZ+nrrzk/cCD/h75BaeDjeee53+n//pefww7e0//atfw7ONtWrJj/iI2F/T0RKWxn1fHMWpttjLkTwH/BEmIT8gpwRSRyDR9eeoPaQHmNiHcmfHOLfbVu7T/vtNIVlNMqBzBgzcpiQH38uH+Q7PtYuza3t5b5zO3bc758eVbd8A2sjx0LHuAC7DA4Zw5wxRWcP3iQnQ6tZTDn8bjPfR/btmWQu2sX0066dWOQu24dg+38tGjBYG/lSqabdOvGIHfuXODOkIlyrhUrGOTOnMnt9+5lcPfee26LfF6SkoJ30nzpJf9hYZ0LBd9A06mIYgz/907qAMBBYQYMCB6c+r7e4aTIOGbMyH/ffV1+uf98ixan93qg8L8nIsVJg0GISKmllm0qzta6YEFx4GOFCkDZsgy+MzJYN7VMGQaYhw8Hf53v80aN+B4HDrDUXvPmfP3OnQy8A7cPfN6zZ/BUFmOY/uG0gpaWUQr1PZFIphHPREQkJOXk+tMtepGSI68gt5Rch4qISCjDhzOgbdSIrZWNGpXeABdgS2VghQDdohcpeYqkJdcYsw9AkOvgIlcTwP4w/FyJfPpsSF70+ZAANasD9eoDZcsBp04CO9OB/b+Fe68koui4ERkaWWsTg60okiA3XIwxP4RqspbSTZ8NyYs+HxKKPhsSij4bkU/pCiIiIiISdRTkiojkwRjzqDHmy4Blv4ZYNtTQ3caY1caYo8aYHcaYKcaY1t7t3jHGWGPMgIDXv+hdflPA8u7e5Q8H2bebjTHrjTEZxpg9xpgvjDEJ3nVJxphpxpj9xpjD3v25KfA9RESiVbQFuWnh3gGJWPpsSF7y+nzMB3CRMSYWAIwxdQGUBdAuYNl53m1fBnAPgLsBVAfQBMDHAK70ec9fAPzRmfEOunMtgI1Bfv6NAH7z3d77mksAPA1gmLU2AUBzAB/6bPJvANsBNAJQA8ANAPbk8XtKcDp2SCj6bES4qMrJFREpbMaYcgAOAbjYWrvMGHMtgL4AzgFwv8+yp73L1wPobK1dGuL93gE7q9wAoJm19qAxph+AOwAkAHjbWvuOd9tKAHYDGAlgIoCLrLU/eNeNBtDVWvuHED8n07t+xdn+DURESqJoa8kVESlU1tqTAJYA6OZd1A3AtwC+C1g2Hxz+fEeoANfHcQCfABjqnf8jGMQGGgQgE8AUcNTJG33WLQFwuTHmSWNMF2NM+YDXLgbwujeFomE++yMiEnUU5IqI5G8e3ID2YjDI/TZg2TwwLWBXAd9zIoA/GmOqArgETGkIdCOAD621OQDeBzDUGFMWAKy134JBcCqA/wA4YIx5wUmhAHCNdx//CmCzMWaFMaZjAfdNRKTEi5og1xjTxxjzszFmgzHmkXDvj0QGY0wDY8wcY8xaY8waY8w94d4niSzGmFhjzI/GmM/z2Gw+gK7GmOoAEq21vwJYCObqVgfQyrvNAQB1C/JzrbXfAUgE8DiAz621xwL2qwGASwFM8i76BEAF+OT2Wmu/tNZeBeb+DgBwE4BbvOsOWmsfsda2BFAbwAoAHxtjTEH2r7QzxlQ1xkz1duxbZ4zpHO59kshhjLnPe05ZbYz5wBhTIdz7JLlFRZDrbbl4HcyHawFgmDGmRXj3SiJENoAHrLUtAHQCcIc+GxLgHgDr8tlmEYAqYG7sAgCw1h4BsNO7bKe1djOAbwAkGWMKWjvzPQAPIHiqwg3gMfozY8xuAJvAIPfGwA2ttR5r7TcAZoMBd+D6/QCeB1APDIglfy8DmGmtbQagLfL/jEgpYYypD3Ys7WCtbQUgFm7qkUSQqAhyAVwAYIO1dpM3f24y2KohpZy1dpe1drn3eQZ4oqof3r2SSGGMSQJbRt/OaztvK+sPAO4HUwAc33mXzfdu9yuANwB84C39Vc4YU8GbFxvsDtMrAHo5rw9wI4AnAZzvM10N4ApjTA1jzADv+1bzli27AEx7WOz93Z41xrQyxpTxlhW7DTxOHsj3D1PKGWOqgKko4wHmZVtrD4V1pyTSlAFQ0VsZJQ684JUIEy1Bbn2wVI5jBxTISABjTDKAdmCHHREAeAnAQwA8Bdh2HoBaYGDr+Na7zDdIvRvAa+DdpUNgWbCBAD4LfENr7W/W2m9sQJkbY0wnsPTX69ba3T7TpwA2ABgG4CDYivwrgCNgq/Bz1lonvSEOwAzvPmzyvl//AvyeAqQA2AfgX95Ulre9lS5EYK1NB++MbANz8A9ba2eFd68kmKgoIWaMGQygj7X2Fu/8DQAutNbeGd49k0hhjIkHg5Sx1trp4d4fCT9v2a4rrLW3G2O6Axhtre0X3r2SSOBNN1kMoIu1dokx5mUAR6y1fw3zrkkEMMZUAzANwBDwInIKgKnW2vfCuV+SW7S05KYDaOAzn+RdJgJvb/RpACYpwBUfXQD0N8ZsAVOcehhjdJISgHcDd1hrnbs+U8EqFiIAcBmAzdbafdbaUwCmA7gozPskQURLkPs9gMbGmBRv4fahAD4N8z5JBPD2JB8PYJ219oVw749EDmvto9baJGttMnjMmG2tvT7MuyURwFq7G8B2Y0xT76KeANaGcZcksmwD0MkYE+c9x/SEOiZGpDLh3oHCYK3NNsbcCRZLjwUwwVq7Jsy7JZGhC9hLfZUxZoV32WPW2i/Ct0siUgLcBWCSt+FkE4A/hXl/JEJ4U1imAlgOVvD5ERriNyJFRU6uiIiIiIivImnJrVmzpk1OTi6KtxYRERERAQAsW7Zsv7U2Mdi6Iglyk5OT8cMPPxTFW4uIiIhIpHniCU7FzBizNdS6aOl4JiIiIiLh4PEATz4Z7r3IJSo6nomIiIhIMcrKAj75BHjvPSAuLtx7E5SCXBERERHJn8cDxHiTAG6+GZg82X+9MXwcMyYsqQuBiqS6QocOHaxyckVERKS0O3XqFHbs2IHjx4+He1fO3MmTwNGjnGrXBsqW5TKPByhfnsHt1q1Ao0ZFtgsVKlRAUlISypYt67fcGLPMWtsh2GvUkisiIiJSRHbs2IGEhAQkJyfDOC2dJUF2NrBvH3DgAHDqFIPZWrWA+vWBihVzb3/0KNC8eZHsirUWBw4cwI4dO5CSklLg1ynIFRERESkix48fLzkBbnY2A1oniN25E6hUiS201aoBZfIIG+vVK7LdMsagRo0a2Ldv32m9TkGuiIiISBGK6ADX4wEOH2aL7eHDDGqbNWNA26YNUxMKogiDXODM/oYKckVERERKo927gV27gJwcBrWJiUCNGu76gga4EUp1ckVEREQiTVFUJzh2DEhPZ1oCAMTGAlWqAI0bA23bAg0bsiU3D3PnzkW/fv0AAJ9++inGjRsXcttDhw7hjTfeOO3dfOKJJ/D888+f9usCKcgVERERiTSFNbjCqVPAnj3A2rXAmjVsuc3M5LrEROCcc4AqVZDj8Zz2W/fv3x+PPPJIyPVnGuQWFgW5IiIiIsWle/fckxMIZmW5y3y3fecdzu/fn/u1eTl5Eli5EluWLEGzfv0w/Jln0Pz66zH4lluQlZWF5ORkPPzww0hNTcWUKVMwa9YsdO7cGampqbjmmmuQ6Q2GZ86ciWbNmiE1NRXTp0///e3feecd3HnnnQCAPXv2YODAgWjbti3atm2LhQsX4pFHHsHGjRtx/vnn48EHHwQAPPfcc+jYsSPatGmDMWPG/P5eY8eORZMmTdC1a1f8/PPPZ/SnDaScXBEREZFIMHYsMG+eO+88r1oVuOmmvF9rLXDkCDuQGQOkpADlyjEFIT4eP2/ejPH//je6dOmCESNG/N7CWqNGDSxfvhz79+/HoEGD8PXXX6NSpUp49tln8cILL+Chhx7CyJEjMXv2bJx33nkYMmRI0B9/991345JLLsGMGTOQk5ODzMxMjBs3DqtXr8aKFSsAALNmzcKvv/6KpUuXwlqL/v37Y/78+ahUqRImT56MFStWIDs7G6mpqWjfvv3Z/S2hIFdERESk+MydG3rd2LGcAAaqgQN21ayZ+/VZWQxsf/uNqQmxsf6dx2rVArKy0KBBA3Tp0gUAcP311+OVV14BgN+D1sWLF2Pt2rW/b3Py5El07twZ69evR0pKCho3bvz7a9PS0nLt+uzZszFx4kQAQGxsLKpUqYKDBw/6bTNr1izMmjUL7dq1AwBkZmbi119/RUZGBgYOHIg47/DA/fv3D/03Og0KckVERERKkhMnWPkgJgY4eBDYu5etvdWrsyNZTO5s1MASXM58JW9HM2stevXqhQ8++MBvO6cVtjBYa/Hoo4/iz3/+s9/yl156qdB+hi/l5IqIiIhEGp98VQCsiLB3L7B+PbBqFVMTAA6z27YtcO65HLAhSIALANu2bcOiRYsAAO+//z66du3qt75Tp05YsGABNmzYAAA4evQofvnlFzRr1gxbtmzBxo0bASBXEOzo2bMn3nzzTQBATk4ODh8+jISEBGRkZPy+zeWXX44JEyb8nuubnp6OvXv3olu3bvj4449x7NgxZGRk4LPPPjuNP1RoCnJFREREIo1TQiw7G9iwAVi5Eti2jfP16wPeW/soUybvkci8mjZtitdffx3NmzfHwYMHcdttt/mtT0xMxDvvvINhw4ahTZs2v6cqVKhQAWlpabjyyiuRmpqKWrVqBX3/l19+GXPmzEHr1q3Rvn17rF27FjVq1ECXLl3QqlUrPPjgg+jduzeuu+46dO7cGa1bt8bgwYORkZGB1NRUDBkyBG3btkXfvn3RsWPHs/nL/c7YwHyPQtChQwf7ww8/FPr7ioiIiJQk69atQ/PmzQv+AmtZ4uvUKaYfWMvW2/h45tpWrMh83dOwZcsW9OvXD6tXrz7NvY8swf6Wxphl1toOwbZXTq6IiIhIuB075nYgO3kSKF+e6QfGAKcTJMvv8g1yjTENAEwEUBuABZBmrX25qHdMREREpFRIT+cgDQA7jtWvz45kp9liG0pycnKJb8U9EwVpyc0G8IC1drkxJgHAMmPMV9batUW8byIiIiLRJSeHFRF++w1ISmJubdWqzKutXp1VE6RQ5BvkWmt3AdjlfZ5hjFkHoD4ABbkiIiIi+bEWOHyYge2hQ4DHw3SEU6e4vlIlTlKoTisn1xiTDKAdgCVB1o0CMAoAGjZsWBj7JiIiIlIyWQvs28fnHg+waRPLe9WowalSpUJLR5DgClxCzBgTD2AagHuttUcC11tr06y1Hay1HRITEwtzH0VERERKhg0bgCefBJo0AXr04LLYWKBZM6BNG6BRI1ZKUIBb5AoU5BpjyoIB7iRr7fSi3SURERGRCOfUsXX85z9A585A48YMchs0AO6/3x2aNy4u5EANviZNApKTuWlyMueLy9y5c7Fw4cKzeo/4+PhC2puzV5DqCgbAeADrrLUvFP0uiYiIiES4J58EWrQALr4YqFuXubZZWcCzzwLDhjHIBYB16wr8lpMmAaNG8W0AYOtWzgPA8OGFu/vBzJ07F/Hx8bjooouK/ocVg3wHgzDGdAXwLYBVADzexY9Za78I9RoNBiEiIiJRJTsbWLoUWLSI07RpXP7SS8A99zDvNkhLbeAABt27h/4RixcDJ07kXl6jBrB/P6fBg/3XzZ2b/67/4Q9/wPbt23H8+HHcc889GDVqFGbOnInHHnsMOTk5qFmzJsaPH49OnTohNjYWiYmJePXVVzF+/Hj069cPg70/ND4+HpmZmcjMzMSAAQNw8OBBnDp1Ck899RQGDBjgt01RKPTBIKy13wEoGYkjTzyR+/aBiIiIyOnasYPBbEIC0KcPcPw40KVL7u3uvZclwQoh/ggW4AIcI+JsTJgwAdWrV8exY8fQsWNHDBgwACNHjsT8+fORkpKC3377DdWrV8ett96K+Ph4jB49GgAwfvz4oO9XoUIFzJgxA5UrV8b+/fvRqVMn9O/fHybC8oyja8SzJ59UkCsiIiJn5p//BL7+msHt9u1c1qcPp/h44KuvgFatgDp12HEsn7vhweTV8pqczBSFQI0a8bFmzYK13AZ65ZVXMGPGDADA9u3bkZaWhm7duiElJQUAUL169dN6P2stHnvsMcyfPx8xMTFIT0/Hnj17UKdOndPfuSIUXUEuAPz977zqqlyZU/PmQMuW/CCmp3NdfDx7OoqIiEjps2uXm3Zw4AAwYQKXf/gh8OuvwEUXsRNZ587A+ee7r7vssiLdrbFj/XNyAfZXGzv2zN9z7ty5+Prrr7Fo0SLExcWhe/fuOP/887F+/fp8X1umTBl4PMxU9Xg8OHnyJABg0qRJ2LdvH5YtW4ayZcsiOTkZx48fP/OdLCIlP8h94gm24Dr+9jf/9fffD/zjH/zEOEngAAPdypWBhx5iLs3Bg8DNN7vBsRMo9+oFpKby9d9/77+ucmUWc46w5nkRERHxOnXKHUXstdcYE2zZwvly5YALL+QoZLGxwKefMqosiDFjCn1Xnc5ljz8ObNsGNGzIAPdsOp0dPnwY1apVQ1xcHNavX4/Fixfj+PHjmD9/PjZv3uyXrpCQkIAjR9wqscnJyVi2bBmuvfZafPrppzjlHbzi8OHDqFWrFsqWLYs5c+Zga7Dm5wgQHUGuk6JgDD/MGRnAkSOcqlbluthYIC3Nf92RI4C3qR5ZWbx6O3LE3SYnh8WaU1OBjRuDZ4uPHw+MGAGsWAHceKMb/DrTbbfxKnD7dt7mCFyfnFzwL9TZ/G1ERApCxw0p6fbudVtpFy1iA9XPP7OhKyEB6NABuOsuttKmprKxynE65+Mi+p4MH164lRT69OmDt956C82bN0fTpk3RqVMnJCYmIi0tDYMGDYLH40GtWrXw1Vdf4aqrrsLgwYPxySef4NVXX8XIkSMxYMAAtG3bFn369EEl76hsw4cPx1VXXYXWrVujQ4cOaNasWeHtcCHKt7rCmQhbdYUzzI8Jylrg2DH2lKxQAcjM5BfFN0A+cgTo1w9o3RpYvRr4y1/c5U6g/O67QO/ewCefAH/4Q+6fM2cOg+cPPwRuucU/AE5IAN58kzX3Fi8GPvss9/qePRmIHznCpHjf1uXC/HuIRDMFdi4dN6Qkyc4GVq0C6tcHatUCpk8Hrr6a68qUYRDbuTPwwAP+d3OLUbCKAHJmCr26QolSmLcOjPG/oouPBy69NPT2rVoBH38cev3ll/P2SGAQ3LIl1593HhNxAoNoJ3d4+XLW3svJ8X/fLVsY5L72Gu9vALwtU7kyn//2G1C9OvDf/wI//gjUrs2E+Tp1+LxuXaVbSOnm8TDl6fHHGdxZy+9QTAxPoCdOuMudycnrP34cOHrUfx3Aej+xse73PHB9UhLf3xnHPvD9mzThdrt2MZXKd11MjHvc2LKF7+G7vmxZN4dw/frc6ytWZEsWwDtQge8vEsmOH3c7hi1axJJeR48Cb7zBO6cXXgj87/8ysG3fnp93KbWiqyU32jmty76BcuvWzClavpxf+OnTgdmzc7/2wguBJUv8l8XGAidP8qT5978D8+e7wW+dOjwRDx3KbY8e5cGiAKO1iISdtbx427+f34s9e3gLc88eTn/7G4u4v/9+8PuC33/PQDAtDfjzn3OvX78eaNqUuX3eUjt+0tOBevVy9xlwHDnCOzEPPAC8EGSMHY+H+z9qFHt7+6pUiXeWAOC664APPvBfX6cOg2MAuOoq4PPP/dc3bgz88gufd+8OzJuX++c7UlL4va9Xj1P9+kC7dsA113D9nj28iHbyHUUKU04OsGYNv8N16wL9+/OisFo1nr/OP9/tHHbppdwmAqklt/CU7pbcaOe0LsfF8UTmKzWV0x13+G/vexGTlcWT0u7dfDx0yA1aY2N54vzuO64/fpy3dpwg95prgFmzeDvIaQlu0wYYN47r583jz3KC5KpV1UIshcdaXtQ5gWqjRrwI27qVdzic4NVZP2ECb1muWMEToyMhgZ/PAwdCB6A9ejCYA3hx+OyzbvqPMyUmcv2llwIvv5x7fZUqXH/VVQwOA9c7OYBDh/J7FLjeMXIkU5J815XxOWzffz8wZIj/+goV3PVPPgnceaf/et87VC++CBw+7L++Wzf3uDFmDFOxdu5katWuXcAVV7hBbmoqlyUmuoHwlVcCt9/O9bNmseZR/frcRhfJUhDjxgHffMOGmYwMLrv2Wn6Xq1Zl+l6rVrzgKyGstRFXQ7akOZNG2ahoyZ00qXB7IkaNM82tcwKKQ4f4BwWAjz4CfvqJAbATJCclAd66e2jblusd5crxgDRlCufHjOH7+qZLOIGKlF45OWxVdIJUJ1Dt1o257Dt2AF27crlveRpnhKH167m+dm1OtWrx8Y9/5K3Kw4fZ4cRZF+rWpfJQXXn9LTweXiw7Y9NPmMBOtTt3ulOfPjwIZ2fzOOC8V5ky/N7fcw9bv0+eBJ5/3r+VuF49XSCXFh4Pv78LF7KlNivLvSvRowfTaJxW2s6dgXPPLbGfi82bNyMhIQE1atRQoHuGrLU4cOAAMjIyfq/t64jqltxwj/Mc0c40R9kYt3Ob49prOYXy0Ue8ResbBPsm+U+ZwmDD43GXDRvG28UAbztVquSfLnHxxWwps5ZXMLVr+7dSnQ51LCoe2dnAvn38P9evz//diy+6nwlnGjiQKQMnTzJtwFdsLG9/9+7N25IXX+wGsU6w2ro1t23WjCkJoVSpAlxwQdH9vtEor+NGTIwb4AKsLBOKMWxxS0/3D4Kd48KePW4/Al//+7/Agw9y2/vu8w+A69Xj/75GjTP73SR8MjJ4JwUAnnqKqT6HDnG+enX3DoIxrEQURbXsk5KSsGPHDuzbty/cu1KiVahQAUmn2TBW4lty8xodxCmDJxEiJ4cBiRPwVKsGdOzI5Tfc4KZS7N7NzjIPPsgTXkaGG3BXqeK2BP/5zwyUjx5lkO0Ex04g5JsnqJY6f6cT9J844Z/PumcP/w9OD+ZrrwXWruXyAwf4dx482G3Fr1GD/yPfQLV/f96KB/i/q1nTXVe9evHf1tZFUHgcO8Z0B99AuFs3tsKvXs3PWHo6Pz+OSZOYi7x4MfCnP7nBrxMMDxzIQNoZH9W3PNSZ0Gfj9FnLOzROK+2iRcyt3bWL3/F//5upcU4rbZMmJbaVVsIvr5bcEh/kxsSEjl3GjWMjTvv2/o2SUgKcPMmax5UqsZl+8mQ3AHaC5FtuYXC8bl3u1kCAHXZuuQXYtIm3uoYNY+tATAwfb7+dnYvWrQNef91/XUwMX9ukCQO4jz7KvX74cJ5U161j/pjv+thYloyrVo0H+x9/9H9tbCxzLePigM2buY+B6zt0YKC+cydbRwPXn3ceTwwHD/Jv5PuzY2L4s52/pdMrPzaWr/H94sybx9/BN5CtVs3t8NShA7Bsmf/ftnNnnsAApgZkZPgHsS1bApdcwvWZmfw/6iQmZ8JJn9q5kwFvy5a8mP3xR+Dpp/1biU+e5Oe5Wzfe+r7uOl5A+QbCY8YwDWvHDh5L6tXjZzZUy6EukF2hAv7MTFY5aNGC/5uJE1k3HmD6SadOPGbceisbIEQKUVQHuaFacsuU4Z1TgMeoZs0Y8I4YweOfRJHsbPeE5RsE79zJ3vGBqlThbbP/+z92ovnmG7ZGejxsVXYeP/+cgeiUKcFTNRYt4sH7X/8Kftt21Sp2jnjlFeYhBtqyhbccxo5ljeVABw6wVfORR9j5KdDJkwyC77yTQbqv8uXdHNYbb+RJJ5Dz3R8wgKP8AAxua9dmZ6gPP+SyyZN5EnPyXZ2W8qIaxETkTFjL70xCAj//q1axrKNvELxzJ1sQU1KA557jiJcAL/rq1GHA+8UX7CS3cCEv/m65hccA5yKxf3+eVNauZdDte+FZtiyPCQAvXg8d8r/wLF8eOOccrt+3j63Nga93Oi06LdG+F7fh5gT8mZn82zottT/9xOOm07CwYwfLVnbuzJNvJOy7RK2oDnIDc3IBnnvT0tj/4YcfeIG5dCk7aj7/PBueVq/md/G119hQ5cQLamyKUmfbGmNt7iC4QgW3VmpGhv86j4flbMqVY+rFnj251zvl37ZvZ8AbuL5HD34o16xha3Dg+mHDePJYsIDb+K4zhsEvwGB91Srmuc2Zk/t3u/dedgRKTOT+iJQGW7cCK1e6LcTO4yefAM88E7zyBuB+v0aOBN5+239dfLxbDWDoUPdC0VG3Ln8OwIGE/vMf//VNmrDvAsA7IfPn+6/v2JEnM4C56itX+gfBF1/MMpIA+zNs3ep/h6dHD570AKBvXx6bfF/fu7d7wT1oUO4gfPp0Hgv37+fxIiHBbaV1JidIl1Ij3J3/o7rjWX7jPF9+OSfAjVMApniVL++O+jthAvDoo2zt9Z1q1y7WX0cilTHuiSJQhQp5d4irXp1TKA0a5D0ST8uWbvH/YLp04RRKv36cHn3UXaZbsFLaNWrEKZgnnuD3ZdcutvquWsULyJwctyXk4Yd5lyTw4tIxejQDXd+LU9/84Lvv5l0U39f75tWNGsWWGufnejz+pSMHDXL7NDjv0bixu75jR1avcdbl5PhXs3GOSb6v9z0mHDjAE6XTodjh/I633867VFHUQUxOX6R3/i/xLbmFZf58/rOWLnWPZwCDZt+gt0sX/zKVUkKo84g/BbkiBaPvikt/C/FhLa+bnJsTvoqz839Ut+QWlm7d3FzdrCz2aXDSHJYuBaZO5UW4cydq8mTeyXFy6yXCKcD1V5hDYItEM31XRH535AhHVf7yS2DmzOABLsA765Eg3yDXGDMBQD8Ae621rYp+l8IvLi73HeD9+5kq5VSlevdd5t47Qe6oUexA7rT4nnOO8nslginoFykYfVdcCvhLHWt5d/vLLzktWMC+3pUrA5ddxkbB337L/TpnHKlwyzddwRjTDUAmgIkFDXJLYrrC6bKWVzRVqvB5r17saHrsGNdXr+6f5tCxoyqniIiISMmwaBFLnjuttW3bsr9i377sY1i2bN6d/4srJ/es0hWstfONMcmFvlclnO/w9Maw+T47m53cnUoOS5dyYBens5sztoHHw6uh9u1VhUlERETCLzOTVTWHDQNuu413pC+6iEFtnz6ssBcov87/4VagjmfeIPfzvFpyjTGjAIwCgIYNG7bfGqx4bSmUmQksX86At107ll1dvx5o3pzlVW+6iRWkZs5ki2/LlurYJiIiIkXn4EFWlfzyS6YevPwylw8cyMIdN9wQ3v07HcXS8cxamwYgDWC6QmG9b0kXH+/fqQ1gb8TPPmNLLgDMnu2W3IiL43LfVIdGjZTfKyIiImfG42GHeqfD2KJFXFatGjBkiLvdjBnh28eiUGgtub5KQ05uYbIW2LjRv5rD8uXugDeJiQx209J4u8Dj0QAyIiIikrdvvuGAlzNncuR2gANgOSkIF1xQ8u8e59WSq1ApAhgDnHceh1l/6SV2YMvIAJYtA958k3X8t293a3c/8gjTGpxc361b3Q5vABPBk5MZCCcnc15ERESi29atwP/8DzvGA8DixRxYr2dPBrt79gDff89tLrqo5Ae4+SlIdYUPAHQHUBPAHgBjrLXj83qNWnKL1uTJvO3w7LOc79qVHd1at+ath+++4zDFjuLu6SgiIiJFb/9+4L//Bdq0YQwwdy5Hb/7qKwa2WVms8R/NA9Pl1ZKrEc+igFO7bulSVnkI9i+tWRPYt6/4901EREQKR04OW2Kd3Nrvv+c5//HHWc3p1Cng0CGmOZYWCnJLkZiY0KMuWsspNZUd3W67jV+YbduY1qDObSIiIpFlzx621s6cCcyaBRw4wHP9hRcyr7ZvX3ZYL619dTSsbynSsCFzcgI1aMDHrCygaVOgRg3Ob9gANGvGEiJt2gDnn8+Cz23bAq1aARUrFtuui4iIlHo5OcCWLcC553L+oouATZs4oNSVVzKo7dXLPY9LaApyo8zYscFHH3nmGT6vVIk5vY4aNZivu2IFsHIlhyvOyOC6mBgGxG3bAqNH80rR42GLr1p9RURECsfevUwxMAa49Vbgk0+A3bt5Hn7tNQa47dqV3tbaM6UgN8qc7ugjNWsCI0e68x4PsHkzA96VKxn8LloEHD3K9R9/zC/g/PlsAd6yhQNeNG3KIf5EREQkb9nZrHzg5NYuX86Bopo2BUaMYEutUy60b99w723JpZxcOS2LF7Pl97XX2EL88MMcqrhcOaY3OKkOTtpD1arh3mMREZHw27mTAe3Mmax+cOgQqx44Q+f+6U9AnTrh3suSRx3PpMhs2sSWXifdYeVKt+A0wJbkdu2AadP4ZT58GEhI0C0XEREpHTZt4lC5K1dyvl49dzCGyy5TY9DZUsczKTLnnMPJNx1i927/oNe5WgW43b59rOsLAJ9/zjyk1q3ZMiwiIhLJJk3KOyXQWg7u1Lo18NhjQP36bKEdNozBbevW6tdSXBTkSqGrU4dXqH365F73xz+6o7NZC9x0k1sOpXFj/1SHtm15xauDgYiIRIJJk/w7d2/dCtx8M/DBB0Dt2sD48Txn+d6tLF+eKQpS/JSuIGFjLTuuOa2+zuOWLe42NWsC99wD/OUvnF+9Wp3cRESk+FnLcpzp6cHX9+4NfPFFdI8uFonySldQZqSEjTFASgowcCDwxBOs3LB5M3DwIDBvHvDKK8CAAW6N3z17eJvntdfc+RdfBGbPZmtwMJMmcaCLmBg+TppU9L+XiIiUbAcPcgTRU6c4/8ILzJ0NFeAawwEbFOBGFqUrSMSpWhXo1o2Tr7g44P33gQ7e67UffgDuv99dn5Tkn+qwbRvwt7/531YaNYrPQ5VUExGR0mPvXmDNGk6rVwN33QW0bAn85z/ADTdwWcuWQJMmwPXXMy3h4MHc79OwYfHvu+RP6QpSou3Z41/Td+VK1hrMyQn9mgYNGABnZwNldJknIhL1srLYMOIb0K5ZA+zf725TrRowcSLQrx/PLWvXcuhc307RgTm5ANenpanxJFxUQkxKlePHefDqEPQjz9tKHg/w0EPAW2+x52tSkjsFzteooc5vIiIlQU4OUwYyM1kB4YorgMsvB378EUhN5TYJCazr3rKlO7VqxU7TBTnW51ddQYqXSohJqVKhAocgbtSIKQqBnNtKl1wCnDwJ7NjB6auvgF27GAA7jGHQXK4cc4TXrmVgDABLl/IxKYm9apWLJSJSPDIyeDx2WmSd1tmrruIxumJFprelpDDIbdGCo4u1bMlj9tk0XAwfrqC2pFCQK1Fr7Njgt5XGjuXzK6/k5Cs7m7epnMD3wAEGuACX+wbNDzwAfPcdn8fGstxZYEtwkyY86ALuEI0iIlIwTsssAIwZw5SD1avZiuqoWBFo3hzo2ZONFwBfs3evG8yWLx+8rKVEN6UrSFQryttKa9dyJBsnIHam9HRg+3bg6FGgY0e3xbdjR+C889hxAWBZtEqVcqdJaFAMESltjh1jf4r0dObEAsCQIRxcaN48znfuzO180wxatmRrre6klV5KV5BSqyhvK7VowSkYa4EjRzg5rruO+b3O+rffZutwoGrV3MC3f3/g1lu5/OuvgWbNuPxMKZdMRMLpxAng55/dFAMnzWDTJt7tqlCB+bSxsUCvXv7H0IUL1T9CTo+CXJEiYAxQpQonx333+a/fvZupFOnp/q3Avq3CO3dy++PHecD/n/8B/vpXvrZ79+Ad5Zz5mjX90yOCjdSjkmoiUlR27WJK16BBDFrHjOGFtVP9JjbWHenyuuvcllknkL3lFv/3U4Arp6tA6QrGmD4AXgYQC+Bta+24vLZXuoJI4crOBpYsYd5vSgrTIR54wD8YDiybVq4c8OqrDGR37WIrsG+riCMpibcJ4+J0EhGR3PK6A5SdDWzc6N8yO2YMc2T/9S9gxAjg11+ZqvXFF2yNdYLZpk2ZKytyNs6qhJgxJhbALwB6AdgB4HsAw6y1a0O9RkGuSPHKyWHqQ2BL8B/+wDy2JUuATp3yfo+YGCA+nuV1EhKAypVZUeLCC4Fly4AJE3iiq1ePtxd/+MHdNtikoZdFSr5gdWHLlmWJxqwsXiCfOOGuS0kBxo8HLr0U2LePx6GWLd0OvCKF7Wxzci8AsMFau8n7ZpMBDAAQMsgVkeLlVHeoV48d3AJdeCFbYHx7JDuqVwcefpgleXynI0eYHwcwtWHyZDfl4ssvWWc4L+XLAz/9xAoTkyaxrM/MmexsN20asGBB6AC5cmU+nntu0VakUI6yRJPsbOazOulS1gLz5zOF6bzz2Bl2/Hhuk5nJeee57zRiBHDHHbxDdP31uX/OqVPsUNu7N9OonJqzzZvz++1ITOQkEi4FCXLrA9juM78DwIWBGxljRgEYBQANNb6dSMR5+ungJdVeeSX/wG7QIE6O224DBg8OHhj7ztesye3LlGHrj3Nr8vvv2fEuIyPvn3vsGAPt++4Dpk93S7g99hhbpwODYt+pRg123AOY3hETw5O9QznK/hTw+yvKv4e1/Nz5Bpa+AWeVKiyHBQD/+AdTioYM4fzVVwOHDwcPTp0W1RtvBN55h8Fur15MbXrmGeb233MPt4mN5fekUiXewXGmunU5tDrgPgbj8TD9QCSSFVrHM2ttGoA0gOkKhfW+IlI4nBN0YZy4nRNiQQ0Z4p6kAWDcOE4eD0/ugcGyMzlB8cUX+3fii4nhCXvfPv8A+9Qpd5u6dd0g9/bbmcqxfLn7fgsX+g/8ATDwuPlmBgjNm/MCAGBLd2IiMHo050eP5rZO8F6mTO6pWTNg4EBu/+67bJXu2pUBzkcfBX+N71SnDgc0sZY5jTVrstU9J4f1m4O95kxbvRXw+8vr79GuHTt+hmoFzczkRdczz3D7O+7g5zItjfNt2wKrVvH/Gsoll7hB7j//CVxwgfv92b2bj1Wq8KLNN0B1ptat3ff66it+jgBWbtm/n9uUK5d/Dn5cXP6D6ohEsoIEuekAGvjMJ3mXiUgJE2kj9cTEuC2veQlsSX7qqeDbnTjhBr2+eYIPPMCgxDFggDuQR7D3cFrZHD//7D//5ZcsNJ+dnXtyDBzoBrmjRwPXXssgNzsbGDo0798XYGv5G28wqG3aFPj731lbOT3dDVoCGeMGvE88wZSS9HQGSS+8wEBp5Ur2ZPcNzles8P97AfwbPP44cP75wF13Ac8+y1SYefO4H9bmPb3yCvPBv/mGv/9HH7En/Qcf8K5Cfq///HP+3hMnsiPTsmUM8p99lh0qQ70O4OP69WzN//vfuf3evVx3663Ae+/l/bNzcnIHoc7fo3FjlvMLpnx5BpBNmrjLEhL8O4XecAM/n/HxuVtRnWXVq7vbr1vnH4wuWBDiAxOCMzgCwO+bU8awoPIbVEckkhUkyP0eQGNjTAoY3A4FcF2R7pWIyBkoX56Tkybh6N7df370aOC114K3UDVqlDuQ+Phj//k1a4L/fCdA8g12AXbUc1qlY2MZuAQGx6dO+c838DYtGMOWRad1rmpV4PXXgwfYvu+Tmur+Ta64wq2vXKECW6l9XxMY4DqcHO7sbLfVOyaGAbIxeU9OR6O4OP4uZbxnmypVGCjm9/qKFbl9vXpAt25uR8bGjTlMa16vdX5vgC2vvhd23bszkMzr9U4rbLC/x7RpvOAJFpyWCXJGHRdQi8i5G1BQ4a54Uph3gESKW0FLiF0B4CWwhNgEa22e13CqriAikS5Yr/G4ON5WLm0n8OTk0AH/li3FvTfhp7+HSMmRV3WFAmVwWWu/sNY2sdaem1+AKyJSEgwfzoC2USO2ljVqVDoDXIAtc4HDSZfmW9L6e4hEhyIsziMiEtmGD2fLnMfDx9IY4AIK+APp7yESHQqUrnDab2rMPgBBbvYUuZoA9ofh50rk02dD8qLPh4Siz4aEos9GZGhkrQ1akblIgtxwMcb8ECovQ0o3fTYkL/p8SCj6bEgo+mxEPqUriIiIiEjUUZArIiIiIlEn2oLctHDvgEQsfTYkL/p8SCj6bEgo+mxEuKjKyRURERERAaKvJVdEREREREGuiIiIiESfqAlyjTF9jDE/G2M2GGMeCff+SGQwxjQwxswxxqw1xqwxxtwT7n2SyGKMiTXG/GiM+Tzc+yKRwxhT1Rgz1Riz3hizzhjTOdz7JJHDGHOf95yy2hjzgTGmQrj3SXKLiiDXGBML4HUAfQG0ADDMGNMivHslESIbwAPW2hYAOgG4Q58NCXAPgHXh3gmJOC8DmGmtbQagLfQZES9jTH0AdwPoYK1tBSAWwNDw7pUEExVBLoALAGyw1m6y1p4EMBnAgDDvk0QAa+0ua+1y7/MM8ERVP7x7JZHCGJME4EoAb4d7XyRyGGOqAOgGYDwAWGtPWmsPhXWnJNKUAVDRGFMGQByAnWHeHwkiWoLc+gC2+8zvgAIZCWCMSQbQDsCSMO+KRI6XADwEwBPm/ZDIkgJgH4B/eVNZ3jbGVAr3TklksNamA3gewDYAuwActtbOCu9eSTDREuSK5MkYEw9gGoB7rbVHwr0/En7GmH4A9lprl4V7XyTilAGQCuBNa207AEcBqK+HAACMMdXAu8UpAOoBqGSMuT68eyXBREuQmw6ggc98kneZCIwxZcEAd5K1dnq490ciRhcA/Y0xW8AUpx7GmPfCu0sSIXYA2GGtde76TAWDXhEAuAzAZmvtPmvtKQDTAVwU5n2SIKIlyP0eQGNjTIoxphyYAP5pmPdJIoAxxoB5deustS+Ee38kclhrH7XWJllrk8FjxmxrrVpjBNba3QC2G2Oaehf1BLA2jLskkWUbgE7GmDjvOaYn1DExIpUJ9w4UBmtttjHmTgD/BXs5TrDWrgnzbklk6ALgBgCrjDErvMses9Z+Eb5dEpES4C4Ak7wNJ5sA/CnM+yMRwlq7xBgzFcBysILPj9AQvxFJw/qKiIiISNSJlnQFEREREZHfKcgVERERkaijIFdEREREoo6CXBERERGJOgpyRURERCTqKMgVERERkaijIFdEREREos7/A6y0gzbCRwkbAAAAAElFTkSuQmCC\n", 494 | "text/plain": [ 495 | "
" 496 | ] 497 | }, 498 | "metadata": { 499 | "needs_background": "light" 500 | }, 501 | "output_type": "display_data" 502 | } 503 | ], 504 | "source": [ 505 | "from time_series.utils import evaluate_model\n", 506 | "\n", 507 | "y_predicted = gp.predict(tds2d.X_test)[0].numpy().reshape(-1, tds2d.dimensions, tds2d.n_steps)\n", 508 | "evaluate_model(tds=tds2d, y_predicted=y_predicted, columns=train_df.columns, first_n=10)" 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": null, 514 | "id": "230c78ac-57b4-400f-bd16-1cafa36a68f2", 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [] 518 | } 519 | ], 520 | "metadata": { 521 | "kernelspec": { 522 | "display_name": "Python 3", 523 | "language": "python", 524 | "name": "python3" 525 | }, 526 | "language_info": { 527 | "codemirror_mode": { 528 | "name": "ipython", 529 | "version": 3 530 | }, 531 | "file_extension": ".py", 532 | "mimetype": "text/x-python", 533 | "name": "python", 534 | "nbconvert_exporter": "python", 535 | "pygments_lexer": "ipython3", 536 | "version": "3.8.8" 537 | } 538 | }, 539 | "nbformat": 4, 540 | "nbformat_minor": 5 541 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.19.0 2 | pandas~=1.2.4 3 | tensorflow>2.5.0 4 | pytest 5 | requests~=2.25.1 6 | pyreadr~=0.4.2 7 | scikit-learn~=0.24.1 8 | statsmodels~=0.12.2 9 | fastcache~=1.1.0 10 | tensorflow-addons 11 | gpflow~=2.2.1 12 | scipy~=1.6.2 13 | joblib~=1.0.1 14 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = .git 3 | max-line-length = 99 4 | ignore = W503, W391, E203 5 | max-complexity = 10 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | 4 | with open("requirements.txt") as f: 5 | install_requires = [ 6 | req 7 | for req in 8 | f.read().strip().split("\n") 9 | if req 10 | ] 11 | 12 | print(install_requires) 13 | 14 | setup( 15 | name="time-series", 16 | version="0.2", 17 | description="Time-Series models with keras and Tensorflow", 18 | author="Ben Auffarth", 19 | url="https://github.com/benman1/time-series/tree/master", 20 | install_requires=install_requires, 21 | packages=find_packages(), 22 | ) 23 | -------------------------------------------------------------------------------- /tests/test_dataset.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import unittest 3 | from time_series.dataset.time_series import TimeSeries 4 | 5 | 6 | class TestRecurrentTs(unittest.TestCase): 7 | def setUp(self): 8 | 9 | self.data_to_pad = pd.DataFrame( 10 | { 11 | "feature_1": [i for i in range(6)], 12 | "feature_2": [i for i in range(6)], 13 | "target": [i for i in range(6)], 14 | } 15 | ) 16 | 17 | self.input_data = pd.DataFrame( 18 | { 19 | "feature_1": [i for i in range(100)], 20 | "feature_2": [i for i in range(100)], 21 | "target": [i for i in range(100)], 22 | "category": [str(int(i // 10 + 1)) for i in range(100)], 23 | } 24 | ) 25 | 26 | self.data_to_pad_with_categorical = pd.DataFrame( 27 | { 28 | "one_hot_yes": [1, 1, 1, 1, 1, 1], 29 | "feature_2": [i for i in range(6)], 30 | "one_hot_no": [0, 0, 0, 0, 0, 0], 31 | "target": [i for i in range(6)], 32 | } 33 | ) 34 | self.data_to_pad_with_multiple_categorical = pd.DataFrame( 35 | { 36 | "one_hot_yes": [1, 1, 1, 1, 1, 1], 37 | "feature_2": [i for i in range(6)], 38 | "one_hot_no": [0, 0, 0, 0, 0, 0], 39 | "other_no": [0, 0, 0, 0, 0, 0], 40 | "other_yes": [1, 1, 1, 1, 1, 1], 41 | "target": [i for i in range(6)], 42 | } 43 | ) 44 | 45 | def test_len_padding(self): 46 | rec_instance = TimeSeries(pandas_df=self.data_to_pad) 47 | results = rec_instance._pad_ts(pandas_df=self.data_to_pad, desired_len=10) 48 | self.assertEqual(results.shape[0], 10) 49 | 50 | def test_zero_len_padding(self): 51 | rec_instance = TimeSeries(pandas_df=self.data_to_pad) 52 | results = rec_instance._pad_ts( 53 | pandas_df=self.data_to_pad, desired_len=6 54 | ) # len is the same as the original time series 55 | self.assertEqual(results.shape[0], 6) 56 | 57 | def test_next_batch_production(self): 58 | rec_ts = TimeSeries(self.input_data) 59 | X_feature_space, y_target = rec_ts.next_batch(batch_size=4, n_steps=10) 60 | self.assertEqual(len(X_feature_space), 4) 61 | self.assertEqual(len(X_feature_space[0]), 10) 62 | self.assertEqual(len(X_feature_space[0][0]), 2) 63 | self.assertEqual(X_feature_space[3][0][0], y_target[3][0][0]) 64 | 65 | def test_padding_with_one_hot(self): 66 | rec_ts = TimeSeries( 67 | pandas_df=self.data_to_pad_with_categorical, one_hot_root_list=["one_hot"] 68 | ) 69 | results = rec_ts._pad_ts( 70 | pandas_df=self.data_to_pad_with_categorical, desired_len=10 71 | ) 72 | 73 | self.assertEqual(results.shape[0], 10) 74 | self.assertEqual(results.one_hot_yes.values[0], 1) 75 | self.assertEqual(results.one_hot_no.values[0], 0) 76 | 77 | def test_padding_with_one_hot_multiple(self): 78 | rec_ts = TimeSeries( 79 | pandas_df=self.data_to_pad_with_categorical, 80 | one_hot_root_list=["one_hot", "other"], 81 | ) 82 | 83 | results = rec_ts._pad_ts( 84 | pandas_df=self.data_to_pad_with_multiple_categorical, desired_len=10 85 | ) 86 | 87 | self.assertEqual(results.shape[0], 10) 88 | self.assertEqual(results.one_hot_yes.values[0], 1) 89 | self.assertEqual(results.one_hot_no.values[0], 0) 90 | self.assertEqual(results.other_yes.values[0], 1) 91 | self.assertEqual(results.other_no.values[0], 0) 92 | 93 | def test_next_batch_covariates(self): 94 | """ 95 | Feature space is supplied in input if target_only is False (no need to lag y dataset) 96 | """ 97 | rec_ts = TimeSeries(self.input_data) 98 | X_feature_space, y_target = rec_ts.next_batch(batch_size=1, n_steps=10) 99 | self.assertEqual(len(X_feature_space), 1) 100 | self.assertEqual(len(X_feature_space[0][0]), 2) 101 | 102 | def test_sample_ts(self): 103 | """ 104 | When the length of the pandas df is longer than required length the function should sample 105 | from the time series and return that sample 106 | """ 107 | rec_instance = TimeSeries(pandas_df=self.data_to_pad) 108 | results = rec_instance._sample_ts(pandas_df=self.data_to_pad, desired_len=3) 109 | self.assertEqual(results.shape[0], 3) 110 | 111 | 112 | if __name__ == "__main__": 113 | unittest.main() 114 | -------------------------------------------------------------------------------- /time_series/__init__.py: -------------------------------------------------------------------------------- 1 | """Init.""" 2 | -------------------------------------------------------------------------------- /time_series/dataset/__init__.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | 3 | 4 | class Dataset(ABC): 5 | def __init__(self): 6 | super().__init__() 7 | 8 | def next_batch(self, **kwargs): 9 | pass 10 | -------------------------------------------------------------------------------- /time_series/dataset/time_series.py: -------------------------------------------------------------------------------- 1 | """Time-series data classes.""" 2 | from dataclasses import dataclass 3 | import logging 4 | from typing import Union 5 | 6 | import numpy as np 7 | import pandas as pd 8 | 9 | import tensorflow as tf 10 | from statsmodels.tsa.tsatools import lagmat 11 | 12 | from time_series.dataset import Dataset 13 | 14 | 15 | ArrayLike = Union[np.ndarray, pd.Series, pd.DataFrame] 16 | LOGGER = logging.getLogger(__file__) 17 | 18 | 19 | class TimeSeries(Dataset): 20 | def __init__( 21 | self, pandas_df: pd.DataFrame, n_steps: int = 1, batch_size: int = 10, 22 | ): 23 | super().__init__() 24 | assert isinstance( 25 | pandas_df, (pd.Series, pd.DataFrame) 26 | ), "Must provide a Pandas df to instantiate this class" 27 | self.batch_size = batch_size 28 | self.n_steps = n_steps 29 | self.dimensions = len(pandas_df.columns) 30 | 31 | data = np.array(pandas_df, dtype=np.float32) 32 | self.ds = tf.keras.preprocessing.timeseries_dataset_from_array( 33 | data=data, 34 | targets=None, 35 | sequence_length=self.n_steps, 36 | sequence_stride=1, 37 | shuffle=True, 38 | batch_size=self.batch_size, 39 | ) 40 | 41 | def __next__(self): 42 | """Iterator.""" 43 | return self.ds.next() 44 | 45 | 46 | def sample_to_input(sample: pd.DataFrame, lag: int, two_dim: bool = False) -> ArrayLike: 47 | """Reshape a time-series to be suitable for the models. 48 | 49 | Arguments: 50 | sample (pd.DataFrame): time x value columns. 51 | lag (int): the number of previous steps to use as predictors. 52 | two_dim (bool): whether to reshape as 2D (default 3D) 53 | Output: 54 | points x time/lag x columns or (for 2D) time x (columns*lag) 55 | """ 56 | in_dim = sample.shape[1] 57 | # drop rows with unknown values both at beginning and end 58 | if two_dim: 59 | return lagmat(sample.values, maxlag=lag, trim="both") 60 | else: 61 | return np.concatenate( 62 | [ 63 | np.expand_dims( 64 | lagmat(sample.values[:, i], maxlag=lag, trim="both"), axis=2 65 | ) 66 | for i in range(in_dim) 67 | ], 68 | axis=2, 69 | ) 70 | 71 | 72 | @dataclass 73 | class TrainingDataSet: 74 | """Utility class that can be used for training and testing. 75 | 76 | Create lags and split between train and test. 77 | 78 | Attributes: 79 | lag, train_split, X_train, y_train, X_test, y_test. 80 | """ 81 | 82 | X_train: ArrayLike 83 | y_train: ArrayLike 84 | X_test: ArrayLike 85 | y_test: ArrayLike 86 | 87 | def __init__( 88 | self, 89 | df: pd.DataFrame, 90 | lag: int = 10, 91 | train_split: float = 0.8, 92 | two_dim: bool = False, 93 | ): 94 | self.lag = lag 95 | self.train_split = train_split 96 | self.two_dim = two_dim 97 | lagged = sample_to_input(df, lag, two_dim=two_dim) 98 | y = np.roll(lagged, shift=-lag, axis=0) 99 | split_point = int(len(df) * train_split) # points for training 100 | self.X_train, self.X_test = ( 101 | lagged[:split_point, ...], 102 | lagged[split_point:, ...], 103 | ) 104 | self.y_train, self.y_test = ( 105 | y[:split_point, ...], 106 | y[split_point:, ...], 107 | ) 108 | 109 | @property 110 | def n_steps(self): 111 | """How many steps (lags) to use as predictors.""" 112 | return self.X_train.shape[1] if not self.two_dim else 1 113 | 114 | @property 115 | def dimensions(self): 116 | """Number of dimensions.""" 117 | return self.X_train.shape[-1] 118 | 119 | @property 120 | def n_classes(self): 121 | """Number of classes. 122 | 123 | This is appropriate for classification tasks. 124 | """ 125 | return len(np.unique(self.y_train)) 126 | 127 | @property 128 | def input_shape(self): 129 | """The input shape for a model.""" 130 | return self.X_train.shape[1:] 131 | 132 | @property 133 | def output_shape(self): 134 | """The input shape for a model.""" 135 | return self.y_train.shape[1:] 136 | 137 | @property 138 | def exo_dim(self): 139 | """This class doesn't handle exogenous attributes.""" 140 | return 0 141 | 142 | @property 143 | def horizon(self): 144 | """How many steps to forecast to?""" 145 | return self.y_train.shape[1] if not self.two_dim else 1 146 | -------------------------------------------------------------------------------- /time_series/dataset/utils.py: -------------------------------------------------------------------------------- 1 | """Utility functions for data loading.""" 2 | import requests 3 | import pyreadr 4 | import numpy as np 5 | import pandas as pd 6 | from sklearn.preprocessing import StandardScaler 7 | from fastcache import lru_cache 8 | 9 | 10 | @lru_cache(maxsize=1, typed=False) 11 | def get_energy_demand(scale: bool = True): 12 | resp = requests.get( 13 | "https://github.com/camroach87/gefcom2017data/raw/master/data/gefcom.rda", 14 | allow_redirects=True, 15 | ) 16 | open("gefcom.rda", "wb").write(resp.content) 17 | result = pyreadr.read_r("gefcom.rda") 18 | df = result["gefcom"].pivot(index="ts", columns="zone", values="demand") 19 | if not scale: 20 | return df 21 | return pd.DataFrame(data=StandardScaler().fit_transform(df), columns=df.columns, index=df.index) 22 | 23 | 24 | @lru_cache(maxsize=1, typed=False) 25 | def get_ford(train: bool = True): 26 | """Classification dataset.""" 27 | root_url = "https://raw.githubusercontent.com/hfawaz/cd-diagram/master/FordA/" 28 | filename = root_url + "FordA_TRAIN.tsv" 29 | if not train: 30 | filename = root_url + "FordA_TEST.tsv" 31 | data = pd.read_csv(filename, sep="\t") 32 | y = data.values[:, 0].astype(int) 33 | x = data.values[:, 1:] 34 | y[y == -1] = 0 35 | return np.expand_dims(x, -1), y 36 | -------------------------------------------------------------------------------- /time_series/models/LSTM/__init__.py: -------------------------------------------------------------------------------- 1 | """Recurrent neural network.""" 2 | from typing import Sequence 3 | 4 | import tensorflow as tf 5 | 6 | from time_series.dataset.time_series import TrainingDataSet 7 | from time_series.models.transformer import Transformer 8 | 9 | 10 | class LSTM(Transformer): 11 | """Forecasting with an LSTM.""" 12 | 13 | def __init__(self, data: TrainingDataSet, lstm_units: Sequence[int] = (100,)): 14 | super().__init__(data) 15 | self.lstm_units = lstm_units 16 | 17 | def recurrent_layers(self, inputs): 18 | x = inputs 19 | for i, dim in enumerate(self.lstm_units): 20 | x = tf.keras.layers.LSTM(units=dim, return_sequences=True)(x) 21 | x = tf.keras.layers.Dense(self.data.dimensions)(x) 22 | return x 23 | 24 | def build_model(self): 25 | """Build model.""" 26 | inputs = tf.keras.Input(shape=self.data.input_shape) 27 | lstm_output = self.recurrent_layers(inputs) 28 | self.model = tf.keras.Model(inputs, lstm_output) 29 | self.model.compile( 30 | loss="mse" if self.regression else "sparse_categorical_crossentropy", 31 | optimizer=tf.keras.optimizers.Adam(), 32 | metrics=self.metrics, 33 | ) 34 | print(self.model.summary()) 35 | -------------------------------------------------------------------------------- /time_series/models/TCN/__init__.py: -------------------------------------------------------------------------------- 1 | """Temporal Convolutional Neural Network. 2 | 3 | Based on the implementation by Philippe Remy: https://github.com/philipperemy/keras-tcn 4 | """ 5 | import inspect 6 | import logging 7 | from typing import List, Optional 8 | 9 | import numpy as np 10 | from tensorflow.keras import backend as K, Model, Sequential, Input, optimizers 11 | from tensorflow.keras import layers 12 | from tensorflow.keras.layers import Activation, SpatialDropout1D, Lambda 13 | from tensorflow.keras.layers import ( 14 | Layer, 15 | Conv1D, 16 | Dense, 17 | BatchNormalization, 18 | LayerNormalization, 19 | Reshape 20 | ) 21 | 22 | from time_series.dataset.time_series import TrainingDataSet 23 | from time_series.models.transformer import Transformer 24 | 25 | 26 | LOGGER = logging.getLogger(__file__) 27 | 28 | 29 | def is_power_of_two(num: int): 30 | return num != 0 and ((num & (num - 1)) == 0) 31 | 32 | 33 | def adjust_dilations(dilations: list): 34 | if all([is_power_of_two(i) for i in dilations]): 35 | return dilations 36 | else: 37 | new_dilations = [2 ** i for i in dilations] 38 | return new_dilations 39 | 40 | 41 | class ResidualBlock(Layer): 42 | def __init__( 43 | self, 44 | dilation_rate: int, 45 | nb_filters: int, 46 | kernel_size: int, 47 | padding: str, 48 | activation: str = "relu", 49 | dropout_rate: float = 0, 50 | kernel_initializer: str = "he_normal", 51 | use_batch_norm: bool = False, 52 | use_layer_norm: bool = False, 53 | use_weight_norm: bool = False, 54 | **kwargs 55 | ): 56 | """Defines the residual block for the WaveNet TCN 57 | Args: 58 | x: The previous layer in the model 59 | training: boolean indicating whether the layer should behave in training mode or in inference mode 60 | dilation_rate: The dilation power of 2 we are using for this residual block 61 | nb_filters: The number of convolutional filters to use in this block 62 | kernel_size: The size of the convolutional kernel 63 | padding: The padding used in the convolutional layers, 'same' or 'causal'. 64 | activation: The final activation used in o = Activation(x + F(x)) 65 | dropout_rate: Float between 0 and 1. Fraction of the input units to drop. 66 | kernel_initializer: Initializer for the kernel weights matrix (Conv1D). 67 | use_batch_norm: Whether to use batch normalization in the residual layers or not. 68 | use_layer_norm: Whether to use layer normalization in the residual layers or not. 69 | use_weight_norm: Whether to use weight normalization in the residual layers or not. 70 | kwargs: Any initializers for Layer class. 71 | """ 72 | 73 | self.dilation_rate = dilation_rate 74 | self.nb_filters = nb_filters 75 | self.kernel_size = kernel_size 76 | self.padding = padding 77 | self.activation = activation 78 | self.dropout_rate = dropout_rate 79 | self.use_batch_norm = use_batch_norm 80 | self.use_layer_norm = use_layer_norm 81 | self.use_weight_norm = use_weight_norm 82 | self.kernel_initializer = kernel_initializer 83 | self.layers = [] 84 | self.layers_outputs = [] 85 | self.shape_match_conv = None 86 | self.res_output_shape = None 87 | self.final_activation = None 88 | 89 | super(ResidualBlock, self).__init__(**kwargs) 90 | 91 | def _build_layer(self, layer): 92 | """Helper function for building layer 93 | Args: 94 | layer: Appends layer to internal layer list and builds it based on the current output 95 | shape of ResidualBlocK. Updates current output shape. 96 | """ 97 | self.layers.append(layer) 98 | self.layers[-1].build(self.res_output_shape) 99 | self.res_output_shape = self.layers[-1].compute_output_shape( 100 | self.res_output_shape 101 | ) 102 | 103 | def build(self, input_shape): 104 | 105 | with K.name_scope( 106 | self.name 107 | ): # name scope used to make sure weights get unique names 108 | self.layers = [] 109 | self.res_output_shape = input_shape 110 | 111 | for k in range(2): 112 | name = "conv1D_{}".format(k) 113 | with K.name_scope( 114 | name 115 | ): # name scope used to make sure weights get unique names 116 | conv = Conv1D( 117 | filters=self.nb_filters, 118 | kernel_size=self.kernel_size, 119 | dilation_rate=self.dilation_rate, 120 | padding=self.padding, 121 | name=name, 122 | kernel_initializer=self.kernel_initializer, 123 | ) 124 | if self.use_weight_norm: 125 | from tensorflow_addons.layers import WeightNormalization 126 | 127 | # wrap it. WeightNormalization API is different than BatchNormalization or LayerNormalization. 128 | with K.name_scope("norm_{}".format(k)): 129 | conv = WeightNormalization(conv) 130 | self._build_layer(conv) 131 | 132 | with K.name_scope("norm_{}".format(k)): 133 | if self.use_batch_norm: 134 | self._build_layer(BatchNormalization()) 135 | elif self.use_layer_norm: 136 | self._build_layer(LayerNormalization()) 137 | elif self.use_weight_norm: 138 | pass # done above. 139 | 140 | self._build_layer(Activation(self.activation)) 141 | self._build_layer(SpatialDropout1D(rate=self.dropout_rate)) 142 | 143 | if self.nb_filters != input_shape[-1]: 144 | # 1x1 conv to match the shapes (channel dimension). 145 | name = "matching_conv1D" 146 | with K.name_scope(name): 147 | # make and build this layer separately because it directly uses input_shape 148 | self.shape_match_conv = Conv1D( 149 | filters=self.nb_filters, 150 | kernel_size=1, 151 | padding="same", 152 | name=name, 153 | kernel_initializer=self.kernel_initializer, 154 | ) 155 | else: 156 | name = "matching_identity" 157 | self.shape_match_conv = Lambda(lambda x: x, name=name) 158 | 159 | with K.name_scope(name): 160 | self.shape_match_conv.build(input_shape) 161 | self.res_output_shape = self.shape_match_conv.compute_output_shape( 162 | input_shape 163 | ) 164 | 165 | self._build_layer(Activation(self.activation)) 166 | self.final_activation = Activation(self.activation) 167 | self.final_activation.build( 168 | self.res_output_shape 169 | ) # probably isn't necessary 170 | 171 | # this is done to force Keras to add the layers in the list to self._layers 172 | for layer in self.layers: 173 | self.__setattr__(layer.name, layer) 174 | self.__setattr__(self.shape_match_conv.name, self.shape_match_conv) 175 | self.__setattr__(self.final_activation.name, self.final_activation) 176 | 177 | super(ResidualBlock, self).build( 178 | input_shape 179 | ) # done to make sure self.built is set True 180 | 181 | def call(self, inputs, training=None): 182 | """ 183 | Returns: A tuple where the first element is the residual model tensor, and the second 184 | is the skip connection tensor. 185 | """ 186 | x = inputs 187 | self.layers_outputs = [x] 188 | for layer in self.layers: 189 | training_flag = "training" in dict(inspect.signature(layer.call).parameters) 190 | x = layer(x, training=training) if training_flag else layer(x) 191 | self.layers_outputs.append(x) 192 | x2 = self.shape_match_conv(inputs) 193 | self.layers_outputs.append(x2) 194 | res_x = layers.add([x2, x]) 195 | self.layers_outputs.append(res_x) 196 | 197 | res_act_x = self.final_activation(res_x) 198 | self.layers_outputs.append(res_act_x) 199 | return [res_act_x, x] 200 | 201 | def compute_output_shape(self, input_shape): 202 | return [self.res_output_shape, self.res_output_shape] 203 | 204 | 205 | class TCN(Layer): 206 | """Creates a TCN layer. 207 | 208 | Input shape: 209 | A tensor of shape (batch_size, timesteps, input_dim). 210 | 211 | Args: 212 | nb_filters: The number of filters to use in the convolutional layers. Can be a list. 213 | kernel_size: The size of the kernel to use in each convolutional layer. 214 | dilations: The list of the dilations. Example is: [1, 2, 4, 8, 16, 32, 64]. 215 | nb_stacks : The number of stacks of residual blocks to use. 216 | padding: The padding to use in the convolutional layers, 'causal' or 'same'. 217 | use_skip_connections: Boolean. If we want to add skip connections from input to each residual blocK. 218 | return_sequences: Boolean. Whether to return the last output in the output sequence, or the full sequence. 219 | activation: The activation used in the residual blocks o = Activation(x + F(x)). 220 | dropout_rate: Float between 0 and 1. Fraction of the input units to drop. 221 | kernel_initializer: Initializer for the kernel weights matrix (Conv1D). 222 | use_batch_norm: Whether to use batch normalization in the residual layers or not. 223 | use_layer_norm: Whether to use layer normalization in the residual layers or not. 224 | use_weight_norm: Whether to use weight normalization in the residual layers or not. 225 | kwargs: Any other arguments for configuring parent class Layer. For example "name=str", Name of the model. 226 | Use unique names when using multiple TCN. 227 | 228 | Returns: 229 | A TCN layer. 230 | """ 231 | 232 | def __init__( 233 | self, 234 | nb_filters=64, 235 | kernel_size=3, 236 | nb_stacks=1, 237 | dilations=(1, 2, 4, 8, 16, 32), 238 | padding="causal", 239 | use_skip_connections=True, 240 | dropout_rate=0.0, 241 | return_sequences=False, 242 | activation="relu", 243 | kernel_initializer="he_normal", 244 | use_batch_norm=False, 245 | use_layer_norm=False, 246 | use_weight_norm=False, 247 | **kwargs 248 | ): 249 | 250 | self.return_sequences = return_sequences 251 | self.dropout_rate = dropout_rate 252 | self.use_skip_connections = use_skip_connections 253 | self.dilations = dilations 254 | self.nb_stacks = nb_stacks 255 | self.kernel_size = kernel_size 256 | self.nb_filters = nb_filters 257 | self.activation = activation 258 | self.padding = padding 259 | self.kernel_initializer = kernel_initializer 260 | self.use_batch_norm = use_batch_norm 261 | self.use_layer_norm = use_layer_norm 262 | self.use_weight_norm = use_weight_norm 263 | self.skip_connections = [] 264 | self.residual_blocks = [] 265 | self.layers_outputs = [] 266 | self.build_output_shape = None 267 | self.slicer_layer = None # in case return_sequence=False 268 | self.output_slice_index = None # in case return_sequence=False 269 | self.padding_same_and_time_dim_unknown = ( 270 | False # edge case if padding='same' and time_dim = None 271 | ) 272 | 273 | if self.use_batch_norm + self.use_layer_norm + self.use_weight_norm > 1: 274 | raise ValueError("Only one normalization can be specified at once.") 275 | 276 | if isinstance(self.nb_filters, list): 277 | assert len(self.nb_filters) == len(self.dilations) 278 | 279 | if padding != "causal" and padding != "same": 280 | raise ValueError( 281 | "Only 'causal' or 'same' padding are compatible for this layer." 282 | ) 283 | 284 | # initialize parent class 285 | super(TCN, self).__init__(**kwargs) 286 | 287 | @property 288 | def receptive_field(self): 289 | return 1 + 2 * (self.kernel_size - 1) * self.nb_stacks * sum(self.dilations) 290 | 291 | def build(self, input_shape): 292 | 293 | # member to hold current output shape of the layer for building purposes 294 | self.build_output_shape = input_shape 295 | 296 | # list to hold all the member ResidualBlocks 297 | self.residual_blocks = [] 298 | total_num_blocks = self.nb_stacks * len(self.dilations) 299 | if not self.use_skip_connections: 300 | total_num_blocks += 1 # cheap way to do a false case for below 301 | 302 | for s in range(self.nb_stacks): 303 | for i, d in enumerate(self.dilations): 304 | res_block_filters = ( 305 | self.nb_filters[i] 306 | if isinstance(self.nb_filters, list) 307 | else self.nb_filters 308 | ) 309 | self.residual_blocks.append( 310 | ResidualBlock( 311 | dilation_rate=d, 312 | nb_filters=res_block_filters, 313 | kernel_size=self.kernel_size, 314 | padding=self.padding, 315 | activation=self.activation, 316 | dropout_rate=self.dropout_rate, 317 | use_batch_norm=self.use_batch_norm, 318 | use_layer_norm=self.use_layer_norm, 319 | use_weight_norm=self.use_weight_norm, 320 | kernel_initializer=self.kernel_initializer, 321 | name="residual_block_{}".format(len(self.residual_blocks)), 322 | ) 323 | ) 324 | # build newest residual block 325 | self.residual_blocks[-1].build(self.build_output_shape) 326 | self.build_output_shape = self.residual_blocks[-1].res_output_shape 327 | 328 | # this is done to force keras to add the layers in the list to self._layers 329 | for layer in self.residual_blocks: 330 | self.__setattr__(layer.name, layer) 331 | 332 | self.output_slice_index = None 333 | if self.padding == "same": 334 | time = self.build_output_shape.as_list()[1] 335 | if ( 336 | time is not None 337 | ): # if time dimension is defined. e.g. shape = (bs, 500, input_dim). 338 | self.output_slice_index = int(self.build_output_shape.as_list()[1] / 2) 339 | else: 340 | # It will known at call time. c.f. self.call. 341 | self.padding_same_and_time_dim_unknown = True 342 | 343 | else: 344 | self.output_slice_index = -1 # causal case. 345 | self.slicer_layer = Lambda(lambda tt: tt[:, self.output_slice_index, :]) 346 | 347 | def compute_output_shape(self, input_shape): 348 | """ 349 | Overridden in case keras uses it somewhere... no idea. Just trying to avoid future errors. 350 | """ 351 | if not self.built: 352 | self.build(input_shape) 353 | if not self.return_sequences: 354 | batch_size = self.build_output_shape[0] 355 | batch_size = ( 356 | batch_size.value if hasattr(batch_size, "value") else batch_size 357 | ) 358 | nb_filters = self.build_output_shape[-1] 359 | return [batch_size, nb_filters] 360 | else: 361 | # Compatibility tensorflow 1.x 362 | return [ 363 | v.value if hasattr(v, "value") else v for v in self.build_output_shape 364 | ] 365 | 366 | def call(self, inputs, training=None): 367 | x = inputs 368 | self.layers_outputs = [x] 369 | self.skip_connections = [] 370 | for layer in self.residual_blocks: 371 | try: 372 | x, skip_out = layer(x, training=training) 373 | except TypeError: # compatibility with tensorflow 1.x 374 | x, skip_out = layer(K.cast(x, "float32"), training=training) 375 | self.skip_connections.append(skip_out) 376 | self.layers_outputs.append(x) 377 | 378 | if self.use_skip_connections: 379 | x = layers.add(self.skip_connections) 380 | self.layers_outputs.append(x) 381 | 382 | if not self.return_sequences: 383 | # case: time dimension is unknown. e.g. (bs, None, input_dim). 384 | if self.padding_same_and_time_dim_unknown: 385 | self.output_slice_index = K.shape(self.layers_outputs[-1])[1] // 2 386 | x = self.slicer_layer(x) 387 | self.layers_outputs.append(x) 388 | return x 389 | 390 | def get_config(self): 391 | """ 392 | Returns the config of a the layer. This is used for saving and loading from a model 393 | :return: python dictionary with specs to rebuild layer 394 | """ 395 | config = super(TCN, self).get_config() 396 | config["nb_filters"] = self.nb_filters 397 | config["kernel_size"] = self.kernel_size 398 | config["nb_stacks"] = self.nb_stacks 399 | config["dilations"] = self.dilations 400 | config["padding"] = self.padding 401 | config["use_skip_connections"] = self.use_skip_connections 402 | config["dropout_rate"] = self.dropout_rate 403 | config["return_sequences"] = self.return_sequences 404 | config["activation"] = self.activation 405 | config["use_batch_norm"] = self.use_batch_norm 406 | config["use_layer_norm"] = self.use_layer_norm 407 | config["use_weight_norm"] = self.use_weight_norm 408 | config["kernel_initializer"] = self.kernel_initializer 409 | return config 410 | 411 | 412 | def compiled_tcn( 413 | num_feat, # type: int 414 | num_classes, # type: int 415 | nb_filters, # type: int 416 | kernel_size, # type: int 417 | dilations, # type: List[int] 418 | nb_stacks, # type: int 419 | max_len, # type: int 420 | output_len=1, # type: int 421 | padding="causal", # type: str 422 | use_skip_connections=False, # type: bool 423 | return_sequences=True, 424 | regression=False, # type: bool 425 | dropout_rate=0.05, # type: float 426 | name="tcn", # type: str, 427 | kernel_initializer="he_normal", # type: str, 428 | activation="relu", # type:str, 429 | opt="adam", 430 | lr=0.002, 431 | use_batch_norm=False, 432 | use_layer_norm=False, 433 | use_weight_norm=False, 434 | ): 435 | # type: (...) -> Model 436 | """Creates a compiled TCN model for a given task (i.e. regression or classification). 437 | Classification uses a sparse categorical loss. Please input class ids and not one-hot encodings. 438 | 439 | Args: 440 | num_feat: The number of features of your input, i.e. the last dimension of: (batch_size, timesteps, input_dim). 441 | num_classes: The size of the final dense layer, how many classes we are predicting. 442 | nb_filters: The number of filters to use in the convolutional layers. 443 | kernel_size: The size of the kernel to use in each convolutional layer. 444 | dilations: The list of the dilations. Example is: [1, 2, 4, 8, 16, 32, 64]. 445 | nb_stacks : The number of stacks of residual blocks to use. 446 | max_len: The maximum sequence length, use None if the sequence length is dynamic. 447 | padding: The padding to use in the convolutional layers. 448 | use_skip_connections: Boolean. If we want to add skip connections from input to each residual blocK. 449 | return_sequences: Boolean. Whether to return the last output in the output sequence, or the full sequence. 450 | regression: Whether the output should be continuous or discrete. 451 | dropout_rate: Float between 0 and 1. Fraction of the input units to drop. 452 | activation: The activation used in the residual blocks o = Activation(x + F(x)). 453 | name: Name of the model. Useful when having multiple TCN. 454 | kernel_initializer: Initializer for the kernel weights matrix (Conv1D). 455 | opt: Optimizer name. 456 | lr: Learning rate. 457 | use_batch_norm: Whether to use batch normalization in the residual layers or not. 458 | use_layer_norm: Whether to use layer normalization in the residual layers or not. 459 | use_weight_norm: Whether to use weight normalization in the residual layers or not. 460 | Returns: 461 | A compiled keras TCN. 462 | """ 463 | 464 | dilations = adjust_dilations(dilations) 465 | 466 | input_layer = Input(shape=(max_len, num_feat)) 467 | 468 | x = TCN( 469 | nb_filters, 470 | kernel_size, 471 | nb_stacks, 472 | dilations, 473 | padding, 474 | use_skip_connections, 475 | dropout_rate, 476 | return_sequences, 477 | activation, 478 | kernel_initializer, 479 | use_batch_norm, 480 | use_layer_norm, 481 | use_weight_norm, 482 | name=name, 483 | )(input_layer) 484 | 485 | print("x.shape=", x.shape) 486 | 487 | def get_opt(): 488 | if opt == "adam": 489 | return optimizers.Adam(lr=lr, clipnorm=1.0) 490 | elif opt == "rmsprop": 491 | return optimizers.RMSprop(lr=lr, clipnorm=1.0) 492 | else: 493 | raise Exception("Only Adam and RMSProp are available here") 494 | 495 | if not regression: 496 | # classification 497 | x = Dense(num_classes)(x) 498 | x = Activation("softmax")(x) 499 | output_layer = x 500 | model = Model(input_layer, output_layer) 501 | 502 | # https://github.com/keras-team/keras/pull/11373 503 | # It's now in Keras@master but still not available with pip. 504 | # TODO remove later. 505 | def accuracy(y_true, y_pred): 506 | # reshape in case it's in shape (num_samples, 1) instead of (num_samples,) 507 | if K.ndim(y_true) == K.ndim(y_pred): 508 | y_true = K.squeeze(y_true, -1) 509 | # convert dense predictions to labels 510 | y_pred_labels = K.argmax(y_pred, axis=-1) 511 | y_pred_labels = K.cast(y_pred_labels, K.floatx()) 512 | return K.cast(K.equal(y_true, y_pred_labels), K.floatx()) 513 | 514 | model.compile( 515 | get_opt(), loss="sparse_categorical_crossentropy", metrics=[accuracy] 516 | ) 517 | else: 518 | # regression 519 | x = Dense(output_len)(x) 520 | x = Activation("linear")(x) 521 | output_layer = x 522 | model = Model(input_layer, output_layer) 523 | model.compile(get_opt(), loss="mean_squared_error") 524 | print("model.x = {}".format(input_layer.shape)) 525 | print("model.y = {}".format(output_layer.shape)) 526 | return model 527 | 528 | 529 | def tcn_full_summary(model: Model, expand_residual_blocks=True): 530 | import tensorflow as tf 531 | 532 | # 2.6.0-rc1, 2.5.0... 533 | versions = [int(v) for v in tf.__version__.split("-")[0].split(".")] 534 | if versions[0] <= 2 and versions[1] < 5: 535 | layers = model._layers.copy() # store existing layers 536 | model._layers.clear() # clear layers 537 | 538 | for i in range(len(layers)): 539 | if isinstance(layers[i], TCN): 540 | for layer in layers[i]._layers: 541 | if not isinstance(layer, ResidualBlock): 542 | if not hasattr(layer, "__iter__"): 543 | model._layers.append(layer) 544 | else: 545 | if expand_residual_blocks: 546 | for lyr in layer._layers: 547 | if not hasattr(lyr, "__iter__"): 548 | model._layers.append(lyr) 549 | else: 550 | model._layers.append(layer) 551 | else: 552 | model._layers.append(layers[i]) 553 | 554 | model.summary() # print summary 555 | 556 | # restore original layers 557 | model._layers.clear() 558 | [model._layers.append(lyr) for lyr in layers] 559 | else: 560 | print("WARNING: tcn_full_summary: Compatible with tensorflow 2.5.0 or below.") 561 | 562 | 563 | # if time_steps > tcn_layer.receptive_field, then we should not 564 | # be able to solve this task. 565 | batch_size, time_steps, input_dim = None, 20, 1 566 | 567 | 568 | def get_x_y(size=1000): 569 | import numpy as np 570 | 571 | pos_indices = np.random.choice(size, size=int(size // 2), replace=False) 572 | x_train = np.zeros(shape=(size, time_steps, 1)) 573 | y_train = np.zeros(shape=(size, 1)) 574 | x_train[ 575 | pos_indices, 0 576 | ] = 1.0 # we introduce the target in the first timestep of the sequence. 577 | y_train[ 578 | pos_indices, 0 579 | ] = 1.0 # the task is to see if the TCN can go back in time to find it. 580 | return x_train, y_train 581 | 582 | 583 | class TCNModel(Transformer): 584 | """Temporal Convolutional Neural Model.""" 585 | 586 | def __init__(self, data: TrainingDataSet): 587 | super().__init__(data) 588 | self.model: Optional[Model] = None 589 | 590 | def build_model(self): 591 | tcn_layer = TCN(input_shape=self.data.input_shape) 592 | self.model = Sequential( 593 | [tcn_layer, Dense(np.prod(self.data.output_shape)), Reshape(self.data.output_shape)] 594 | ) 595 | self.model.build(self.data.input_shape) 596 | self.model.compile( 597 | loss="mse" if self.regression else "sparse_categorical_crossentropy", 598 | optimizer="adam", 599 | metrics=self.metrics, 600 | ) 601 | LOGGER.info(self.model.summary()) 602 | -------------------------------------------------------------------------------- /time_series/models/__init__.py: -------------------------------------------------------------------------------- 1 | """Template for models.""" 2 | from abc import ABC 3 | import tensorflow as tf 4 | 5 | 6 | class NNModel(ABC): 7 | """Model class.""" 8 | 9 | metrics = ["mean_absolute_percentage_error", "mae", "mse"] 10 | callbacks = [ 11 | tf.keras.callbacks.EarlyStopping( 12 | monitor="loss", patience=5, restore_best_weights=True 13 | ) 14 | ] 15 | 16 | def __init__(self): 17 | super().__init__() 18 | 19 | def net_structure(self, **kwargs): 20 | pass 21 | 22 | def instantiate_and_fit(self, **kwargs): 23 | pass 24 | 25 | @staticmethod 26 | def load(filepath, custom_objects=None, compile=True): 27 | from tensorflow.keras.models import load_model 28 | 29 | return load_model(filepath, custom_objects, compile) 30 | -------------------------------------------------------------------------------- /time_series/models/deepar/README.md: -------------------------------------------------------------------------------- 1 | # Time-Series 2 | 3 | Tensorflow implementations of Time-Series models including Amazon DeepAR, Gaussian Processes, Transformer, and NBEATS. 4 | 5 | ## Example usage: 6 | Fit a univariate time series: 7 | 8 | ```python 9 | from tensorflow.python.framework.ops import disable_eager_execution 10 | 11 | disable_eager_execution() 12 | 13 | from time_series.dataset.time_series import MockTs 14 | from time_series.models.deepar import DeepAR 15 | 16 | ts = MockTs(dimensions=1) # you can change this for multivariate time-series! 17 | dp_model = DeepAR(ts, epochs=50) 18 | dp_model.instantiate_and_fit() 19 | ``` 20 | 21 | Plot results with uncertainty bands: 22 | ```python 23 | import tqdm 24 | import pandas as pd 25 | from matplotlib import pyplot as plt 26 | import numpy as np 27 | 28 | batch = ts.next_batch(1, ts.n_steps) 29 | 30 | ress = [] 31 | for i in tqdm.tqdm(range(300)): 32 | ress.append(np.expand_dims( 33 | dp_model.get_sample_prediction( 34 | batch[0] 35 | ), axis=0, 36 | )) 37 | 38 | res_np = np.concatenate(ress, axis=0) 39 | fig = plt.figure(figsize=(12, 10)) 40 | 41 | for dim in range(ts.dimensions): 42 | ax = fig.add_subplot(ts.dimensions, 1, dim+1) 43 | res_df = pd.DataFrame(res_np[:, :, 0]).T 44 | tot_res = res_df 45 | 46 | ax.plot(batch[1].reshape((ts.n_steps, ts.dimensions))[:, dim], linewidth=6) 47 | tot_res['mu'] = tot_res.apply(lambda x: np.mean(x), axis=1) 48 | tot_res['upper'] = tot_res.apply(lambda x: np.mean(x) + np.std(x), axis=1) 49 | tot_res['lower'] = tot_res.apply(lambda x: np.mean(x) - np.std(x), axis=1) 50 | tot_res['two_upper'] = tot_res.apply(lambda x: np.mean(x) + 2*np.std(x), axis=1) 51 | tot_res['two_lower'] = tot_res.apply(lambda x: np.mean(x) - 2*np.std(x), axis=1) 52 | 53 | ax.plot(tot_res.mu, 'bo') 54 | ax.plot(tot_res.mu, linewidth=2) 55 | ax.fill_between(x = tot_res.index, y1=tot_res.lower, y2=tot_res.upper, alpha=0.5) 56 | ax.fill_between(x = tot_res.index, y1=tot_res.two_lower, y2=tot_res.two_upper, alpha=0.5) 57 | fig.suptitle('Prediction uncertainty') 58 | 59 | ``` 60 | 61 | ![Image of gaussian](imgs/prediction.png) 62 | -------------------------------------------------------------------------------- /time_series/models/deepar/__init__.py: -------------------------------------------------------------------------------- 1 | """DeepAR model. 2 | 3 | Based on https://github.com/arrigonialberto86/deepar 4 | By Alberto Arrigoni. 5 | """ 6 | from functools import partial 7 | import logging 8 | from typing import Optional, Union 9 | 10 | import numpy as np 11 | from numpy.random import normal 12 | import pandas as pd 13 | 14 | from tensorflow.keras.layers import Dense, Input, LSTM 15 | from tensorflow.keras.models import Model 16 | 17 | from time_series.dataset.time_series import TrainingDataSet 18 | from time_series.models.deepar.loss import gaussian_likelihood 19 | from time_series.models import NNModel 20 | from time_series.models.deepar.layers import GaussianLayer 21 | 22 | 23 | LOGGER = logging.getLogger(__name__) 24 | 25 | 26 | class DeepAR(NNModel): 27 | """DeepAR model.""" 28 | 29 | def __init__( 30 | self, data: TrainingDataSet, loss=gaussian_likelihood, optimizer: str = "adam", 31 | ): 32 | """Init. 33 | 34 | Arguments: 35 | df (pd.DataFrame): a dataframe of shape time x value columns 36 | loss: a loss function. 37 | optimizer: which optimizer to use. 38 | """ 39 | self.data = data 40 | self.inputs, self.z_sample = None, None 41 | self.loss = loss 42 | self.optimizer = optimizer 43 | self.model: Optional[Model] = None 44 | self.nn_structure = partial( 45 | DeepAR.basic_structure, n_steps=data.n_steps, dimensions=data.dimensions 46 | ) 47 | self._output_layer_name = "main_output" 48 | self.gaussian_layer: Optional[Model] = None 49 | 50 | @staticmethod 51 | def basic_structure(n_steps=20, dimensions=1): 52 | """ 53 | This is the method that needs to be patched when changing NN structure 54 | :return: inputs_shape (tuple), inputs (Tensor), [loc, scale] (a list of theta parameters 55 | of the target likelihood). 56 | 57 | Please note that I've made up scaling rules of the hidden layer dimensions. 58 | """ 59 | input_shape = (n_steps, dimensions) 60 | inputs = Input(shape=input_shape) 61 | x = LSTM( 62 | 4, # int(4 * (1 + math.pow(math.log(dimensions), 4))), 63 | return_sequences=True, 64 | dropout=0.1, 65 | )(inputs) 66 | # int(4 * (1 + math.log(dimensions))), 67 | x = Dense(4, activation="relu")(x) 68 | loc, scale = GaussianLayer(dimensions, name="main_output")(x) 69 | return input_shape, inputs, [loc, scale] 70 | 71 | def fit( 72 | self, **fit_kwargs, 73 | ): 74 | """Fit models. 75 | 76 | This is called from instantiate and fit(). 77 | """ 78 | self.model.fit( 79 | self.data.X_train, self.data.y_train, callbacks=self.callbacks, **fit_kwargs 80 | ) 81 | 82 | def build_model(self): 83 | input_shape, inputs, theta = self.nn_structure() 84 | self.model = Model(inputs, theta[0]) 85 | LOGGER.info(self.model.summary()) 86 | self.gaussian_layer = Model( 87 | self.model.input, self.model.get_layer(self._output_layer_name).output, 88 | ) 89 | self.model.compile( 90 | loss=self.loss(theta[1]), optimizer=self.optimizer, metrics=self.metrics 91 | ) 92 | self.gaussian_layer.compile(loss="mse", optimizer="adam") 93 | 94 | def instantiate_and_fit(self, do_fit: bool = True, **fit_kwargs): 95 | """Compile and train models.""" 96 | self.build_model() 97 | if do_fit: 98 | self.fit(**fit_kwargs) 99 | 100 | def predict_theta_from_input(self, input_list): 101 | """Predict from GaussianLayer. 102 | 103 | This function takes an input of size equal to the n_steps specified in 'Input' when building the 104 | network. 105 | :param input_list: 106 | :return: [[]], a list of list. E.g. when using Gaussian layer this returns a list of two list, 107 | corresponding to [[mu_values], [sigma_values]] 108 | """ 109 | if not self.model.history: 110 | raise ValueError("Model must be trained first!") 111 | 112 | return self.gaussian_layer.predict(input_list) 113 | 114 | def get_sample_prediction(self, sample_df: pd.DataFrame): 115 | """WIP.""" 116 | self.ts_obj.test_df = sample_df 117 | sample = self.ts_obj.test 118 | output = self.predict_theta_from_input(sample) 119 | samples = [] 120 | for mu, sigma in zip(output[0].reshape(-1), output[1].reshape(-1)): 121 | sample = normal( 122 | loc=mu, scale=np.sqrt(sigma), size=1 123 | ) # self.ts_obj.dimensions) 124 | samples.append(sample) 125 | 126 | return np.array(samples).reshape( 127 | (self.ts_obj.label_width, self.ts_obj.dimensions) 128 | ) 129 | 130 | 131 | if __name__ == "__main__": 132 | """For debugging.""" 133 | from tensorflow.python.framework.ops import disable_eager_execution 134 | 135 | disable_eager_execution() 136 | from tensorflow.compat.v1.experimental import output_all_intermediates 137 | 138 | output_all_intermediates(True) 139 | 140 | from time_series.dataset.utils import get_energy_demand 141 | 142 | train_df = get_energy_demand() 143 | 144 | dp_model = DeepAR(train_df, epochs=10) 145 | dp_model.instantiate_and_fit(verbose=1, epochs=1) 146 | -------------------------------------------------------------------------------- /time_series/models/deepar/imgs/gaussian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benman1/time-series/b075d9009cb9ec7fee86f10b16207b00a356a6ac/time_series/models/deepar/imgs/gaussian.png -------------------------------------------------------------------------------- /time_series/models/deepar/imgs/prediction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benman1/time-series/b075d9009cb9ec7fee86f10b16207b00a356a6ac/time_series/models/deepar/imgs/prediction.png -------------------------------------------------------------------------------- /time_series/models/deepar/layers.py: -------------------------------------------------------------------------------- 1 | from tensorflow.keras import backend as K 2 | from tensorflow.keras.initializers import glorot_normal 3 | from tensorflow.keras.layers import Layer 4 | 5 | 6 | class GaussianLayer(Layer): 7 | def __init__(self, output_dim, **kwargs): 8 | """Init.""" 9 | self.output_dim = output_dim 10 | self.kernel_1, self.kernel_2, self.bias_1, self.bias_2 = [], [], [], [] 11 | super(GaussianLayer, self).__init__(**kwargs) 12 | 13 | def build(self, input_shape): 14 | """Build the weights and biases.""" 15 | n_weight_rows = input_shape[2] 16 | self.kernel_1 = self.add_weight( 17 | name="kernel_1", 18 | shape=(n_weight_rows, self.output_dim), 19 | initializer=glorot_normal(), 20 | trainable=True, 21 | ) 22 | self.kernel_2 = self.add_weight( 23 | name="kernel_2", 24 | shape=(n_weight_rows, self.output_dim), 25 | initializer=glorot_normal(), 26 | trainable=True, 27 | ) 28 | self.bias_1 = self.add_weight( 29 | name="bias_1", 30 | shape=(self.output_dim,), 31 | initializer=glorot_normal(), 32 | trainable=True, 33 | ) 34 | self.bias_2 = self.add_weight( 35 | name="bias_2", 36 | shape=(self.output_dim,), 37 | initializer=glorot_normal(), 38 | trainable=True, 39 | ) 40 | super(GaussianLayer, self).build(input_shape) 41 | 42 | def call(self, x): 43 | """Do the layer computation.""" 44 | output_mu = K.dot(x, self.kernel_1) + self.bias_1 45 | output_sig = K.dot(x, self.kernel_2) + self.bias_2 46 | output_sig_pos = K.log(1 + K.exp(output_sig)) + 1e-06 47 | return [output_mu, output_sig_pos] 48 | 49 | def compute_output_shape(self, input_shape): 50 | """Calculate the output dimensions. 51 | 52 | The assumption here is that the output ts is always one-dimensional; 53 | """ 54 | return [(input_shape[0], self.output_dim), (input_shape[0], self.output_dim)] 55 | -------------------------------------------------------------------------------- /time_series/models/deepar/loss.py: -------------------------------------------------------------------------------- 1 | import math 2 | import tensorflow as tf 3 | 4 | 5 | def gaussian_likelihood(sigma): 6 | """Likelihood as per the paper.""" 7 | 8 | def gaussian_loss(y_true, y_pred): 9 | """Updated from paper. 10 | 11 | See DeepAR: Probabilistic Forecasting with Autoregressive Recurrent Networks. 12 | """ 13 | return tf.reduce_mean( 14 | tf.math.log(tf.math.sqrt(2 * math.pi)) 15 | + tf.math.log(sigma) 16 | + tf.math.truediv( 17 | tf.math.square(y_true - y_pred), 2 * tf.math.square(sigma) 18 | ) 19 | ) 20 | 21 | return gaussian_loss 22 | -------------------------------------------------------------------------------- /time_series/models/gaussian_process/__init__.py: -------------------------------------------------------------------------------- 1 | """Gaussian Process models.""" 2 | import logging 3 | from typing import Optional 4 | 5 | import gpflow 6 | from gpflow.utilities import print_summary 7 | 8 | from time_series.dataset.time_series import TrainingDataSet 9 | from time_series.models import NNModel 10 | 11 | 12 | LOGGER = logging.getLogger(__name__) 13 | 14 | 15 | class GaussianProcess(NNModel): 16 | """Gaussian Process model based on GPFlow library. 17 | 18 | Data should come in this shape (we'll have to reshape our data to 2D): 19 | X_train: (instances x variables) -> y_train: (instances x values) 20 | """ 21 | 22 | def __init__( 23 | self, 24 | data: TrainingDataSet, 25 | kernel: gpflow.kernels.Kernel = gpflow.kernels.Matern52(), 26 | meanf: Optional[gpflow.mean_functions.MeanFunction] = None, 27 | ): 28 | self.data = data 29 | self.kernel = kernel 30 | print_summary(self.kernel) 31 | self.meanf = meanf 32 | self.model: Optional[gpflow.models.BayesianModel] = None 33 | self.opt = gpflow.optimizers.Scipy() 34 | 35 | def build_model(self): 36 | """Build model.""" 37 | self.model = gpflow.models.GPR( 38 | data=(self.data.X_train, self.data.y_train), 39 | kernel=self.kernel, 40 | mean_function=self.meanf, 41 | ) 42 | print_summary(self.model) 43 | 44 | def fit(self, **fit_kwargs): 45 | """Fit the model.""" 46 | _ = self.opt.minimize( 47 | self.model.training_loss, 48 | self.model.trainable_variables, 49 | options=fit_kwargs, 50 | ) 51 | print_summary(self.model) 52 | 53 | def instantiate_and_fit(self, **fit_kwargs): 54 | """Create model and fit.""" 55 | self.build_model() 56 | self.fit(**fit_kwargs) 57 | 58 | def predict(self, X_test): 59 | """Return predictions for new data.""" 60 | mean, var = self.model.predict_f(X_test) 61 | return mean, var 62 | -------------------------------------------------------------------------------- /time_series/models/nbeats/__init__.py: -------------------------------------------------------------------------------- 1 | """Keras implementation of N-BEATS. 2 | 3 | Based on Philippe Rémy's implementation at https://github.com/philipperemy/n-beats. 4 | Paper: NBEATS: Neural basis expansion analysis for interpretable time series forecasting 5 | """ 6 | import logging 7 | from typing import Dict, Optional 8 | 9 | import numpy as np 10 | from tensorflow.keras import backend as K 11 | from tensorflow.keras.layers import Concatenate 12 | from tensorflow.keras.layers import Input, Dense, Lambda, Subtract, Add, Reshape 13 | from tensorflow.keras.models import Model 14 | 15 | from time_series.dataset.time_series import TrainingDataSet 16 | from time_series.models import NNModel 17 | 18 | 19 | LOGGER = logging.getLogger(__name__) 20 | 21 | GENERIC_BLOCK = "generic" 22 | TREND_BLOCK = "trend" 23 | SEASONALITY_BLOCK = "seasonality" 24 | 25 | _BACKCAST = "backcast" 26 | _FORECAST = "forecast" 27 | 28 | 29 | def linear_space(backcast_length, forecast_length, is_forecast=True): 30 | ls = K.arange(-float(backcast_length), float(forecast_length), 1) / forecast_length 31 | return ( 32 | ls[backcast_length:] 33 | if is_forecast 34 | else K.abs(K.reverse(ls[:backcast_length], axes=0)) 35 | ) 36 | 37 | 38 | def seasonality_model(thetas, backcast_length, forecast_length, is_forecast): 39 | p = thetas.get_shape().as_list()[-1] 40 | p1, p2 = (p // 2, p // 2) if p % 2 == 0 else (p // 2, p // 2 + 1) 41 | t = linear_space(backcast_length, forecast_length, is_forecast=is_forecast) 42 | s1 = K.stack([K.cos(2 * np.pi * i * t) for i in range(p1)]) 43 | s2 = K.stack([K.sin(2 * np.pi * i * t) for i in range(p2)]) 44 | if p == 1: 45 | s = s2 46 | else: 47 | s = K.concatenate([s1, s2], axis=0) 48 | s = K.cast(s, np.float32) 49 | return K.dot(thetas, s) 50 | 51 | 52 | def trend_model(thetas, backcast_length, forecast_length, is_forecast): 53 | p = thetas.shape[-1] 54 | t = linear_space(backcast_length, forecast_length, is_forecast=is_forecast) 55 | t = K.transpose(K.stack([t ** i for i in range(p)])) 56 | t = K.cast(t, np.float32) 57 | return K.dot(thetas, K.transpose(t)) 58 | 59 | 60 | class NBeatsNet(NNModel): 61 | """NBeats model with exogenous variables. 62 | 63 | Data come in as (num_samples, time_steps, input_dim). 64 | 65 | We could be moving a window generator here: 66 | self.ts_obj = WindowGenerator(input_width=10, label_width=10, shift=8, train_df=df) 67 | """ 68 | 69 | cast_type: str = _FORECAST 70 | 71 | def __init__( 72 | self, 73 | data: TrainingDataSet, 74 | backcast_length=10, 75 | stack_types=(TREND_BLOCK, SEASONALITY_BLOCK), 76 | nb_blocks_per_stack=3, 77 | thetas_dim=(4, 8), 78 | share_weights_in_stack=False, 79 | hidden_layer_units=256, 80 | nb_harmonics=None, 81 | ): 82 | self.data = data 83 | self.stack_types = stack_types 84 | self.nb_blocks_per_stack = nb_blocks_per_stack 85 | self.thetas_dim = thetas_dim 86 | self.units = hidden_layer_units 87 | self.share_weights_in_stack = share_weights_in_stack 88 | self.backcast_length = backcast_length 89 | self.input_shape = (self.backcast_length, self.data.dimensions) 90 | self.exo_shape = (self.backcast_length, self.data.exo_dim) 91 | self.output_shape = (self.data.n_steps, self.data.dimensions) 92 | self.weights = {} 93 | self.nb_harmonics = nb_harmonics 94 | assert len(self.stack_types) == len(self.thetas_dim) 95 | self.models: Optional[Dict[str, Model]] = None 96 | 97 | def net_structure(self): 98 | """Build the network structure.""" 99 | x = Input(shape=self.input_shape, name="input_variable") 100 | x_ = {} 101 | for k in range(self.data.dimensions): 102 | x_[k] = Lambda(lambda z: z[..., k])(x) 103 | e_ = {} 104 | if self.has_exog(): 105 | e = Input(shape=self.exo_shape, name="exos_variables") 106 | for k in range(self.data.exo_dim): 107 | e_[k] = Lambda(lambda z: z[..., k])(e) 108 | else: 109 | e = None 110 | y_ = {} 111 | 112 | for stack_id in range(len(self.stack_types)): 113 | stack_type = self.stack_types[stack_id] 114 | nb_poly = self.thetas_dim[stack_id] 115 | for block_id in range(self.nb_blocks_per_stack): 116 | backcast, forecast = self.create_block( 117 | x_, e_, stack_id, block_id, stack_type, nb_poly 118 | ) 119 | for k in range(self.data.dimensions): 120 | x_[k] = Subtract()([x_[k], backcast[k]]) 121 | if stack_id == 0 and block_id == 0: 122 | y_[k] = forecast[k] 123 | else: 124 | y_[k] = Add()([y_[k], forecast[k]]) 125 | 126 | for k in range(self.data.dimensions): 127 | y_[k] = Reshape(target_shape=(self.data.n_steps, 1))(y_[k]) 128 | x_[k] = Reshape(target_shape=(self.backcast_length, 1))(x_[k]) 129 | if self.data.dimensions > 1: 130 | y_ = Concatenate()([y_[ll] for ll in range(self.data.dimensions)]) 131 | x_ = Concatenate()([x_[ll] for ll in range(self.data.dimensions)]) 132 | else: 133 | y_ = y_[0] 134 | x_ = x_[0] 135 | 136 | if self.has_exog(): 137 | n_beats_forecast = Model([x, e], y_, name=_FORECAST) 138 | n_beats_backcast = Model([x, e], x_, name=_BACKCAST) 139 | else: 140 | n_beats_forecast = Model(x, y_, name=_FORECAST) 141 | n_beats_backcast = Model(x, x_, name=_BACKCAST) 142 | return n_beats_forecast, n_beats_backcast 143 | 144 | def build_model(self): 145 | """Build the models.""" 146 | n_beats_forecast, n_beats_backcast = self.net_structure() 147 | self.models = { 148 | model.name: model for model in [n_beats_backcast, n_beats_forecast] 149 | } 150 | self.models[_FORECAST].compile(loss="mae", optimizer="adam") 151 | LOGGER.info(self.models[_FORECAST].summary()) 152 | 153 | def has_exog(self): 154 | # exo/exog is short for 'exogenous variable', i.e. any input 155 | # features other than the target time-series itself. 156 | return self.data.exo_dim > 0 157 | 158 | def _restore(self, layer_with_weights, stack_id): 159 | """Mechanism to restore weights when block share the same weights. 160 | 161 | This is only useful when share_weights_in_stack=True. 162 | """ 163 | if self.share_weights_in_stack: 164 | layer_name = layer_with_weights.name.split("/")[-1] 165 | try: 166 | reused_weights = self.weights[stack_id][layer_name] 167 | return reused_weights 168 | except KeyError: 169 | pass 170 | if stack_id not in self.weights: 171 | self.weights[stack_id] = {} 172 | self.weights[stack_id][layer_name] = layer_with_weights 173 | return layer_with_weights 174 | 175 | def create_block(self, x, e, stack_id, block_id, stack_type, nb_poly): 176 | """Register weights. 177 | 178 | This is useful when share_weights_in_stack=True. 179 | """ 180 | 181 | def reg(layer): 182 | return self._restore(layer, stack_id) 183 | 184 | # update name (useful when share_weights_in_stack=True) 185 | def n(layer_name): 186 | return "/".join([str(stack_id), str(block_id), stack_type, layer_name]) 187 | 188 | backcast_ = {} 189 | forecast_ = {} 190 | d1 = reg(Dense(self.units, activation="relu", name=n("d1"))) 191 | d2 = reg(Dense(self.units, activation="relu", name=n("d2"))) 192 | d3 = reg(Dense(self.units, activation="relu", name=n("d3"))) 193 | d4 = reg(Dense(self.units, activation="relu", name=n("d4"))) 194 | if stack_type == "generic": 195 | theta_b = reg( 196 | Dense(nb_poly, activation="linear", use_bias=False, name=n("theta_b")) 197 | ) 198 | theta_f = reg( 199 | Dense(nb_poly, activation="linear", use_bias=False, name=n("theta_f")) 200 | ) 201 | backcast = reg( 202 | Dense(self.backcast_length, activation="linear", name=n("backcast")) 203 | ) 204 | forecast = reg( 205 | Dense(self.data.n_steps, activation="linear", name=n("forecast")) 206 | ) 207 | elif stack_type == "trend": 208 | theta_f = theta_b = reg( 209 | Dense(nb_poly, activation="linear", use_bias=False, name=n("theta_f_b")) 210 | ) 211 | backcast = Lambda( 212 | trend_model, 213 | arguments={ 214 | "is_forecast": False, 215 | "backcast_length": self.backcast_length, 216 | "forecast_length": self.data.n_steps, 217 | }, 218 | ) 219 | forecast = Lambda( 220 | trend_model, 221 | arguments={ 222 | "is_forecast": True, 223 | "backcast_length": self.backcast_length, 224 | "forecast_length": self.data.n_steps, 225 | }, 226 | ) 227 | else: # 'seasonality' 228 | if self.nb_harmonics: 229 | theta_b = reg( 230 | Dense( 231 | self.nb_harmonics, 232 | activation="linear", 233 | use_bias=False, 234 | name=n("theta_b"), 235 | ) 236 | ) 237 | else: 238 | theta_b = reg( 239 | Dense( 240 | self.data.n_steps, 241 | activation="linear", 242 | use_bias=False, 243 | name=n("theta_b"), 244 | ) 245 | ) 246 | theta_f = reg( 247 | Dense( 248 | self.data.n_steps, 249 | activation="linear", 250 | use_bias=False, 251 | name=n("theta_f"), 252 | ) 253 | ) 254 | backcast = Lambda( 255 | seasonality_model, 256 | arguments={ 257 | "is_forecast": False, 258 | "backcast_length": self.backcast_length, 259 | "forecast_length": self.data.n_steps, 260 | }, 261 | ) 262 | forecast = Lambda( 263 | seasonality_model, 264 | arguments={ 265 | "is_forecast": True, 266 | "backcast_length": self.backcast_length, 267 | "forecast_length": self.data.n_steps, 268 | }, 269 | ) 270 | for k in range(self.data.dimensions): 271 | if self.has_exog(): 272 | d0 = Concatenate()([x[k]] + [e[ll] for ll in range(self.exo_dim)]) 273 | else: 274 | d0 = x[k] 275 | d1_ = d1(d0) 276 | d2_ = d2(d1_) 277 | d3_ = d3(d2_) 278 | d4_ = d4(d3_) 279 | theta_f_ = theta_f(d4_) 280 | theta_b_ = theta_b(d4_) 281 | backcast_[k] = backcast(theta_b_) 282 | forecast_[k] = forecast(theta_f_) 283 | 284 | return backcast_, forecast_ 285 | 286 | def fit(self, **fit_kwargs): 287 | """Fit model.""" 288 | self.models[_FORECAST].fit( 289 | self.data.X_train, self.data.y_train, callbacks=self.callbacks, **fit_kwargs 290 | ) 291 | 292 | def instantiate_and_fit(self, **fit_kwargs): 293 | self.build_model() 294 | LOGGER.info("Model built!") 295 | self.fit(**fit_kwargs) 296 | 297 | @property 298 | def model(self): 299 | """Get the forecast model.""" 300 | return self.models[_FORECAST] 301 | -------------------------------------------------------------------------------- /time_series/models/transformer/__init__.py: -------------------------------------------------------------------------------- 1 | """Time-series forecast using a Transformer model. 2 | Based on: Timeseries classification with a Transformer model 3 | By Theodoros Ntakouris, https://github.com/ntakouris 4 | """ 5 | import logging 6 | from typing import Optional, Sequence 7 | 8 | import tensorflow as tf 9 | from tensorflow.keras import Model 10 | 11 | from time_series.dataset.time_series import TrainingDataSet 12 | from time_series.models import NNModel 13 | from tensorflow.keras import layers 14 | 15 | 16 | LOGGER = logging.getLogger(__file__) 17 | 18 | 19 | class Transformer(NNModel): 20 | """Transformer model for time-series. 21 | 22 | The model includes residual connections, layer normalization, and dropout. 23 | Data come in as (batch size, sequence length, features). 24 | """ 25 | 26 | def __init__(self, data: TrainingDataSet, regression: bool = True): 27 | self.data = data 28 | self.model: Optional[Model] = None 29 | self.regression = regression 30 | 31 | def fit(self, **fit_kwargs): 32 | self.model.fit( 33 | self.data.X_train, self.data.y_train, callbacks=self.callbacks, **fit_kwargs 34 | ) 35 | 36 | def instantiate_and_fit(self, **fit_kwargs): 37 | """Create model and fit.""" 38 | self.build_model() 39 | self.fit(**fit_kwargs) 40 | 41 | @staticmethod 42 | def transformer_encoder( 43 | inputs, 44 | head_size: int, 45 | num_heads: int, 46 | ff_dim: int, 47 | dropout: float = 0.0, 48 | kernel_size: int = 1, 49 | ): 50 | """Encoder: Attention and Normalization and Feed-Forward.""" 51 | # 1. Attention and Normalization: 52 | x = layers.MultiHeadAttention( 53 | key_dim=head_size, num_heads=num_heads, dropout=dropout 54 | )(inputs, inputs) 55 | x = layers.Dropout(dropout)(x) 56 | x = layers.LayerNormalization(epsilon=1e-6)(x) 57 | res = x + inputs 58 | 59 | # 2. Feed Forward Part: 60 | x = layers.Conv1D(filters=ff_dim, kernel_size=1, activation="relu")(res) 61 | x = layers.Dropout(dropout)(x) 62 | x = layers.Conv1D(filters=inputs.shape[-1], kernel_size=kernel_size)(x) 63 | x = layers.LayerNormalization(epsilon=1e-6)(x) 64 | return x + res 65 | 66 | def nn_structure( 67 | self, 68 | head_size: int, 69 | num_heads: int, 70 | ff_dim: int, 71 | num_transformer_blocks: int, 72 | mlp_units: Sequence[int], 73 | dropout: float = 0.0, 74 | mlp_dropout: float = 0.0, 75 | kernel_size: int = 1, 76 | ): 77 | inputs = tf.keras.Input(shape=self.data.input_shape) 78 | x = inputs 79 | for _ in range(num_transformer_blocks): 80 | x = Transformer.transformer_encoder( 81 | x, head_size, num_heads, ff_dim, dropout, kernel_size 82 | ) 83 | 84 | # conv_layer = tf.keras.layers.Conv1D(64, self.data.dimensions) 85 | # x = tf.keras.layers.TimeDistributed(conv_layer)(x) 86 | 87 | x = layers.GlobalAveragePooling1D(data_format="channels_first")(x) 88 | for dim in mlp_units: 89 | x = layers.Dense(dim, activation="relu")(x) 90 | x = layers.Dropout(mlp_dropout)(x) 91 | outputs_d = layers.Dense( 92 | self.data.dimensions * self.data.n_steps 93 | if self.regression 94 | else self.data.n_classes, 95 | activation="softmax", 96 | )(x) 97 | outputs = tf.reshape(outputs_d, (-1, self.data.horizon, self.data.dimensions)) 98 | return inputs, outputs 99 | 100 | def build_model(self): 101 | inputs, outputs = self.nn_structure( 102 | head_size=256, 103 | num_heads=2, 104 | ff_dim=self.data.n_steps, 105 | num_transformer_blocks=1, 106 | mlp_units=[256], 107 | mlp_dropout=0.4, 108 | dropout=0.25, 109 | kernel_size=self.data.n_steps, 110 | ) 111 | self.model = Model(inputs, outputs) 112 | self.model.compile( 113 | loss="mse" if self.regression else "sparse_categorical_crossentropy", 114 | optimizer="adam", 115 | metrics=self.metrics, 116 | ) 117 | LOGGER.info(self.model.summary()) 118 | -------------------------------------------------------------------------------- /time_series/settings.py: -------------------------------------------------------------------------------- 1 | import logging.config 2 | import os 3 | 4 | LOG_CONF = { 5 | "version": 1, 6 | "disable_existing_loggers": False, 7 | "formatters": { 8 | "simple": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"} 9 | }, 10 | "handlers": { 11 | "stream": { 12 | "class": "logging.StreamHandler", 13 | "level": "DEBUG", 14 | "formatter": "simple", 15 | "stream": "ext://sys.stdout", 16 | }, 17 | }, 18 | "loggers": { 19 | "time_series": { 20 | "handlers": ["stream"], 21 | "level": os.getenv("DF_LOG_LEVEL", "DEBUG"), 22 | } 23 | }, 24 | } 25 | 26 | logging.config.dictConfig(LOG_CONF) 27 | -------------------------------------------------------------------------------- /time_series/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Utility functions.""" 2 | from typing import Sequence 3 | 4 | from matplotlib import pyplot as plt 5 | from sklearn.metrics import mean_squared_error 6 | from tensorflow.keras import backend as K 7 | import tensorflow as tf 8 | import numpy as np 9 | 10 | from time_series.dataset.time_series import TimeSeries 11 | 12 | 13 | def set_seed_and_reset_graph(seed=42): 14 | tf.reset_default_graph() 15 | tf.set_random_seed(seed) 16 | np.random.seed(seed) 17 | 18 | 19 | def clear_keras_session(): 20 | K.clear_session() 21 | 22 | 23 | def evaluate_model(tds: TimeSeries, y_predicted: np.ndarray, columns=Sequence[str], first_n: int = 0): 24 | """Evaluate the model based on the 1step-ahead prediction""" 25 | print(f"MSE: {mean_squared_error(y_predicted.reshape(-1,), tds.y_test.reshape(-1,)):.4f}") 26 | print("----------") 27 | dimensions = len(columns) 28 | plt.figure(figsize=(12, 18)) 29 | grid = plt.GridSpec(dimensions, 1 if first_n else 2, wspace=0.5, hspace=0.2) 30 | 31 | for i in range(dimensions): 32 | if len(tds.y_train.shape) == 2: 33 | pred, y_actual = ( 34 | y_predicted[:first_n, i], 35 | tds.y_test[:first_n, i] 36 | ) 37 | else: 38 | pred, y_actual = ( 39 | y_predicted[:first_n, 1, i], 40 | tds.y_test[:first_n, 1, i] 41 | ) 42 | 43 | ax = plt.subplot(grid[i, 0]) 44 | plt.plot(pred, 'r+--', label="predicted") 45 | plt.plot(y_actual, 'bo-.', label="actual") 46 | ax.set_title(list(columns)[i]) 47 | print(f"{columns[i]}: {round(mean_squared_error(y_actual, pred), 2)}") 48 | plt.legend() 49 | --------------------------------------------------------------------------------