From a9c1f8c2a0dd86adfc1683d82bfe6acb6d2e5497 Mon Sep 17 00:00:00 2001 From: Haoyu Wang <48108414+javaThonc@users.noreply.github.com> Date: Fri, 27 Nov 2020 17:16:35 +0800 Subject: [PATCH 01/30] Update workflow_config_xgboost.yaml --- .../XGBoost/workflow_config_xgboost.yaml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml b/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml index 31eee8206..398f8bd9e 100644 --- a/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml +++ b/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml @@ -30,14 +30,12 @@ task: module_path: qlib.contrib.model.xgboost kwargs: eval_metric: rmse - colsample_bytree: 0.5 - eta: 0.2 - gamma: 0.55 - max_depth: 2 - min_child_weight: 1.0 - n_estimators: 647 - subsample: 0.8 - nthread: 4 + colsample_bytree: 0.8879 + eta: 0.0421 + max_depth: 8 + n_estimators: 650 + subsample: 0.8789 + nthread: 20 dataset: class: DatasetH module_path: qlib.data.dataset @@ -62,4 +60,4 @@ task: - class: PortAnaRecord module_path: qlib.workflow.record_temp kwargs: - config: *port_analysis_config \ No newline at end of file + config: *port_analysis_config From c62f3164a270813996e72640b345c70094cf8b8a Mon Sep 17 00:00:00 2001 From: Haoyu Wang <48108414+javaThonc@users.noreply.github.com> Date: Fri, 27 Nov 2020 17:17:57 +0800 Subject: [PATCH 02/30] update config --- examples/benchmarks/XGBoost/workflow_config_xgboost.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml b/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml index 398f8bd9e..1352c496d 100644 --- a/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml +++ b/examples/benchmarks/XGBoost/workflow_config_xgboost.yaml @@ -33,7 +33,7 @@ task: colsample_bytree: 0.8879 eta: 0.0421 max_depth: 8 - n_estimators: 650 + n_estimators: 647 subsample: 0.8789 nthread: 20 dataset: From e4e730bada1791cf9d228bc18b6f86af7effb95f Mon Sep 17 00:00:00 2001 From: Dong Zhou Date: Fri, 27 Nov 2020 17:46:41 +0800 Subject: [PATCH 03/30] update portfolio example --- examples/portfolio_optimization_example.ipynb | 161 +++++++++--------- 1 file changed, 76 insertions(+), 85 deletions(-) diff --git a/examples/portfolio_optimization_example.ipynb b/examples/portfolio_optimization_example.ipynb index 4d6c2b3d2..7ef593efa 100644 --- a/examples/portfolio_optimization_example.ipynb +++ b/examples/portfolio_optimization_example.ipynb @@ -57,10 +57,10 @@ "output_type": "stream", "name": "stderr", "text": [ - "[35366:MainThread](2020-11-27 10:31:09,528) INFO - qlib.Initialization - [__init__.py:41] - default_conf: client.\n", - "[35366:MainThread](2020-11-27 10:31:09,531) WARNING - qlib.Initialization - [__init__.py:57] - redis connection failed(host=127.0.0.1 port=6379), cache will not be used!\n", - "[35366:MainThread](2020-11-27 10:31:09,531) INFO - qlib.Initialization - [__init__.py:76] - qlib successfully initialized based on client settings.\n", - "[35366:MainThread](2020-11-27 10:31:09,532) INFO - qlib.Initialization - [__init__.py:79] - data_path=/home/dongzho/.qlib/qlib_data/cn_data\n" + "[36502:MainThread](2020-11-27 16:26:57,240) INFO - qlib.Initialization - [__init__.py:41] - default_conf: client.\n", + "[36502:MainThread](2020-11-27 16:26:57,242) WARNING - qlib.Initialization - [__init__.py:57] - redis connection failed(host=127.0.0.1 port=6379), cache will not be used!\n", + "[36502:MainThread](2020-11-27 16:26:57,243) INFO - qlib.Initialization - [__init__.py:76] - qlib successfully initialized based on client settings.\n", + "[36502:MainThread](2020-11-27 16:26:57,244) INFO - qlib.Initialization - [__init__.py:79] - data_path=/home/dongzho/.qlib/qlib_data/cn_data\n" ] } ], @@ -102,14 +102,14 @@ "output_type": "stream", "name": "stderr", "text": [ - "[35366:MainThread](2020-11-27 10:31:29,731) INFO - qlib.timer - [log.py:81] - Time cost: 20.103s | Loading data Done\n", - "[35366:MainThread](2020-11-27 10:31:30,557) INFO - qlib.timer - [log.py:81] - Time cost: 0.241s | DropnaLabel Done\n", - "[35366:MainThread](2020-11-27 10:31:38,518) INFO - qlib.timer - [log.py:81] - Time cost: 7.960s | CSZScoreNorm Done\n", - "[35366:MainThread](2020-11-27 10:31:38,519) INFO - qlib.timer - [log.py:81] - Time cost: 8.786s | fit & process data Done\n", - "[35366:MainThread](2020-11-27 10:31:38,520) INFO - qlib.timer - [log.py:81] - Time cost: 28.891s | Init data Done\n", - "[35366:MainThread](2020-11-27 10:31:38,527) INFO - qlib.workflow - [exp.py:180] - Experiment 2 starts running ...\n", - "[35366:MainThread](2020-11-27 10:31:38,651) INFO - qlib.workflow - [recorder.py:234] - Recorder c81375e3b5474feb9c77711babd158c3 starts running under Experiment 2 ...\n", - "[35366:MainThread](2020-11-27 10:31:38,652) INFO - qlib.workflow - [expm.py:251] - No tracking URI is provided. The default tracking URI is set as `mlruns` under the working directory.\n", + "[36502:MainThread](2020-11-27 16:27:17,338) INFO - qlib.timer - [log.py:81] - Time cost: 19.994s | Loading data Done\n", + "[36502:MainThread](2020-11-27 16:27:18,164) INFO - qlib.timer - [log.py:81] - Time cost: 0.245s | DropnaLabel Done\n", + "[36502:MainThread](2020-11-27 16:27:26,086) INFO - qlib.timer - [log.py:81] - Time cost: 7.921s | CSZScoreNorm Done\n", + "[36502:MainThread](2020-11-27 16:27:26,087) INFO - qlib.timer - [log.py:81] - Time cost: 8.747s | fit & process data Done\n", + "[36502:MainThread](2020-11-27 16:27:26,088) INFO - qlib.timer - [log.py:81] - Time cost: 28.744s | Init data Done\n", + "[36502:MainThread](2020-11-27 16:27:26,097) INFO - qlib.workflow - [exp.py:180] - Experiment 2 starts running ...\n", + "[36502:MainThread](2020-11-27 16:27:26,221) INFO - qlib.workflow - [recorder.py:234] - Recorder 3fa4def1f6694119a3d336a7a06c88cb starts running under Experiment 2 ...\n", + "[36502:MainThread](2020-11-27 16:27:26,223) INFO - qlib.workflow - [expm.py:251] - No tracking URI is provided. The default tracking URI is set as `mlruns` under the working directory.\n", "Training until validation scores don't improve for 50 rounds\n", "[20]\ttrain's l2: 0.990559\tvalid's l2: 0.994332\n", "[40]\ttrain's l2: 0.98687\tvalid's l2: 0.993702\n", @@ -164,7 +164,7 @@ " \"segments\": {\n", " \"train\": (\"2008-01-01\", \"2014-12-31\"),\n", " \"valid\": (\"2015-01-01\", \"2016-12-31\"),\n", - " \"test\": (\"2017-01-01\", \"2020-08-01\"),\n", + " \"test\": (\"2017-01-01\", \"2017-12-31\"), # NOTE: use a shorter time range\n", " },\n", " },\n", " },\n", @@ -271,22 +271,19 @@ " )\n", "\n", " # optimize target portfolio\n", - " if init_weight.sum() > 0:\n", - " target_weight = self.optimizer(cov, score_series, init_weight)\n", - " else:\n", - " target_weight = self.optimizer(cov, score_series)\n", - " target_weight = target_weight[target_weight > 1e-6]\n", - " for stock_id, weight in target_weight.items():\n", - " try:\n", + " try:\n", + " if init_weight.sum() > 0:\n", + " target_weight = self.optimizer(cov, score_series, init_weight)\n", + " else:\n", + " target_weight = self.optimizer(cov, score_series)\n", + " target_weight = target_weight[target_weight > 1e-6]\n", + " for stock_id, weight in target_weight.items():\n", " target_position[stock_id] = int(traded_value * weight / trade_exchange.get_close(stock_id, pred_date))\n", - " except Exception as e:\n", - " # TODO: unknown exception\n", - " print('Exception:', e)\n", - "\n", - " # for debug\n", - " print('trade date:', trade_date)\n", - " print('target weight:', target_weight.to_dict())\n", - " print('target position:', target_position)\n", + " except Exception as e:\n", + " print('Unknown exception:', trade_date, e)\n", + " for stock_id in score_series.index:\n", + " if stock_id in current_position:\n", + " target_position[stock_id] = current_position[stock_id]\n", "\n", " # generate order list\n", " order_list = trade_exchange.generate_order_for_target_amount_position(\n", @@ -319,8 +316,8 @@ "output_type": "stream", "name": "stderr", "text": [ - "[35366:MainThread](2020-11-27 10:31:56,951) INFO - qlib.timer - [log.py:81] - Time cost: 6.763s | Loading data Done\n", - "[35366:MainThread](2020-11-27 10:31:56,953) INFO - qlib.timer - [log.py:81] - Time cost: 6.766s | Init data Done\n" + "[36502:MainThread](2020-11-27 16:27:43,722) INFO - qlib.timer - [log.py:81] - Time cost: 6.369s | Loading data Done\n", + "[36502:MainThread](2020-11-27 16:27:43,724) INFO - qlib.timer - [log.py:81] - Time cost: 6.371s | Init data Done\n" ] } ], @@ -334,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 8, "metadata": { "tags": [] }, @@ -343,60 +340,54 @@ "output_type": "stream", "name": "stderr", "text": [ - "1': 0.08936553334387595, 'SH601800': 0.011014844457113308, 'SH601939': 0.013378001170219945, 'SH603993': 0.013820193926861863, 'SZ000338': 0.002455991798001457, 'SZ000423': 0.004893338273543826, 'SZ000538': 0.010686211189620477, 'SZ002065': 0.09095125419435357, 'SZ002074': 0.010299013738522475, 'SZ002085': 0.19844965949420615, 'SZ002236': 0.09210003831704765, 'SZ002310': 0.05664352912360013, 'SZ300017': 0.0197442255539771}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 272224, 'SH600009': 604839, 'SH600018': 3097398, 'SH600028': 335726, 'SH600196': 23243, 'SH600276': 71634, 'SH600519': 17354, 'SH600585': 269686, 'SH600900': 2501521, 'SH601111': 2400659, 'SH601800': 334062, 'SH601939': 1283164, 'SH603993': 742901, 'SZ000338': 95285, 'SZ000423': 21697, 'SZ000538': 14518, 'SZ002065': 498253, 'SZ002074': 111674, 'SZ002085': 591507, 'SZ002236': 394197, 'SZ002310': 2202674, 'SZ300017': 206128}\n", - "target weight: {'SH600000': 0.02310668460556249, 'SH600009': 0.06170206213753432, 'SH600018': 0.027608180837257277, 'SH600028': 0.00971532319525714, 'SH600196': 0.0036133308423111116, 'SH600276': 0.093195014492093, 'SH600519': 0.013476706174774766, 'SH600585': 0.036024919027310476, 'SH600660': 0.04512159672692613, 'SH600900': 0.12506534473579556, 'SH601939': 0.013494851810297546, 'SH603993': 0.07619418669734077, 'SZ000338': 0.0024673392047414363, 'SZ000423': 0.00485981529404862, 'SZ000538': 0.010602880875660015, 'SZ002065': 0.09064325205359221, 'SZ002074': 0.0011889996597580427, 'SZ002085': 0.1982091371262038, 'SZ002236': 0.09254320484936242, 'SZ002310': 0.05152917909181458, 'SZ002466': 0.00014732765084648903, 'SZ300017': 0.019490662910321074}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 272079, 'SH600009': 604359, 'SH600018': 3095205, 'SH600028': 335471, 'SH600196': 23407, 'SH600276': 71567, 'SH600519': 17345, 'SH600585': 269447, 'SH600660': 129265, 'SH600900': 2499305, 'SH601939': 1282317, 'SH603993': 4058172, 'SZ000338': 95223, 'SZ000423': 21703, 'SZ000538': 14509, 'SZ002065': 497821, 'SZ002074': 12787, 'SZ002085': 590955, 'SZ002236': 393895, 'SZ002310': 2190685, 'SZ002466': 4483, 'SZ300017': 205994}\n", - "target weight: {'SH600000': 0.0014042138463464568, 'SH600009': 0.11511740651805806, 'SH600018': 0.026968513725965638, 'SH600028': 0.009566603496832042, 'SH600150': 0.016339328084607228, 'SH600276': 0.09374974543357856, 'SH600489': 0.021876512936684123, 'SH600585': 0.035840818294258524, 'SH600900': 0.12414161958870683, 'SH601888': 0.005682635273269834, 'SH601939': 0.013289788356428228, 'SH603993': 0.07491407610535435, 'SZ000338': 0.002426716760042838, 'SZ000423': 0.00492071038737461, 'SZ000503': 0.005617017904986693, 'SZ000538': 0.010859006699485451, 'SZ002065': 0.08924691553942904, 'SZ002085': 0.19757848255238786, 'SZ002236': 0.09381012783787722, 'SZ002310': 0.03737359938389514, 'SZ300017': 0.01927616131502695}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16809, 'SH600009': 1075516, 'SH600018': 3091248, 'SH600028': 335128, 'SH600150': 114804, 'SH600276': 71473, 'SH600489': 66586, 'SH600585': 268644, 'SH600900': 2496175, 'SH601888': 173824, 'SH601939': 1281108, 'SH603993': 4052802, 'SZ000338': 95107, 'SZ000423': 21684, 'SZ000503': 80461, 'SZ000538': 14507, 'SZ002065': 497197, 'SZ002085': 590211, 'SZ002236': 393412, 'SZ002310': 1573728, 'SZ300017': 205818}\n", - "target weight: {'SH600000': 0.0013962189421662084, 'SH600009': 0.09330267135244051, 'SH600018': 0.026443154116291615, 'SH600028': 0.009581412428525829, 'SH600150': 0.016443917649559808, 'SH600276': 0.09378402212481758, 'SH600703': 0.0005233118350013756, 'SH600741': 0.10117549074044105, 'SH600900': 0.12435147566444608, 'SH601888': 0.00560250787284307, 'SH601939': 0.013238798853730008, 'SH603993': 0.07455231781733267, 'SZ000423': 0.0048695925705555185, 'SZ000503': 0.006070996956328167, 'SZ000538': 0.010870567565742796, 'SZ002065': 0.08722983720892508, 'SZ002074': 0.00037126948590009574, 'SZ002085': 0.19840484837030906, 'SZ002236': 0.09365186287123867, 'SZ002310': 0.03806080531862309, 'SZ300017': 7.492025186876957e-05}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16889, 'SH600009': 867443, 'SH600018': 3086467, 'SH600028': 334573, 'SH600150': 114383, 'SH600276': 71360, 'SH600703': 1760, 'SH600741': 665366, 'SH600900': 2491839, 'SH601888': 173465, 'SH601939': 1278590, 'SH603993': 4045939, 'SZ000423': 21674, 'SZ000503': 80212, 'SZ000538': 14499, 'SZ002065': 496361, 'SZ002074': 4086, 'SZ002085': 589224, 'SZ002236': 392766, 'SZ002310': 1571463, 'SZ300017': 805}\n", - "target weight: {'SH600000': 0.0014143911110003147, 'SH600018': 0.026834186435965166, 'SH600028': 0.00961324990522086, 'SH600150': 0.015905361405158292, 'SH600276': 0.09486308638260738, 'SH600685': 1.0253334545374858e-06, 'SH600703': 0.0005108576602907958, 'SH600741': 0.10252334336233063, 'SH600900': 0.1250632059809011, 'SH601888': 0.005830869532670813, 'SH601939': 0.01336945356138906, 'SH603993': 0.07101851124599835, 'SZ000423': 0.004899981502195361, 'SZ000503': 0.006113894785564276, 'SZ000538': 0.011081925761176491, 'SZ000709': 1.06442568357325e-06, 'SZ002065': 0.08812103684766726, 'SZ002074': 0.0003564773234700175, 'SZ002085': 0.19097427428977284, 'SZ002236': 0.09299395368630246, 'SZ002310': 0.03841630892378685, 'SZ002475': 0.10001934454071283, 'SZ300017': 7.322667303400442e-05}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16886, 'SH600018': 3080789, 'SH600028': 334087, 'SH600150': 114360, 'SH600276': 71234, 'SH600685': 10, 'SH600703': 1709, 'SH600741': 663932, 'SH600900': 2486951, 'SH601888': 173417, 'SH601939': 1276335, 'SH603993': 3740672, 'SZ000423': 21667, 'SZ000503': 80191, 'SZ000538': 14495, 'SZ000709': 11, 'SZ002065': 495371, 'SZ002074': 3867, 'SZ002085': 588051, 'SZ002236': 392002, 'SZ002310': 1568834, 'SZ002475': 1264636, 'SZ300017': 809}\n", - "target weight: {'SH600000': 0.0013872765178790307, 'SH600018': 0.026321999857337998, 'SH600028': 0.009491029058787367, 'SH600150': 0.015749871987744815, 'SH600276': 0.09581999547114961, 'SH600703': 0.000518490273176083, 'SH600741': 0.1037547619508012, 'SH600900': 0.12396253436063161, 'SH601258': 0.02298494942988327, 'SH601888': 0.005915886046387033, 'SH601939': 0.013177336599075601, 'SH603993': 0.06888468621566025, 'SZ000423': 0.005102036718661418, 'SZ000503': 0.00602692511970311, 'SZ000538': 0.011127923667697532, 'SZ000709': 0.07688609680386178, 'SZ002065': 0.08693397271897534, 'SZ002074': 0.000347445594871718, 'SZ002085': 0.1905176824564206, 'SZ002236': 0.035835596544641496, 'SZ002475': 0.09918059167278087, 'SZ300017': 7.291118905149903e-05}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16948, 'SH600018': 3086676, 'SH600028': 334750, 'SH600150': 114560, 'SH600276': 71372, 'SH600703': 1715, 'SH600741': 665129, 'SH600900': 2491433, 'SH601258': 4190669, 'SH601888': 174070, 'SH601939': 1278836, 'SH603993': 3747283, 'SZ000423': 21744, 'SZ000503': 80490, 'SZ000538': 14538, 'SZ000709': 871429, 'SZ002065': 496245, 'SZ002074': 3887, 'SZ002085': 589120, 'SZ002236': 145147, 'SZ002475': 1268582, 'SZ300017': 814}\n", - "target weight: {'SH600000': 0.001373124016867567, 'SH600018': 0.02646941123076474, 'SH600028': 0.009458335378810856, 'SH600150': 0.015442533996257352, 'SH600276': 0.09620341387657301, 'SH600649': 0.012613476480118908, 'SH600703': 0.0005280976985716832, 'SH600741': 0.06577156829314017, 'SH600900': 0.12455488881029539, 'SH601258': 0.02270943336842379, 'SH601939': 0.013066707696697587, 'SH603993': 0.0649427819283919, 'SZ000423': 0.0051167756388828005, 'SZ000503': 0.006076486564538039, 'SZ000709': 0.0770418453012855, 'SZ000778': 0.08738918304165759, 'SZ002065': 0.08804613990036694, 'SZ002074': 0.00034315924263262563, 'SZ002085': 0.18241434394629127, 'SZ002475': 0.10035998625624482, 'SZ300017': 7.809604376099223e-05}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16935, 'SH600018': 3089469, 'SH600028': 334906, 'SH600150': 114496, 'SH600276': 71430, 'SH600649': 337388, 'SH600703': 1714, 'SH600741': 419916, 'SH600900': 2493978, 'SH601258': 4194599, 'SH601939': 1279661, 'SH603993': 3750968, 'SZ000423': 21734, 'SZ000503': 80440, 'SZ000709': 872293, 'SZ000778': 366855, 'SZ002065': 496756, 'SZ002074': 3880, 'SZ002085': 564610, 'SZ002475': 1269872, 'SZ300017': 812}\n", - "target weight: {'SH600000': 0.0013497287789570015, 'SH600018': 0.02647482761554837, 'SH600028': 0.00941080088689994, 'SH600150': 0.01556139303593115, 'SH600276': 0.09732218714743374, 'SH600649': 0.012606184789019243, 'SH600703': 0.0005334649726542859, 'SH600900': 0.12593267687041163, 'SH601258': 0.021199485570796834, 'SH601939': 0.013025993149697816, 'SH603993': 0.06446918682668012, 'SZ000423': 0.005311875734339093, 'SZ000503': 0.006125989728635501, 'SZ000709': 0.0707610058353687, 'SZ000778': 0.14004715956352495, 'SZ002065': 0.08746446321200681, 'SZ002074': 0.00033710686535540885, 'SZ002085': 0.15238971653801253, 'SZ002146': 0.042585776887618575, 'SZ002475': 0.10701429615740456, 'SZ300017': 7.667981013711115e-05}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 17031, 'SH600018': 3109084, 'SH600028': 336978, 'SH600150': 115126, 'SH600276': 71888, 'SH600649': 339316, 'SH600703': 1724, 'SH600900': 2510148, 'SH601258': 4237748, 'SH601939': 1287810, 'SH603993': 3775382, 'SZ000423': 21853, 'SZ000503': 80885, 'SZ000709': 878077, 'SZ000778': 625157, 'SZ002065': 499988, 'SZ002074': 3901, 'SZ002085': 469624, 'SZ002146': 2000993, 'SZ002475': 1278084, 'SZ300017': 814}\n", - "target weight: {'SH600000': 0.0013594926998639766, 'SH600009': 0.021101252574639438, 'SH600028': 0.009528554544265834, 'SH600150': 0.015013601602404225, 'SH600276': 0.09860402207319302, 'SH600649': 0.01292550325031454, 'SH600685': 0.00703471182662378, 'SH600703': 0.0005218767517596246, 'SH600900': 0.12786995199482584, 'SH601258': 0.04401496515184404, 'SH601398': 0.025932829520167643, 'SH601939': 0.0134408200189716, 'SH603993': 0.06319752369639879, 'SZ000423': 0.005221187626834546, 'SZ000503': 0.006085670359590286, 'SZ000568': 0.003081214755480397, 'SZ000709': 0.07061122716452324, 'SZ000778': 0.1379488795662632, 'SZ000839': 0.019142903464547063, 'SZ002065': 0.04714685528331623, 'SZ002074': 0.00033291622875151913, 'SZ002085': 0.11947661465752588, 'SZ002146': 0.043205942689553425, 'SZ002310': 0.0009243182551654129, 'SZ002475': 0.106199974013018, 'SZ300017': 7.709323254732814e-05}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16933, 'SH600009': 196068, 'SH600028': 337025, 'SH600150': 115100, 'SH600276': 71926, 'SH600649': 339354, 'SH600685': 75328, 'SH600703': 1713, 'SH600900': 2511928, 'SH601258': 8791935, 'SH601398': 1146896, 'SH601939': 1288215, 'SH603993': 3777819, 'SZ000423': 21728, 'SZ000503': 80869, 'SZ000568': 10375, 'SZ000709': 878683, 'SZ000778': 625604, 'SZ000839': 312116, 'SZ002065': 268413, 'SZ002074': 3860, 'SZ002085': 369761, 'SZ002146': 2002072, 'SZ002310': 40341, 'SZ002475': 1278918, 'SZ300017': 811}\n", - "target weight: {'SH600000': 0.0013764694393366029, 'SH600009': 0.021541655860797534, 'SH600028': 0.009752609535237182, 'SH600276': 0.06514222178877259, 'SH600649': 0.01273168785031133, 'SH600685': 0.006989932070614982, 'SH600900': 0.12998548252109676, 'SH601258': 0.13157540821422453, 'SH601398': 0.02641881439805636, 'SH601939': 0.0136141957873422, 'SH603993': 0.0602411123337629, 'SZ000503': 0.006084251045333903, 'SZ000709': 0.06977363144499521, 'SZ000778': 0.1385461140272643, 'SZ000839': 0.018579865431307987, 'SZ002065': 0.046270476942690986, 'SZ002074': 0.00025974854597178115, 'SZ002085': 0.10060756172850334, 'SZ002146': 0.043204792194791966, 'SZ002310': 0.0009022784286642987, 'SZ002466': 0.011748866835406593, 'SZ002475': 0.08457581284822364, 'SZ300017': 7.701070501151889e-05}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16938, 'SH600009': 196239, 'SH600028': 337355, 'SH600276': 46535, 'SH600649': 339479, 'SH600685': 75274, 'SH600900': 2514488, 'SH601258': 26730440, 'SH601398': 1148157, 'SH601939': 1289259, 'SH603993': 3781937, 'SZ000503': 80900, 'SZ000709': 879645, 'SZ000778': 626285, 'SZ000839': 312384, 'SZ002065': 268717, 'SZ002074': 3093, 'SZ002085': 309206, 'SZ002146': 2003901, 'SZ002310': 39782, 'SZ002466': 367691, 'SZ002475': 1026389, 'SZ300017': 812}\n", - "target weight: {'SH600000': 0.0013689894888766726, 'SH600009': 0.021087495457198752, 'SH600028': 0.009589419355091226, 'SH600276': 0.0644304399184473, 'SH600535': 0.016420787426513667, 'SH600649': 0.0267771761277641, 'SH600900': 0.12784455237901315, 'SH601169': 0.004374459372110214, 'SH601258': 0.13288651981531077, 'SH601398': 0.02615927477879055, 'SH601939': 0.013573361058977978, 'SH603993': 1.157895161672162e-06, 'SZ000503': 0.009069218941980683, 'SZ000709': 0.07014466816191627, 'SZ000778': 0.13956352821962528, 'SZ002065': 0.045206445945654664, 'SZ002085': 0.08649963592018277, 'SZ002146': 0.04234588186007612, 'SZ002310': 0.0008924777422846245, 'SZ002466': 0.07334842360184116, 'SZ002475': 0.08834296814868704, 'SZ300017': 7.311841306821287e-05}\n", - "Exception: ('SH601169', Timestamp('2017-04-25 00:00:00'))\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16929, 'SH600009': 196092, 'SH600028': 337333, 'SH600276': 46571, 'SH600535': 57649, 'SH600649': 731641, 'SH600900': 2515321, 'SH601258': 26740467, 'SH601398': 1148635, 'SH601939': 1289434, 'SH603993': 72, 'SZ000503': 122157, 'SZ000709': 879908, 'SZ000778': 626506, 'SZ002065': 268767, 'SZ002085': 267906, 'SZ002146': 2004576, 'SZ002310': 39745, 'SZ002466': 2332750, 'SZ002475': 1026858, 'SZ300017': 806}\n", - "target weight: {'SH600000': 0.0013439859873209908, 'SH600009': 0.02075652616964347, 'SH600028': 0.00939963933310415, 'SH600276': 0.06236017906066887, 'SH600535': 0.016369568294734148, 'SH600649': 0.025541724367766302, 'SH600900': 0.12768966131041845, 'SH601258': 0.1370446945486361, 'SH601398': 0.02601619218529119, 'SH601939': 0.013440958024818669, 'SH603993': 4.144559709761373e-06, 'SZ000503': 0.0084237188568659, 'SZ000568': 0.020576387679160105, 'SZ000709': 0.056783757531829446, 'SZ000778': 0.06920027928808208, 'SZ002008': 0.07943378393922318, 'SZ002065': 0.045339177613740886, 'SZ002085': 0.08505902525865962, 'SZ002146': 0.031624633954490035, 'SZ002310': 0.0008996156348854183, 'SZ002466': 0.0764983539831682, 'SZ002475': 0.086193992434369}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SZ300017': 812.4573136217659, 'SH600000': 16923, 'SH600009': 196076, 'SH600028': 337279, 'SH600276': 46567, 'SH600535': 57624, 'SH600649': 731549, 'SH600900': 2515891, 'SH601258': 26747448, 'SH601398': 1148886, 'SH601939': 1289307, 'SH603993': 263, 'SZ000503': 122158, 'SZ000568': 69471, 'SZ000709': 700781, 'SZ000778': 302643, 'SZ002008': 746285, 'SZ002065': 268804, 'SZ002085': 267988, 'SZ002146': 1473970, 'SZ002310': 39739, 'SZ002466': 2333288, 'SZ002475': 1027134}\n", - "target weight: {'SH600000': 0.0014508867295425067, 'SH600009': 0.022137935734971876, 'SH600028': 0.01003980705499816, 'SH600276': 0.065554410760754, 'SH600535': 0.017337663954140436, 'SH600649': 0.026752732524884384, 'SH600900': 0.13610376526017787, 'SH601258': 0.14230666244775886, 'SH601398': 0.027847743092481312, 'SH601939': 0.014306563408357105, 'SH603993': 2.7770868647848817e-06, 'SZ000069': 0.10104502775773525, 'SZ000503': 0.009049444347506782, 'SZ000568': 0.005686495401232644, 'SZ000778': 0.0715782861850023, 'SZ002008': 0.08609584908472251, 'SZ002065': 0.04706561122827146, 'SZ002085': 0.09099179117275048, 'SZ002146': 0.03204301334262787, 'SZ002475': 0.09241758644387384, 'SZ300017': 0.00018594702102337797}\n", - "target position: {'SZ000709': 700825.0269758024, 'SZ002299': 6184584.0980107365, 'SH600000': 16845, 'SH600009': 195098, 'SH600028': 335689, 'SH600276': 46340, 'SH600535': 57343, 'SH600649': 728078, 'SH600900': 2504242, 'SH601258': 26624542, 'SH601398': 1143577, 'SH601939': 1283067, 'SH603993': 160, 'SZ000069': 367637, 'SZ000503': 121565, 'SZ000568': 17626, 'SZ000778': 301250, 'SZ002008': 742790, 'SZ002065': 267559, 'SZ002085': 266737, 'SZ002146': 1467579, 'SZ002475': 1022346, 'SZ300017': 1776}\n", - "target weight: {'SH600000': 0.0013484985106016394, 'SH600009': 0.020750773768622693, 'SH600028': 0.009285673867962157, 'SH600104': 2.9067007814076732e-05, 'SH600196': 0.10012804077099052, 'SH600276': 0.05943563439541343, 'SH600535': 0.015902136087846228, 'SH600649': 0.025189836387314323, 'SH600900': 0.12584805827140388, 'SH601111': 6.857382365314848e-06, 'SH601258': 0.03895938466363849, 'SH601398': 0.025753888553878806, 'SH601939': 0.013275755331575599, 'SH603993': 4.249178615404585e-06, 'SZ000069': 0.09445579375504781, 'SZ000503': 0.008532747266799033, 'SZ000568': 0.0052599046052527266, 'SZ000709': 0.06003418476540357, 'SZ000778': 0.06923031488245988, 'SZ002008': 0.07903025205993618, 'SZ002065': 0.04448484691775433, 'SZ002085': 0.08426354045447453, 'SZ002146': 0.031142767130486235, 'SZ002475': 0.08747938111190227, 'SZ300017': 0.00016841662419817417}\n", - "target position: {'SZ002299': 6184584.0980107365, 'SH600000': 16906, 'SH600009': 195107, 'SH600028': 335257, 'SH600104': 197, 'SH600196': 630404, 'SH600276': 46282, 'SH600535': 57311, 'SH600649': 727170, 'SH600900': 2500379, 'SH601111': 203, 'SH601258': 7443096, 'SH601398': 1142014, 'SH601939': 1281361, 'SH603993': 263, 'SZ000069': 366998, 'SZ000503': 121479, 'SZ000568': 17699, 'SZ000709': 699639, 'SZ000778': 300752, 'SZ002008': 741767, 'SZ002065': 267133, 'SZ002085': 266334, 'SZ002146': 1465489, 'SZ002475': 1020693, 'SZ300017': 1756}\n", - "target weight: {'SH600000': 0.0012976336004362882, 'SH600009': 0.0204756895024156, 'SH600028': 0.008883617000656601, 'SH600104': 2.592943319382378e-05, 'SH600196': 0.09617041827497698, 'SH600276': 0.05681162545715886, 'SH600535': 0.015294256733040745, 'SH600649': 0.02417676167926707, 'SH600900': 0.12233373885315162, 'SH601398': 0.024531954099214746, 'SH601628': 0.005044154324745466, 'SH601888': 0.09500034426651846, 'SH601939': 0.012657033879067425, 'SH603993': 4.079522960136806e-06, 'SZ000069': 0.09054142453059062, 'SZ000503': 0.008036587259744734, 'SZ000568': 0.0049533657881637655, 'SZ000778': 0.06904486736535222, 'SZ002008': 0.06688985213943154, 'SZ002065': 0.04278977877238287, 'SZ002085': 0.0820368284038888, 'SZ002299': 0.06899317887598991, 'SZ002475': 0.08384652594205952, 'SZ300017': 0.00016035416530955983}\n", - "target position: {'SH601258': 7443495.190430395, 'SH600000': 16952, 'SH600009': 195676, 'SH600028': 336044, 'SH600104': 183, 'SH600196': 631454, 'SH600276': 46372, 'SH600535': 57498, 'SH600649': 728582, 'SH600900': 2504660, 'SH601398': 1143938, 'SH601628': 695470, 'SH601888': 2951253, 'SH601939': 1283887, 'SH603993': 255, 'SZ000069': 367641, 'SZ000503': 121875, 'SZ000568': 17775, 'SZ000778': 301255, 'SZ002008': 638620, 'SZ002065': 267645, 'SZ002085': 266802, 'SZ002299': 6194843, 'SZ002475': 1022527, 'SZ300017': 1765}\n", - "target weight: {'SH600000': 0.0013469483722729403, 'SH600028': 0.009286467498269333, 'SH600104': 2.368500734977497e-05, 'SH600196': 0.10145424564201923, 'SH600276': 0.06002237364700993, 'SH600535': 0.01588332650422844, 'SH600649': 0.025440421851940002, 'SH600900': 0.1279028471227695, 'SH601258': 0.035917606048396986, 'SH601398': 0.02559318344055778, 'SH601628': 0.005221942888216608, 'SH601888': 0.14928498761757883, 'SH601939': 0.013161430940131148, 'SH603993': 4.350147095904942e-06, 'SZ000069': 0.14038473724819095, 'SZ000503': 0.008556251357999256, 'SZ000568': 0.005243511514392524, 'SZ002008': 0.06824325050397591, 'SZ002065': 0.04420632869308568, 'SZ002085': 0.074424247013131, 'SZ002299': 0.0010812901181988855, 'SZ002475': 0.0871460668952185, 'SZ300017': 0.00017049992832446128}\n", - "target position: {'SZ000778': 301254.84776855103, 'SH600000': 16873, 'SH600028': 335064, 'SH600104': 156, 'SH600196': 629613, 'SH600276': 46235, 'SH600535': 57245, 'SH600649': 726346, 'SH600900': 2497776, 'SH601258': 7423462, 'SH601398': 1140689, 'SH601628': 692346, 'SH601888': 4557826, 'SH601939': 1279908, 'SH603993': 261, 'SZ000069': 551887, 'SZ000503': 121344, 'SZ000568': 17697, 'SZ002008': 636943, 'SZ002065': 266904, 'SZ002085': 231781, 'SZ002299': 97527, 'SZ002475': 1019747, 'SZ300017': 1749}\n" - ] - }, - { - "output_type": "error", - "ename": "KeyboardInterrupt", - "evalue": "", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 30\u001b[0m \u001b[1;31m# backtest & analysis\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 31\u001b[0m \u001b[0mpar\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPortAnaRecord\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mrecorder\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mport_analysis_config\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 32\u001b[1;33m \u001b[0mpar\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgenerate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;32md:\\qlib\\qlib\\workflow\\record_temp.py\u001b[0m in \u001b[0;36mgenerate\u001b[1;34m(self, **kwargs)\u001b[0m\n\u001b[0;32m 230\u001b[0m \u001b[1;31m# custom strategy and get backtest\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 231\u001b[0m \u001b[0mpred_score\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mload\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 232\u001b[1;33m \u001b[0mreport_normal\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpositions_normal\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnormal_backtest\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpred_score\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstrategy\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstrategy\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbacktest_config\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 233\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrecorder\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msave_objects\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m**\u001b[0m\u001b[1;33m{\u001b[0m\u001b[1;34m\"report_normal.pkl\"\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mreport_normal\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0martifact_path\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mPortAnaRecord\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_path\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 234\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrecorder\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msave_objects\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m**\u001b[0m\u001b[1;33m{\u001b[0m\u001b[1;34m\"positions_normal.pkl\"\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mpositions_normal\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0martifact_path\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mPortAnaRecord\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_path\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32md:\\qlib\\qlib\\contrib\\evaluate.py\u001b[0m in \u001b[0;36mbacktest\u001b[1;34m(pred, account, shift, benchmark, verbose, **kwargs)\u001b[0m\n\u001b[0;32m 269\u001b[0m \u001b[0mverbose\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mverbose\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 270\u001b[0m \u001b[0maccount\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0maccount\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 271\u001b[1;33m \u001b[0mbenchmark\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mbenchmark\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 272\u001b[0m )\n\u001b[0;32m 273\u001b[0m \u001b[1;31m# for compatibility of the old API. return the dict positions\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32md:\\qlib\\qlib\\contrib\\backtest\\backtest.py\u001b[0m in \u001b[0;36mbacktest\u001b[1;34m(pred, strategy, trade_exchange, shift, verbose, account, benchmark)\u001b[0m\n\u001b[0;32m 100\u001b[0m \u001b[0mtrade_exchange\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtrade_exchange\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 101\u001b[0m \u001b[0mpred_date\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mpred_date\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 102\u001b[1;33m \u001b[0mtrade_date\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtrade_date\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 103\u001b[0m )\n\u001b[0;32m 104\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m\u001b[0m in \u001b[0;36mgenerate_order_list\u001b[1;34m(self, score_series, current, trade_exchange, pred_date, trade_date)\u001b[0m\n\u001b[0;32m 76\u001b[0m \u001b[1;31m# optimize target portfolio\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 77\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0minit_weight\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msum\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m>\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 78\u001b[1;33m \u001b[0mtarget_weight\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moptimizer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcov\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mscore_series\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minit_weight\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 79\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 80\u001b[0m \u001b[0mtarget_weight\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moptimizer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcov\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mscore_series\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32md:\\qlib\\qlib\\portfolio\\optimizer.py\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, S, u, w0)\u001b[0m\n\u001b[0;32m 100\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 101\u001b[0m \u001b[1;31m# optimize\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 102\u001b[1;33m \u001b[0mw\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_optimize\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mS\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mu\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mw0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 103\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 104\u001b[0m \u001b[1;31m# restore index if needed\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32md:\\qlib\\qlib\\portfolio\\optimizer.py\u001b[0m in \u001b[0;36m_optimize\u001b[1;34m(self, S, u, w0)\u001b[0m\n\u001b[0;32m 126\u001b[0m \u001b[1;31m# mean-variance\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 127\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmethod\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mOPT_MVO\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 128\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_optimize_mvo\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mS\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mu\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mw0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 129\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 130\u001b[0m \u001b[1;31m# risk parity\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32md:\\qlib\\qlib\\portfolio\\optimizer.py\u001b[0m in \u001b[0;36m_optimize_mvo\u001b[1;34m(self, S, u, w0)\u001b[0m\n\u001b[0;32m 162\u001b[0m \u001b[1;32mand\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mlamb\u001b[0m\u001b[0;31m`\u001b[0m \u001b[1;32mis\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mrisk\u001b[0m \u001b[0maversion\u001b[0m \u001b[0mparameter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 163\u001b[0m \"\"\"\n\u001b[1;32m--> 164\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_solve\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mS\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_get_objective_mvo\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mS\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mu\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m*\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_get_constrains\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mw0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 165\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 166\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0m_optimize_rp\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mS\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mw0\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m->\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32md:\\qlib\\qlib\\portfolio\\optimizer.py\u001b[0m in \u001b[0;36m_solve\u001b[1;34m(self, n, obj, bounds, cons)\u001b[0m\n\u001b[0;32m 252\u001b[0m \u001b[1;31m# solve\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 253\u001b[0m \u001b[0mx0\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mones\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m/\u001b[0m \u001b[0mn\u001b[0m \u001b[1;31m# init results\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 254\u001b[1;33m \u001b[0msol\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mso\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mminimize\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mwrapped_obj\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mx0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mbounds\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mbounds\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mconstraints\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mcons\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtol\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtol\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 255\u001b[0m \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0msol\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msuccess\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 256\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mwarn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mf\"optimization not success ({sol.status})\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m~\\AppData\\Local\\Continuum\\miniconda3\\envs\\qlib\\lib\\site-packages\\scipy\\optimize\\_minimize.py\u001b[0m in \u001b[0;36mminimize\u001b[1;34m(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)\u001b[0m\n\u001b[0;32m 624\u001b[0m \u001b[1;32melif\u001b[0m \u001b[0mmeth\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m'slsqp'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 625\u001b[0m return _minimize_slsqp(fun, x0, args, jac, bounds,\n\u001b[1;32m--> 626\u001b[1;33m constraints, callback=callback, **options)\n\u001b[0m\u001b[0;32m 627\u001b[0m \u001b[1;32melif\u001b[0m \u001b[0mmeth\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m'trust-constr'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 628\u001b[0m return _minimize_trustregion_constr(fun, x0, args, jac, hess, hessp,\n", - "\u001b[1;32m~\\AppData\\Local\\Continuum\\miniconda3\\envs\\qlib\\lib\\site-packages\\scipy\\optimize\\slsqp.py\u001b[0m in \u001b[0;36m_minimize_slsqp\u001b[1;34m(func, x0, args, jac, bounds, constraints, maxiter, ftol, iprint, disp, eps, callback, finite_diff_rel_step, **unknown_options)\u001b[0m\n\u001b[0;32m 419\u001b[0m n1, n2, n3)\n\u001b[0;32m 420\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 421\u001b[1;33m \u001b[1;32mif\u001b[0m \u001b[0mmode\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m \u001b[1;31m# objective and constraint evaluation required\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 422\u001b[0m \u001b[0mfx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msf\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfun\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 423\u001b[0m \u001b[0mc\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0m_eval_constraint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcons\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mKeyboardInterrupt\u001b[0m: " + "[36502:MainThread](2020-11-27 16:27:43,761) INFO - qlib.workflow - [exp.py:180] - Experiment 3 starts running ...\n", + "[36502:MainThread](2020-11-27 16:27:43,779) INFO - qlib.workflow - [recorder.py:234] - Recorder 67d105113f424259889fc0b6b0b94973 starts running under Experiment 3 ...\n", + "[36502:MainThread](2020-11-27 16:27:43,780) INFO - qlib.workflow - [expm.py:251] - No tracking URI is provided. The default tracking URI is set as `mlruns` under the working directory.\n", + "[36502:MainThread](2020-11-27 16:27:43,991) INFO - qlib.workflow - [record_temp.py:127] - Signal record 'pred.pkl' has been saved as the artifact of the Experiment 3\n", + "[36502:MainThread](2020-11-27 16:27:44,050) INFO - qlib.Evaluate - [evaluate.py:161] - Create new exchange\n", + "'The following are prediction results of the LGBModel model.'\n", + " score\n", + "datetime instrument \n", + "2017-01-03 SH600000 -0.053414\n", + " SH600008 0.001820\n", + " SH600009 0.023472\n", + " SH600010 -0.005625\n", + " SH600015 -0.137476\n", + "/home/dongzho/miniconda3/lib/python3.7/site-packages/ipykernel_launcher.py:55: DeprecationWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.\n", + "/home/dongzho/qlib/qlib/portfolio/optimizer.py:256: UserWarning: optimization not success (9)\n", + " warnings.warn(f\"optimization not success ({sol.status})\")\n", + "Unknown exception: 2017-01-16 00:00:00 ('SZ300104', Timestamp('2017-01-13 00:00:00'))\n", + "Unknown exception: 2017-01-23 00:00:00 ('SZ000671', Timestamp('2017-01-20 00:00:00'))\n", + "Unknown exception: 2017-03-03 00:00:00 ('SZ002465', Timestamp('2017-03-02 00:00:00'))\n", + "Unknown exception: 2017-03-07 00:00:00 ('SH601127', Timestamp('2017-03-06 00:00:00'))\n", + "/home/dongzho/qlib/qlib/portfolio/optimizer.py:256: UserWarning: optimization not success (4)\n", + " warnings.warn(f\"optimization not success ({sol.status})\")\n", + "Unknown exception: 2017-05-08 00:00:00 ('SH601727', Timestamp('2017-05-05 00:00:00'))\n", + "Unknown exception: 2017-06-20 00:00:00 ('SH600036', Timestamp('2017-06-19 00:00:00'))\n", + "Unknown exception: 2017-06-21 00:00:00 ('SH600739', Timestamp('2017-06-20 00:00:00'))\n", + "Unknown exception: 2017-06-29 00:00:00 ('SZ300168', Timestamp('2017-06-28 00:00:00'))\n", + "Unknown exception: 2017-09-01 00:00:00 ('SH601088', Timestamp('2017-08-31 00:00:00'))\n", + "Unknown exception: 2017-09-12 00:00:00 ('SH601872', Timestamp('2017-09-11 00:00:00'))\n", + "Unknown exception: 2017-09-21 00:00:00 ('SH600100', Timestamp('2017-09-20 00:00:00'))\n", + "Unknown exception: 2017-09-22 00:00:00 ('SH600021', Timestamp('2017-09-21 00:00:00'))\n", + "Unknown exception: 2017-10-11 00:00:00 ('SH600959', Timestamp('2017-10-10 00:00:00'))\n", + "Unknown exception: 2017-10-25 00:00:00 ('SZ000792', Timestamp('2017-10-24 00:00:00'))\n", + "Unknown exception: 2017-12-26 00:00:00 ('SH600682', Timestamp('2017-12-25 00:00:00'))\n", + "[36502:MainThread](2020-11-27 17:28:14,269) INFO - qlib.workflow - [record_temp.py:249] - Portfolio analysis record 'port_analysis.pkl' has been saved as the artifact of the Experiment 3\n", + "'The following are analysis results of the excess return without cost.'\n", + " risk\n", + "mean 0.001247\n", + "std 0.005437\n", + "annualized_return 0.314237\n", + "information_ratio 3.640637\n", + "max_drawdown -0.033416\n", + "'The following are analysis results of the excess return with cost.'\n", + " risk\n", + "mean 0.001028\n", + "std 0.005432\n", + "annualized_return 0.259041\n", + "information_ratio 3.003970\n", + "max_drawdown -0.041455\n" ] } ], From 2311af5e47b44d17f46326c170e1a8b71bbe6214 Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 19:46:52 +0800 Subject: [PATCH 04/30] Update script --- README.md | 37 ++--- docs/start/initialization.rst | 2 +- examples/run_all_model.py | 280 +++++++++++++--------------------- requirements.txt | 3 +- 4 files changed, 127 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index c890afaca..dc9df109b 100644 --- a/README.md +++ b/README.md @@ -192,24 +192,6 @@ The automatic workflow may not suite the research workflow of all Quant research # [Quant Model Zoo](examples/benchmarks) -## Run a single model -`Qlib` provides three different ways to run a single model, users can pick the one that fits their cases best: -- User can use the tool `qrun` mentioned above to run a model's workflow based from a config file. -- User can create a `workflow_by_code` python script based on the [one](examples/workflow_by_code.py) listed in the `examples` folder. -- User can use the script [`run_all_model.py`](examples/run_all_model.py) listed in the `examples` folder to run a model. Here is an example of the specific shell command to be used: `python run_all_model.py --models=lightgbm`. For more use cases, please refer to the file's [docstrings](examples/run_all_model.py). - -## Run multiple models -`Qlib` also provides a script [`run_all_model.py`](examples/run_all_model.py) which can run multiple models for several iterations. (**Note**: the script only supprots *Linux* now. Other OS will be supported in the future.) - -The script will create a unique virtual environment for each model, and delete the environments after training. Thus, only experiment results such as `IC` and `backtest` results will be generated and stored. - -Here is an example of running all the models for 10 iterations: -```python -python run_all_model.py 10 -``` - -It also provides the API to run specific models at once. For more use cases, please refer to the file's [docstrings](examples/run_all_model.py). - Here is a list of models built on `Qlib`. - [GBDT based on LightGBM](qlib/contrib/model/gbdt.py) - [GBDT based on Catboost](qlib/contrib/model/catboost_model.py) @@ -226,6 +208,25 @@ Here is a list of models built on `Qlib`. Your PR of new Quant models is highly welcomed. +## Run a single model +`Qlib` provides three different ways to run a single model, users can pick the one that fits their cases best: +- User can use the tool `qrun` mentioned above to run a model's workflow based from a config file. +- User can create a `workflow_by_code` python script based on the [one](examples/workflow_by_code.py) listed in the `examples` folder. +- User can use the script [`run_all_model.py`](examples/run_all_model.py) listed in the `examples` folder to run a model. Here is an example of the specific shell command to be used: `python run_all_model.py --models=lightgbm`. For more use cases, please refer to the file's [docstrings](examples/run_all_model.py). + +## Run multiple models +`Qlib` also provides a script [`run_all_model.py`](examples/run_all_model.py) which can run multiple models for several iterations. (**Note**: the script only supprots *Linux* now. Other OS will be supported in the future.) + +The script will create a unique virtual environment for each model, and delete the environments after training. Thus, only experiment results such as `IC` and `backtest` results will be generated and stored. (**Note**: the script will erase your previous experiment records created by running itself.) + +Here is an example of running all the models for 10 iterations: +```python +python run_all_model.py 10 +``` + +It also provides the API to run specific models at once. For more use cases, please refer to the file's [docstrings](examples/run_all_model.py). + + # Quant Dataset Zoo Dataset plays a very important role in Quant. Here is a list of the datasets built on `Qlib`. - [Alpha360](./qlib/contrib/data/handler.py) diff --git a/docs/start/initialization.rst b/docs/start/initialization.rst index 423d7edf8..5615556b6 100644 --- a/docs/start/initialization.rst +++ b/docs/start/initialization.rst @@ -69,7 +69,7 @@ Besides `provider_uri` and `region`, `qlib.init` has other parameters. The follo "class": "MLflowExpManager", "module_path": "qlib.workflow.expm", "kwargs": { - "uri": "python_execution_path/mlruns"), + "uri": "python_execution_path/mlruns", "default_exp_name": "Experiment", } } \ No newline at end of file diff --git a/examples/run_all_model.py b/examples/run_all_model.py index 2f6c4299e..c02077b32 100644 --- a/examples/run_all_model.py +++ b/examples/run_all_model.py @@ -4,18 +4,20 @@ import os import sys import fire +import time import venv import glob import shutil +import signal +import inspect import tempfile +import traceback +import functools import statistics +import subprocess from pathlib import Path from operator import xor -from subprocess import Popen, PIPE -from threading import Thread from pprint import pprint -from urllib.parse import urlparse -from urllib.request import urlretrieve import qlib from qlib.config import REG_CN @@ -23,144 +25,50 @@ from qlib.workflow import R from qlib.workflow.cli import workflow from qlib.utils import exists_qlib_data + # init qlib provider_uri = "~/.qlib/qlib_data/cn_data" +exp_manager = { + "class": "MLflowExpManager", + "module_path": "qlib.workflow.expm", + "kwargs": { + "uri": "file:" + str(Path(os.getcwd()).resolve() / "run_all_model_records"), + "default_exp_name": "Experiment", + }, +} if not exists_qlib_data(provider_uri): print(f"Qlib data is not found in {provider_uri}") sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) from get_data import GetData GetData().qlib_data(target_dir=provider_uri, region=REG_CN) -qlib.init(provider_uri=provider_uri, region=REG_CN) +qlib.init(provider_uri=provider_uri, region=REG_CN, exp_manager=exp_manager) +shutil.rmtree(str(Path(os.getcwd()).resolve() / "run_all_model_records")) + +# decorator to check the arguments +def only_allow_defined_args(function_to_decorate): + @functools.wraps(function_to_decorate) + def _return_wrapped(*args, **kwargs): + """Internal wrapper function.""" + argspec = inspect.getfullargspec(function_to_decorate) + valid_names = set(argspec.args + argspec.kwonlyargs) + if "self" in valid_names: + valid_names.remove("self") + for arg_name in kwargs: + if arg_name not in valid_names: + raise ValueError("Unknown argument seen '%s', expected: [%s]" % (arg_name, ", ".join(valid_names))) + return function_to_decorate(*args, **kwargs) + + return _return_wrapped -class ExtendedEnvBuilder(venv.EnvBuilder): - """ - Thie class is modified based on https://docs.python.org/3/library/venv.html. - This builder installs setuptools and pip so that you can pip or - easy_install other packages into the created virtual environment. +# function to handle ctrl z and ctrl c +def handler(signum, frame): + os.system("kill -9 %d" % os.getpid()) - :param nodist: If true, setuptools and pip are not installed into the - created virtual environment. - :param nopip: If true, pip is not installed into the created - virtual environment. - :param progress: If setuptools or pip are installed, the progress of the - installation can be monitored by passing a progress - callable. If specified, it is called with two - arguments: a string indicating some progress, and a - context indicating where the string is coming from. - The context argument can have one of three values: - 'main', indicating that it is called from virtualize() - itself, and 'stdout' and 'stderr', which are obtained - by reading lines from the output streams of a subprocess - which is used to install the app. - - If a callable is not specified, default progress - information is output to sys.stderr. - """ - - def __init__(self, *args, **kwargs): - self.nodist = kwargs.pop("nodist", False) - self.nopip = kwargs.pop("nopip", False) - self.progress = kwargs.pop("progress", None) - self.verbose = kwargs.pop("verbose", False) - super().__init__(*args, **kwargs) - - def post_setup(self, context): - """ - Set up any packages which need to be pre-installed into the - virtual environment being created. - - :param context: The information for the virtual environment - creation request being processed. - """ - os.environ["VIRTUAL_ENV"] = context.env_dir - if not self.nodist: - self.install_setuptools(context) - # Can't install pip without setuptools - if not self.nopip and not self.nodist: - self.install_pip(context) - - def reader(self, stream, context): - """ - Read lines from a subprocess' output stream and either pass to a progress - callable (if specified) or write progress information to sys.stderr. - """ - progress = self.progress - while True: - s = stream.readline() - if not s: - break - if progress is not None: - progress(s, context) - else: - if not self.verbose: - sys.stderr.write(".") - else: - sys.stderr.write(s.decode("utf-8")) - sys.stderr.flush() - stream.close() - - def install_script(self, context, name, url): - _, _, path, _, _, _ = urlparse(url) - fn = os.path.split(path)[-1] - binpath = context.bin_path - distpath = os.path.join(binpath, fn) - # Download script into the virtual environment's binaries folder - urlretrieve(url, distpath) - progress = self.progress - if self.verbose: - term = "\n" - else: - term = "" - if progress is not None: - progress("Installing %s ...%s" % (name, term), "main") - else: - sys.stderr.write("Installing %s ...%s" % (name, term)) - sys.stderr.flush() - # Install in the virtual environment - args = [context.env_exe, fn] - p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=binpath) - t1 = Thread(target=self.reader, args=(p.stdout, "stdout")) - t1.start() - t2 = Thread(target=self.reader, args=(p.stderr, "stderr")) - t2.start() - p.wait() - t1.join() - t2.join() - if progress is not None: - progress("done.", "main") - else: - sys.stderr.write("done.\n") - # Clean up - no longer needed - os.unlink(distpath) - - def install_setuptools(self, context): - """ - Install setuptools in the virtual environment. - - :param context: The information for the virtual environment - creation request being processed. - """ - url = "https://bootstrap.pypa.io/ez_setup.py" - self.install_script(context, "setuptools", url) - # clear up the setuptools archive which gets downloaded - pred = lambda o: o.startswith("setuptools-") and o.endswith(".tar.gz") - files = filter(pred, os.listdir(context.bin_path)) - for f in files: - f = os.path.join(context.bin_path, f) - os.unlink(f) - - def install_pip(self, context): - """ - Install pip in the virtual environment. - - :param context: The information for the virtual environment - creation request being processed. - """ - url = "https://bootstrap.pypa.io/get-pip.py" - self.install_script(context, "pip", url) +signal.signal(signal.SIGTSTP, handler) +signal.signal(signal.SIGINT, handler) # function to calculate the mean and std of a list in the results dictionary def cal_mean_std(results) -> dict: @@ -174,6 +82,36 @@ def cal_mean_std(results) -> dict: return mean_std +# function to create the environment ofr an anaconda environment +def create_env(): + # create env + temp_dir = tempfile.mkdtemp() + env_path = Path(temp_dir).absolute() + sys.stderr.write(f"Creating Virtual Environment with path: {env_path}...\n") + execute(f"conda create --prefix {env_path} python=3.7 -y") + python_path = env_path / "bin" / "python" # TODO: FIX ME! + sys.stderr.write("\n") + # get anaconda activate path + conda_activate = Path(os.environ["CONDA_PREFIX"]) / "bin" / "activate" # TODO: FIX ME! + return env_path, python_path, conda_activate + + +# function to execute the cmd +def execute(cmd): + with subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True, shell=True) as p: + for line in p.stdout: + sys.stdout.write(line.split("\b")[0]) + if "\b" in line: + sys.stdout.flush() + time.sleep(0.1) + sys.stdout.write("\b" * 10 + "\b".join(line.split("\b")[1:-1])) + + if p.returncode != 0: + return p.stderr + else: + return None + + # function to get all the folders benchmark folder def get_all_folders(models, exclude) -> dict: folders = dict() @@ -212,11 +150,12 @@ def get_all_results(folders) -> dict: result["information_ratio_with_cost"] = list() result["max_drawdown_with_cost"] = list() for recorder_id in recorders: - recorder = R.get_recorder(recorder_id=recorder_id, experiment_name=fn) - metrics = recorder.list_metrics() - result["annualized_return_with_cost"].append(metrics["excess_return_with_cost.annualized_return"]) - result["information_ratio_with_cost"].append(metrics["excess_return_with_cost.information_ratio"]) - result["max_drawdown_with_cost"].append(metrics["excess_return_with_cost.max_drawdown"]) + if recorders[recorder_id]["status"] == "FINISHED": + recorder = R.get_recorder(recorder_id=recorder_id, experiment_name=fn) + metrics = recorder.list_metrics() + result["annualized_return_with_cost"].append(metrics["excess_return_with_cost.annualized_return"]) + result["information_ratio_with_cost"].append(metrics["excess_return_with_cost.information_ratio"]) + result["max_drawdown_with_cost"].append(metrics["excess_return_with_cost.max_drawdown"]) results[fn] = result return results @@ -237,6 +176,7 @@ def gen_and_save_md_table(metrics): # function to run the all the models +@only_allow_defined_args def run(times=1, models=None, exclude=False): """ Please be aware that this function can only work under Linux. MacOS and Windows will be supported in the future. @@ -275,53 +215,46 @@ def run(times=1, models=None, exclude=False): """ # get all folders folders = get_all_folders(models, exclude) - # set up - compatible = True - if sys.version_info < (3, 3): - compatible = False - elif not hasattr(sys, "base_prefix"): - compatible = False - if not compatible: - raise ValueError("This script is only for use with " "Python 3.3 or later") - if os.name == "nt": - use_symlinks = False - else: - use_symlinks = True - builder = ExtendedEnvBuilder( - system_site_packages=False, - clear=False, - symlinks=use_symlinks, - upgrade=False, - nodist=False, - nopip=False, - verbose=False, - ) + # init error messages: + errors = dict() # run all the model for iterations for fn in folders: - # create env - temp_dir = tempfile.mkdtemp() - env_path = Path(temp_dir).absolute() - sys.stderr.write(f"Creating Virtual Environment with path: {env_path}...\n") - builder.create(str(env_path)) - python_path = env_path / "bin" / "python" # TODO: FIX ME! - sys.stderr.write("\n") + # create env by anaconda + env_path, python_path, conda_activate = create_env() # get all files sys.stderr.write("Retrieving files...\n") yaml_path, req_path = get_all_files(folders[fn]) sys.stderr.write("\n") # install requirements.txt sys.stderr.write("Installing requirements.txt...\n") - os.system(f"{python_path} -m pip install -r {req_path}") + execute(f"{python_path} -m pip install -r {req_path}") sys.stderr.write("\n") + # setup gpu for tft + if fn == "TFT": + execute( + f"conda install -y --prefix {env_path} anaconda cudatoolkit=10.0 && conda install -y --prefix {env_path} cudnn" + ) + sys.stderr.write("\n") # install qlib sys.stderr.write("Installing qlib...\n") - os.system(f"{python_path} -m pip install --upgrade cython") # TODO: FIX ME! - os.system(f"{python_path} -m pip install -e git+https://github.com/you-n-g/qlib#egg=pyqlib") # TODO: FIX ME! + execute(f"{python_path} -m pip install --upgrade cython") # TODO: FIX ME! + if fn == "TFT": + execute( + f"cd {env_path} && {python_path} -m pip install --upgrade --force-reinstall --ignore-installed PyYAML -e git+https://github.com/you-n-g/qlib#egg=pyqlib" + ) # TODO: FIX ME! + else: + execute( + f"cd {env_path} && {python_path} -m pip install --upgrade --force-reinstall -e git+https://github.com/you-n-g/qlib#egg=pyqlib" + ) # TODO: FIX ME! sys.stderr.write("\n") # run workflow_by_config for multiple times for i in range(times): sys.stderr.write(f"Running the model: {fn} for iteration {i+1}...\n") - os.system(f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn}") + errs = execute(f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn}") + if errs is not None: + _errs = errors.get(fn, {}) + _errs.update({i: errs}) + errors[fn] = _errs sys.stderr.write("\n") # remove env sys.stderr.write(f"Deleting the environment: {env_path}...\n") @@ -335,13 +268,12 @@ def run(times=1, models=None, exclude=False): # generating md table sys.stderr.write(f"Generating markdown table...\n") gen_and_save_md_table(results) + sys.stderr.write("\n") + # print erros + sys.stderr.write(f"Here are some of the errors of the models...\n") + pprint(errors) + sys.stderr.write("\n") if __name__ == "__main__": - rc = 1 - try: - fire.Fire(run) # run all the model - rc = 0 - except Exception as e: - print("Error: %s" % e, file=sys.stderr) - sys.exit(rc) + fire.Fire(run) # run all the model diff --git a/requirements.txt b/requirements.txt index d3511d780..638ce22f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,5 +22,4 @@ scikit_learn==0.23.2 torch==1.6.0 tqdm==4.49.0 yahooquery==2.2.7 -mlflow==1.12.1 -pytorch-tabnet==2.0.1 \ No newline at end of file +mlflow==1.12.1 \ No newline at end of file From f8101214ebdc3ed953e74ab25433f9376734bf39 Mon Sep 17 00:00:00 2001 From: zhupr Date: Fri, 27 Nov 2020 20:00:05 +0800 Subject: [PATCH 05/30] Fix report, support google colab --- README.md | 2 +- examples/workflow_by_code.ipynb | 43 ++++++++++++++++++++++++++++----- qlib/contrib/report/graph.py | 14 ++++++++++- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index dc9df109b..884bbb5c0 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ For more details, please refer to our paper ["Qlib: An AI-oriented Quantitative # Framework of Qlib
- +
diff --git a/examples/workflow_by_code.ipynb b/examples/workflow_by_code.ipynb index 692e52078..4860bbf9e 100644 --- a/examples/workflow_by_code.ipynb +++ b/examples/workflow_by_code.ipynb @@ -10,14 +10,42 @@ "# Licensed under the MIT License." ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "import sys, site\n", + "from pathlib import Path\n", + "\n", + "TEMP_CODE_DIR = str(Path(\"~/tmp/qlib_code\").expanduser().resolve())\n", + "\n", + "try:\n", + " import qlib\n", + " scripts_dir = Path.cwd().parent.joinpath(\"scripts\")\n", + "except ImportError:\n", + " # install qlib\n", + " ! pip install pyqlib\n", + " # reload\n", + " site.main()\n", + " # download get_data.py script\n", + " scripts_dir = Path(\"~/tmp/qlib_code/scripts\").expanduser().resolve()\n", + " scripts_dir.mkdir(parents=True, exist_ok=True)\n", + " import requests\n", + " with requests.get(\"https://github.com/microsoft/qlib/blob/main/scripts/get_data.py\") as resp:\n", + " with open(scripts_dir.joinpath(\"get_data.py\"), \"wb\") as fp:\n", + " fp.write(resp.content)" + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "import sys\n", - "from pathlib import Path\n", "\n", "import qlib\n", "import pandas as pd\n", @@ -32,7 +60,7 @@ "from qlib.utils import exists_qlib_data, init_instance_by_config\n", "from qlib.workflow import R\n", "from qlib.workflow.record_temp import SignalRecord, PortAnaRecord\n", - "from qlib.utils import flatten_dict" + "from qlib.utils import flatten_dict\n" ] }, { @@ -48,7 +76,7 @@ "provider_uri = \"~/.qlib/qlib_data/cn_data\" # target_dir\n", "if not exists_qlib_data(provider_uri):\n", " print(f\"Qlib data is not found in {provider_uri}\")\n", - " sys.path.append(str(Path.cwd().parent.joinpath(\"scripts\")))\n", + " sys.path.append(str(scripts_dir))\n", " from get_data import GetData\n", " GetData().qlib_data(target_dir=provider_uri, region=REG_CN)\n", "qlib.init(provider_uri=provider_uri, region=REG_CN)" @@ -202,7 +230,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ "from qlib.contrib.report import analysis_model, analysis_position\n", @@ -320,7 +350,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.7.9" }, "toc": { "base_numbering": 1, diff --git a/qlib/contrib/report/graph.py b/qlib/contrib/report/graph.py index 15cc5fd0e..0ac0ffbc9 100644 --- a/qlib/contrib/report/graph.py +++ b/qlib/contrib/report/graph.py @@ -96,7 +96,19 @@ class BaseGraph(object): """ py.init_notebook_mode() for _fig in figure_list: - py.iplot(_fig) + # NOTE: displays figures: https://plotly.com/python/renderers/ + # default: plotly_mimetype+notebook + # support renderers: import plotly.io as pio; print(pio.renderers) + renderer = None + try: + # in notebook + _ipykernel = str(type(get_ipython())) + if 'google.colab' in _ipykernel: + renderer = 'colab' + except NameError: + pass + + _fig.show(renderer=renderer) def _get_layout(self) -> go.Layout: """ From f05df04320e2db30831167efe602182038c1fda8 Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 20:00:58 +0800 Subject: [PATCH 06/30] Fix --- examples/run_all_model.py | 4 +++- qlib/contrib/report/graph.py | 4 ++-- qlib/workflow/cli.py | 7 +++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/run_all_model.py b/examples/run_all_model.py index c02077b32..620632588 100644 --- a/examples/run_all_model.py +++ b/examples/run_all_model.py @@ -250,7 +250,9 @@ def run(times=1, models=None, exclude=False): # run workflow_by_config for multiple times for i in range(times): sys.stderr.write(f"Running the model: {fn} for iteration {i+1}...\n") - errs = execute(f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn}") + errs = execute( + f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn} {exp_manager}" + ) if errs is not None: _errs = errors.get(fn, {}) _errs.update({i: errs}) diff --git a/qlib/contrib/report/graph.py b/qlib/contrib/report/graph.py index 0ac0ffbc9..3fa688d36 100644 --- a/qlib/contrib/report/graph.py +++ b/qlib/contrib/report/graph.py @@ -103,8 +103,8 @@ class BaseGraph(object): try: # in notebook _ipykernel = str(type(get_ipython())) - if 'google.colab' in _ipykernel: - renderer = 'colab' + if "google.colab" in _ipykernel: + renderer = "colab" except NameError: pass diff --git a/qlib/workflow/cli.py b/qlib/workflow/cli.py index 08c13de2a..451337343 100644 --- a/qlib/workflow/cli.py +++ b/qlib/workflow/cli.py @@ -41,7 +41,7 @@ def sys_config(config, config_path): # worflow handler function -def workflow(config_path, experiment_name="workflow"): +def workflow(config_path, experiment_name="workflow", exp_manager=None): with open(config_path) as fp: config = yaml.load(fp, Loader=yaml.Loader) @@ -50,7 +50,10 @@ def workflow(config_path, experiment_name="workflow"): provider_uri = config.get("provider_uri") region = config.get("region") - qlib.init(provider_uri=provider_uri, region=region) + if exp_manager: + qlib.init(provider_uri=provider_uri, region=region, exp_manager=exp_manager) + else: + qlib.init(provider_uri=provider_uri, region=region) task_train(config, experiment_name=experiment_name) From b3afcc67d4cc83e6fdaf56b6b3053e9cf9fbe1aa Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 20:03:55 +0800 Subject: [PATCH 07/30] Add path --- examples/run_all_model.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/run_all_model.py b/examples/run_all_model.py index 620632588..93d7ac822 100644 --- a/examples/run_all_model.py +++ b/examples/run_all_model.py @@ -28,11 +28,12 @@ from qlib.utils import exists_qlib_data # init qlib provider_uri = "~/.qlib/qlib_data/cn_data" +exp_path = str(Path(os.getcwd()).resolve() / "run_all_model_records") exp_manager = { "class": "MLflowExpManager", "module_path": "qlib.workflow.expm", "kwargs": { - "uri": "file:" + str(Path(os.getcwd()).resolve() / "run_all_model_records"), + "uri": "file:" + exp_path, "default_exp_name": "Experiment", }, } @@ -43,7 +44,8 @@ if not exists_qlib_data(provider_uri): GetData().qlib_data(target_dir=provider_uri, region=REG_CN) qlib.init(provider_uri=provider_uri, region=REG_CN, exp_manager=exp_manager) -shutil.rmtree(str(Path(os.getcwd()).resolve() / "run_all_model_records")) +if os.path.isdir(exp_path): + shutil.rmtree(exp_path) # decorator to check the arguments def only_allow_defined_args(function_to_decorate): From 8b281957b0f7444493565857f073eb446c314645 Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 20:21:23 +0800 Subject: [PATCH 08/30] Fix fire --- examples/run_all_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/run_all_model.py b/examples/run_all_model.py index 93d7ac822..ee98177c2 100644 --- a/examples/run_all_model.py +++ b/examples/run_all_model.py @@ -253,7 +253,7 @@ def run(times=1, models=None, exclude=False): for i in range(times): sys.stderr.write(f"Running the model: {fn} for iteration {i+1}...\n") errs = execute( - f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn} {exp_manager}" + f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn} '{exp_manager}'" ) if errs is not None: _errs = errors.get(fn, {}) From 10747a3219cc59474613184cb4bafdd5d202ed7d Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 21:19:27 +0800 Subject: [PATCH 09/30] Fix --- examples/run_all_model.py | 5 +++-- qlib/workflow/cli.py | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/run_all_model.py b/examples/run_all_model.py index ee98177c2..f40f11444 100644 --- a/examples/run_all_model.py +++ b/examples/run_all_model.py @@ -28,7 +28,8 @@ from qlib.utils import exists_qlib_data # init qlib provider_uri = "~/.qlib/qlib_data/cn_data" -exp_path = str(Path(os.getcwd()).resolve() / "run_all_model_records") +exp_folder_name = "run_all_model_records" +exp_path = str(Path(os.getcwd()).resolve() / exp_folder_name) exp_manager = { "class": "MLflowExpManager", "module_path": "qlib.workflow.expm", @@ -253,7 +254,7 @@ def run(times=1, models=None, exclude=False): for i in range(times): sys.stderr.write(f"Running the model: {fn} for iteration {i+1}...\n") errs = execute( - f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn} '{exp_manager}'" + f"{python_path} {env_path / 'src/pyqlib/qlib/workflow/cli.py'} {yaml_path} {fn} {exp_folder_name}" ) if errs is not None: _errs = errors.get(fn, {}) diff --git a/qlib/workflow/cli.py b/qlib/workflow/cli.py index 451337343..e0c957f60 100644 --- a/qlib/workflow/cli.py +++ b/qlib/workflow/cli.py @@ -1,13 +1,14 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -import sys +import sys, os from pathlib import Path import qlib import fire import pandas as pd import ruamel.yaml as yaml +from qlib.config import C from qlib.model.trainer import task_train @@ -41,7 +42,7 @@ def sys_config(config, config_path): # worflow handler function -def workflow(config_path, experiment_name="workflow", exp_manager=None): +def workflow(config_path, experiment_name="workflow", uri_folder="mlruns"): with open(config_path) as fp: config = yaml.load(fp, Loader=yaml.Loader) @@ -50,10 +51,9 @@ def workflow(config_path, experiment_name="workflow", exp_manager=None): provider_uri = config.get("provider_uri") region = config.get("region") - if exp_manager: - qlib.init(provider_uri=provider_uri, region=region, exp_manager=exp_manager) - else: - qlib.init(provider_uri=provider_uri, region=region) + exp_manager = C["exp_manager"] + exp_manager["kwargs"]["uri"] = "file:" + str(Path(os.getcwd()).resolve() / uri_folder + qlib.init(provider_uri=provider_uri, region=region, exp_manager=exp_manager) task_train(config, experiment_name=experiment_name) From 1b781527155e3515feb17b3865977dce93825446 Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 21:20:04 +0800 Subject: [PATCH 10/30] Fix --- qlib/workflow/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qlib/workflow/cli.py b/qlib/workflow/cli.py index e0c957f60..65d9a14b4 100644 --- a/qlib/workflow/cli.py +++ b/qlib/workflow/cli.py @@ -52,7 +52,7 @@ def workflow(config_path, experiment_name="workflow", uri_folder="mlruns"): provider_uri = config.get("provider_uri") region = config.get("region") exp_manager = C["exp_manager"] - exp_manager["kwargs"]["uri"] = "file:" + str(Path(os.getcwd()).resolve() / uri_folder + exp_manager["kwargs"]["uri"] = "file:" + str(Path(os.getcwd()).resolve() / uri_folder) qlib.init(provider_uri=provider_uri, region=region, exp_manager=exp_manager) task_train(config, experiment_name=experiment_name) From be2173d8392cebbe3a2385429a4ff96f1bf20b5c Mon Sep 17 00:00:00 2001 From: zhupr Date: Fri, 27 Nov 2020 22:08:35 +0800 Subject: [PATCH 11/30] Fix --- examples/workflow_by_code.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/workflow_by_code.ipynb b/examples/workflow_by_code.ipynb index 4860bbf9e..f8370789b 100644 --- a/examples/workflow_by_code.ipynb +++ b/examples/workflow_by_code.ipynb @@ -35,7 +35,7 @@ " scripts_dir = Path(\"~/tmp/qlib_code/scripts\").expanduser().resolve()\n", " scripts_dir.mkdir(parents=True, exist_ok=True)\n", " import requests\n", - " with requests.get(\"https://github.com/microsoft/qlib/blob/main/scripts/get_data.py\") as resp:\n", + " with requests.get(\"https://raw.githubusercontent.com/you-n-g/qlib/main/scripts/get_data.py\") as resp:\n", " with open(scripts_dir.joinpath(\"get_data.py\"), \"wb\") as fp:\n", " fp.write(resp.content)" ] From f0454667f3f1767c98506f3b3f3886a7eaa5e059 Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 22:16:10 +0800 Subject: [PATCH 12/30] Fix workflow --- qlib/model/trainer.py | 1 + qlib/workflow/expm.py | 13 +++++-------- qlib/workflow/recorder.py | 2 ++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qlib/model/trainer.py b/qlib/model/trainer.py index e4fc8eef9..0ef062021 100644 --- a/qlib/model/trainer.py +++ b/qlib/model/trainer.py @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. + from qlib.utils import init_instance_by_config, flatten_dict from qlib.workflow import R from qlib.workflow.record_temp import SignalRecord diff --git a/qlib/workflow/expm.py b/qlib/workflow/expm.py index 156beb690..80d471845 100644 --- a/qlib/workflow/expm.py +++ b/qlib/workflow/expm.py @@ -239,20 +239,17 @@ class MLflowExpManager(ExpManager): return self._client def start_exp(self, experiment_name=None, recorder_name=None, uri=None): + # set the tracking uri + if uri is None: + logger.info("No tracking URI is provided. Use the default tracking URI.") + else: + self.uri = uri # create experiment experiment, _ = self._get_or_create_exp(experiment_name=experiment_name) # set up active experiment self.active_experiment = experiment # start the experiment self.active_experiment.start(recorder_name) - # set the tracking uri - if uri is None: - logger.info( - "No tracking URI is provided. The default tracking URI is set as `mlruns` under the working directory." - ) - else: - self.uri = uri - mlflow.set_tracking_uri(self.uri) return self.active_experiment diff --git a/qlib/workflow/recorder.py b/qlib/workflow/recorder.py index b3069b9ac..b381a914a 100644 --- a/qlib/workflow/recorder.py +++ b/qlib/workflow/recorder.py @@ -224,6 +224,8 @@ class MLflowRecorder(Recorder): ) def start_run(self): + # set the tracking uri + mlflow.set_tracking_uri(self.uri) # start the run run = mlflow.start_run(self.id, self.experiment_id, self.name) # save the run id and artifact_uri From 0824e0e65cb7d838fb6c61ad42068555e35ae11a Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 22:21:03 +0800 Subject: [PATCH 13/30] Fix recorder --- qlib/workflow/recorder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qlib/workflow/recorder.py b/qlib/workflow/recorder.py index b381a914a..4c1ddfdfe 100644 --- a/qlib/workflow/recorder.py +++ b/qlib/workflow/recorder.py @@ -225,7 +225,7 @@ class MLflowRecorder(Recorder): def start_run(self): # set the tracking uri - mlflow.set_tracking_uri(self.uri) + mlflow.set_tracking_uri(self._uri) # start the run run = mlflow.start_run(self.id, self.experiment_id, self.name) # save the run id and artifact_uri From 7952d7993209978094417ffa2b4f536d2abd6dfa Mon Sep 17 00:00:00 2001 From: Jactus Date: Fri, 27 Nov 2020 22:26:53 +0800 Subject: [PATCH 14/30] Fix script --- examples/run_all_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/run_all_model.py b/examples/run_all_model.py index f40f11444..05839a125 100644 --- a/examples/run_all_model.py +++ b/examples/run_all_model.py @@ -153,7 +153,7 @@ def get_all_results(folders) -> dict: result["information_ratio_with_cost"] = list() result["max_drawdown_with_cost"] = list() for recorder_id in recorders: - if recorders[recorder_id]["status"] == "FINISHED": + if recorders[recorder_id].status == "FINISHED": recorder = R.get_recorder(recorder_id=recorder_id, experiment_name=fn) metrics = recorder.list_metrics() result["annualized_return_with_cost"].append(metrics["excess_return_with_cost.annualized_return"]) From bebce24a7c35c81d2ad4ed492d9e9e56d8f6662b Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Fri, 27 Nov 2020 22:30:05 +0800 Subject: [PATCH 15/30] Update all baseline models. --- .../benchmarks/GATs/workflow_config_gats.yaml | 28 +- examples/benchmarks/HATS/README.md | 15 - examples/benchmarks/HATS/requirements.txt | 4 - .../benchmarks/HATS/worflow_config_hats.yaml | 77 --- .../benchmarks/LSTM/model_lstm_csi300.pkl | Bin 209290 -> 209290 bytes examples/benchmarks/TabNet/README.md | 4 - examples/benchmarks/TabNet/requirements.txt | 5 - .../TabNet/workflow_config_tabnet.yaml | 66 --- qlib/contrib/model/catboost_model.py | 8 +- qlib/contrib/model/pytorch_alstm.py | 60 ++- qlib/contrib/model/pytorch_gats.py | 100 ++-- qlib/contrib/model/pytorch_gru.py | 34 +- qlib/contrib/model/pytorch_hats.py | 491 ------------------ qlib/contrib/model/pytorch_lstm.py | 34 +- qlib/contrib/model/pytorch_sfm.py | 115 +++- qlib/contrib/model/tabnet.py | 85 --- qlib/contrib/model/xgboost.py | 12 +- 17 files changed, 282 insertions(+), 856 deletions(-) delete mode 100644 examples/benchmarks/HATS/README.md delete mode 100644 examples/benchmarks/HATS/requirements.txt delete mode 100644 examples/benchmarks/HATS/worflow_config_hats.yaml delete mode 100644 examples/benchmarks/TabNet/README.md delete mode 100644 examples/benchmarks/TabNet/requirements.txt delete mode 100644 examples/benchmarks/TabNet/workflow_config_tabnet.yaml mode change 100755 => 100644 qlib/contrib/model/pytorch_gats.py delete mode 100644 qlib/contrib/model/pytorch_hats.py delete mode 100644 qlib/contrib/model/tabnet.py diff --git a/examples/benchmarks/GATs/workflow_config_gats.yaml b/examples/benchmarks/GATs/workflow_config_gats.yaml index 33aa0fe8d..7212e0ee2 100644 --- a/examples/benchmarks/GATs/workflow_config_gats.yaml +++ b/examples/benchmarks/GATs/workflow_config_gats.yaml @@ -8,6 +8,20 @@ data_handler_config: &data_handler_config fit_start_time: 2008-01-01 fit_end_time: 2014-12-31 instruments: *market + infer_processors: + - class: RobustZScoreNorm + kwargs: + fields_group: feature + clip_outlier: true + - class: Fillna + kwargs: + fields_group: feature + learn_processors: + - class: DropnaLabel + - class: CSRankNorm + kwargs: + fields_group: label + label: ["Ref($close, -2) / Ref($close, -1) - 1"] port_analysis_config: &port_analysis_config strategy: class: TopkDropoutStrategy @@ -26,8 +40,8 @@ port_analysis_config: &port_analysis_config min_cost: 5 task: model: - class: GAT - module_path: qlib.contrib.model.pytorch_gats + class: GAT_Classic + module_path: qlib.contrib.model.pytorch_gats_classic kwargs: d_feat: 6 hidden_size: 64 @@ -38,8 +52,7 @@ task: early_stop: 20 metric: loss loss: mse - base_model: LSTM - with_pretrain: True + base_model: GRU seed: 0 GPU: 0 dataset: @@ -47,7 +60,7 @@ task: module_path: qlib.data.dataset kwargs: handler: - class: ALPHA360_Denoise + class: ALPHA360 module_path: qlib.contrib.data.handler kwargs: *data_handler_config segments: @@ -58,11 +71,6 @@ task: - class: SignalRecord module_path: qlib.workflow.record_temp kwargs: {} - - class: SigAnaRecord - module_path: qlib.workflow.record_temp - kwargs: - ana_long_short: False - ann_scaler: 252 - class: PortAnaRecord module_path: qlib.workflow.record_temp kwargs: diff --git a/examples/benchmarks/HATS/README.md b/examples/benchmarks/HATS/README.md deleted file mode 100644 index b70dbff25..000000000 --- a/examples/benchmarks/HATS/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Requirement - -* pandas==1.1.2 -* numpy==1.17.4 -* scikit_learn==0.23.2 -* torch==1.7.0 - -## HATS - -* HATS is a a hierarchical attention network for stock prediction which uses relational data for stock market prediction. HATS selectively aggregates information -on different relation types and adds the information to the representations of each company. HATS is used as a relational modeling module with initialized node representations.Furthermore, HATS -can predict not only individual stock prices but also market index movements, which is similar to the graph classification task. - -* HATS uses pretrained model of GRU and LSTM. The code of GRU and LSTM used in Qlib is a pyTorch implemention of GRU and LSTM. -* Paper address:HATS: A Hierarchical Graph Attention Network for Stock Movement Prediction https://arxiv.org/pdf/1908.07999.pdf \ No newline at end of file diff --git a/examples/benchmarks/HATS/requirements.txt b/examples/benchmarks/HATS/requirements.txt deleted file mode 100644 index 16de0a438..000000000 --- a/examples/benchmarks/HATS/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pandas==1.1.2 -numpy==1.17.4 -scikit_learn==0.23.2 -torch==1.7.0 diff --git a/examples/benchmarks/HATS/worflow_config_hats.yaml b/examples/benchmarks/HATS/worflow_config_hats.yaml deleted file mode 100644 index b08df14e0..000000000 --- a/examples/benchmarks/HATS/worflow_config_hats.yaml +++ /dev/null @@ -1,77 +0,0 @@ -provider_uri: "~/.qlib/qlib_data/cn_data" -region: cn -market: &market csi300 -benchmark: &benchmark SH000300 -data_handler_config: &data_handler_config - start_time: 2008-01-01 - end_time: 2020-08-01 - fit_start_time: 2008-01-01 - fit_end_time: 2014-12-31 - instruments: *market - infer_processors: - - class: RobustZScoreNorm - kwargs: - fields_group: feature - clip_outlier: true - - class: Fillna - kwargs: - fields_group: feature - learn_processors: - - class: DropnaLabel - - class: CSRankNorm - kwargs: - fields_group: label - label: ["Ref($close, -2) / Ref($close, -1) - 1"] -port_analysis_config: &port_analysis_config - strategy: - class: TopkDropoutStrategy - module_path: qlib.contrib.strategy.strategy - kwargs: - topk: 50 - n_drop: 5 - backtest: - verbose: False - limit_threshold: 0.095 - account: 100000000 - benchmark: *benchmark - deal_price: close - open_cost: 0.0005 - close_cost: 0.0015 - min_cost: 5 -task: - model: - class: HATS - module_path: qlib.contrib.model.pytorch_hats - kwargs: - d_feat: 6 - hidden_size: 64 - num_layers: 2 - dropout: 0.6 - n_epochs: 200 - lr: 1e-3 - early_stop: 20 - metric: loss - loss: mse - base_model: GRU - seed: 0 - GPU: 0 - dataset: - class: DatasetH - module_path: qlib.data.dataset - kwargs: - handler: - class: ALPHA360 - module_path: qlib.contrib.data.handler - kwargs: *data_handler_config - segments: - train: [2008-01-01, 2014-12-31] - valid: [2015-01-01, 2016-12-31] - test: [2017-01-01, 2020-08-01] - record: - - class: SignalRecord - module_path: qlib.workflow.record_temp - kwargs: {} - - class: PortAnaRecord - module_path: qlib.workflow.record_temp - kwargs: - config: *port_analysis_config \ No newline at end of file diff --git a/examples/benchmarks/LSTM/model_lstm_csi300.pkl b/examples/benchmarks/LSTM/model_lstm_csi300.pkl index ff7fee4506e7f74de8b105ab960f83338ff9a62e..84d6419da2801bbe484bb71cfd40188225fe7951 100644 GIT binary patch literal 209290 zcmb5V2{cyU7ypltnIfSwRFX(YDbL+IlvF4~ie$CWN4(6=k7yk z)I_ONnpK)-&H6u|&*%GJ|L^zr|Ng&g{hzh2wVvzV{W{M+=e*Ckd++P&)JIxELQYQN z|MgLm=r0i%859=2B4kW(V055a#Ns7W?Nub^{l_O>Dky0Ak|iNQ(c#ONMJddR3=WA5 z37!%j6s;hXFm@M8j`UI!n~q$z%xq;y_`X-yg{e!H2S$7Rb5cPlcGaT6y@9BW}Il=YGopA3o#4zw{B_H~hHi5AMaIZ3!kNIJ%>KcQi58l>3oZUze^>}D{}*QLzhkWa#*7nVlK+Jn{}0CcAIyYk;Y4?# z&40S;WiG132;VnUbyW`C@?#RO|}E8)z4 zw?E6tHd^Q^o*GVn4_UW=T;%=_z$02X+g<4CrTG5<{u;HyIse7Hg~hlD=2qjagmeGh zyqA+?w9xx+^BaZp{%X6=Kc?qL3w_;%elc-Ee=m8ldHuAhub(%!w z)XycK{I6HghTy0C!GmV-ezgm1-nj+_C=^mdV-Doa+z8P{8Q`j4B=TL7PG6%kxFN3x z(}%qm;rqY>7`QE#j!(MFKl)WmUiU&^tWrOSxMB$0(Cw7E<_@2nG66nxDZ!YsIy%zu z%U*G@L6RjbmQDRL!EZM#ee*~ZA4^(b} zF}A;9N_jRcFPe;MpM7!D!?8HLZ3P4$CkX#?1U)|)V92=wYvW$;kOSX;rwHO5Oqf=#EJf%?@=GKcJ+&Q(NdZTxwXO%0<_v*Y z=Zj$eVkK{XeIvd+(;wbnjX{+s2JH5mKn$DR#Qk}z46R8M!7%1E-B{Jh6+ZF8s$U1- z#H>f$&tFbVO|O{cT^z$U#LBUePFG-FYCIWd)l<5q1XBrX_k;so*c|+zyGq$FLD&-hiubHpL8C!cHY*L7Fg$rcKp{m*RWn z?F(VmP+b;$K^@)tHgR|D_KREx#PF{iZqT>vJKW;K)sRs!7=@9IFz4BKD*7^qa-Yv5 zzm(HZ*)9Ny^TR-{aeDwHGiO zW=-2wY(XO9z2Jo+;Enslwc||GoHZLen<61MOpg^=n^Nqn!H}3S6}Al6#Gcu`rW2=D zvngi_nEjHoqWhs~tb6`NRx;r-Y;9G=rld0zp-?Ed|$?kg66RpjXsV0;$DK%t6o}v@FWcQyqP;;ZO&4j~ z=e}%aa}=8^+qco?NIBDudC9CcRKUP@vY4@13fm<+;fcEh#_t@9u3?qXEG*(A$ArQg z#~4z5z8`{ubfM!wHHijnLd`IJShKL1duBBbuPCKJbpK2mBP|7?J=!<@g{S+ z+zYW=3i*?HWo*?K8C-3U0($YuIBmQkM(nMmta;M-px-*k*Y1Zm1f^KzT?6MolyFD& z0$9Pqb@<@wZ2WLYAMd}+himl@XwQsMEX6#P@7z@>m;%3`s^u=TZS~}CH*CcanLAW( z@dk`h1BG+kar2pK;7;@h4gcxPfj`KO&D_GWYu2!DKRoE^<)LWrql?!*C86ha9Tdvg zvW|BL`1Q>#VBkEO?Md3hr%zK9?O72Fk7vAqaBDRjohid>1}4J%g0GOIa*3oL8bf*J zS%`SOiG>%b!Jid}X`J>@IJ9UjeLFanHJm<6YqX_lMWF(z7+n@PAM8h!PuFI`lY8xzcKavJ?KwVaUw=BJp90mp3gPM65O^@b z4!F(N`FlwwXumQEo~&%`l4?H~OXE zpn_#wor?#}T{nPPJ~bg%|8HEp7Qm;Y=6EEmjT>S+mr5%AFe2HEySz>skLODZcEk@x zJ#TsbcZLhfsdbV^A>&=m69x0s6+mZgJ^lGEh5e(B(i#)_hTR26sC1bp#7G?zRCd_1 z{zuNz5&W*Tqk151D+(BV;|Pg`PoC6 z$ZZ)m9n?feohjH=_yF6LR?-+7d(_J=gF_}7^nKDcYALV1p6n!x7 z|4A_Sq#9bKEhdHed3ZI+7)55EV9j1fJZ~S3&f_nTvsxNIvoa4Z>8Z0JTV+_v=tQ#G zZU-N?__HBz6KPH23z6|2WBh%tn6sJLMR(Tx=BpN;q17wuY1`;be7duQ?V02W;eGpX za3+8oF6+S*65QZ@xEh&G{tnqXA@Ic_n59})oS?TGxRDf0OHSBPbHo5PIeG>9%}m81 zi<8mQMhTmG^aS@?CxUCyeX4cJVd^{gvSo^PVx2LBR@V|xd2@l>u7!c+?+>60H{sAe zHR^q+fCn&%`gWFpT)+sl{Imv>rF*DXC5-|iHj_zPG%YDhppTVH;PJvFXmM?Ymfn>h z{nnS?x9u9_4Hd7MEfrv(V8PnFn?d)Z78d&_K*Yj4w4JC=#V$I$pL;t$t8h4+70R<+ zTjJ4n&SB1A>p;HibB&<8UpB}r2_l0(`7m`mc%!QDP*)%(KDm4`=Qt7JswCAHamKP7iA=70zN&S5^ zcUmL2XF>^Yde)I^Z@)<=#d|1{B*0~_l6^c?a&uH24zKn`0N&kd8zOPI~>pMBn{rfru zNPp+kj>_ZW=38(=!x>dB@4-}s=bUccB$^lTgUSaa@NHKfk%sYk=$?HT^!ru7w73tn z!fOq?`E(~;JEzO0BqY=12jd#%7k9$R75?~g+cDU=ZUqK^yax6p9JIcC zgW%{G9Pd~TR&!+OBeaMz{IvLh&`Gq*HJ5@vb%CSSK(@L#4?tWjUebju!0@i5nhNk9XkUpWrK8O3@iqYS}A^IVGS}w(G%xA&4Q*IP~ zb0SMG$%cS$W&97tT=+P%5B6KO73Qpa(=b!70k%B84QXw&acqedha)N>AM(%Qhocek`0? zWgPi7b7{Kghp6_)nY(eQhaB#vQACggPE6WF3*UU^VpWdQ;|1R2vvDJLx5S>U&C$YJ zBX7dAq&JYIUd-*=yq%WrlVw#`hts8iPWrrTFTCGBADuQ7+PwB$!5Ovp!ZKeiHg#ti zl}*#cd$!}4#_=OS%aILRAsCw}haZjjcNZ!dOps9_snDXB^x4_A`a; zwt<+Q9RA7HD)wN#0jrkT#Oll%sI_JcTu4lyKi9(8s4foElb-YMZDzpO5r*tjY#L49 zHytOd8KOaO6Z~4FN=;(DR_#8>7hcJulXZ36<4eP_zke4^c$7l^>K8@Pzf9PQomDXQ z8NtH~k7>%FTb$0jY0zJPJeqj8!KXdjsnxL?s?|Ec=TtoG?ta1Td5{dce{9(Mqz0HV zw*Vi$6+cZs(q{|5U*_EYDD%tsr_^TignJe`P@pqqE__{M49E9Wvd3Q@!hF5ykXhu( zf?p-GdwtzmZEhude>{qxsd|h~*!Pgz;As@Na5N3u>jO!5&cMOT*U_sX8VAlf%}@UG z0{Sgd!1sGD@Fu~{l?Y?>n67VKqEn zpNji07V_rD7r~>lbjtf!LfnBw{?U~gEU;Gxho7Dk?4dTKWfU%%j}zm5S`$G?Il3gWe@|0XOYBPLhyf&@dVxyz%~8NbwkyRae}t=dMi zo0{2R*T06ss$~$1l0_SWW@6u$%6Q1rg-=Y9Vso?l;+t(#@P^x93|CBIUazgF%+Zjx ze4NIH)Q-p7@{Z!?6cx5_Z3^Z2eWHmc9`jyhL-FI_DRlVwIP6If!5}GlOf=PDosFTm zBdUVV+dH$c@*8l(eJxp?_U85M_k)#PI0MN&5Iv(EUKEUEiJMiRtJj|OXxd>(`wG}h zd6f8QH%Iq!d6SnpaAVj_8szW7Y?g0;rJ-u<&8@SP`k{iH993|U?`j%;>MA{SRirD! zf6%B>HzvIDh91_e5${`Gaf$te{NUUBMaTN**&JNDnJ#AOfkf-^<9*t%Qo~qdd>e6+ zbLwk`jxwL9YS?4AySM?CC0nvpl~y=jxse+cslxVboXabejbw&r-EdKyEsGkX#A+`s zz<|YmTiunm*bLFL&cxb}Jg zJAQt%XiJV6u083&=FIefhSR&q-?KmSySWq!t#3nT+jflnrN_D-*TNC4G}tykopcSx zF+Uv*h<#N;d*=Uu{m*ULbjpW#qj#X)=?>2Q#^G@n1MuvaM*P~VIOmxoUbH1pjM{~F zMs`rakP#?ToV!me*~A+W&?zilACuw2}CwcdczrJ*?|AI8+^@E4=7%vLmBtf&|6OvcmB9Y(0C5bzov5*PiknKsVR9*y~=63 z7~n&#yZG?&Ingxr2as901KvG4O-1*u@XOHW6g&9=XkBt+ZSJ4B>8BVAdb@!w9x#we ztV-jX-qnKb=U9SuOyE9$C@f2xj;SkCxE8tD(6{ga8OPoMmlfOj`AgO^iNJ?~AqE-r zS$Q;zK4rj$`zO#mV_U+31(cY5np^Qj9fY>Fn^dQbNyjYOZ&d0%Hb4id9JhIBW|M#c9-ffrX@g-WmLjMWaYah$ zxK}kC?>F-#_o*4er1aU;Tm4v0*$DQiOM|7GJp>Ni_@6!qvMB~=R3^_}J1oIvHdkqU zR2um%D<(&Qh_C7y0n53)Z2o{kSe6 zLd%Xd@l!MWuxW;a;G{zacqt>Tza)ziQY%n*@H|jUoj{pyZBhC0dc6Ea5xr)*;>&Oo z7LZuLJxSQg&L>}Ei*DD@=Z0NOAv%@ChqW~fu~9|aB`1rimY(~`)xt<=NZntImcr^~6G zFULo%69n5cve>2$Lsm3p59MslE=MK@KB`Gv}h%_^KY|M_7{XPRVpoOpFBE zlWGoeKii;Qe*!zi|jW(}0Z@lDmV&`*sGk-Y&oBi8UnI(un=hXs0?s+gceikWP-30CSlj)}Y4*Ir8pH(y+hNyu< zaK_zDoI;%pnxml_YM_ z25U(A{hR9CkHY>x&mprpjrKi`rNaY8;a$5ausFRJgzGKP#5afgenS%8=_-=54zgyE z3YwR!ra_Aapv3MQobAd5Y`^3XYTr2sO7(`Zb@n@0N!<1mH2VzM`tIQ>gazEwUMaFm z_{{lfUxj6l*OAeiZvK2Rhx@X^n5)1FuYVtbPDhJav+qWH6H)=2+>F_!KIb53-$WjB zrE%ntnS6I=GJTlR%!_z?G7s&7^pr~!-=>W6Cl0|++iGxa-A)TOMAIy9A2xoMBU){} zO#^PHQ{azY{(!t0?8{fjxG$UGLyI8`vppxMQtzT-i{-F@nNuI{+oFKLo`&`ZJ+N@s z8rE}hJNS;9%w2kRg4~Q|kfp${q2s9PYr%bqtn9KjjL=d(X% z`dr$@SUP`T8FsIBq6ulXOsVg0E_k;OUtHQvA(OS(sOBqN;_Gxw)7N8?DV7lZGfyx_ zFpQ0JjKfOp6cX;(Ob%w@*eH1ohU%7q@8gZABihbB4SERYf~JD%nM!aPnMBHeh@6La zag&3B*m;R~JpNFV_DYXt0hfj3s5FlKx@|}A1^rll@?$#cHbd}gtb}Nw*>~!EIhVIg zvtj|+VfexK6tu>=K~;G=EFJL;;{L3q8m<<|(Wlos=J)aZ<3t76$4s4uU2_KAlDg5Uun7^u$DjwgaZMpi) z-b)#-H>R+L;g?7#zktoWsDN2A{lu|_iA>V(D`^BJkW|eYN{Fe04o@xmbytzw{K5mT z$MnNXGecR{_Hc2`Ko?gI3&v7~7IKleNq&PmVX;RayswymS4Zlx3}Y20ccGJBNe$-p zZ`!c=mr5Y)ojojmYY2-U4#v|VjFW256*S&fe}-0k!(^aKQ2I>8Lk{J-s7pV zta3N*Q$Q~HI;4O@0a_6sjK4K|`JnYaxX{Reb&GYT z%l{IXtL&unay2x#+J-AX_Jr!rO~wzIduhs)aBNPFY-l!>M&bGv_?2VGZ_Zc&nM1ep zmCOCh;f!}w{>ufIIO#Enk7s8KRMvF?3RoLz78p*vS_F{6lLK2@_E(iU!dibEoX8tNL1^k ziz@fGa{K{%_Bm3Q-9LYmP8-K_htd+T-wJOwZIKjO4n7163NG02Rccs&axdJJ7>BcU z($RWVF%-9%Q|sj2Ht%#(ShIq@cJ(lIy%w@ms%JI6-qr|c1e3ZZGMX{aJ>9*!! zJmNf^g@|L$9;qB#l-I#69#{wGHHyJ?q$JChl}FW|BQWXx2qqI}O+kGIzzSxK{nK5r zs-^|@$#u}r^Rmn-vb8~TAwT7 zokk?6j!(jHh#JUul$s- z>g8c@Si$JY%6s(a=}+pJuZDZ4+?iOnXbq=%Rfgr;O<=k22Ep7@JE3{+TDHgk7$nVh z$7J;%bZ@E#Ejnt(?)I($ok{8}clLXz-}Q$YuIZsmh5=icJBn1qdRgxBWxV&<<3EzRT(@}P63x4*J*>H0;t;F0)B5S`MF!*>b<7o8i^G4 zOPmXGTzZV_8yd#FyQ9QD%zwxg^pW6|U)1s?9Vt}QatMk$?(jWh(n0-0A$=NpjNcX= zLXiie`J6#1Abgj|dL{K(#O}|WXwqIvU$2cy)5bGxX2f0_+{FQ4P zNF#I(2wWfXSFX137nkY-|5T1`DEmNFRXt$zs1d>gKEQ6(5bpGSY5Y}snBI6@giU81 z$Wp>jbnNFHPCYMx&Cu?{iZf(bj=eET)IFupDGIE5Bave3VjSJ>#~bG@#IFI?RL+-! zkyJ9PFxXFlx32K1$v)hrrAyhW6Umr+s)8RDqru`F&p@~1B39n3#{69K>2c@?JeFyV zad%a*-_uz55_ykHs;Q<~3;MILU8a29T0dw^E2oOOfjDUG96Fsgm)5XOxKf=*nE`U_ zV!;85Y|vu`#m4Mn;ap~s*pI#>KERbL$I~gNnb-rmOcJX_@2Z2)1BE&6+L>+rKbh)-wJN=9Zmt{yw#ZP z`niolI&uXk-|vQV@;gZT`XHvX)}Hru+=UK{a@nccQ(Rp7W-OJ_L(^-6;kY=K_;G=N z^DW(p4NuHioH*8-u~?fWpHjtwr)QvI+#cBdHVh?}H-q%z`MB_S6J=fS!uE;hc;~wb zWVL4^d$uqejZZaUL3uW6cr|dEIdROzocJa2F5KXt^LXl8FQvcoqqeT!pw!@j=k~gD zw?AgH1BYMoC3AmaqTwNuY|CP6<4@tF)~jrHLNwVa-x(aZJI_28ST_bP}BS z^_!}LlbHUoBC3s-!wEeel*ZqKE!$j}|HysZ9!Ik@n_%W`?Z_Ta zs6eNf3-scqF)lS*LOmsgR2~sWKI5XH{!%uDN$jWc1^201HHs!|k%CG?Nu=31(Akl} z+Vy1kBSEF~w9yB8qdC;>tA+7(Kj2YkDtwRb&jx>1rR}lFaB5^XjCQdU^u!u66{mD= zz-$jP=sCvgDD2|DEK`BqUw84NL!_DB;sEAT{8*G~^h|Kb{X3o0OlBQR`eK~^0{n7) zI(~U99xH{dlzaClO&(`Uemf^H$6lV#a-YP`Ki5Ia{sOkKJRG+Mn4vhb4E-NIA;YQR zkpEGe^^bc7Wp4~nRpJ6=B#mI}-Nkv8Fgeh^BofRi+y>8E^ZBY$Jsdsk25;mR1Z|-i zv~trmGIFR8T0#+v(T~pJDECf zVl6Yza!n5R`8_t_RCs&~+yBxMP1Tp7?aEQi@~2qaEsRlb>U7#Px{6N5mXTlK0tg>8 z6BdL>LHEklboF=wD5myjNBhKa^R};`Pc90~Y5Y>o>hdxEpkFPxU0whqc6IYh)Klo| z#BQhw?1Ck^3z&^=8}~qMJN7v+olTwE1ofUXP%+b=+9T}ozU_SqdVdsKN;cubg6;S* z?X+n9ffIDqONCwg$>n|L~$8 zH>ydEed4m2j^|vuHMN5~Mk&V(cN<4mK7u^C~gDL!a4K0wax(1sb+=ZvrJNORx0X>)0*u}?Y_-9=< zSE*^qR_avniicP7b=|eFHQ$T>9pA-Y=Mox{#D2>=U_->U()DR`V6<36pRJ9dp;emf z(!JsAhPx#jT6dqquEf!?tbBN;ZjQl?j?Ag1l%i(;A@|XJu=f5Sh(jS-+*l1E<>GvB z^c0+#=+2$*vE*$uX0e1STTW*EWIUlY360#kX-wsC49Kkl=SNGJ-Mpjxzs&x{?{0qD%~VP%mPHKG>!1% z$G)Nl=`(DyP96r9Z(>}R3zIuAf-Q9~W)C;0VEl9eJdx=GZd3A@AR-ON4f{jd&+pOF z>NNIogA;mKuEEluZT#n5gIU8ub1=QA3H?uHbLo$c^0!}WV&8FP@bL3*ioE2AO;e>r zd7iKN$)0xDSM8+8u5||{U7XBbwr_#G@q3_Q)N}ahvzv}ZI@2VZM7ngMi6W&m+3eE( zY{BL!Xi>^jh|OW@{9?trzYM^rt$nbuct5w@WH4v3SPDNhIW3)4I3WVb0xJfsATzjSbGd?+ouqs?wTDutfr(ahRAi_}{- zbEi+Vafy2N_|#_+P1)c`Y5QkF!i-iWQI$8yf|7_qDm8IBrxCqa`#es0t9(+^j%G#3$;Nb(d zpeh)^Dzu2dnrh9S&RUPDnQh$A6*73BC*u$M8{DnHp6%F#10AiIabq0>_lu#8K~wmx9#QO4K_fWr+|H)l+XubVjKsN+ zw_I{%Fm{P^>iTmY!T8@%oW7JBeso)mRo6FzclvNnaq>WVcGwDwvt1k94@{)9YmGtP zXB_T1uLqS?A+*-I2@-tHvvAWmRQ$7xOro#jg^N6FJo*^cSxK;-`!n#toD(?ly*nJs- z*PMHd+7uON1TT%WVe%TWhF-lUnz7~yb-JA-8{Y>oTya0tYd4e1)(VD z&*YYq^TOtaNF`MmcXv9j8~=xv`?m5o&P9s8G7<5&cVn%|H9ELL6MQ@z;izmd-MXpF zh6N52bnLs!ExMP%X`O$`w|?v7kG0i;;leGP*^zF@789Com0=B=u|ww%QtaJ#AEGcH>K7pirB|-f zHtlGr-rdfvJhYE`B@LMDY( z{_aN(Zzn*jLo_OG*#lBWCDb@}FGyUHVTV_jvcYqeK@_eA*{(ShZJ$qqgdwb_YqKal zsDWmOWn$mGZVYD*XN$hh;0`xBvD&107_rk^kgnB-ZEboE`F^JCtkgD0d~E{WKRThT zM;EkL^>X*_eBs}{^QHpJJs8my%o2}n#tR`?Y>9Ut79MB7-#Vy>FKrxn*KoJi9*+a)Q53t4MnP6Dl-Ee717~g$%86^eoMXAjKoS8nv>asdze07^1|wwcc`{77JKP-+NS^^q7loeZ$RJ?2Y~x zlj--mk!;tw5U36m$7bSWam13hoUmvVnAV%I=khlM2}`%IaHk|XuHwVKraGd2VI)MX zGer=O+qfUQMK9*r+AMwOfEO1JW^Yaopy6+3=g*E={J7tg+`yWkcGq?ThQEZ@-xy2E9$+WHW1nWg0Z z@Hivc90-fq%M$HZL+FAscxfGv`)-Veu@4QI=R$YV@!rH#EK9g_zuRD%X$$jySI z!ZWF`@=754Y+6ZnPy#Ix;@Z3^n_$|S*{tc&3YL@XgW7%t)L^@S@>gG>0-bP5eWNMz zr?Kq8b`DqFeL@pd({a?@czSZ`9yPCj0-94k@LyB((9+wQ{knIE!4L8-L zPk(ah=(`0l&_jbcrK#b94-Ie`1~bQ{)0v(4U74O6FZc_RifnVc30Q`Wr(wPkP~G{9 z=65WoZ4(OcfH*fG(K{ODM|`0*b)z{~lUiRDK#tI7c$ip(^;oa2|W|Yz_ZQ2$!nAFKaOkXK$Iju9&( zlNm!h?ep-GrVqI_oPd747W@h)NhT_r!PE>Zxc2cC6mjMhdDM#d2Zt654lB=rsV9*p zdo+;js|p%>|0zsglSbY*>S^kwU5o|J!sxnv+{IgS;psw0#*e-N=lmitY{^G)EJhYR z2Jd2X&9vZ1pE^*toh;gE=)=Dc4}zg9B>CifUj<)ZAEfTuJS?>;z&znt)JvVh3<}$c z-!mG$l{)!2J9XOj{iL8rtXVUznX_Xxli73+1vY!V7fesOPWw(b(VBJNKx?TQb2_ca zc6h6^5xc-=2*_>Ro=zATcQujd4vW-}DCYrq$gy2(0WtQAu2#0sck=tcG zPFT;erI}>{gVUkBdWSCce!Ky4!xLC&M-&DWI-&^N7Cc3tW; z$vnubdFL=qe&sv1~`5V~_zs-Cf)i3yQNCNF%UWjAm@ocH}6W*8CB*z0CP9sdq)nhG${tnZr1s-$q(^ zT#0E8kz|L~3`FTanUK4AD~)?G3KrfS#=4_>;DlAV;B8GT3{H4Uw^pWsV(oCO`J6>B zk0oKQ!aOX1wftNaC9K$559xm=QIbP}>P|E?7{nqK4Y7EL20NI45A=@!tB_j2vc&JPneV?1D@QNj+79c};Py-| zMd2h~G!c@*_x|ig-Fhk+HWP18aA*Gcmm#q#ljax2LdmQMT=8N8P93gH~H76LCUjS((89>^US* zJK9TkMo7UTaqZ5=KUd-YB}EM5r^BvJS$ZR*&c?`X!7UD(S(C?hd@yb!#_rZ)%RJAb z;7}K2_~fu)yW^PWCXbiTsZ!S*8Qf?nMInV}@T&12lS$4_9VML|@$!H~OrBIm`rgYctkPN}b96!tw z*P|wne+d!4=F+{pmE;mzgKPJ#+@LuUv$cXZ0IG``OocBS2<9-+&ycGQ0WN6(d zSH3iG1HLxii`y1f@kjQ4gF<^Nco^WybT5TA{QB}7Bxl&bf;wb>L?XJzXF^0(9`CwS z{C-!898@_c;t_j4c4yXeFzoO|SLcP?T#mUtv-#LvV^RLnez+6$NFZIW%(g^rhuc;zP+LIQ-nSO}KT>4}PH%xV z&x-lhS4G_2HxiWRIg$Ec8K0GtN^)1bfxFjD0fG8#61X6 z7k0q*S}imhJP3EZ*h=a_Poegj3Jq`F%xk(n0|%d{qN9)UV9@cMDEoRRJh4T9y!oWA zyOFwHO%hdGYSYUzx@^oaJudD1PHt_LSZCUQ(ePhQ6cA*_2Q2QS(>0%AVO0)iUed$& z(M`myJ3bX2?4gYx{`eW#C482t7r39hHl5!I8Sjs%|gr=TL^9w#ADnjmzPSB!X`yM48Em`cJe_u zXE{+~x+Cgz=7B@l8~P)zulu?%l=+$5q5Fz~>|k0u9Cvf#?~PbQ?;hK-%vI(%Z&M<^ ztdqm(b_v|tL~%Vp_db}r&KXMXn6qEiy}aX*JrG=IOZ^XL!VaEeS&^HeVP7%joe=R< zBunQeMpMAEEY5h0BQAWln5Efv!NZybc>ZlT4(@1%1np^T=&s%HF-HdNN_zS7;>*yy zK93*xfW&bhV;m7wPikt)xam$Ach;;wv?q$={!uEl;jS&#Zu>ynR2}j7(oAqrmBW35 z2C+v)ulds&>C`oN9SrCk4BUQYSoR}}GJ93P`N0XYxSGh)CA#>f*CSYELM%%Q$i)e1 ze%QzV2v{bUiR*^$lZBBsn)lUWGdyiXKZY*CprdMnx1XH&wngF^(U?*?nN64}tIVRB zMR2gZn9{GNi5yB3Q0~S@&`do--)|oQNf~5nPH7lysM`24a~VIYsT2a0^YMbsF}iE3 zM18jm09}(r=+!$14ZHec*ZejXI$4KZ>oXq4rtf9RlH-{9kT>9~oWlCfe$Gm6iEHi# z=HRn+2T?_D68F&7luhb?oeJJu6pcYsxUhIXbnopFs8$uC$v}NrE0}{T&)p=W(KXy= z|8pY6?H5H6LGQ@MAq$7S*iWBBYccgbVOiG`7UKUCoPIm=BX)Ix%BVw}N`@Ib-(-!w z{)1S>$UQhN=_*Z3m1QyQvbZYk3T3#oz&!KykX711-viF`&+e$QR#gt4jZ0uX#ozeF zx5H`rp_SO@r74>own?x{MF-y9ekD2?U%-V)1;9#&-(aAg4?=Mr<`q3(ihKNjQFI=D zHUD26kM>X^DI%$qb`;h9oKs4aL?R&)GKvsoM?+fLDMXXZQc=|XoHyc|oz)_dl8j1Z z&)?_w57fQ)@fq*)8qafBq;s{EPG0pQt&SVq9hy$f+e=_|s1k-9G-o9;ekjiRNlT39 z)0pO^%q6Ld-gn%CN9Ij5OV!FI^n@0hbkYge<$HqTgR$(X-Fdofa~9Bk4ezZIjdqGU zxaHphwk}W;Jso*?e(*3IyFQFO6}+f#+GWtdWiVA@18n=N&!U&@f-3!^BE7xSnS1F~ zdL@%c&n1l*{W{J5%H{Esd|g>wz7s_4y~CLd-H9awJfZPPHGLixz*l5@(de}raMN)) zyEOhGFVz-|g!Y1%OJ%#j+%d#0(Dri|*3xicRf@HrOp1&oB4^o;*_T&(R%v2J0ZNyzp_Apa^ zDXmi#=I-!9Y(IVu{tNA;qNefKX3zmwGOXxp@gJ_#>@YPY{NUb}$>OG9W#G%N(9kJD zcTi&;^p(G-lFnr`UT`flf=l6mWie2vC;phRo9{guojM+-6zw z8+n!Ung{HDS2i3tX~w)~4r8x(Y2t~)+o3;N5ucBF0D~+3fmyyW4WFyeCtcF#y0#t0 zM596YWT6R}J00RKovNmQ-+QQMye8Uo8DdzwC%zjxh4WTR04C&D=E^O`GwOFpW|ls{ z?sD;U?Q3wpawO;n?Bol+9maLn16k^4Q}(-h8vExe1PNBz;EHJ(XmkBJ?{22VUN2rv z?;E#cKzRgTCmoC04fo)kf&nF0s!{lwaV%@^AgcT@iph50g58VEn6IdZ_kNTHHO3L7 zTR#n+7EWWQB+S{cutyveDPm3=$KE~|i4Rmwae%Tc-b{!DqkAgs=B_%Lrr^cwq;`|B zVka!LPseVj6i7RM9d-8V@OM%yS)iRAE)z*npnC>a_Micdo_2?Cjq$7?E1R??4}=wp zDsa@V4r_j^p-0hCY`z);*FzWMJnlIRnh_0Yx9m7CX(NoDr9r>$Kc(bn`n=-_DelXv z+tgxKL%AnAL`79rG<%K}f3G|RS{-kJS@tJL@Q$D_rqS%3Stj_FJ3!o@%ka%(A7#=; zrd>G~0)Ad|%BW(LZ zr|Mq;5&V(2m!K;}i5|K9fb=MBl+_%?W)x{)6u*K>L!&wEePyILH;k5B#6E`sW3%pIqo;8LbkdD zf88}6SDo62vr6CaW$IPD&9}E)`n!=ZVR$vnxtNY#o7(AxIDreklE+!g9>lB*%VDcl z1n+)n5NGx-4x1%k(%xhT*nWny`)Dl1_T8%|IQ58j$nV70SCZ+(lDD)pFagH9^;7dA zL2J#5rtVN{JTP0H#p_?=KKrj77yWQpxi^NwC6ADzhbMm1 zDB%0AmZEcCH2s)54i@M)!{RtU_TltfN(x%THGj~fjn?L9uG|Pwa-nF=wvv``pWb$U zODjJnu+?)VnT_2-+H#70==N4?7-yZJos0^CDMVCDquF0;KrO~vP zTkwL)K)m_>Ew%Yck@Qz{-0c&LU4FUPGDm^!HE`h~Eg!(|((&xtS`Ug|)2SU;glK&TwoaI|7H6n2`5%A<8&(5SzK}6^jo$ z&Bx8_gb@*`yzhU4k5=Xj+A)i$XY>~ea64EnxqlN2%b7*Vs$F8J4B*zs#AgzVl7COe1H)7X=Ii-9+*m3lyL>{g)D9x?0sv>Q%M6p@+v z3HqMchDBfQz@6%9cKFS9s6ID|#k?%$M-Izm1JDH|*TvE0xr_1fhg zT#9M>XJP3xASe<1rkqrk_jW&5@x_=bDlhT7Jp15F4bP* z9deuSe49Iss=tCo^W*SFZvd-Xgc!Tujm6Kq2?fK#aH*1T@9$rU7I$J<-L7=hw#yT% zWDA)DWpg2ed>dw4EN7X|)^g7*2cqIHf0)oP6SfYT46m!bAaCV-^tvhHuY2p@=NJBL z+_2a5-Ao>roZO0i6S_cR*aDn;p@kcN6zToAO?1`i9DV4u$L)qZ=X1}MAFU~mLykE? zsqzG@em0Kp8m5EFA1iU(q3h@yY*Za~OepGa?4!XM^RYImiaaHvs({{n&B4lG5cAoRCf(7SDx#gbLFPiWUvOE@schJ>wci z0w1?;A|4pK3Rm?fv)g_%Fyrwi=sYEZOaGI=HID-Dt|$gGpRA?{mTt`TNDL{k58R&Y zePnaM6{eV%(v3Z{@bB@xcy0V%?2UPhvilV-?;W4ay~M);d*lsW_t(Xe?JJnwZa1u- z7R#R155=GxVQkm0U+^xs7m9PF*z|XiSSs_LO<4Aej>@~D=8tEj|NJgSH@L8u>WbKQ z;}uvuekYQaI>dqk%R&9%F)Rv^hYw6R>nyB?Nv3P@#=>QI=3YLk-d@RGCqE>WRiCmYP6`e$Nj%iRZqa+7fL-i>^8^cGf$PC_5oiVa+C z1csSDaA!q3O;UWsZT0(69WpTqD&I^c^~Mc!d;M`3s`>!^jK|=`O9l}3Y&#?vYOtCC z-$ZdT>h#)mFTdX_59;oXLUXUZEVM9!6>WV0!_4PF--5k(cy1?b_q~B{Q4u@ChoOPq z0$!E|Aonc<6Gbxk=7|*BaM%DbzlOrQ)8Wy7!{Jl20TVCJBa65zq_`O~RX2WKU|$%E}r3DlOud z8znPM{|4?`k~+Gon!?|brEEm=KhP*%MK5h@c(?y%Qi}U6Uaedc)l_D)qjRrvKf89b zI1j+HwORbEjCZu2gc)|c0tWBjL+`YtQR~!Dmf2AQUgO%TMQQW-$=8ljJ@=3j?+Up? ze;wQ))bo5whk;JM2iQ*h##in$q>1weF~deLTz+CBE^nKHjot+;r&ykiO|wD+mQ@*Io_kp#^y zYxKq&XR-bF{E$1d)j^bCJ3uKuUDeU}JAF8v+WHFv)R8ltq zVS)_{nyG{_*1_=R)JguDv^m~;v7h}~97^^#s%W%G%!XGMW9XMz7_i$5Y;1K|t9cKv zF;NCH-8Zs}DnH6wsG^yra-)k~^vFU+_CKf;4C z%Sr@);^c6^-Ud4SPM6ue8H9bpEbFZBjQFqGxY~3gP8}D+om%AyXU05)Z;o&1{Kz4A ztWqCCT=z2PhEgtOaV>rHQ>TMJ6cj61r*N-zh8IGrbST(+`=HC5Ipj9&I z^UUGV0dh~ugD&T_tTuiVGq}HwWPR?^$AH)315fU9BX`XL-*um9#Iw~j{OMN;Z~6gY zQ?5cyz7+jkkPe$QM&Q8ALYTc|KF%H}%w=n>V5`wKSS&U`*&V~lTzN41d&}XZ{if(u zQw~|dN164GY^YuC!mrzK5ahgaM9P;2@_!N+p?0SnevR8yopWX&-ihsnTmE~{uH+!U zH87f{=?HwdN9CMf$TJwd*P7)iSg|!zYspY8nYn*Y$E=x-aBZVCyJ`24#Qp`SWblxD zWxHV5HD7jc-9jdgA4$7vg?D$h2t7kwacPT!j!z}fS zCzs{!#P$pbr@k@f_;B1b_Q~rwCF2ju4@iY0YNJpT{6(blI}X2l4uYz8N$gP1QMT7g zj

~VN#0vAR`K7Lv>bS!o~Ca&bn*x$LKn(crXO3k`>`YuRf+nyYl6iUUPj`!*Nzr zq1fSs1-s|M@kQZA)(9pG+*nr!y-c zU_=H)!4>NJF&#VloUpwyn>W(&ru8bD;PkDVqQ*_ZFyXBP%!{7STe$3Fp>GzTw}hZ^ zRAex%-eI^8B3a~PD{NcR$8E>~7ASfMNA4=2(x~e!@=z0DUlhJC@#PohKA}4MOt6V?wQU{)|!gYGsS=Y6&&i)6?7lbq{((w(Eq*6q~5mqIorR?3?` z72oI7SD&-Hv0)I&>c+AkbMMlyOlzDnAPc7c$zVsHon+F3#GLx)YUnxl4J2b8gZ}k4 zKJcRf+il#@wTT=g55lB#-Q;T^bV?&%!TtEP z=n>Hl7@t5tvm?1T>9?S_>>mUw^r7B?3t;=rkhz8IW%u?bGcWHQaDJQ&`=wxuIZ~@| zK*4+*9FR;di)7g`r8=;>8HEiG%IR;SDJka-MxzD4*(cLwv}m6#b4$x6*{M>XFu9wJ ze&mbqG*Xz~rp55gt^-e2p@OkQay8CGP%Zo4#SzwsE%ggUeVg2v+H z@yb+EvV`5+bD7pmJH}mZ_{e|1Z~{84gx@D`6m0w=&9WOCaQ$*)>`eLtD&4a9a^^$K zA3Xyq?x$hfJSBXg*#UmFTFiOvRZd>_Ane;AiwEZ(r>HF<>;|t+>IV5t_iQUHsJ_U? zJ?w{^S9VyXxRCzscmPX3q|&mXZfw#%eeN+A#eez`LZ$M1Y5RHyHoa*BEEc_^7thzh zFD+*l5K{^RynA4OoFUUQ_)W>DT7cdZG2I_C@r-f=nBPhvi+g}mjK)#8ScyJ89V6&R zC6GUN3#8a_EI~FFR5!K3Em-ax4N(aps%kUF+1qnTcT>jP|5w5ss z!iwJ9;XJ12!LzFQ@b=(7cKwo{kfoLvuQ5=D@`bzU$Ygyg?vi5lp_Ss4V_n<;+dA?c zT7<2Ez^5)d$R>_Xg5CjHZ0)yp+ZX5vqedx^|G7AlIuXjuuR5XjO)<>T{>91m1Dy1` zL)*Sz=j6K9)6t*mENinWw%<)>M-PP4EgfgrSNjS)xV7L=*2~E?C6Q6Wb=oB!BT`>c zPn{WFIA=)S4Z$JU3&o5o=jh2m|hBkmy5fAhhgi(fsm-62D>%X$ol6D*74FA7sSLvv7G~*7pY@p?q9zBjyoEdm}9kT z7oWi8gJgXQZ~b}?a3)H0d4Rxnlnh6+GBpCV;k4?pjrdH%VjQVkOsTppcgZnxu>3mr1(C_xKt#*bnlqBWRp5u zu_=Nz$WEkKeMvh0I2=ljl%n=`YtBMDg-^8CrK^`E*_+NyU|Tqu+j+x{J9+aETda}5 zpZ+C9K9^*;X>$d>MYzC))b?d(uKpk`hj6-|5CQfr>QL8rSsW|qLu`jTyfU7PidO*_ zdd$P;{3JeEd=camy6JM}A!c*)7^H_p)7q#u+LqGBH?(GwY3y8@+OvfdYI=O~*M-bT zQU1i|5~kr*B7_2RVC`!CY(ctDkMTp2#McZ z`~iwF9tdSm`IP-ZxH{Sc^J?s|ziuL0ToO8dBN93PGmM^q0V_=K!MMIyHm1 zqvTm^24{rH=Pm%Jq)vJFjL`fc3p&K{8SUKW=dp0{o+E4% zRSMlM39^jTxB8$*EN)7C5zR&{HRy*95>c83LI{>h<7ZH zh3{?qA$M*jL@rK&O%qi>Don`x)tm>d&=h*1(GT6?IKKT^BOQ2K3in-((KU|-7;ZWg zD_);~dHMY!&(K;3Qmdt+@Pl9(uEt$!mEZ>V{DHBS$z1O5QM?dG7Vo~DNcHuK)X|zp z!LEs-;TsEJ-@*^J_ma&J>w8JOR1>GQJsou&fl1wK+n~Uxq;R1$*W*LzX{3tAPv`*)Z{7fg|Bk$sZhhg03YL@x8Mfq5Ie? zUZx@z%FGSfJ=5dl;@bl6GArog`y}zS4Mp5-i4%OP>tiyTKMFM$YqI>Tc_N9wxR(ea;)W@Hh`9QF=1Zc;DB<|krB%%k4(MmNO`X)c*6)!%cS>F3V&aRZQ6>jo%OQdk-{zP77 zu^aqmkGbOuTxj!4JvLI{PG;BDLCpOLTzjJ{JGSu&zro}`dK zPkZ&y$<>&JT}madv_=RX_JRhF4CXe;9wfEvV{n9p5j&ON07;8SldrWWpL@F%o_|c_ z_SE)5_Lf2Xo%5me)$0tsFT2D~4D=HG4RhLj%@iYE{{$tU)m*H`2Dp|e#ng?Qaj$GC zh%Ssl`yd~P(nzO#fm^%oOgc#X{tN@Y>cQ}(A73ICVf?o*0IH` zUAF>l=ee@9{ja$j4Z(JsuQkB7Fk{S#Unu_4>d3Me$AJ3lv%I%+9-DId4)314jt|-U z0V;Z)a*GDWbIFGKyu76v)3*;G?e-3EI`^KNBYz0`#*Y^C?IxP^_ZoL->|j>$Lf}OG zCyAAF=g{R#%4j9%q16)CxcTQS@Rjy;`aNMht=TTe7S;`89oPQyyWArv@uw>v@M{C# zGcOlXUGli7!?vP4m3%&`S&apc^y3c69irpUf5Yz+N$mWU`9e0~1Wm4a0qqSbR49&O zjMai4Gc(~AxAbm4*u@>}g;jX6(?qP~bV22a78S_`@0yKQhc zAPu(4N@MB`9X7~yBb|{ggo9tBp}yrUv>z&?;p@h->Yx?W{j7vv=@%vRxgxpKjdH-g zYJlr@9oQRg#7b^1r{A#v#@DOp^L|`PYA;Qel4g=Q+0-2OLS)Kcrs>JI zVMSX5)#YSR`Owu=dE_|BAFrgquvgGfp~kJXRAr_cYhc2~99$fljH~BMv-2lMKz*_e z+jRd2ucGb%eiEq^78%GpPr1ll?e}EqyQY%m{Mn+1-kUMR_9~>yJ%TMAqi|=@JD4~q zizLFD!N6UC&V8LPGP!brdvCCZ{7!y_Pp6-W{zL=jmW*H*Jfyg52Zqv;_m(svF$7L+ z0SG**&MJNFV9%~Ma3SETa2?CCnxTiGd3+<3{OY05Zv(+Ges+9HdRJ(b@HdXgjb#J=>hsm(BLwes z4k5B;P{~UqKC49L1f;Vk6u!0L1Zq0Ih4Pkv$4m6*?1QV5?04{7TpeiUsXe!Ln>(Q$pQuO4lHl)0n=}D*z#6j{oTpc<|enn z>+l-1S5Z#$B4 zQTk!_!Cn_9KAFhOcHV=p+HO?rX-ElWTgAV(2Vv}z1~{B~0u~tD6@BOwgZ^M28Xq-= zJ$V?(^;O03feETK-d6;}{+y!gl|k%I&1A09-HdIjOvQnb6H#j0Kaz~rVpHw~(2KR^Q^u|nvQZjAXuhJjX{VkDcj_>0$&&Pwq$xCp`FpTz0Uq)AMW-zrkkGP9{E;y1? zz@vNwbxFpvcL(I~`A}d!KQ@qWR5snzHev_wKDEnfJuP@6q2TPRj038LY+!;U{T{y* zc1PsEpTTalSF!+}m287B)$MTkTpp+AS5NBC6!GOWTX?lngLO16VJ|-SQQx~nkhe4- z!{XV{Ffxc`=WM|#Ee`NXJfB7UnTo6R6WRHuM?yE^Ieq%1z%&m{;*8G^z@1OE$mDGV z)g^D`j|Ljza9;@+{JVtBmrZ5uGY_yT6B}$dkH(o1Dv*%lfo~fVnBz$k_SL}-Gvvb9 z=V$VGbm|kn=5Q&P9M}U-ocl$)FUjK5Zg=L!Vldgn44U5Wz-r0+{61Sja}v35`*ayP zo-ARS#VYJv-8nj8wH4}x%$uRHB-?2D9;SIIV|h^)%^gCPtwPRgwvaI# zV2{Qp)5zK~m3Hgvf|pG(f2ApaeNFJ^Esu|bFgq!JK()b%mg?y;*S#W8DIE?!2W_N^Wd~U7 zt#c6Fa~MBI8wowYMR*aB^%;56)^$k`Ws(KSdZayJK5s=vYb@n!7p%>vSTkO2?;FT;@qd+@~ew{SZykpzxA z{AwG_F7I-w8EHS#ALlB}h+8qz`U^QQE+?LB|gw_ z44UZZlj}mnN0pkGV&%*`+1l3+J_)i#PKm7(sZ7et>HDlqjGf9d=( zE%b~W!fD&A1$X%^n0(oc2UgIsN*-P=I|WYO(#%&chEg?#P(c3>rgNhK z-n_X6Tg%45{_ee8mYp`7K9tT8pH4AupJ2k2J{Yt;n#MN!aSr@vZt1p4>W`SkDo)N| zRwlWePp=$iH2(p!XHjI?90NHD`CN&$8ke3|#9zAbmUfGlGqbL7R6AY}w*4cx|Er&kBdK2P;1E{x+-l zX?2eLiCuf?<+Eb?Vv)|yq$faBqR=gzHw4;>ySNknFX)~`4DET92a=yG!Fz@h9)EUF z?0I*DkDs?;G8j8ID5f#GT-BaWHPUy%?^zuA*zQtL?@xdr)#Y3=eK|WY?_B zp4N|n-Cm1m=m|BZQ$Gi~*DA4#>;6*F4h8loekr2m2Tm$}9KXJ^Px##R)V*#jCztsp*PQ5T^)%F%_u=(26tLg_FF$@y2{pJ8O={gwyy_sD+9vRds{&|6 zP&DLb_>lOI0>-RJph=l!;HclpxgBgKOQp&9hN+XrjpYJea6T?OYDop3JK%C>0c&2h z5&jFbLwk=iWHeopty`uij(^@Esukt}2g3v8^D`11Je%Q%MiEQjyOQ?o)Wm?#_UvoV zS-ySCLlUREk=NaQ5O~%ew(kFlm+daw8Nz26qm;q-_x$BHcJy$EwnuPRC;SxuZHUDU zHpf9XI8Nx&hq76Yf5f%^YG`}+09-AejBkys!S1*Ub*I>~$$8PxD9pl-)83NYA}v-Q zxB*UwS>nZ$r}%~~8&Id;4(3%<@)@_4na>Ax3~FlCzaE;LFIvvldS!ZA7XI5qTb?HFy-+)%!^%$Uuq;#DkMl`J5-)^`mSb4KAWC@zGS6>9YcjlJ3HXp@W&D&1}4Hl1?K9W@2J^ioh@M z!99%^X!DNYT=kPlxMRpJb|+s0mlzwuasM-LE=~*01g6@e=N;g-q>_~KcR=6o*RW~K zW{Pr8WB1g{phDV&&Afh|nF!Bn<#bC>&@SV;2hOTe_jpBDeHFwSLvmPL?h)uS4`=g= zMnIRzHZ~*G7j=dYpjnsu!6t~YldH#J_KcG#U64g3twOKhpB-Iui(>9++u5bbQOs1F zN?Tq>V2*hK9mi$Zo|gdTE7Nda+d`V93$K_}eH@@0;aZq0YD zNL3mQUoIhup+DeHa2idGcu9T5qjBcVQkt_hnN}={z`J7v4$#L^9F;0`%*Xsks+<%~ z)lU)CwK%ZJYXf;r$u3G7r^(d*d7@sy6MFe7kz&sFQH$Sy&^fS>e!1GSpvM}xeXlBe z@ou45c6Kjq&3(X2XRBe|nU>pC*Hd(mV!j!`}jm=ANtk{Cp8`ObCSO&bA zTFZ@@QAHnP-|!vs!aT7@jS}{T^X4l`=;*VB;Jb7-lquZc+XGH;+l#E(nS~eWz09Gi z;8QbbY>gSscmFP$?7ImI%`I7yzW|ThB+q-MJ_ePl2k^K1E8Kq7A#|tJ@#pt6A={lv zL!Tdn@Uj_#{@%%{{E7m(qkg!()Eizn--hOc!|1@;M|3!M1x&Llq^@9Xl#Eru>A7YC zKk5?PeUbum)1_Hus} zz95BiO6d?`rYyr5LZ(Ou*EEPow z+0l9#*0^vsy(-y+zPajLWRs)dQGOCwC!OMt>+M)&&v%&d;~0Ir zu{iJfN^*I-1N(>GqZ2vTV69CgrKRp=$CV>7Z>}>e`*4=RRK{YPf)4AkHDwp=)LG}3 z!04esjpy{eDGha26Jz8^?hWydNX15UhZ_Q4AU5_GL zzTp!r8M(ju)#Kmd?z7Est2~;bjv*AUF~&7Xb1+N2pELb989G!4V)U{FbZY2II(H!g z`!BD++o!&9NgGnQhz?cOQTGqN%vi_FgZd#Z;UxW6zJ#rs_lGYxdqQest)k&r@S&M6cqqx2sJthqUuB$e?J839p3m$g9hm3*;ka-{JTzPC;Yph> za3wDW4tVa~sn34ygKiBQng#%Yh4jB z>oi}F`(@JTzM=vP+E)+nUInrXLKq}4Z88j8?gJXNBbe5lEPPw}l=SMZz)_2Ng4ctL zuXqfrif_W-G+p+%e;Sdp78~<*0ybPZ0H<_9us`%H_5aYt{Dc3&R&QPQA!8O)WVOJ< znP0e%lWJ-A5p8DeG>K{X97aw?hE-j?&v~lPfea-}zA)UGWoitgqk+;a;^ZV;ml1`Q zp(lyfH?rk3r$A8BT8KQmnJFa;GjNUsr0i^_4@Q}s>PJglp%IGf;#^ol=t2@r^hL9H zWq=E{Z241PY#f{lp^KE+n7a!6hUX2GTl|-L9#Rt72p9g8MzDs!gd3e(mI}N7)=3MowJrj7Ju>ttl!kOLdECiYC3Dn)(4*rqd z;uF7A@%LwGre~voQ^Q@EgJ}>JJgDdNj*Vvgjm6RcaR72WHH;5g*?;1)=GVL_{(()J3kAo-96x> z_$XI?<}P)0Sb+3gA2zCLH?O`Uj;rcus$Oz)5f06kW!(E)q&-0oMyYHBw|9ADo4sA| zVI=7IiUj^+-7Nm>;F&1npMbA*8#v=N5;)s0lQ+mHrGgc45d1zAy|qr^j3KwkP1cw9 zeAq(0I}l?U;>qvOVUm5i5+r+6(cz6L4Kg-kv(EW|NJSkN>J)=yn+vn6kzi?5z%)ip z$01##F#Fjo_F7*R#$FqXD@J6qL4`&v>XIEhuPkJY=Mnmq%hSgST}qh!6c!te2N~g6 zttXrfS33*3<$?Qjq$C*v3Ot0K&~HfGlSrkbglD19t5p(bVSvm^W^B^}MU!1%ov|$5 zshP>O4_*!jFTJEqH?7z@>GhQ6_g35|_^!hSIA^?uBGpUH+f$(C&Lg^lPM_<{B{#Lz0|c;FnDkvPGCvsf9-9#~Fcu9?qa@{%d+ z-iEQ_s2v_q?yiF#K`Ssa$e7*m+782YB0zptkyuAywca#d4ZfGZQ>TkOn;Mh}-!4z1 zQh`vBKfjonYR2*V)*YhW{Ie*YT}esXKhrnyY_xL`JhamR7!x#uQ+aR7&kJ%O_2oM3 z8+#3EEs?Neg3$kv(ZDNJ(KuzMJ0=~|z_cmLaAJ%aHU-}o_gs8I)k2>7{jNmr%-wVJ zaWSCez?JZ+t(iZu^N#rHYGaT*V1-kE-siIArm}FsYfJfI4O3nlGv^{Pe}0)4`?B4T zrE8d=#P>5?$f}!gkhi7D>&b$Mqpz}8nxZIM!S^Th-blYWHPTYpU4 z-YR4to8OZDfhIV+-IBTPTEvpKbGSU$ies_1B~xWDTvymZ zeM_WSfJQi5xYC1nJ1>Qk)BR}bh|h53au|epID&ev3AVj`Pvd%)@iW#gA|v0|qNg>3 zY4(x#qNU#^u&=*v@dsTU*++9_I<{Ll!$c=S=$Wx_K6O8}27kPsz1Np(3b>fo}uVCkL=c;pCUc-XnKJ4+D`LIcOFti)np`Z7D7#I43@2wcf zmyVnX-H*@Hxc))dJje*v=EgB&=}O*Uqc$7bcbGRlCG1np(88Z1GpPFgAFz&pCA{}~ zxt)Qd*v9qM`YM)8GY z9bDUL$%8%#H&)G2gdLtFD%!iKEskl}zibg*y28C~_SfIRy z^Hf|0vE7X{y($_wo5jqcU^@-}91Jh#*6>ZXr{G+NAYw|za_`3f9Qfu z@AzTQ3NP7`*AESWS1}&FoUTCn{3~QK`w-W9Ap>TYUlBQaT&8zcY2?uV8!{!P;@tD& z@X-fpJkDY`&&#*%riL}}#s>~S@>gN6p28>Ke+j!sDizTl^{{ip31%y$$V`$lprb#X zk3u_)xmv@`dhQAp!JFBXbJM6mB&NANgPBcUJv_UzkAAE~p+^(METVUFon9NM&;KkH z1x`Rsc|CU0`<~s!X$Q$WZy%WKnTTp@f0BZl4$fZg$d`51aS;$Nu--W{8-$-2gi#yfc2$2)*NhMIo)FjV?!(~CJS2Te-bG2Fe z{0s|eGR;$EAFPa+#7sl<@SI8O&aea@rVThXXt{>09MAoL4;q zrhOcUd8xs4G2%6arObV*=CVeoh(xnJnBCc6Y$^*Kg7gye4p=I{>UL+X4EP8>&CC>Zb>@?>IMip zkPQ6!VTnz;!ZXI$U+i;5#H$_bqzx)F;NAT-?C-2H3Oxj*w`>sW-Fyws_8fwcQsG_c z63%VyQ-TNHMR50q33HzJiN82PnNB1vrQDA;g2o<8gL1>!*2x0nZ`KNG8#n^%T-|7+ zaVtnW-iIOHf#O;HgHh?55wmsV>3CEle7G}%Za+7nZVeZz^^Bn6rK;zT>W#VJrk;sr8Jkd}%b0(E zYY4iH@!;#?HCfKH06sT8iFLa^;Zn=;xeG$R`io97%?~*QBGtQKC3T;abC%%HqhpYp z>w)&!_82>$1WxW5&IaU$;Ee%OarQ7HoZ0+?aztZ=J=!O!+BNHKR+C<{I29C+%~~U zJ;FY~RCPA3SC6wR_hqs(=i`x^@~m}oDr7hfhiOHT0-IGr*sFJz+xg9ndZZk2roh+U z)tbT|>ByiBU;O#~QbPVGEP@LtRA(#GYEeAjfw7@o)Tum$t<}{A&`Lq6>GN4)?mJqj zokknIwo+Y&8iuaw6;ClR!BNjH&<_JcE=$llBaeIY4lW+d8Pa@?&;^d07c>Xn6^Y%iZzLjdKu{!=$&x4IJ7{88yv>l{HtO zqU{oC>I%%^g0X0{RfL8GR;2VYhm@ly!{$4h?2CR9*;XsiqLnLPo9SQhub;zCEZM`q z_<9NUnx25C_EK=HqK49ome9bK0qEdS3nzd4q_|^7fX9zk=ec~O0}e^dB}$H!%noHO zGc&2hHBYPt?X<}EjF3f|O1V|LApT}FBu+KJEsqbtxaR3NPEy#N#tHkaz2|c=q9gF~ z!$+t;6~LQ5i~+flkK`of%m0s}^M2&&eZzQ0Lbi--B@szR$@|=okg`fe$w;L_inbyu z83`dFkxf=;*za>c(xx<&PqZXaq@_Xod%pjGAKu4#&i&lib-k`-gIPpsofs|cOCWZs zIpkpFUT`0Niv5q($!*a(xYh3hFJ||H*O&&b(EEg1!y%+;r6SlWJJXxN_HZXVi!Rm< z$K~azBthXmZ~qYIo4q;C@(-!QLuYX)==*>ZUz_4bO+6Z49D}yo&p>^;CG8zZf(N49 zj<2K;EXoWpIGgKdpDIp=EZnHz)@8);UKVWss=;qv*^dLy+97MA6@A$(3I3m*X!E07 z(0)_FF%#b7X1+U>+@nstOdjLfPt@Z(%laIw|G zEFV#_-prC6?3AYxg&&z&tAD|Dz7{!@xCw{Cy1+4hFOKI~bySdkr-`(so>|cFr$~wvgGjpnCMdr03_0T))hSf~tDT|nGUhX* z_v<#B7yX_UZ>$4P;Q@9CAjUf3i^D^hVoORkQb`Yrn_E))4ms={Hz^i+;VX8gHpsD94o6N z0_;C+Vm)nCNXWM!%+Zo{iwv&rDjlDArA#TYZt_w4 zD)^DEKBU>^8UM#%6!|sU0XEuyVCLd=Fp4$gnvD&v%@U_=Nen4)i-gN9m8@2oI~DB9 z1?9^b=)KmA?euaY_r%_ymZ2kv(=n*sFNymn&!q#Lhv1XcSN7lqTVmL;1PmAb2T~pG z*dv?)7n{r2N?tNZGFRZ1g)ErMcj2XZlflj01HZlXAZEAAsAtV#v|e?DwB)#wtwwsJ zNaj16v@sG31lN)I)1>Kcek=akIL>NstU~9AaMqg`Qt!!|*o?Kw%$c@gy7WUgSk)9@ z`I~BT_+l$k*G z7Oo-5&I#mFbqab`B$3;y9@tYZNf!51f_-rT=iMyD_dSbvDqYh_;%rS4YCRKj;Tn5! zwI?eW5KK=KQtz*MGtAw~dEu1s36(m@6=0$eZ;)qEA+QYQY6A z>U8*J24&WWqFCt~TAN(UB)j~i5_g=4;b1jbWpF&#E7de1EdkGX>CzBEu3LMNAZ?I8 z$2N{nw)!fpML$~PQ)pX2F9NL& zUL-3b8K?#ocAtSMmDegI{?YqMYPSq^`^s^9%f(38-w6CEWlbb)7Qn2gIn?!oJlz@h z9TpgEU?$kB)2e}PCb>0{T`pV0)8i51Z4khDOif{|RRAu{5r8wtR^e)?8mRYph4?ms zxh!9UUuQU?&){KZo?k)p=-vgy*`gdJFF<%fij!xvGr&O8lHUx)k-SFvCEGy zxbho2n7@%0Z28W7z43?fjkAS2tR5?5$K8**S(rY`Q0+E>=3XUne919{&o1;LYoBiTP3)RUP0UHjbmB6j8cl5WlNPNv=<=kQ zv?c6@=eBlGEUHV)Ip)1&!xEb4KZ%&YEwHMufxy0Ln47 zmHLV&3QvOJWkXW4$QPSpJjvxLBD9l8kxedca6v!^wtvnLVcoMvYiYaiid`6 zXGy!jW_nMc1eb4epdY_{!tRy=rd&H1`W{b1xzw|q-#m}CbCaU))h(&Swwe*W1}--TWpzw*U-|74r6Y z&#)A)TES0$`G(ap4rhcM`=OUF1$&Ra!RbOzm~UJ@np&%n?=JG>(TP~*hfF)zNUlNE z*L$E$xQ1@^_y;y0ktr$ot77)|vTW-1Hvx7kiUeno;!e zc2lx{M=72%nhX^yoKa&omsymErNs*sNt{Ol>iK9>*&b`!*K-xLAIOuY*plo6Cl7?qCi@K9O>p69$` zZjQdJKiAj!#Lt`NJip4$QqE+upA>-lqRHgO-cz(O{tVpq-$xE_=wWoT8zJr7Jyh_K zB#UgO&=8F(bkQ!Qt?N17;I}3Gy^VJ4^|XZ~N^!qCoH^zYa=96$=&4V=? z7NlOAH!~_p6RGDBE)Utq{icG4Ai52RJ{SUfuXBy7m~ zWiVUjEJ&Wa&Ro1r@Z97%^u5zmvd?K77Vh82ifwrW8#zx2&oPcS<3l1fYyS%ehdB*a zJsEF)kfY|p5h!ZQ-Km#wK7Z3X#-x$cHkEFp?br1%PpX~Yg10~6`eu~-=9 zi-EwtJ?K?xO#W!Cqheekx#Xd#^i+BR>masfyH1(xTZ=o%@F|{m~Dt zD;J_~r!tY%=i~IMZ`dA2xGr-({Pk#H2hQ-J^U5oTKOstr@*hA)@By|o*BNhcxow4` z3HZGhA0fSPwAZmymJEqR{7t92!l~Qz)m=ob& zpFtjVaUNm&bmo%12rhU#02eQOgFVXmsGS@Om3|XYp(2=;&*b)C<%j6yh6~KH?>%s9 zX%ds~Cr2u_+`^ZGh-$s^^vbgg-dLq1i3^%UH=p1#babV|O;VIjI{`ml z`hc-m6QdHIL>f=}5bI|ZJd1XYBWEBwiW#HXB{!x(CdSe$ zw1ihWxTl=o*?cqY)PvE`_qMvmmcB89#Y?lDlhFEp29>WJcbp6T#LZCVX}cNIi9enD|qq z{P!1T%QhnzEKg$QJMejj&#RO8_zAQ?_#60zRN!~>Z~Wi^eVQSD4$sHGW~S=~;N!g; zY5Y+G+;V4G%4-ITJK0@Xlr=&vNv>oOwA-=7hDRf*Aoo0%^;!HmFlH}nZo!Oe<* z+NBljz1{|EGUb<6BXmS4NU0OBOMT8(v3b`cg3yiPcUF5i)jrV(4_th zJvPV^iGx?UGg=<}KdF$$Yl7sWM;B(B9VdCC8+hL566lnR3gnr_7t3>l6EJ?A4(;#U zLY*?Np)%+1PHh`z%L|IxJER><@hb^ea`A9nd$5(dNnV*<;X(4$!tV_Nv)I@2H z*(VfnhP?IlLyI9Uk8pDzbUeGr?GO@4@YO*slW-H8v$${OgJ3#(O`5SkW5N>?K0qrU zaApV90aPvf1iT(=xUugZ?9osoy85P2<9-M8e&n%NS{IS>`h4(;_{Umx`O?b+`efQy zpv%4Q!>Z47Ky%Rq;y2_D+?IqIn_pviqh%U`lqHmHD z$*z-?RLI~oIo22qRXR1S{;nG!u;L_&?DApWnTe1Mov9em5>H3*ICEr!FGSsJ#$8jY zU{m>8^7(EyyJq@RT>s}Kx6i$SU(Zh?K?O5tzDq9Zx`*?-8x|7d1r_K#VK4YjDq-9d zni9U?)Ec(4^}>s3R;4}X@tRLtI*q8YW*aQLJ&ro>^vI$M z(GarS65j^JlJE>`RQ~D0stWaE(e1~OTt$F0fWqc;9B1_>Wg|wmkad&w(e}qH$T5k8 zf3tRzf^Cmsdhv5yBkV+#A9P?rwmki9KgQf~(I9t+MM!PZak@*^5Gu^_V2SM(-n^>U z?3#xnG}mAixxL4k{x+RUYz) zAJp8%vYz&L*ufdI*x+$pYVszN#uquk$FoXoXp1T|#A}iXAr9mhPYYub4pW)S8pOMJ zFK_0#dbs${ftFt%hvk)<=&Hk~@O7O(IV?Jh-Z(IYIM-dqO%b zsW)+jBFECOyNaEKj@--_%hYzgrX4&fYS42AUl-P6!plTD^Ld`h_M0SHdZTcbyzq>uC-$2|=`oAwkK2+Yxe4^}pDD~g(XYJuvaR&$lVq$6jAQwD2D_x@5c#X? z>EeyX#4B(sM1@O}u$S}b>I*vP?IcT5Y)*o@V{ zV8_`yroY&ijz$R5=p7|^y?8e}Q*a{vX68Ygca)>j#6(gr^^uu3R1P6?wJ^OUn#>v= zLgx8Xe6}4_)ITaZ<<5yA2pz_xV!C&m}X}D(Q4XMwuq|FEXAnz+d+M2Fx;<>N9{{D zn%nYBaQ8cVV!^S{f4F|-y|uUc(jDv*qyMAW*z3ZwI4#Lwm=(mcC%emY|AZ}{B%EA=6aSj&pnUs1rChh zQ;zp>As1JvU}G&m>{s6-u!LA z|B!ox=2UaKhrJFlz3`4{Jje%=?Kfa|z zG~NCT-s#LFw?;0b_EZ6?K#=ZMQI_kTAX5myhx4!Z*yiKbN7xDv6FYc`%ta8uYI}cNZ65Mf9d{Uh75Nx$>_HKm9tz z-w`hZEC`{%3lp+RQk7x)rE$0Vb13J2)4k3X)UcY{C0(-Ndfv{#E^i)YE?iE6O#_I` zx0(DAjSyyae;{;Moxvp>XX^GH&ddGz9O*fg0OAJ)h~}0~-m;UE;fz@i6Ldiu_YY@7 zjTgritjR|o4;#|+`XT)N{f15Mn?be9J6>{wIGdwLFcLb?*YSAms#^2vCvY{22a;lqmMv5yWw&vu+b@u`0UrX<6$A=5mf^58ZMAK zeis_g<%87Se(*F?foJ@=7_uP|5}w^hZk2?*H7}vh{{yPEKE_!88z?UC3iCX}h`pB$ z+h68J-uyNvOOtfKz-T=#yQ>b*KQ1B8yeDjy+YBP;oB*{KGU>`0!cg+&0(@{vXT-h~ zl5mrApj9J8l4JsL?a}EZM|ve{+tgvlgKB)CqDGVzJ5ghw0ZB=@f^*;Bgb!U7WcA`V zjOW`IFdo~>3$&5q#rPy*$a_bg*+>HJFg?mz{OQ1b%T%cK`BsoAIf@sa-emUG)U_wwrj+}Vb^ZxPSys#Y$wDIY2c`| z5jd9qf*TjUqRYrmPS=ltm}!ojW~4yEG=ivd>q}U2ZiH{o9&`EtyL%zdH^RQyyXNuDter6p;l4zNO+xE@P?Cxe%JUZq4W&cP>u(FSTgT)zFZl2#u_O}1P721uk%1evXrlm1=y`&&e*dKWx zyddn>BY3Q+3t5YolknNu;B`%u{9C>Q(q?=^*CqFvp6E!J_v|;b)$2FUKmRWywO)qK z(lnyxWi4p7)0Q+tFC3W4!rgWWayKHH*YviV`L?Nsk@xe*&qW_`@n=K&<4Ft0;l9a0 zyFZa%af+95dn@f>Rf&A6I4zo;4QNfieF;rRf2G=_MnOPV~kTYo9R1 z$)05Y;59H}Kk@WVETl;fRKR=1OxorwM*Fm+$nbyf;lEEZWa|Gtq2VWhe{U+C-#^40 z9nc5w>)xca$DBUM41wNCYdG?88@`-0j2v3JIYbbFz3~+KpC4zPRup4yb2r$Z^@Z+{ zQGCBSjrTj=nkSiqKH1!228gD??!7OZ&;$gD) zdR98x#j^EM5*1wbi;+ypgz`mGId1hmJf33(r*0?_fh9e>&F%tpGv;7q%4??9xQ$ts z8pEXK?1qOaci2yZ*6al1U0BOk@;9Q#U`X?`Bcrg5_kh(^ z*Flfq16ISMjP=~H5l=KLgI|^u(ddgrl~8@!#c9>MZ*#hRsUf>FP=I(=ox(78FJ|Tn z7vi|3j18A-fn`h0@Q0Wk%CwGPfapSw@zV&GE(y`sMjsf(CAuh_uS(^0jIiI3N7jZ) z(@EUfm|k`hJ&6WKw69&*<%L;bqX5w9uy-?rIY2e3q;MiUs9+i*6NP!Oac9%1K z_jxfKC=Fm9Wlbgd&;DV;&b9d1dIG&^*g(TPtU&+hWxi0^8pioUFzB5g!Gv!SP_m@~ zXKp@1+-Gz`rBo{r_jh>lY9%&Lcg1+qSkOHuLSkdxd6Oo{(6x=-;NkrUejZMO*0F9j zg=0=^t*d}v-l4Sc&M1opS^b>ViO+1w!`IQG?(j6i~o&`9@l&M zM2EakTm%y%->^a=+>F3wFSpk(z_e0lO2fyQ+s&EK>g-Kh%vDHQxG-Hfev~N8nL(4E zJ;Mo8=m(C<5rQCbpw}qV^ zj1Xa%O0Bj1@n5MU6bgA_;YcY@{M;+Fc`tzyzt*s!95?gcy(l7kB%1zAFd`E+FJK*$ z@ALDNHsZ`S7p}830Gd8@LTKbeX3TjmwdbqTS92$lst6x)$zc*aJ)lfhQw=hI-52Hv zuYxITsK85i9I=FzrbA6<(aTj7pH=CjY`Yc{wmXs<*D0dqNIrCqMH8{<`ee~(C6c~( zCsbUm!6dH@;5=kXPOs#;qP^oe{;)4T4H2bRMb{Eb(Rw_Raup=+8l$I|I6Zr$5r4j{ zMK|^W$0e61^2*DgRI~-2#pdJd4Vo|~xs08xISs#Zy2^>GWuPUif+zR>Lz79)ct6XW zp3Z8<<^4&p(kPhSk>Ss}4PQsEd)L9WR-8&X$HMXTK{!U<;gghCm}xVc3??bl7cWIf zr=ufHnXikMQDKm__$jR7cz5#N(=jdT1@8BLfT#Be(rJ3mWS61<-X-BAuoIccp+ZQy zw-qL<2H-$a0-Rdegl}{Fam~B~w0}h)f&3@9I`R@qSnMOGrY*p+m3;o>>ki~aOa!>R zDZ-EN*C^|#p~)^cl5M7DApQ5!wS~Z*~1QW&0sjTk1oV3RRi$+H_%+SAo6MK z6q9Y;gsu0~NRqk`)irtzzD3KZ#^8HYR(Xg=Cw_&{_Z57pnNsA^3ojBIMqzhE8~B=s zGu!_D2V~C?Y}C|-l10wc`ub*kw3*AQXDE}$hd9o?i7k~Kde&gIq6-wS#4{1aS@>x1 zA%A!-m%j_0K(^knrn48ua(N&Vf}!cGcJdJTZ8W8+#zRE3`6Bx{B@B6Q+R%QWor&L& zixoU6kepWpFPATZ>^WQpBElWd-ks0;xXPGK@Mn33>!pe30YbE*gh-E}5=oM+V3aPj zf?e5BHu0V+TX5Z;jC5=!kxm*+N2DEH9Pk&z4{E~oSqj+q-GE+I2RIueMNaP$BSJ}Y ziA}W$HMn`5)eyEKCW(Apu=6~Q#Y`k;cxQ2LZ3e8YnTR8%Iy5{c7A83^goD9OO!=22 zY&ipvu>1;qU&NzdPTqt31%2QnG7Pb{Cvnwm7W=n6;!WIi93A((V3{#v`dQit)eL)K zPvlxq)L=N4sW)1lAHWCPZdNf{icI*?i@pce;+ddARERP{i|$fXJ|Byl%`12-V-@h= zTM=~eola~5jOmUC8BFr^DqdUl6zUw20^ubm;3k(#T3ufP<#{S}^09LCnD>hD56!`8 zOD@2g>I~eK5uzd~a&+7GbI`qGHFoVh2Tmh(7^~R>TaHFTw3!=Qp)i$J z?3KYP(?0$rBPBYgIhK~UWa2!(YhWMR0%60kjE+|^!=d-lsEykLC|6^#S}PvNo`zOV zoJZQXnpqkt3&vT}M8TjGch30-8Iy+a*SeRMLtFj>m+ya>_L5MhDRLt#)NF$6pD0{n zXibmTCbE<4bm-)7j?|z=fR5j6fR3fxQ0{dtqyNjCD5+`EwSQId!L^5&zB>l>_dLL7 zZy&M~@Jm zw0y;M_pPN**2xgbXI;E%Ed%mOH3^nii{QJ)5#0S#g)C701e@;kHIJwUp|Pko(XI4{ z8|*3U#xj^48w6i=vCN&6ahSTk4c+r5(l2UP;H3Rs`1yARK5eK)8wXRIf8Z}8%w=-z zs%~PiLmJ*xMxIx_FG=MxlM-WzXjJnB1o^e(2FJ~oRyYCoJ;u@1gyrjXrbD;hOmfC$ zFC?{bdAiI**j^<7-|E$2qt{yce!w5RnV(F^$TM8@AP?6FuE7tsk>IN$PSuWmXU)0$ z%Y`qZbaUKArnPPgm8ljbksMp{7fb=+J5oU7^O)Dl6}*q>M(lB?0T4BCV6zUFW7oOq zpwe9q%P;gnaHTulJgCn*fv0fc_KT?XVK=DWPG+-W3)u*{HSCIOKG^Uoj{Up^P^)tR znjiXxZPl~M1wlDD;&FvNxU~R0XH>%2zLO|6F$xo|9p+8Zvf*~3THGvS#64$66kN*b z<{aBwOPTS+jKC#S)Zqr6E zyjQ^7-TVgUo@&EG(vg_Tu?Lnr*Py~@0U|wk4F8!Ipw|OuxT3uWA3yrR^siDO?Q(X+ z)jo=p4r#r$}8F`Lc`82z+BBL zwlr!h&gL@k^;|ucuE$Ec{DC((+x%n3ziQFxZBOBF3d>uwv;;5Rl*PGWef<1+T6i~W zh&djrK|brNv9`feFvg*d5oa>7o!@}ct2Bu0!&xZPwShs7vFyC4hFSjN0sF~8nnn&^ zMZI2zWBqdPsC6WUbv5zK()w^9Mh|Yp*RaWsdUU;|IX?aHi8(aenfL5pJ;!EW4{s}N zFmS#-^Za5Q`|;pe%#d|O+j=SL@G=1fY}SA-Pl;CiCrcJ+Dxrz813&hNAstzriCZa_xsFz>{amGfNYj6i= zM47X$7w+?}Havo@d){&WEETfrQa0Y7n6coFuMW#?p1|bDD$*14hU;cbg4#3kO)q5T zas0euEZMz^^F*A(q8`zPtUH-Rr++3~*y>C@ss>mq)6IYZj*t_$3580pL;D(63Q=8X z*))|@%QAFJO$l1rt!4|>>5@#Vcw&25j(UDFB+l-MjB2Ae^$eB9$_3FlUX+h-pO>-P zyeK>vmdz%*&m<-mKcW1`c~s1M32R$SX_4?vG(BI+1pEttkFf;8if2;$s%W@dAWPat zr_iRyp7bo2A%3{!Ig0pCC-UJXOx~hMs9IHs-&$oz;FD0+b7vhE3!K76^Z87*fD-gn z%;fr`WU1{?0@Had8dbeT>GWmtWVT=zKh_`y*6WPI>Af1{WKt41F4iaY6Jl7=1Cz<8 z?s=^5+D~}n<~efuoj2UObdIm>EQhLZx8TUHO{~C~6}WD}B-#|7$`nTEvR%)nlPFDb zBDcK({ub_H4ps`2Dbdp8Zkz#e+ayA=EH&_5el55gInf4g_j$8Vk4!PGgt?>ESaiPz zLf(usrDb|JFK8y7ATcl!IR_(S5}@Qfi>pt_py!R5aQF9B%oMhPaii0)`MM_T-y=$v zw52df|3uiS<+9{+Ml+mPkcCr3rRd1wL&UJx1H;|+Gw;eQX~Vazxcj*<{r3%Fx~d?K zn!E!2(R;Y~ejdpfNa%+&vf1MDKpVmX8dgb5juh{lW3x z_o(5rAqR}v)y`x^#N&mR_u#2`Kf29O#__0BX8pM>%&Tpw%z)h^TrGctl~uU}TIFl0 z-GXY^miZgpg+`&NXaarVqe6P0vluct4=T}*E{vY@!9V6g2A8*c3c%Ufz_yJ;~koU)Wi%+SUz%ggw!WfA9hdJDH! zZsbo2RHoNzr(yY|moO>ak#2`~xOjUx+lRYflt4Z{_O=wqIB*{$w zg9?UepwsXlzWmR=@!H&8*tjAAKNpze{U?vXLBND>|2Uarimaoe8HV(0s0b0@^2Hk- zuc1dKbg(mrA|X(JkooYR7)|-E32Uc`!nlwJHBnFkYpYNArf@q|*DFAsh)JZqeJPyi zEWyV?&FoIgTd?uJV^}wShfQ}&BHo5!^d)$)#f_0rkvaoQ=k$YjZ~^04r3L9x(3spfx3n*`n9ZTk9~F`j#etGCh7n2R`n*EAZH;RO6~j=BG35`85!gEqhS$Hi?3=gV!-#9xZMIj>99PsLG1 z{vH0eJ=fU1IrZ4^Wf^u`*D+e@B6_tNWN zs}~13y3>g39)!=)KbT~kMQm>FV@5)>so!r4CRAV!=IYdGp|c19=33u6BbiSu)aW+(Z9G6Pwq~Kd*1n=Imd}Cu{+2!@VJ8F zqasjuC!NXpr~dW3HD2v@mEf>qiTN&A=Z4)gk~1r>OtM4-ls)Y%VW{?_ljSR+NM;LYt_hGJl`vkP8Q z)+Xx33$S#d4fWeD%3d20q+J4+VO5_cmA=lH5Wm!c@X*$bs2_`vj;)Qc;?W3A?cv)KkkB&94_GcZ*_KRH4f};lH ztcpQqyEhfuxQeRGpHJrroQ708OM0&|ffP%`;M*Oi$mgf0p=as=6x{2``?=Z~lqcum zoIzo_@0B?!HO+!ewj%ThcB1yuGknMRC#>zwMjXsafN0YaSheOh6gzgq$7S_+=0C10 zlbhow>_5ZzYvs;`u7$+!j2Milt)pR20-1<6PccMK0Zu-gN^fo8nil>%) zu}|xmu>%q0;qMOg>XpMK79vm`-3$-k*ujd0HoT~LCvbVv7XB}f10-;60EU;HXTOaI zvEzZQtiSAGdP3Zc*>mq06LQCg)NNZtJM*7G%V-_Da%Bv;qPrU{4m@C#=cM4|&^qe&0bEVuz%(oz^<0NR?OhUfhMMv7Bc7oCs^eH(cLvXo9A<8cgo5ic?u_NU?cP80 zxN}RBO2vkQN5xep_x*KNCF--~k;`g$a!iEApN;1^^mM_;Hc#5;Gr~x^%9HHf4sc$L zp|3utv$?7tP_$+=y){=hbGC>vTa7JJr7~bNrZ;@h(U5~2a|ath-2iMP(cBG zlE2Ry)}OY+;}^MS7j_2@#5I$YN7r$^mkbSZG$Y4N{y|}v11&nLL~oql%0z9E<94iu zWGp=o!`42+;J;j^^wbeX`-BnU1so(t{xjo0v>b)aO6FwP_Zaxj-9QV&TR{BxbR5^; zMUPT2C5LZHuv^ zgsu?H1&iA`uo0HwM&ogQ+*W%^)|SIJqbRcWt|mEtEt=K!*QU#kro*Z3{ctV7gEjDz zBI0|x;n<6_{BM5(>5-H=R#$EsUdb**kp&Knbf+b53pfJ}ZV&MND+e%kO+z`k1k&BF zWRZXIF{ZVjW)0pHvy6}?eW9oTi7#)%!ic-TzOkoYVv)2DEb4CKUCl2{kz5LMC^HG)?#v`^ef4aU$RVdt|sC{g_pa}Th#EGNqTv-seq|q-$X3 z!4&!_uAV)SRDuIC3rITmoo@Z`jjxw`j_aW(Fc9oQmNUN0e*w0jT4X>3_sj7F9rHj; zWG=NiRf&nN_n>nqlXpDl8a`g!#K=Y8fWE=^FetQ!hF`43VRe04{>+9vxseDVMKUyT zJb*qGTg`C*#HiHj1l)2bl$Lshu;W)vvEs&2_Gm#S%F8Um?3>nP$vaKvLs%imZL$Vd zy%If?Pr$d&@?=>&m#x30ggyTu4r#fNnjNlWNAg}~^tvh;{5J-tGuM(e9A|R=dQ0;D zdo|WRc1InND7JU11H@uj6n!OO9Mte^uuzD4WB=hhh{7|7we;gXZ z_f!94Wu~do{?qI5!YT>6?%WkPmQjXIpTr^P;AA?R>k^CID?^@ae~1?xN|-8-G2v9{|0o~XM9QaO>>UHk_(i}qlB>}xb>l%tv!lC*z{HJ0V*5TzB#?4Q$3s8p;* zZ+Emndte>BZwP_^E{oF+x|K|v-U_qdju5P;#Q!hO~QJh2Qc%l-pHd zjh-iw5u6B(4-GBDA~ng;Pi`ba)tbCN6b_#gyD>*khgcL+&?)$balMB0Ms+Aiis(>p z?moHx?`_t^=Ni6JH)ll+M8P#_46NU)!*K-4s~+O>cG1ldhuG($3T_+s)5PeFB&F=A+^KOmOUa1gfRhJR^P;B&Q@p%HS>5I)Xb( z#l~@C!Wv>wok0pzqfk>phZfo?!?1=Z*{&V{n~#)W?}JAC@s+~FXm=`b+lqcs4MU@A zK;O?f$`e>?f{)*c(eH|bxU^G@XwDnLA8%H`$CN6BRRipB6PL$06b-=*A2IyIOm+xR4=fnH9!wlYSH-Qb8)VVIUUc9RR z3wx%Vpyi5$$jvClxEc|1@y%>{C?W^%oZ~vy4lH3i-zj0g**m=7mIb%!63O2=k<7vT zSSqA@8oyXl-nQ>qtffsoDAX&Fh9||KaP|Ytth~qQrw5W(sdnVy*B#7;nzJa)vGl)5 zn3BzxOOb5-&a8Gbz|Z>)`3iThaI9=KBgEJ%gXatkEXK* zPduoEf)s5k`plYN2%<*4(ZqYU2x(rqkjPvcXQ(IQWT(%tN5>N%j9bAa@{ry3WiIk`M);2VK=oVFfCdFbZM6;yhwTsYp&`M z8TV?~dw&EYR%@X9`3KM-na?+;p=c0Y$M%ZLbJ}Pst)8Y&Wiw?+pJ*d*@!S8{+L%a~ z+TYZaxmt;}b(lyxt25a3VkP7g%p)F)yWz=bJqB6XfpPUUCSFt=<%8qUYIXx25|f8r zY3Je6M-!4gONY7C{gjp16-3>_1?#Z=zof~c10BFKn@aDjSEW|D z_aNC{oyg4mz&OWn-aHFo@=jL@i&v=A&DU7Gdhr}H$4!)m`whX+d~3QQZ6Q8RIm&5> zKNh@FnMzmas!`|Y%j~G&dU!nH9n=-_;KWmafx#Z&Wz8dpzRv^wN3qO`!CqG8VI=(Q zy2X~Ps)DMY{V2WTGjpP42kJ(}qVsNhvj1B&4g4tr&+H6gd}}#w&U_2@zYf9rKtWp9 zyaek+cVN|#N({(Tq(b5X^vm+g?YpS1K1ctSDohCQ(r9-3;+Q%B0gv zhWNa{0)OVr1<~?)+}qoTFWyWA*?A_6h=Dm)CGLi0#woCJ;S};(D1_dcB|@j#jbYVD z5Y9co<+?WVn8FRu_>ZpFu+w6ssow=JylW)J?I!Ipw{Qs@c-75VM`c4^vnow>Q6=ui zJW@1VheFRpV6OFDHt#=UGR0s3{u?<2^NJrr_;Kz&@FtqA5VECL6f@x7lpYX_G>1o- z7a5Pfsl@SZG0HqpLV=maEG|fe`SrEn@==Pi9qp{t=FJ!wcAUS})rj_QWWmEM4bFE_ zCf+-p+&d6WbO#q)(Fu=!npw6C%X%NE}W3IgbBDYmtVN(^y~UW}ISi7&f}k zg1L!1!O!;;j5vS6j~y%MUuz*eJ64FNO0S^*=~Hm{P(Ln+Uj##9H!V+8htiAv;*5(7 zLmr|lHjX9GGhgoW8TD7ow_gKVOw&kb)pNYtN3r0|0P+@5)-5<5bRzCRoxd#VG@k)Q zaesiVA~;E^8)tf-<$s04c#+GaRGWT)UhTgyVR{sq;zzdDPVKlV}nw0+Fo5r(bi9|J2&3p$&XUvGDL_O@2IK+m1Frn)lr;`BZ zH1v6T-||F0_czND;eQmJ_d||v8^%j}YiXC1h?Yd-xzDSNq(vd6qEs}HExR-og(hiA zD$bYAQb`uM>W4lOikSrhu2|5O_G+;w=Lc zu21(L%Ip$^pw+UZHhK~HeJdI2BBOEHd@bVEK7)}i_d-AQ9(>pJijlL~h1srNsDr}v zV}S$4Yzl^&NELGH&Q$tsWeE%ma~Xx>nWR=xh(=fV(vZU(FCxmH(pUCms*We#5&Oz6 zJa~fhd2;N&b@9}t_$1feoy^RAyAcyTZqic|w$hMI9N%(IE@^+T4&y929*Te<(b5wk zZMm~xh23Rl?X*#jbFqL(J@97zc3y@U`6!Nw^A=L)i^9T!Lb%l&51XT-N%VTcvHw3p zQ^+XuLAsgl{?8Z#C3h0*>xb!%L9Ou_bz^xO$fR{864v0wO z(C&D+&eSjlr9Lpp@*#{QPof82w}7=~99`|Q7R9CXU~c<$_%#y2mir!KwnQsZXQ7u& z`xQ})OnS+(e(I1rd#7J+oB*kydN%epTnGTYmh5! zV(uyyW9uslOaIA}F>ejFxm$xdVw=G~ZYx7m&Yoh_?uyX=`wwHy7Xu8cU@AIA z@x`>fhC$Qg;Jxa5L&2{HIK0<@T>NUue!P^0C;n*AcNyk%X&jf;(~~35$AgGbuq`us zG?MMGjbJu3no~nxOURbW0hPhOOhjZdDb@eMW6O_#l6e~VU2`D$7jj|D>@42+eFl>! z`rtQfOXhT%0-btg0{s%Lgsa{LQHjiC-aOB6?he`qjSsb9-L4LsHrt>4qFzs|)v3m{ zMiz8YcNzv$6byOwTMjZQpw4Q;s<7x>y3H7hR~P zp%)(fUWnz(?a}^k4kl+ThS9%wc^>-z7~OS~>A$MCkYn-&%J=rLZXeZ1X|e(RUe?Zr zRc&SEWIPyqe*^q{_Z*vNwiMzx_JrK#IqU|BN$gEhWplvElqwaif}QE3plPYVo)ELe z6-q0>$KpFLS#l&X^WYNM}c@6Vd$ONB*3(T4ai>FErCZ56b~t?JIK0W5dB~Ry2GyEpY~EN9y?=2DlxGzXWjKlQZa^a+#Db@~ z6`f&{MTgf7Lj*0Q!Gou8(sv_5SM{^ggsVWoI|rn!qv(ZFE0SO}hGn;7N$CbDI(q#+ zY}_tN`@V79o2nv^>Awsua?x0ZYA6}d3~pPzAZrn_w-0zz%M=Tu@?|&n^d99c3w?+p zH~cZ|;m7}n8rYSo^RPc@m>2b^5x1Y&$eMUOW$(Yb2&TO2N5^Hv*xCO1Q zS&D*FboqBWEYLtE34YZ_gXdEq6G9TOaXH$!^HVtHyKX+vnPvdg^0$I z<9s{cbiCc#X&J=z$prpJfcFnaHYBr(kz34p!jw92(Ha$+X57UTTvehzTT0=QP8RY6 zH(|_Xc`Bv85l0+r@sgf3wX^=j+G-_(DmtKfN;J-Bl&03Qz2Fp~NMvW!uyseA*;D=> zVA)A=__>_m#EKxu3qDTd6kZ_zvmZM7k1;mqW)k+zZkllK2po+*hc;7!iEeT?-rpHU z)Hr59+L}?=nN$nD2VSsRJ9bkYX-nL&FbV<^Oo+>6Cm4u7VqH`AgP_&6^HAX_XsSG?C>VU^WXL&XQlUeU4evD)+AA9{QNtkOEZJpZ$I-zr^ zhT0+4clvz#`s5d3I-Dc~>385t*=(`e{gk>o2@;O|*;WuviBLh-;^PtPA1f#EOGD(V@ zx3VId9e@7;x9rrUJ9R$6EO&WkZ*4rg$3cMJwc)rHSz{>j;uqtdRtCv=y=YxLnOKF0 zkSS_&$?W+agzw+OY}dSx;=%^FFr$-bYXT{KwvWY>T6v3aDUV zO5*%RP$p8C{@i9y2PP%+A81s;;u(Ih%j+>-v1I6B1rf5NDH&(ENl>M;ePH+@8B8Xa zktyZz)I=kM7N4eg@x2(T30QD$=-aGBeGKRR%O(yVxjWDDO$>5p*0%x2VXdtVO&2wsBya8!EFo;_(_F_qn6bW2(2)>3c#N4h8D0%NUv}tdl zvN~5_y|Xma(kMf||GvYxhgmThaW-)OWDK>sBS|J_UBP!#CeVM<%_uGGLY4%TLEnBW zdOmMIy%kh}|0ZS8T@$aetBo=-gq!KEi0@=JJUM~QQ`h3I8(WC}vnp0TG@A5i*s-%~ z*Fcdlcb;?5B$+)2(aQI}&4~6-uvy9Cf{`S!4-_KFvft3ha0Jfmcm=o8mtwW{d8py$ z9pmbI;JRZo?GEhHuEJh-&vM!Jzb3JIOhf)Rim%`hWHEHTwu*Zf4+925)3&3RtOGF`ktwbV1D$+HhDKlnuQDu`ymT@ZEEXh@H>Ie|xmh?Kf0^l#sA2wX&cF*R zx58D=M7-{^i&p0m*>^7+E_(g`PeZ|xoM3`qo&QzBG_^8+VG97nnP z-lSEXyE7HK)8nUIsnP6Cv=%N#o0hwv_sa|fvct&u&KY!vTQ;oMIs_|!P9XJ*7GaL+ zN%$hJLsHht5XpjHY_MfJ8oT#HS>_!6)ral89}f$#I4_lI{A6HchBZFVdtnn{=0$sB zRIuWGESZ1D9}g$DarY8c+AGzDYi4WlUStKa@1h)tsP7dvcd89duwN|>g+Q)t@o1HDSU za9VpQoYFanfJCKV*EwV=Xyg+$c`(K9C~g@719~>O4DCmTrB|w~I+@5uw??d$7sA9F8m)M3H+7 zX+>ub4zHg;x&~$wpZ3G-NX-Rwc0Y{X!Cyh=%T{jB(r zXZPP5#*BvB@HcV_Ge3T20R_S0`C95un=R%d2hc zK;x|pKA13%yth-J9ZM9*p!IYjF3UM-tbXI~+cQY!Ob4jEbqhO``LLpT20i#zf<)|m z3tAJk$+hvX-0UftQ5`6TMAhY#zw`~?Iq@?4QP>_X?w$_uRzsL@-jD9C_QWGArh=S% z40x1hpuB!5UfB-R>ueHRJfETOzw=r3DVw-`aX9T#^oIsP9}*k3jM$sam!JD`&d5@!N zoSP{0HM7tD8V<@{V7x2-f?%{5nBSg4zZ6tr-|sBUL>1V4@+V_D^Z~`gxjxs>Cz#`O z7R;q5vv%ox{B}W;^V$aD;}=UHKJqgztjMrBm1>m#;|I@YSR55>lnGPe4wH7yfPsN9&WHIC zN-QSB1!>NEr0@^4d@kcgvF*IX1Pw~NPO!Ewr;AP@!I+%{K$IZWBXrd zpD9I3I{rbtHDcSeK9H}>WbU*o(tEG1@uF)ZcsdrLRlO*cFTaGn1HV~;Y6=s-WfG^2 zLWF~L;SBCQ$Td68?Ch|o3eKP4%k;T8=~@{tU!e@g9@&uG4}{g+mV~XUhLqJ`j)%F- zq@$ZL)lU(kXZ=#a*ewfmH|mg+(MF_uN-^X-GzUG0+@LvBk-z9Aw{`vgerZALqBP3kMzyEQZchMr)a!7@_yYM6CpPELd zsUE`V)di4`SNYmeTJR+OC-c_9idK9>s3}q<DeS6fmSZpd zXt?8dk$qPxN_XT-LV8FfR#(I_1}`Y=N>znz9BW!--@}GR>dgu}JHc4&b#A`%9#yAT z+H4FcMnyYOdZj^%mIR!FJA=pYNX;vTLu}*iUC)?{i@R_|xHyeYIgh~w;-tSi3#?YN zjIHz-7@Bvp4rP)=-MWX#oLCIjZx!jY??LpW)KADM*@=~<#u#u>os~*z#0wMlf?Ss= zo#8NyH%r=~Jz9*2sk(tu{Tw(Zt&c6sZ0P&zU!btwfDAt`1goj7Xh-k!v!^~}1FN{* zQno8I-!>JBY&T)ecVYM}8-;t8`SE+7T*txr)?{IcG3TXrXRkQMK<)c^_`t1>eeubP z{`nh5|9*K4V_)*&!^C)|joA;rPaZ()?Ote`t^@m33h+{YG!`G#rM2fDfVE);l#5BmL9*ujCwylzl@%^p49?;C zIE6bejk@jwm!KLnuMA?`ym|Cwq&7Vo5QL%EQc+K<6Mrg*(w;3JaWpOw)~BCjJgyp& z6b&&nVL9hWy&7G1+l4h96{Swc6Cv`#cKkYpLa)jlyu@WX>UQsgc;8WwIhDc&zN+R+ zJ(K5{S>^20o12*AC|8VGc$GP&FHiD}`rs?2Xfc*g&c->iPw!-dKlj|*Z6L!gc(w>; zoG)Ol&#eQM1E-kHmfBoxVhCI&ZD-}aAHk(nmtb0A9lMJ2F2z=Pkbl$ENYnHpl$cY_ zY?{K&fzv~wV~I3*{8p16UeC=uE$_hLNE!HD4~rAENd!{>yB0LDBd<9|m2fjApI8p8 zlQ5bq2BLs|9=vpYi78&({dNBtl=v@>yqe$$2Opl~o)sw|>J^6Sh5 zy|*A2xq+Qpb%!Zg@{nKlNSLnESWNdhuOLsXH`B9qr--TZU-oqxk1~<#X@Q0Ujqz3l zkK0xFsBaee^iv7_5{j9rEdyAY_Z{WDuS_M*G6#Be*N&U}p5PMu*S*Qdb5;Q(Bo`Um>5rV-UP&ON(8 zf-csdPh&z1$uGIr7@M;j+Ozb~$(G|iYa}xMS6xVP;5>{;)FEOcER2Q=Vd64`NIfYu z3@c<(E-xgXuZdDo+1b=!IEtyiDn<6$rQ$DJO=jPx_pIY|`W(Li4VW!0I#T`5~0*es-q6W@>`)vOvbc=OT=Jh$WXto-tZ(9;C6N6IK0_ zsNG=;DwdjtJsExQ=Ai;pA|}J81ITYK0(Pdo7w*N5RmtQxNcMDP%pihj~Vez?!)6U#337i^pc5&XY2H>&X!| zx!2eJYD%1!gri$?1B{>V!3ip3kd(iSzJ50Z*B;k0b;c*bvm=hy*L%Yq)vNg8rxXN? z%aahDTvpy}I`{jPCKCOfJdK~JWS2`JN(z1ku26zEc8szGt-_?*J05eU71P%KD>U)> zOxWsH392^)=%U$C}i6W+u5Yrx+B{7SsJV zvdD-r<*nT42LGaM=!ugH>7m;viJM+2?7i`bJvw(DJ>yXX<9pYTs)%ytwSx|+Jk*cO zfhmxCoGtK@}4oeT`)6KV8Sg|w?G7R3Z1~($%bH*kV5b}VkuxXfRB1XR7 z*TV1IXS=fJ1Nfv)AyUE*s2k_Tee_ZkEOtebuoF`BNk#-U^U>j0XhOUQLvM6AWJ&F{ z-{SpgJ4yA=2-Yj{KFsptyv--K(yN=Yp{QpfRrIr_|H*CSxQLQq(I`QBKl_mncN=I% z-`01>oQ3niw-rFdOX4y(`E5D&98zox)feNKZ^&sPPUlAafng+#ct`-;gucCV8T=SanFs3 zaHjtlmx+1F*iU=|VrdiU&R^xI^fMgi#I8k0J!d9EY#QY>f;6Z*3U#8~=>FHXU>5ul z{moN(H8NA!yw=^E-+C7IrQe6lmTk~z^_4MM6ou5J8VZ-h;t8%hlK%BLjWs>XC~Q2# zzZ88Ryi<*c&I}WD{U%K6ZhNqWz}<~^yV4_{yf9gH7N#j)gi@2OwAUks)%M|T?Y?ec zK5+)=-&P58c`um@PH`yHSi?57G?LF}FYu1v`GQkI4uksUvpC+Zi$8)Ukt_AexHa+~ zCI<-7*7RkZcRB{*e)rf%6#~58{&GMCeHk!YBL(ki238+TD&J zDUPkU(Cq>oco_!9K@r$<>mw_3-5X9?Rxx>9r>!gJ^Kf745W36WV1I4sW^ZrSqMxjL zVf^L>dTi=Exa=-T8_$(7>-BaqW8tDS|FkjLvu+jVl^y51-08rZk!xw$xdpWQnmxO2 zaFD0JM3@}P5JHbE4{Ee-1v~%y4^UL)Gc#w)5tDffaoZI&ye}g~nr!{p)2^!Yo9=7Y z-E1db3flpWqR-&|tznwrM4)Dl6jqLV(Uu8T?Ea%W=#KaD@NtPSeeV zl>WlL`|b^CGjj0ZZhdyFA%m4QdB@9p5KqqZ*}{=e$5AQn0MfsF`~U~={JqPp@Fio` zf~|z^#ShutoL@Hc#S|iXv5cv@_kt1c_a!%O&qW7g1-jM0hS!#=PLtIWz$sD@CHuaj zM4}KXu6fNK%rgevMTWFKuoS8{DY6lZX3(ht+tqu4+LwVtRtoKvb*An{`RCb(#4FH_c{MAnC)!*OW# zUgY}jrpsZM>{>2kv4bJ$QD8k@&2?c#scWApoqIYDBO7MZ$78RVc=0x-Lh>kF*jEgT z4~vpdskU(b$2XKc=gPTw4dD0bE3DH;J}BPZ1!WR3G@|e_yvyet95?*ovF%Na+IAaV zjc>4Co4h$5yb`f-oJk6fc~Fgxv+PNDga*o74nO-8807B-LlCCd-kxKu?|jN-gSugj#}j(u zqcQU#u88^f?HK0vcHnYlJ+`fko0m)!gT=FC*cVUFfPVHS>MuW=4f)iH#S$0soxC%V zb?ky--6%BdOT^{lUL0$JuG*z*j*W+pLRua%Rk= zlFf|vf_F@xt2oiyR>&xQs9?M{-GF$zIrP&X3o34A0`?EyqFOQM`#T~;M^$%_PDN{K zwlo&HKWztv;4tRhBqgRHB?D5D!g51KG}&$$dv{SM zb7E2o#7O6YL46A5ZL3Drer4M7xd%ocFTk4hBBbTcZFt-vMwfrq#PtR~bf&v4dFN7w z+btq7EuH1K^%Xerehq8UC_=Q9bD&|9HLip2tWV%kXcXH@N2F_U>nRuTthXco-cKU? zrfQNcN5A7zK{c3cxEV^$iIUa79>Zyli}7mS56r62rx9}p@LR!0p4}cX_%bztu9v@n zfqQ3?#KO&NQdS^b8Mwd}FAJrRG>KFQUk1~{d*Ir*g!KAN2B*!zG-OdHuR)CC|Hc*3 ze^}4zsV*h*`fvEEnyt)YR)ol%K<4WG1l&?)L8ghQP>-2sna-3+^uwk!IC;yReX`*m zX-wrT8h-T$0lF}R5HtiaERIa@VW<1y8m975BZmpWb zYfuKVYbD3TlO2clOo|h4=RjzO5Nf=R0yl|V`ut8d^R9m~JMB~=+~A$V;Q&d>q%ibW z%MDy8TSqdz{$m=X&!UateKfFZgA;Kw%w#(O8nT0%86BKPel7anOwLA2IZ|(J6N2NCs5r z$}lIm-%tFHXjseCu%feMIXBxd$aDL({WfhF^lk;0BfO8V>^XjWi_};<#=GME3w)^<)YJE#vs`71?asHVp_8TEJR>GJNMnS{4EBp`EZ3zzhSMIK(5%}nxRK8}9FFwC ze(gc@O3#7w%BL7R>3tBf$PM}*>to~ZzwEa-4U&D%geK++;5TrhmXaLTklBPj8eZ5@ zehcrr3}d?R3=(Nt%a(06g#2bnFtm$jT?%6HY^olCw;aD(%#iN;!gahncF_mx8$c)w z2;bm0qj2RHoP89<6WjY0du=S~t^DOEzG(uP|8XImz1E!?-%I zLxKHXl0DHB1LG6$l5-<820IZ8=_TaHw`sK7L6P1JX#@jzOa27s3A9720`BPmYGU|iUvmP=C~Xt=_XQ*ID~9 z<>GD_|B}V7{uaSkzxtPX{E}jGQ6X=UK`s_~3y{A|9IT88g-Gdf)_c)!_$zw=93~Vu zXxHT6!e|NFbzXrM=ZlkyECU$x7o>eQsW`7hkoI4>i!QrZ`*W)ja)1+690rj3?N#vqlGXk<a*M8gR#m*9dedrf%je@f8x=X-dO zKg<~GosZceekJ&w`OZAwU`Za$X~4_DaVYC|iQ7Rvv1Yuvez;{h=zrjN*9wl9a72Y9 z$0eiuWI6I@Wer~6T?RvmRrui5M5wh7qV`!lEImW0&((D7@Z658=C`0Wm+1PLdxMEs zElQ5-ooB79+HvXi$@uoDIn+)TWf%IlGN(s*^h@48ns#6>Nu9I?mv_sM^f%(Pd~i2( zz0x3pA7+uWp~tDqeh2bvS_gCTxB&H2jzmXCTbkaZOe`u_5#78L*7L_9d{NEI%M`lef-iB}dJ>!WDwARPVYW70l&t#B`3Ju4 zC-NZ=AtCq_xoZ3Zj7{!?^v??%Ytf&G-D@Y=OUii1AMM7J^$GYq<`B3E>Vc-kFnV7} zMvu5X`~_(n=$)tG^!ri^lxMlQMb0Qq?1;w)2S?!cU7&jD(L`dW9!+~@i`sj&=;n8o z7~WhS987qnkB;;QKRL_c~I_D!Gs%)XOY_sAr~h zW}qp_{5l^l?H46_apFYSKM$o&KHw9bhwR7RSh8Bw8CHtV!cB&ySo7Bff-b*g&nw08 ztA-cA_nPfc8{LQs*Vf3d6L#`?j%We z23Q2oV-l|lu=0~8Q{L*cY)f4NEpXaPel%@cETX!G&wpKIR zb7i>l{ChQ8#H}P#`qasyoyMe3PlHFyL*>!au;@-Ax$?*f1I*jOX4r`FcV?kt3+D*uavRwAkiFi;bq_x(kw1;| z=@RRWxFAj$b8dWM)~w!v-yB!se{-d|-h%}7HJ=KR9iQQ7uK?kd&Lkrf73rf4J2G42 z9~^o#oepll%9w6lLIdYd#jlPcywcOJ_!9#Ru+>?J3aDOyrA{Z=lif{_Eq9M|#Qgz- ztTy(rOB-}voJY*X1)#l3id@fEhYLTnK#2^nbv@&Z@n35q{!p8&Rq>+kXA9^JTM@cH zz7FK8uAq~fC3bn`Ff|5~$&JA80Kcb`r~g*cHG&2BDIy0V*Po&PEVmQATYBK&d5oF* zc)THHT$t`@j)j!?5q#q7Od^_Uc~2~kfp2OS`i>;wpQDdRM`Hk$HA!P)>i)6|O@Cn5 z;jawOZUuOJnn`pk9YFi25CnAa=sJf6)Hc5e9Rs0we$!o;^0}7!>CYq0Lu;8wH_zaa zr$tP%h$DG^?Fc!@@dee>HHq(nmF!JvE~|L=5@WG?J;uL13(sCz6P5aKEVvj?&z)<+ z>j6B{zk$VpdX8DQaT0Zmip1`TONe@LEd3CE2D|%aQ12{4RoZ{^W%l}-RGRYQD!K`_0MwiUX~&JY zXSXtOZS;YlIj*ENq>y_qxzHKErjdPCrfk{{5&ToKid(Wp0qM7+Vox2=?{Yuv+ljd8 z<^`k+?Z}wn7NRu1oKzJrLTQcj^fLGU>ijEV<;)Z@G3z$my)ca)2T-$(R*U5 zxN&YL@8bbuS{tfGzWh^#w$anajqUHPf2A144z~8&9^+B!&?5T!$DB6TU`~#)NstRpT#&QY9;-n?gMYS!yq@H6CW$r z!_b;p@FK<)eRU5pH;nDs6Qe35`owYaOMDd-9DInsPIqG`)Uufl@+4kClw))4CfEL& zkRyM3c!RgJ;JCCJG=~*H%&T^ce$CAWw*3O%3mq7?H359@kHO9Z)tJ~gk>(qZVcO}5 z>^Tb|@M#sGW?a^2aP9-ND&4`@2rH1-Rd3jRR=z|(<}wOo_hZ!d6Bxd@0M5_pN0%HA zn7N8$z?cNGU%I*dq|YyYMD2fIsUA$vwX7g*Pz4tgwQ=B-G_}2@PpW4w!e48J_%z!A zJ6CY58hJO4C0m6eTZ75PAKU1)-Z1R(+yl8Ua&WVrEOv%BfYzIZR7>at+}&skz74(b zHEb$LxtB|NzEt6iYyo;8^9=0QqChL)->{9X)2N^DVRT(u#m-BgMveY#Cm-%T#R*ASfR@L|RkKpoNGJ*I zu5`>!D&NST;ybq9~!&lq2z4o<3l_&{I{7B6`T!tsZwVEs>2%=IA>J$|SzCPr+_ zM3~bJsc@!z5953%nSPCwrAK1k!`<0cY}i0NGf$Sn5-yXivU@FaP1=vNsE@P8>Jr#8 zy9JE2yI6CzQaW4qD05VaM~;8=hrkkR(z3&ox*O+{S5X4=qVPsG{z3+rT_~aZp6IgW z7xvH)_ZYl6m2**S&P40AL6n%7lGP72@y*f%y6RUX6@0XkguTq7PEmiEH_8@75w79` zyVc~0?{5f?US{phv7s&`Y$x822Cz?1i3BfH!%^b}WV9`ZPO6NhwToK+7HrSE+=zGY%wWwKT(lc%em6KA;40I)E8Xht7*o;^8rBy zO$zbAnO+o17NHZ$u8}5HC6a#8ksgY+rE5KxlQxM|_!XDT+_~Y1v;J_N*Tg{Rsdd2e zqrW(};WQfRQcimgXwiE4wfqTNvgnQvsu(b}pS6;1ME<=#`jumKE~Tlc*qTOW=1ss& z3KdkaG6!j}3b9?BPLGIIF-}d&IQLQ!(8V?&94SYvn_Z~Sy~mhum`8TJw8ez`mxynp zEAgm_p_N9e-<)UXHpvY z37dMZv2PYy!S1Y|cqky14ED-$Y+qq2xGSB6*cr3VjhEqkS0yxlE#X%k2xw5N-%O2Q zE;W`Bgz-BR9f8B&j)|@}B=qPTeC>O_#MwoG20l%s zfsc*osP7JR*}Rm#Z2Jm#yoF%%O*498_CJnknT`Dy^pF;%!LNog3|VSUqNJD5cmok? zzEy!ThkbGBTuGvz-3qUQ4rA3R3%cOsPx!e%6Eh}FA&=L~(b$be%v^~vv`$eckAvl@ z*5Mmies?WtvaDcdO7t=TJNIML?j<-sCWsVT9;Z*cU*VOM0p{V_#l&)53XX2;;x}}~ z;yWc#V%5HYC$SCKHxHkn^=^H-d}s^x@j8lu4wdj~td^BqIg!eXWl)>jT2zs)C%iFJ zvTxXil*g5`5~6BEGbaeLGd0M+XaBJV&f27_qZlkd%_Zu87vROodvU0aV~hHHAaC-N zi7ofsTV%hPF!ep4zL4vbnB0Od2S4G$^a8d@c@>Uiyk-KW#XN_x`}l2#21$FuWi2B8=_$)g zXybN^@9Oppd+$o{%3vLYMBfA>6(!7tKOh~TL0k&$v3b&B$ZEb0ZrAp+aTmCZm5mC0 zFv}m4zm|buyA4UbdLD-KB;ca3GHk9Ep)NCjz^za6GB?;!5Bf0&ejc<^VU(Wx*E-YmKR;p5Sy)B77U^~w*l*l11HsGn!Gj;*H- z`o-*#wDUNcDnt7%6i5em9!>nX9Rr_c;JQK1=^FKf%pR{t+eR%WXIC38H&*3xJe^p# zpL2!3ddW6^tVH5khm%%H(JvrJ`tC*17gaX!K_QLxG&Uf;oJ+Sknd8unIMTU8t(c+e z482m?M9oeV1zwlp*EUACeNguj8B8a}_vR|F=HR!qu7kWs1E)8hu zgw-1skeY4M=)TgIZoBiC)olO5T)7$qsvNr{)oBZF>Ah2MxcC+bzD?zyd>D*v=d3Wx znF6c(f?Za14K<~WP%&vGY#Xjb-_Zp2>MX9;5}yvC$x}$^TLEU}Vg-D==o=bKHaA%N zcJLSQXA;Y2t65p{OhipVICK*tUAxwRAugzaSO0+!OZyTiyaa)`tg=94ehxA3!yGg+GKO5b!H zCM(X8_A9Gysee=MVSEK10MP(AQ;Y66ux7G$}59^5RN zPd9!354ZcDCFb2}MBmGo+tKZ({ncUgSdkfg4|&I&leeQSG#R>N&%lpJc`%nTAxr!h z;j;yE=#EP=e7(bzI&p060t+GXaZr89e+zhTOZ-hmm(!{=A72ck;Cca*Z^xd>f44ml;8#cD% zR97j|oic?M%;cDPmyXhI^V3ig{1zpT8xh0pCbZ-852p3oFzXicZcAil@W1VcayC@YD_1s7NkxO zQsMOzZZ}LN2M+9mYuw3R>lPn1^XMAEjK~Yf^jy*9a z|BkQ=j~;kBM}%rMZU z>MJcs*>8I~C1L@7&HIe%w~V=-{BE#5+{t)kO~uFQrZg@{l)a`chRggG5_et`-EjRF z$Kfa>cUvEF-zDyj@D$+>X&=2^dZ2{}!)2Xk0ER_wP$T$eb(Xz40bRhc&Z2IpWYj^7(3=JP6_x7nl zg7^|zS9pyb-Ep2gY1XI9lyk5|RF*WFtD}4RC+0-+M9>==KrfygyT0284n@~7Pm8ASibv!GB8w$-AyNy3$Yqy_Z~pFCn9> zng|0mrzg?tr?0^1$1|*4-zJowv4WUz%(W)2r=GIL)68o(gI<<6Y zjlvS>lsf_iY%7+91v7HelgW6|0cMww2ovC=Ogm!*Ny**GbfV@X9D3}`P2bP)oI3X7 z1%w?TIF=Txpxnnhv)O=Y&+&8sjlK&gbS)fX;_2%Nt{Cf6Z_bC|mHlp*U zlws?KbLd;FLfJ!nuK%M;KOavd$Ns%THK91NPMq^wZ%c-SEuzH4)(?Z~QnnE^+W~sY|Vz+x)H32-bLrv?xMD{ zwBhMq13Z}PPu8?7f!XTsaPuBJc46T%Ton=xCKE&P#!*SqUl|2ED&|0A=?$<^UW5gE zrc=Wmf@qk27yM5BK$Suda$xZmcGi$GI>knVS^qtpJXk~Z-ZtXvHBMyVOC#dBQkUv) z@5J##9N$cQKG8fKL$Z?$X~^HXXr(29%d=(aGmER#MWvJpe4s*QD!((&Hl)(bmnRyu zcZe{BN&6YcwTX1TV5 zF8E+hJqFc_!*+*Y#{IupM5>Gj!q@*t(Ruh|^}caj_RcDV6dG1!#&fRQh(u`+QKCY9 zHIzzQnI$7DSsAHRvZL^v>lUG*B+;NDlr)s4mVW2=7w~#L=XuV3U)SgJev8qBhDEe? zhYM-r09%{Qd)Pxey|6;s7kh3UC5f5yiR`xw+9kLS2HUhqSXU{tE322?<-;R5I)VN< zlL0ZVDZEhbYM#F4i*fq|2~;&Bx%(T~QGZSHUHlwwm~2DbgYS@!wG+v)83Am%k0n(bGK5d}!%5_$ z%}m%>2?l`Dq zM)SUKo|{&#?=&w7F-|J?vVTA5t0fkoHGl|+12 zA^Pp+oG+7D+HpvM>^%Jfww`b&DRc7i?M^XzX{`=e_ne>^Wv)b^Ckpst>6{l{og536 zhi2|w*VR9lt9tFAdzxD@;_e7LQrwSbqlvIwkIPeZ84~B`OUZTr9O}4%LRDx#N<3Oc zgr6X6+N@4@pJwQ@hCOJ|wVb4k%G0`mH?Vzi2r3^}XZ8ji#n-MXEIYgq4JTg)VMQUh z#M=xX=LBJew;7o|#$*0TOEACKC^}y3N@7@bqQT`)iUqfG43jw!9Ue-qulfUScKgW5 zCC^d0dzjVQtwBQ_X49@?CM4ZSnXHmnNZkMOQRdqz+Pxu-N>!FKr)4T|Q~x(8;xhOh zQaaSc{xSU5vY(dEQ=+rV5i`R_F+$)XJ2qiHUq~2so*r?p&LxktNIh8 z`&(%5gL52f_Yo4mFevH04|Bhyz>APYOc8g*T9L@*BeuKGdo?*GWJia*E2Or#OczC_?uI(y+tI$2?C3WGh1 zh@F`+xjTIl(fGWYOm0mD6VEI%SxbU?H~OP`Gm!J$XJ|=vEo`%~Ch~tH=~iPaIz-Q7 zYrtl*wIPzYtf+;i#%09u(qvSgzL+kO5}{lAYFIG?5#~V8L3rcuOxvSJK}mcPmQQuSY7_*WCF)TE_&$BHntd4=H z88+yzrc3?Lz5^|r3>3Gyhf6#d@@L0KEUvqc>20Oxv91rVr}3C>I~VFW{etzRwly#` zo`g4~ZBhM12Ag|%GL4>@OxF6l(cZQ3uupRuIrq32zwI#P#a>-bPey$QMemE$(kq@! z8aPB}bcm2d4MVn9md7)`aSBWOQ^|)IAJXe@K)(NXnjGI(juFe{>7TiZWU8?!<<|$0 zIZI1mVz?Uf_iv`~@>RtRIK*31YY|_!Y1D%q%kRlM|z%=ZGc>Ye~oo&g-z!j0P5S*?fsWTJR~E ztQl}2Cd(XnLWjq38}BM;{W}9TIa$~-=?qvZ?}MDhUUaqO3Z~;qAv1OMWYX5X0AqbK zuti&xL~G0iMIULByd%1q_b~Cnr(GjqflqR13b(p?%8tPoK zraI5UU{rJ=(iz(^!%Kshy-I=g(sPKe*EsaXh0(Hjao9053*Q`yg7B<;RM7brrj@jj z)8Re1@=H0!S6Gp3$)8{uE=re;>CxW1lI+*wFm_VQ2UhLLAyPJIhF^01=mq(wjHdGd z)K$(w?Z{^!K6-#^HCT~1+1bQYJR4T*=J*k(BGFX9f%u1}L7Q(l(JWV|@zIfV%j%_M z-l82)l{N;6OOFUKz?!n$gHl!z}<5A?xrFMo3@PeiQK}7 zw4LN|nIt;Nx{_4=d63z785#;?FruIa4WCWL*$UU0o9#Kgru~gLe^DO2Fh7+j?wkvs zhAJ`Q+8NMe(n(poCeC-ZB!N~EG;50*efA+7!WdV2%BB%K0_1s`s?tP#*prbyAxGEl z>xad#4wLWmp|hER%A9FbxTGC*b{#?{#+S^9SEs?O5Y9Xvfvsn%@qEk*V*9lQ0@^rps0QVg_dkI}S`Lxp|368&(p`#*MY|-Ao7~L0u3QJ~@P<}Kk-!Y$W{QW6z zyH$e?Za_BZhLds>BU=I^;3}ee{B?s64m)mt+cZibUDUh2FToAqc= zg&S2c;Jm*NCD{c1bQK&3#r(T^#e!0z~cKZL3w@uq{ z^G;`KCXz`%w}#UTHy7a7bt~#zTm`9B-WHVm;lbVvt3lJrOK1W2c^@XRXj<5W2|OKo zVc3Jc%5I^m7PjR5rTcgwKbO9jIzc=pq+`)Sb1Jz&kJKJwSxIg$KIR^R|01)n)vJq^ zZHXd(cRqm(ZiZD`xRfAbDu~ai2EH%Oc z-D99({fvD+Efp_2G_jjWHuN>0f!>ZRN_#J}gPbohNLq%7e{w zCCmZKqnPqA1-T>v@{E5#fpQ7j>;8fCMj?77brd_^|K%bD+1R=091bf~!#ZCn&UGA# zjp`q8^!zw)Uy~cp?blQ#*r|TPu-tZHAUzJDmGsNl3svPva7fa8* zD1~)K(&UTtezI~b9Dd)c#PT*3dTJt%D2NrH-n9gnBi0J8+#K(&bR3Pc(&k;N^vA%H zQ&@#VXK61poz`{EqJ4A2h{T91u9`gHj*c(22U=+;))_&Wk^g>yk{q7KOsYy|)2V6tAUhB{X$GpC|2QMt%i zrizZhp~{VPgJm83-L({-%?n}AI3|$=93%MF_xm6uDMow`a*oVxdNd?{6<9?Y!GmaV zY}Z#L-%x-K29<+)=M~PGvWaf8R-%6mD_Gx{7qH}9CTpCRfuol%!gGHcqLB2M$+Ti2 zW$9`x&5^@|Db2XvE0wlppJujX3eaU5v*}!KcQ*CUR=RPfAQ>7BfJHkGLa|UcMsAe^ zm9EqHMfYC4ec^jdIzNmS;{$ii@-Irj?tgu!3sTn2Kz7GgpY2e`1VN% zseASu+^wVO4GV9})W5!PGQ1k@Q)SqkCPP1+)<%W-8|cub|KFoLLCQc9#r;~C9SZ3< zY1bzZ2%L^bc7{@q{i0y~>m5kd1(A5^Xk>97J&{z7_tKPsEI!VxZ<^9je=we``p}J< zY1et3cLgX5_H?!5RYr*84_Rl*krn2;%=5YDuso=j4V`L4XEqj-Rfccil(i_)Et7;L zYcz>g#|xY#U_uJ>Uy;ZUxtQj)m=>qyaJ}JB9M&!c+Fp#-R!Qt0-EOQDKTTWs>DC69 zMyTqXRbYR*0rI~tf}X}Ma;Ic64wa?xBlAzPB3J$SA1X4?*=Hur>WrZWot6-T)@3;F z=n#0cc2nQx7T8#`nfy&Krn?G0p{c|VC{hlgx=sS*ew8wL=gjp=wu?Z*l?fQIxCS`e zGP%t0u$cYR@J(p}mD*iJS~i`7t=o0T*}^0AQ+5J&>ONqfoD~Iyh1+pD$DkRCJp&<; zlfc}GJ4;`&qIv4_ST*F&vltF!9jDuomB;mX6Zb`;+R6kx<)z7{IJ%JDNhcv^?HbPY zqydw)&%w`+zA(n$4Ijx*=wIniUrM-P;d#!Je19T0=My6eO3&)rr_6`Jv0%dEoC1&T z-DFNJdI^O*84UP0gZ5RJ(Y8M)>8&tJV&nb;PwPpMx305jQGp80*gp*W+pYM`^K{7U z)<2k8mdf#+*Hh~|Q;7GF5#E=yffw=*sNbYhr4<(Kq#-M^{?6Qb#nrs}) zV_a1@W=Gi->ar+|7{!jkXsZcz|L({2iLx=vSQc72=Hl5C9o#PK4|Ht2h;tf3*}}>5 z$k&r$#P5s+&ewOuFOBnv!=hBCzxWykyj3MP;vyi?n!pE+J*zm-0og$h@K)3$x-fAH zZ0l}AnfkZtnEodGPYo|G6UPf@1#HM;J6N809IrU5khwLA zP@DG@%>>%87Eg7T zlO8U|eD8<>Ij6H82X1ySGj>T(={wVCm7_Rbi|S@K%<$wLzW))944uTs;gd;iS|qEn zxgB?Hw`Bu66u3Fe8@#`{65UTUFx%e^L91FBeWT>Uq|sj)=R@8 zaTjVLvJ!7x55&C%E0{&%U)f1%U*WRzEf~n_M_wi8-)s~lzNa$SJCRy&$e|P`XBXA^ z-1No|>HauHMus@{DA7Io0#wudAav!n!6DNO{FG%(s--w?&mm7(SGkgOn3&QjJzSQ; zdn&28x|@Hf`x~xQQ{a`jhN2cP4MVIn@U2%7sm+MS#`K#oaNse15K$o+2ddfdz&MDD zHz8q`ez?PBIZ7*sGTLny=;mmDk}xBQ-8cUbeAQe{1ZK`*WJ>zL@L?k3AZ9?jR!^Xd zD;$s~BVK!T{uF50a0fq5Kfx0?pig_PdBpFz0Hl;$!s!cda@nkF?8U#!82!g@P@t%s z8GKUD3#?i~&Y1f%dH8_I@AN~>iycrRl8isxcQKbLyCF+hiJs+iPV9hf;Gr(a+MfE( zh`Fc`OZh(jf#n87&`Xc%bPCffxj|@6c*xI~%lVyxtZ_Ie8aF*ZiKRyenA&fy^f~_@ zuc0%Ds5mp^)vl>zK**82zBLBV3OQ$-&p79dZpQcRi_o-a5;eUi2H#Ce5I=4uN-v(l z-SH9F(KL-1m2Ae+i<cS_f3PFQKtRjSR0%0LOAQ>23{YI=^3o|G_*Km(`sDv!5Yk zZFdy=^UWL*7;u91-{%NB65}v`n;U<^XD`n45m?tXp_6B6*vI5oPN(bc1rd{(x?srP zkH1$YlNKFuQrk0`IyC%*UxrdN?Zj?UzuAy4>1YI(UZ|2sDOY)$&D-GnvqsEW-Nfqe zI>ySkKjj>_xv=t@H(gblz_vH+MAbGQ>UIAsd+M7y8T-4I@?|yY+`%q7C_D$=PEtYt zJ(q#K{)V~6JqK}XXTim}-EhvP8kRlY$F5T3ksB`#vFFKaw!XOl?pV(vr_LOqr5SGI zKj-a`%UYu38BN$!#%KRtp2QEh#@!jX&eZ#h$+TprBQxvt55D-eQ}E!7Cn_?bQ2cux z=try4sIj$pRPq{5T&6|BrMj^t%!qC6Qse#mxS4qsZwftPhG=WN3_QOkg6s2TWO3aJ zx@=M+n6{bXt&>J{N})F#a2vqH5g%yK5oHe*7P67gMsQx?8Ft7}4fFS@(fQik9B%X& z-Lj|+6#koob(~8ud$$9ue^iWS?wYV8DxOj@4VR8RWRL4_fowTbC=@P6@yaC-r7uZB zKb^H&_d^A4%u8dvW_ZxR&2sz#$LH0fQ=eI&o9mQ|X#+bS$VjQpqT2koPE;JRTy zIQLG0Ik%@#|DQ{VmIZ;US0w4?UDIg%88PxQ0(9>D$|^H`(QqY!nr0kv#ZF=vt{Cbvz6(*vt8zGH-G z-THuOypWH|w>~g&`^s3MVGn$g-hqNX>NMko2oqfu#oUt@rcPP1OjeN)KDT-VnVX+6 z{$AtO-%d5c)-PW`;n)<`%G(A76yNbe|DMNv0onZS4<+p4BpG^!CkjT7i&$;pN?3Z$ z4a$yRhWd}~;Jr5jwcR)laE35GO1cTVq>A|N@5`}*RVHs<*F*RlY0~^@BOcFg!Rr$~ zGK*TK!2AR~@UvQkzd24JJ}bh~Kf!SFZxoxkGYC!6y+N_JAHPpr4njo|L|3f>QqMTU zmaPsX+DL^uB;CSY-yPYR0>@E@F9yeVJ79&c9`0Sg4%>l3r?G?sG$9FODQH9`^JsaN~LFQW79GLo3 z50~Y#5Ov!bCcm9W|MSQJWsl9wp{fApPAAv(zcJ2i%hQB;(H8W|S!41Psxa6{mC=}> zL8OyUV$#(Mtn8gmXsaxR1IMlLvZWCoPv<QK&N%Vn5ZTMQ+~&CGo(tkWuYVre;>geFC2kdnp0b^HZ0DR$I!Y6 zZmu9kyZ#=+$*If0|8XDR=RzlTWe-?SA!&@MQXULyRDqOI4z}cE(Vq(PWa)-Yl$WH0 z8PVm8s+uj=?|X&AV{e(w1to0Nw0LeD@et!b$$@cPA8Z`C3^JzIaqxy5JXd-IS6c!Y z(c|hQ{-rpvs5t>SHwUmw+6QXSN#a0%GoH>~Po_wt+AmMUcZw(aoIa}g&7N@nAHlNerb2zO3<$$Ip76N#z&$^5)qxZ7kgotiJs zmi!oJww{_v-*=y7rsyk?59>Jg!2w0$x#TQ9m;HdBgB)?T^+(ul#BuyjE~HAUE)vU= zr*Y479ccG?!}*$^HR zu%b@Zdc3YDe~>X`vAwqfLgk%!+SS9j`olrg(BPQsocotrtj4w~38*RFh+}_TFfkyD zy>=&y22p!t`DHPqAjgu?j9W;?c6gJ!mvreDe`%^9^Alcu zQQ&>^nTnssgy`ERd&%uZ&Lr`j8v3jpfh^T*{3$wy6H=Fu)aGOe+uaQ+8h;u4^IMpL zo3@1E_*|1Kwlj7+_fwa}C;5}VYvZ#^$Elyf3Ic*BnSB~-@ZIu6c3`FpHJJC7jaiaO ztb2S&$Cfd6w$yxj<4z3z_DF)jc~2p~q7_;!3@DQ_h_Q09WMf`7-B;Sf2LFk|;#1q0 zqbB~KYI7a$U!G2F5}nxcNCmpo)ttSc z=+#=ZTtzb&6enFaqL9SUj^2x6AV3 zMT-VKbTpk;Ht#G%eiy|`6F zJ3A&}Yi$DVxy)S;gdT$57Y15-g)uH}A#>wR7IQ;;F{`v-h`Fn)Pj>&dB*N1BKsIC< zMn6vE=4Elj-q9ZOpOiA;%P*i!P6s4iKLWE1?PyW)J?8y_m#BO@kBvIAk5%KmC+A9h z`HL!L!8xZ0+Jl=QI3ocwY{dANLXyb=Ep^r)bqaA-{#?K2-71`a_a`eeAVK=CI6%tQ zov3ujiQXwzX0F?uM`e2r@X<_Rey(?74mO@)*FSKl7ft@++mK8P1){vc>U3N;$b0 zUP15K+u|21;H{eL&a%t-2)ZT z?fB*k$L=5UVx2AipHVi)!(~=Xh4y@7znOnzufgYF!Mjax3@-OzYkb6tpR`oqn$oXbGE zI}S4+@fpLA1N5Dy06nQao0NcL5Mr2I7Gy;>S#twT`%(dOFn~5V>{#@X|!H{pb@>6d+>JuGGWLvNjQG` z1S)D(f^FGtHbVG2W_SppeBDNj)tpMAMY(MLEDtmu>|hr-eL@gBhRNQZF!$&Sc4MY4 z-8ua|bDg_etdUft*G2QOtZ63^zIB{8X1q8{W<~W{?mH_$fvYIBmthysL_J3*k6uACu{{nU8VqzES?$i}73r^D$%DzYT8(n-Gig z+Yr$p%bq!MkQN3QkS~#nblJcS{sb8}T6t|fohVfVJ;Ls2b;AJ?Kdomi13$7?#8=Rl z-fz+H#1r=TOGOyHb(J6S*om%wJh}dNYdJbA?xAOU-^0#cGgM!?8_-Ib4t&20O)07P z%J?-S*>DUs-iAW|h&-L=CJfv3F5?z=ZDL@`W%M{sym8(pUgyIKX6{1^2v_hxxu)&3 zG~xlOd!0nZylDPbdm*xOxP@Q0Dv3!le*w+RTSnN&lj*X*h)bk?vQHP*z_)3WxEVxe*l$L(2H zze;r(X}Mg)Sguf_uC^Z;jm8kN2PMcuyhM22cSxDRUhsOXM11qN!`Exu>B*pZWUA0* z7}uYOZ-e)e>ON({<42QIr5`Z&KpH%|B1lH|M!|*aCAeW~54;x1BO>#)AvwMm)Ag&c z&LSCI)05$i!*sg#_X47D=R1m7E&@0h%gskc$hUds#Qi}Z&K2fzhY!;Ur?&xXyY)Q( zFYDQsx~nAd)O1o}EKCZU@3G?(j&S<9J(%-7jJ}IH1WLgr5T2(Eb>>UR%>hB$wnC9o zMUD;m#g_aSm$h1SQJd~GK205{EYa3DhHf^UtmC9=y6CADWs3F5bM`vD(Xp96-q(!0 zMKfs5yD>JTF@TO8O|736XNiCE=0U{~Lozuag}l+-hF}^%qva61OtwQnlm`vlSj_op zMPSNSF(T)21lz@s^?oHxPY;Qc_Z!{FtMrp753}RZEmqT|je)P<7^%6vsJA5(7)eEZfGhFl*h#wBrxteo_YQ8aj>h9$KK<#98PsE% zJ$)ILf%~%t@xbDz_@%-h+>Ot%-B(Z0my*Zu!C5Zn(|MP9(zgW5=ZiqYD+>%(wjlR! z?gFPpdPGRik?LjD@uNmFpfLUiJbENT@7bnn~}4I`H4kuqH~SU+~}YHSiIxK>nSJDBP3?Ix4lG(D}?dJ^4LE98Jcn z>2KJ|jzP@2twKrzuEM_D#od{%DnPo)bSq(4K5CFv3yW#pJN}S6JxAqh00&IRA?-uHeoo_wMc|uGbgRJBuxtMq73A zcEd&(?$f~O>x-gc5b z@<0LSIh=++hX@X5U4eiv2(5B@bXi9uzU1}?#3LR&15`1xEP>;24C4Cm7;1WK2&7G~ z;Pho%xS4J-&nI;%t9{oBy4y`~{_6p3h8u9CC=m>7Y{*S}Rg^cniVL%1n1ByW>`Sdn zjAU*&_4xOU9n7nRquwXD?1Ujtd3iR@V6^BOxh^cD*0g4`2fHmxooIS|W^)Ixft}+T z*gj93xjrinXIGcAi*0L|*tl4PcijG5&y(%F^O8v|N=0ey^)SnLH$6L+h&8??&|=+$ zmx^@ZxwZrv@buwioisgoLlKp78Zb*So{gxO#66e4;F0}e?DH?>>96>Y*YHW4Hp^Qv z{FiBP>*id}1v?#{n(Gl$J3koaa;Im`pMY3*9oU@_&Ku+$uyo##e4lguQ}|oo>&wK87jeyQ&A3j z=Gu`*=g+Wxi%sCheR1-nv=p){;y}R8n@Dw>#gM7xFuafl_8&cYiAAwAu&oN_bDmGf zE@u+D_by{|#+P-N5&#xqZZtOO6&PMTO%*mCgfQ(57}Fv}8Z0=LVD}kpHH zESZ;;M;(X^DcYV%TsJzC#u#~4qKtA5P7Ct<=MqwV&Xn+tW>5{=&AdiCEuy*VE$-U! z1OsP^zzG9=I=8Z!F7lTl#$B33x$G!RNQlI$i80_9znVN`bJ#Ko!itW&a{GbDIPH}u zraSvm-=G-cD948>(IpUFEJOTeoyCYvIplMuGk$m%1`eZu>*mfUC5;7WKBz&C7JcD~ ztq`G4U0drUCODD7-A?rES_P^t6a<}lYM>|WNPo<(0Gp%z_G)o&JErwWMPxDDJo>gqdH%ktTFCnMCGz!)ih0zOP1~6gQ9^+EdAsyn4v-iQsbB+*vu%oHX7OWSknBnMh?L z`k+Rfd@4JRhMy8h;+OwOS40-ZH|eulqXJx~+mdv+^ddXZ3{}Zhw61MBZFs`%cOT?4 z)gE#1rJwWO9?gXZUtH+5ueTuL%OyrY$AkEA@4OK&W%@^bEy!~Bvm?=;F?jYW)ao>+ zZzR7#xO61@$TXFWo+k%~TdY9!_Xx6nUDngLtfsHKGSDynBCJUkBxd)AAVO!5*;G=_ zOZ~w4COV|)l0ReYrl%*cd4n$XO`Xg*9qeOg{dd>eaZ%T%(wX_$Sy zQH5-12HIqmgJWNE;ZVgr{yptpbiXV?iXQ&Md#~(>*ST$Q&fS_;dAHzmhTCzA{zN0r z#cS86O3m-L!Q-G9(EVP6jenA`eXbFaRvdr{^>L8;F#}h>>0(~4dylg=%pmc3-}zM$ z7jb){3W@!70>*rF*p++Ez}~fjU&jC^ z?udfez_a)6!$Q*8@s2r|G>KL}bEfC->%czK9ng2X0{%Q2U_QDS(yw0^qm-5iW(vH< zKju}q%lMW$f@Hp$JYLN# z!B2l%AfEHXUiAHq>He=+*#tq>?VKO(x+FoAi=41__!4?}y+B2gQD*zjBU>$`*dHkCXX!Pa-|!cb(ZsmrwrqU-CS;M%pCW%&Y|(nJK&08Az_m)K)v&I zSo_zAJR0P<2UE7;@pA&y@<0^(n12A?^AZ_>oXfbu{2ZmzMCmnW2eMV`JX*pwR4qzn zj!CIe?dsjU`u;p-V$l;eQRN65k8dOiLPGSnrWgAxL>gaeoq~?q74Y8$ z8!E|VM@qNGvZhX7P}t0tC<=&^!JzN_JKZVp;Mpm--4P8<2AY(;yqtKNZGp0D$fzgl z5q;fQb~?v17)iLo9O>H*=GSJDrs=gfIH3rZ-&25HkHlFmiTM=vzhjwP1&lh94o=;r zY{KgXlqnG=i4Iz15|7VuZUaE-H$xYA1c9cMBg53}gI0NpK8+D*A~S#ko-k zb_>sC2@t^s5u9=I9jn?&A)rKpUeC1TQ{%&!ekC+Tl%n~yL^vHO z1v9G;@&@0P;eWZusF&+ym^X!Ewx4w)_v|=kvtB&bZV`c;Pf7HFeIedGE=5x+b?Fn? zTWIupCkf}yvupFD@VfgyNDlRd@U}X<_hBhrExZ}K*ImXpe+9^-j0`$u42*1jg_( zmpvO^9L8Ky-Uhmw+2FQ13MaPCrr$puLf_Q|+?i0EMs4^<=>)ZYV$$_2C>s%>Z#g#QE88TH zbpH!B2X^9wE=OQt79+OoBj-AtPIuMr1T&8VfYl=Cv0)9nE!vT`Jjh}9p1%sK0~XO8 zvqrhQUJt%Dslj{yxsu{#_gD|h8E{YM43ffoxX>K%rS(TlKDYu;yIf;!rAAPsb0U@S zImydf+0Qq4;tI~M*U-g6`TQ%z!SvU$26*J}LmF1}^6pLbWLG3`{#f+_*ux)ULe;n) zbEgo0@RBXv+Y(4VS$Kh;ye`#yyoiW8>?FAbc1#Va^%s4d)(BR?@_QAd~Fm8Wo_2{oJ*;123A6@I=+`?rd=nq2^HQ?q zkRjRcEKlQ9=A(CbEgXw;L4$%q=+lXT*6bi+96psyNLXKAtdfX_w&y^&h#$Ip{$)Of zdf`4jV=~80j1;tQVhbNhptJ&|l}(bM`jBa+05 z%T~{wJP#}~j7gVo4LVP(f;w?0dUT6B`S$HRW1ngT_EDboIyc|di}X~&!U<29<)B1w zJFH_Po>t;M;iDYe)SWcPi&2M!w_vkU5^`JQNV({H==gXEozEs>)P0A0>@WAE8>shMEet@V6S4!JV&iO~Gu3<|c^@YVCvOG8$kJQ*`+^jvODu;? z$40o{gKyCOQl9p3dpR#Tz?{2#sm@Ic;egd5Lc>aPk^OxlNbOmgQ%ZiV(3Uc)C>%C0f>Udh}S3tV&2EyreC2XAWY6#`{k6Ik}MqxoViq0H|fVXi> zPS0QXZj^|dEWbj&j0mjV?S$U4k<1tQ99Y{VO*{KESy!^Hmod~<#M4nFos^@HyRRXip73x4a|gqmbzbT)2dV&2xngJC7gVN;p# zlgpV4JHBGc?ti#n_7Z7cX3Q4ENWhkI2lAsdiu7Hsq&CKr9ThDZI9!q z`;S8L%pTub&wls10SB~wK-MpouO6>VH~bSPJ*pFlez!0wFM9>Y zgkl+!_T#)gLdDGK&*GqUIUN#4hWH7AfAMI(3^~YU`V0n+qkO0vYnu^=x`I}?Jn1V& zDi7lo{!*)mn2j(HZVT=HTz|{`1~k7eg>VO1c1b}#{M0pvKgK7(D0Uj^y@{deNdgpC zd_d(JIgB26_V{(x8a_1p2y1L6XnVd)p?M`Cf)x%2z?_L*`U-uOA$f zSb|o&t#NGaOhzxrg4%8@$8zVLr0{1oyX@IiDr*0dbHChVwp~lcx)Bx9cJeO#OO&QO zD>JhHXeVRfx|F_^e8(J{yo3DV=D~&6)QHom-}rEWD*4G9V7Bi~#W9On_21cFO!4It z?3sN!G~mSuJdf0&vR+;AWMdf^a$WN%lWg2xB?=MSy{UDwDZg*GCrsHK4=Ib~V9br< z1xO8ZE^Px?s;NRWFL&_$2I^7j=6hCEU5OeUlcTR69-s+amgm%L4|vyL!^;mn3Yx_= z?BSkpT>8KX)~Rlwan~HlDd!@PoTiJ{XaSx*c%At&G|p4+9S6N3ZXe!bMeqEqA{BuS zM0rLuERBw)Vjjli&OQmKJi8wT4*!OxCuYRLLV#{cyF^xI++|+nJJIsAc;>X$QEQ8j zTz_KHO=b_@mF;jE27`}$l+X_Vk;Gpxq4*zT@u36U6=%?c8s&UCO&ro zA;Fg-(4S);h%S;LGabUgHTMK~2d>5S2P8>F!~mqP4x!DXKXC208c^%(heyQ|AnbS} z?7b}kH%-8=-FVQDpP^O(2hkjBvnh zXnz~aap5cZGoBm5PO)(|AzqUjetm)lBW6_ZkR*{+bENyEPSJxNuc7No7f5r=8v_kH zF1MxuM<2CdRu6?I-<9b3zB^FBITfx?IfFHo)fj84Mn#6X3h0#g_=~17I$t<9R&9&Z*5*mt+xDyg5$F=S8g>`l=PuVE$1;x z--7Z_SAe;_1#P~gO72XvU_Nvtf!^IkXfmoxY!|KOAGyT9g%|eB_C{gkYs@80kwIX7 zVh~2&WI}*c6==_%Kzi*)(S0(vo8Bfwh6aYHyK)}yJhyfEeq0ado{oV<&9g!6;4HY* z%i=eC3-UM3i@ou$6(cp|X-(iOu-BSQlfLaFSE@cUn+Kd(YP$`#tY1K_o8R%zNqNDx z!@ZblDntHw=Hfk#LWokRf%U0(Acq>!;eaJv$6_vz*Dng4@kzKgKa38Ji;`8Jec`*; zRsIG;7v9W;uOK9`tj_!bx5KqnguEl?SaI(%6q;)UcK?>pXxCvhV(lbFRZL zw2*yhyPL_3;X1iek72<27h`Pr7D5j>=BCr#)K>0G4e=?+XOJ?yEp{b_kBgoq!bK>1Y$p`H397;ppRFprShb z+b8CcmESBdv*Zx^X$zC^7ZZt8%O^&?G!vh^Gr=yS6Cmj*OQyfiMC;NdR_66~91Zz_ z-eqrC%5P zPaL2<^5HsK^N8EkwnpRXW1gU{7K+io&j5o>tmYfU4+);u+d^iO`BO&mzz9diUgL@9 z^}S%W;s%Vo?SUqeQ|Qc~53prw4{G*Bvepeo@V$-mR?Rmf4_hy=N&V9i=FTK5PP?-2 z%MHjaSr$BFpTP~u4#?+A;Em20=FX}CnDzA>rZgU8;#>tePKYs`J1YjZpOE1(FXGv- z17AU*@dMJ*ai&+G3AofLL*`wD^$M~yG+LYOFCJXV4>ZC`p> zb0zud8c4r$_y4}gPKeo14K9Bh!ASQ9J{5Okm#_N8Bz5UfO{-PJDT4cZzNg^T{3uep z{VF&|{9sNCWU&3NW@J9+hr3vN5O=7Y!KESH@O!N1gM#F5;<(ZF}hl+kb{??=9`DC=(_^Or>B}p-6Dur%M$D3X6RVoE;vQ*23pX< zKWgBTWltr3yW`EnGuSKVo(TqUz9fMqg}@>GhgOzBO~ZOO9=}Y5NT5`}UeI zv`~{4t2Y2@pJRoBjX=(d|{c@?6cFjUow2yYC=^zS>#d(SO!f7L8v z`zoDe#hcL^suEQHRxg{BVo#3JRiy23EOz^v!p|eu5v47_uJk%cYYb`ir z3P8-1NBE3)lhi#9=X#HGdEe#-!KoTx5{le?n2fr+(YgA2nDW44kni@RKh5tV>-3)4#O3UEU%$al@oVcKkFMIRPDVlw8a@&^W&qkQ*d&QIotHvhIW+O@~=Nr*4a-Y7>xFXqFh z{d+OAt%At=&Lk6hO1LV>vHRBD!iB%$cs@Z|#2_jLel2`}o?5D?zatXb z@&;h&strDom%-nG2qLe!j^Jf+B0B#B@tNq!KlgYt2~y;^H7@yROI%QC(`1n0zDqxT zzQkaEHF9+EBMe;aQF|j}F+I~aicN}w^euNk+dm#ncZQ@ApIPeE?{F==k)1|9+1qQapK8D+NGcCSxh2RTMAjP zKM(nR%U4htwjAbt?O+3+JjXw)v*{5XO>*PTFl*0c$Ile62L3nhoh4bqI;wE9LXOS6 zsxAU2a64!D58PbZdiZ z;Lii=g=+p}iB&%oG!CFJR5G4p+-%)v8vS#T5OKPM{Cl>7VN2}TF6k2V?$_fVAJCzu zD?8ZDB~wWH$RKu_oky#0&2T$;4kL`C7@s+jsrks|tCx?-_O#fJon&m%t$B-6J{4AqF6OlGC2)7D~H_DE$7 znzEkYa>S3wZ8*exa4QQmf3)L{?HN$NQH4gd&7^wZj%o1@0 z`=*aSOKR|y#Z>rlE)`^Ec*7O*6YMtCM`*hXQK$AizL({Jkgpp0#&|RSZT&p)u3~m% zxdhp>Efb2pWbj8zGP-GT_hvUOGTxnuCwkv7lkcqHm}4jK`87*A;m$N_2d%8pxHd_8 ze;u{f?q~PhHpZ+}BlcsUG}aD&hV9Vs)!W9#c$_FvjNX8YNNbnEdW5TAAr2G-rj!!bd4 zdVwU=IOyXg;cqyQ@|BxI71(bdZ$*{P#dLhj6%0N}xm`&xsd=sD`6zFES`7Zx)dR4Z?U^`GbLO2{4v3FaPhwji;eZjnXE*rxr300({D3( zFBs#&i5%ZKnPb>nhYDx1K#$i(H211{^ZTVzVnU(DKD;Hq^Nvo-NN}({;+&+JWi(7q;7JO>YvuzP1U* z-mS&Zl*gcXYA&`dAAv&&=@@t5CL~LmKv}yMv7BpzB3bfi?|hPMh-$$htr*-OP!1Nw zONnNQ3lV-IND4h0SdZlSn6kDE`etk*`u5qp!s?eWHou42tr0;2RBz$9MHc39uHOTd z7VxCZ5)NHF$=fFK4ouP$c!%asq@J?NiCM!zj9UBvI}dV{q1I&x3yU73g;)_<6MO#R%tNraxgGl$Df0DH0}Jc&39IXd zFP8q~$(>Up?FQ{|e8W=WE0Kjb91QL@Vsv}sA$stjD|y3Z!}g1n;mN*r@GV#p(C$97 zB(4QRf6jy)GdX&0zdlv79YNQ&ElkQM87^12ob>Bn;7R9Agc&;xNWMrA+9X-x37r7O zotxoYk(f#yK62i*oj|VN?1#q@cGQ&X>;I<^kFlRt;K${IU@_=I0>jTif?*wVS=pBL zbQ8u35@WwLpaf)lbKuWGcPeHY%YVG?6uvlc3qs@aP+5F7F5XrTd#9hGCwIrA_f!k2 z8C%XIK9PoV?)Pw?<|;aC`zq4UUg0MO!19xF8hOcd8IpxtDn$ zVNSw+h|)euDe^|H1M7j?-?ww8dE(EztIiP-(F5jrEM zlJ6+M^bdC7e&NH^!|(wHe$2v-eVTBxRFc@&QTQ5F3Ep;lz&?_@LuLl^VhyWMFISt5 zxt4-w`xm0F@GoYpz6yRneS+Jn)yVkZAAZ9`9&tz&gZKzJTpB$KKTiC|{>?4q>&DwK z76u~p38T#DyA&`(j)Y7rkR=Wl$6@`F1rSv74zz*}Li#})F#I*0ENI(^U!Sic6?bLv z_~j=&E&X)lH{F5uEo;e~s~q3|#xstg9ZjY@vZdc#%wS*M0xZ_mp`8K^sC#RK*>Ot~ z{?=c@_CXo!w}YhlTF`xqOX0d-H41eXu$!GLaPKY?bgFpE z3{3b7=R8)DLnEW;Jtm9(%wJslK9E_XFa=7dPGr7z`j9V{jWG3UBh#HI2CrW2VZx%^ zNv)kV6%O(z1^+1S|LKOdt9OzuM_EydSmR~WqlLkMyAg`m+a_En=i z@6x{no>xpRK5$Zom0y&|+T(j^h2Vp0gw^t4 zX2vO$VXARiz*Z`;@*4UKPv%$2C*wjf0qXzwJ+KPyq-wzTk>2Hl*fk7XApliXG-JmRlTo_X^y1p%8uD zj%>++S}6DG25Y}eT=!j>(4YNy>=F^Uka`c5z z7#^RM4E>h`>CyL9xI5|)lV|)GRBdEH`l1mOX{5qLu3H8|Kim(nrL|E6Y7nh zMfqiYu+74WRXR)92f60#hl}a(t80B^HARPPkyC)pQbUbA-JYAx2~?3ON<78gUjCBC7>BZ-D(6o_*Q^7j13G z3oa|Ha>s#eiCK@YjyZw(*Gl}Fkxv|FCgCif2)bQ?g_P07keGNAf~|9T-j>C9c#jxN z_5K4%OM&v=%pw0?#NfJ}whZ~W8)q6C;uc#^^6lAY&Izx|k1`9zOG}@#`4j5U&4lyV z$ePgqs(RT5|0tMq%?TW{WJpAY6zz9;4}}u~snhf&+|ELlR(~hBKQSC^8&0rewf~?Y zRv2O}e&Va*nIJE$N@gzj3b~59WH8l^eoH+JJZBFQxO^S;J8*~&DRNn&_e)U7p%)bH zYCyL307RT&x&KQyDysMt1Ev09`FJe+PT_(GZ!*|{eXp4kmlxRJR*M^bb*UcbQ)1qm zpt))uk$dWZgVENs=fYFC+k90fY7> z(cXoc#Ls>M-L?_PBhH(!I=C9P_-8S9N8a;Q)Ljphjj{dtlz!HgK6JN{Z~W zn8hRaV3kHcvoW_0H*WNzEx%XMWd+t$+jlZ5a($fpm-6X`fSH7KO@VU%GcX)>gc*vR z$a!~-z)?e&`n}=0Xdchdd~^;Q=hcbAlY7_)+&g~bzgp&pqCVa5PLlcxjGG{ttX&ty&Ro& zNC3(mQ~0ksKj8V3*HBR=6TFjRn7g6*jMA&?5aIS2E3RL{?P0of%Gf`Cg0L=D*&f8p zQ64x)`UY%%Yza5dmEmV6Wsb*HfR@(cbn(I#j{o=_tQVih9*)&iE%E^u9OJwRo<)qr zml~WjqldLWr%I-;kSC`#(}_l!5LnhYu;*s|2iwZ+iI9C0cIJCvkgYroJ~4zPYkK&e z>kh+ZZda|gFAo3aD3kL~1TpWDH$E;;08LAV)=IZvb=C;`lRKZ?mmI(;cAP7@YAp#K zn1j1tUV^`oD@b~E3_WPVgBgCd)LJ~0TwL{&eY|6YwH%#7VooTLZMTK+)4G@Jt3@qL zX~$zUZV(_kVtF8ToAX?mg%O>)tyGMghn`6_AYXMDFmFo7H{*+GL~<T?<)5_ds^R1y(*z8!w+(&+^Y*yx6IXVP(w@+hM7-RsWLc< zKP-0R>NV>~!Hm0%z;iz6pN-{x>AuFy7*8VoRZSpixg5f_on)KT&1ms@Mc6#$2o7DV zVMh~xvAG$wyh7$bj9DU#mo+BQn$cGFKS|E}8MTu7%j7ZH^$%DRF^-+E@fos~(=kC? znJ(<8hmdVIP(s3pwr;Z~XZZ?*x8*J@G1&m>wLXxrS&2=Xvxc7AZ9x~~N0hbEq^67i zK%liCd1-qd?rD}XC!|Cmnfo4z&+=qv9@s$TvtGhFsZ8FqdNopAv7fC~?*P8g8rrcd zj%*J-1CQ6*n^&erq(Fs}})LGQ*Z^!_;^TE6l*E)0-{+2?;U@A>5%FS&{J;QYU~mYj>~ zh zDeJHbys0EPMiF|aS)wGrf*(G!75dxd=n;!FjbQhs>c(`?;O3 z-6C?Ne-Rw{6%V^^+Y<8ycHkBxNSD8kgU+xn{`_2PI-0<;of+BawvEpuDF!ovhrTh# zS56{l)kWx$0&8aRs3_HvUj$l%*I`<|HI81gB1N26`()8%s`F3}Z}qPsJBN4jS~|C) zc=3H0HqM9qt?G1#NIvvw_3(W^7D8UcEI2%S3HA5Qz@#O`jQ!m`_=Il+$~~Io`zt>> z@L(b%bv6!-TtlJzu`bcCxlJCFpThFE|4`>^Ck)uWW9Do~!tM_m2+S$g?FmVd{H&GiWsZ+yWA z^1X~pSUenMli=rF9zD795Dv;pk+&B^(Av3*JNFu~B6XjboeCSt9*1?9X=}rnE!<+C zs1^@dI^4Itr-)TLUCZ1$>Q4q%{DJZUZpM)ifvuhtkDN$Hcb9T5vMxr?|51T=cB*(W za5*fGt;59WK8(>TTlViNXWBS(8kxXxVoD2A>1n|ld>3NGlT+1ZWy?;odpPd0@i751 zFfkTeYmUR|>4CT*TNb@D5^n?h1M-?>3 zh0x#obl^)_KQk&Z8M6e+Nc8PRbg=&geCY9q3WF`M;pG)}hAxlpKjKF}U(G}hOY&raPMWt3>+ZjU_O^Y5*Gl|WlN?AvWeFxu~pxOVAl6#R3SWqNgE&MsfaCrv}6CUS>yz+=Q^-e(U+O5F(<$T zEvPi=!VfD{srd40e&{-&atkx z4{`ba$+TZi78k8L2+x1ju>H!~FmYE5bLW;C99SVsgeJz}X73uT@EgK)|5Bk6OPLN}iCe~IB=8kzG6 zLb&y_Gb`2A&QwfnU@sns#4cq8GE=w`?!P|;U*zQBqE;FxtG>k?_m$|t@o_xj{$SYF zV#egxThv(b42)%Eh{OXO__z|Kf1x?vwaO={d}eP@)ZI_j8#Md*hYdBM!SlS7ZAZTY33YtMsZWWxZ8sWjBi!OQbSidj2pU?g*2>(GJGv^*Vf470I+z8qp~S z%b?+*AGD5XkdMwY=!anB4ZPh%HS>$$-kB=!yi|&t)MQAcv;ZFVnTD%h8ndS13QXFx zuZ)Y80`cb$-<+s0fw*}+hYzPlfjIqP6{g*0)_3$_hTjdgN8~8pVv>N*9)(GDizvNx z1fb*sZr!Ad?^IQB$!W% zdD>(Cp{tL;ZtOZ`oc+8{|q{m;UisZBUh=Lf@jyFgb;FZ*eB0`$);hKCVJwBX@U5~#qqI)v*JKsbrF1!8{%JUtHO)!2EZ;Nl-4eW-D=4Yb$1%r9M#6I zYnQ;mn#;CcG^Vxl$C!1hs`O{tDKxrv1{MuH<^2wFWY)dT#(&Zr+nM8^XKgCSsmj3Dj^qwXbA5Kg=H-m_HIY0-sPt#Nr9yR?|2W$1; zV%);#SQ-|?tmK&S;dd!3{W%dd-#$lk|Fw*(;cbSWkk9e(dUlzHM{cg^rWk)sy zST@(})VRX2i`;Rwj1!#SzlXNvJp#|cXQ2JbmU#Y?z^G&GY~NS``Sx==9SllmeOAV? z-5ahz)ZZy=L!CROE)Rfnp?V~4K{b`wxYLcE7^6kTTr+u812kUV5m0+ zKB>i!w#CL|V37^yQ(I3{-th1WoM-E3NVN1WVp5hBy`e0Xnuaz6IyIedT4F&XB` z@rqx6fnoO?V)FPrxmvGJ4%T!+W1a=%a6WQjnpmKJoU-ZzfCI6zQds zY|^%Q5q`6M#AfT|u_`(iIMeePI^Vww|0KVHk-9(6uk{MP-h7IY?A}Z)_UciWjsuKf z^8uK=dNmx}^cy~YN&#)DL-duD1`NKsijdsLgtD@<(Q6i&?&V2a0v^HM^$)q>IKK}%REPI&? z6XIcAZY}Zq=S38?FXQP3F?vh?G@13rgtQeb!^`LQk&W|iGvEGYlGF{gxcNaoP0E`F z9p64e%k*1dX|fCYmzmJy*h8dhWe%NZssK?Q%c1-HEcRc30zR}_LvHPoC+i-cfX*Yn z&{OLS4t|QTu(XlC&h<*wbw=8KHQ4Qb4JD$48YO&3 z)yWTVgxhI`(_kh@^9z5ooemicDq#8?3UIW=n*ON11xjZ-Fv++d2Nd2TS@{j^LiSfW&g@$dcQd2)liP5^_yf3kgJXPi zeZPeToy>X8r8P%Dh1GNZ4|b2M1GELhfq_D3xpom0V^bKbuSv6hEL}$SE>FY@U+yvU z5BDq5zU4?~ik704!C~TJE{W?rI+=S#3vq3i zAKBeoNmcp=Fh4b)c{8a9I}@YWCI5}_W=S6d|9uzW)np^$=PJx-JhmWTp0zLm+bh{n zszNg!{>RL*FJnk-8kPQ;0~+`CvL_E{(W75&3I7y_bBrpa^Rk7h*9kpRX7m+yxh2GHK31`UNRY_zLuR`Wh4HCC6jrOIF zqiNJ}vRxw_zG}+i$9`il)40b|KORr)3UiskY6EH%>jYBf_u);mHWB{Q4VPn9keW#k zd9%c(lTo2Ss1gaI*EE)~YOP_QVey*zUgu2~L_TH=@~;rRuZyV5Urlt7Dgo8uYTW$L zqQ>dV1!8+_3H@^YG}~M`9l8Ye(-mh^`KfEtah_EI6fcUQ3R=Uc9ln*@aoWKI{!;2+ zznQ|#B$9c3oNY2WMHLH@*vt1$F?}Ce*jT}Zq}VPJQn}~uaFi?EvF0%q%5kGBt!$~! zq^+dk52Z)8bjFza%5|}g8&*TW_v@gszJ>ix01j*T!@v0wRQ>if z(D{3W5%9jqZr!cO)}J22q!0Sw6&VBa>-}I+eJ|`e)ByFH0_53YBlt<4p-sIP=%{D0QZlQURn_6l zXDf9QCV2vl^vu8^rw*1W&!M9_r|@Y?Kl+Arvf~r;c%oxB`O0%8;k42f?8~^!|HM0l zwjqf)@MMhn9ioe*xeZpvZJ=t8lu5ni99rC01$W!UvEPLc7Y#XQg7Oh~xW5Pkh9$_9 zy?xlf(ts9c-sg{8ILuZgC{ky`Z`CgT@;D+ljd>`Oh?eY3V$it;ZC+T=s6=hLCcBsG z`A>yS>&_t=OlC6mA7HHI9*8c9CSSf!AU21Q_#O4ac3HGcq^EF<)*!*|g&wbTlpWr9)Yl0V6GZ5a zPrI>r(MfV5K%C5-e;abE+?nqIYIHqiQ9WTJsy1~)vx_kqZT!i+oza8^JL)(O zkRaV39k$Eq;Z0X?j%yq#zuL#TyqkjEbGmse z#~LwCFbBz#4fI64H|b`d;+7#NI_Fa`&Anhx<-I?0{=3a&Zsm`9mMfKAnA=bNz$|Dq4TB7AbN2wX*XMixf^|eH#wW* z>*0AfcGZf=x zet>O;J2>s*vUk-Xu;!;46_+g{&T|dn!pCmjdowNC$uGu>n@p&m*KR7@#m$%Aom19lvNu5a^T-<47D4XfijU%v@g{2K7Lsy^vb8i&YhNtB)cgJZ=%;5R&Zg-cRr zl7C*GAxxkRL31f+BJC`XCk7Jwk-a<;iZH3NWg5+`NKEY>A>xjcq0al^fgxop&4l}Ll(4)c%&Ff96 zx#K8K^ZJ42pO3*Pe~?YrdIKysCw;HC9k(wKp$CQDLg1CtaA|og(>cB#!z7<$L~;b* zpHajX%eVNr7%tDgY8A0gQ=yfm5PWlIouH!C7DM~w_e z$C2sBkK;;hF?vcl6}B`D!1sPVDz82Reu)n74OdSfYO35$VZ?+4eNCYoCRQ=Y&c$%a z-h;?bo==4TO~Q(iyEwRzJFA(<(s@h1F)TNiPkTQEhpP)P$L9EDP^SUWr;SfpZRCnRK04KU!S)6Kq zJ`Q*9dUmLww7jS4x6(zVCkNR8qf_PF>;9MCk!^$TLKa8(uI2Q7fTtz5=rb1BrV zRD$f?4rFX_2Ajn;z07?DqSbXyDN7~H|X`gic6lRng1G+}D~ahSbVj&IKs#IrrpIImzo8!C_j z6MjsfH4o&7(&?`l!gYP?mUls^`D|>BkH=@iw&b`*28N!}BX2iOpr@i+;I`9MUVQ#e zR!)(d!7o!MUN`=-`IaVhSRtQ@aHF80uR&X%1Ftc*nkVkp$$SM}a(!5H-#h&3g^s1{ovvyW24c(s)HM0%ql;VZR zyB5c}uiwGcU+QG`8Wj>}$U z{Omj?gAOqfyW${v_7S@F>P7mxjYp5Xc?b7;+{w9$H4qgZ%D8U5NcaCRr!&K|*_|in zQMvdsHgYaQ3!F5l$_@o+6O^U@UH5{mCEnO`fSVytQX_Pr35qgQXnEON@V+vil&(I& zj;HRWp(|eufpvQb`DVEgJN{ENd9ga!Zfb=Jp5jdL z^a2zxNCI~L3EXR5MsH6#$QV!9#xamSGZz=e!qW0h^u+Wh{E(@Jbjsuo=DqEDsOwfE zPiZyZ^?e`M2_3`Qz)5t+vD#S{HQ5l=djfx`HSm_N(j+TZm{T2gE%+{xgWN~AFyh={ z*rhQFuW%ALo|eG;Z(B+GOCGBe+RAY%R#Tj2LDj2Sl(mcJvu-hjzdV$rzb}MkY09|y z_7(Q0qzcHrPb3Fx%I&QI$DTBl5zJzf5Aadur66V2PaxBUt8vQZgRs7)n!iOvfaT4e zhzZ^>ji<$~-6m_PR?()1}t;S@E`9*yEM22Ra-OiXay+fyNb@sDW zBJz(|P`dIE8yu1gSCUddu*r@3x;$c~Z-?nQj=+r=T@55`^>PqaZY| zja~5|4}+$O5o@>qU})zAs*o#O<-vS>Q1Cz zj$bZ^;guBzP#<&w>+3urwDK|La%`4L;j1@QF9cxS>EBGgN*RV~J%${wX{1iz5Q$jD zxxtkj(Lh>)etR{7TiHw~SoI5XZk)xR2Mu8D?hrarAqCeze2?eSPcdn8*Amb79T=ni z6P~P_0525h(Tu_&=0igg@O(cNyzraDem#n9^?*+H^ga zb&=`yghLreKvK#dWMyKh$E;f3jA0(RkRFZW>{{|*y%;HBXVO^(+|Tmp0sXM|@OnWk zBRWAA>Lq5Ov6~7FYV#tKqNQkE=ySX&k*K|kR#E`J)|KO|{4FRldlAI(7vsT#0NDLrh)QwUNte}<@bYU4mc@Az=?BU* zYRwE%C~=4h?Y|3uXUmbW_xCVoSsJ6YYzyCEhA4)g2&!3I-wF<+=Jfe>H^$+R4KUu7 z_+h>wbLQYxkgwRp%}mcDFHZ+wox6$;70jt9mxGmbi6XylyyALf0%Y*jDJXSLVD1_T z)9}HkjI5_CTV|n1#Qe6gX+4C@UMoqjPc6f{Yjqih+iRr7A7O8l?PK3?8M6^7cW}NS zLUI$Ya{H+w+_-8coHpiq#VZ^jBCnTe81SHX2JA>Ee?KnS+``DWm!Rj?=h$*e0#+0X zk-gbJA!yfj>KQ^|T0#ZB4eaMH?1-k~wN8+@`8D3$>&QB~oWN#*d8F#PFIe4s<{y~$ zcd}B|;`C?aMx6d@8+$9+mwP8nC(T>S;rD|B?Dpp)pcGePpZoPU=x5o&OG_>CWLTJ3 z&g0GplblJ{(0%;W>P)s>al8O4(iBUXa36-C1>P`39ca~bo~4Wu$G)c=MI&Uq4DLI6u1-Re)+O> zyK}H3gTYsR4Ith70{T0Q$>iX1_PKW$dHM7g9L?$-3G$j)rMsqIA$Jtb-w1e0G z_%PfPF2#E^nM5n-)BE}b_W@hWz9S>5l_eBtF{>ec3Ng;Y_>js`lMlj^@r?L)Q zb_uww;i122_~2MN*f|^kt#cdM4cDepk&WC=Ro9fPGOq!l@IaWk@h1B+Xg=!2Jz{1R z$iqM|$Hp*orPKDFgWwOJLGr^}&WCP>E%N5j{fouN0Iu{iy={t|!BtS*O^R zb8W2sq6O48QVLCf%hGo92~>vbJa@&qkk~RCoLIk-QBYh&_@Y0U2^Xa>@#6qvDP;r( zx*}xy^>R?lUCVEr;K*A(MH%juXR-CFPg&_4Po8vLE9>d84tnII*ucRh8+nTr=kys)hB2Ug@xC0)t~p?lH|{Q2@Flzn^w+KL%y z8>P>d6^Eiua6YED>p)N2WY$tThYjbR^Pcd>xV!EkTz$NoHFrA*cXTSD$Mqzhk23}7 zFdf>Y5dmZOGjL+O4*4km1>FbS=*6*LU~^21&%dcnTn`xGRYwg{7?lhkJ!ScueLVQ8 zarO41hvu@CwZ;6Son^>LdGUL@7pS(#6SZADklPo4vc=m%GJ?sp*)KJP6)WP91U zLMKsc-c`;sosUY=C*b0=98mtP#~Sukql^XjEe=1AYp)FB)64=qKE(tB?`1Q=;gM+Y z=_->NcN+9l#Od|MH%#S~Cyep)-#9IW&w}Y9lF`+S_BocYO8ygc{&B~~jMvPvjlxth zdLc~HybE?_$q;b19Q!vA5SyEe$47_J)^IsQ=DE=>30dy@{Dq0CZD6|3&Z4g`c*A7j zLPlF^jQzYLk$si@5VvV>z)2qRRITzXJK@7u=*}L6=QcLBvk~dh%Q?9QI)u?+r7-c7_qYuWG|LRyI^gqaOUK8diTKq$18Q)<<9Dem|%W@5Kh!vjJf}e zfVue{%A(wGeuf)KO;aM<4|>oaTrMheNdU*$dItT66CnA<1<=bArEBCVyV_0;E^>|p zXXn>7ze=9M1hu{7@cu#8cjX;8r<_2)N}9s=dv7`a<_kC^HO?3s-vEDo8P3ZnN#=%4 zVr!;0;5$3czajMn(zlmlE$<_Q#rE-%TOWecM|m(jAxk_tR@7quMxGop^Rw+}Ld z_JMsXOg?;o_wsr=bgVoLFDiCX;f0e?^z=-!rzM>jk4+)IZ;udHuOv3At`YRCi&)ZB zj7J@9NXYMaR+MwF^`y_E^H%5)gCALB)X0F?bVx$JmpZNKkfNJbqyo_fC^K9}svoe} z_vS7%{~Tic4lCg4h-I|H$_zinl>zJ2&ia&|!WHw*P_g)}RBd=G>=)Dkhm1%90>VVs zSCI`$ZewngR&ZzR7`$<)2+vGaC#S3gvHMOsBU`{Z4{eg|&-=x|ZOuibTrib($4ZgI z^$Ijrk)e6@Jo=`)6wh1sT3mfI9}s_uWRU<`-7l6;Vs%^ zD3U!HONm5ZA}#OGgx-q{jO*TF(pu$Au5GwTAL!4<+S@=>kiZc%sVA`opj-+si=W*?UNPu^tSWI?cucFV4L=ZBFf!Vzm*i`X0>^O3T z?Xc8>NU@u!@^Auuer_wZ49dkQUu!O_kc~gj%45lFONs>@7&6)i^CJc6{V`RtvXI43 z4MwEQUKIA2CqmNxHSn@KhMoKJ8tW{#lqFL>;nOFdP;#|7^?2IDShcysttJ=x_H7y8 zR5lO8#zbM7?MHTk@#UIwBM;`_nh-SW@`D#g|ANbgXJ}9JA-P& ztHK0^S(8k4i_Ss1lPT?yvL*NP7tydh3*zTh1%EDWVe@W3fQFaV;F!Gy_Xs58&02A~ zTz>~1oGt+mN-jgr0Yj7?5`lt`dtmqD8c_0XWfpmSf`{g5*pi_J*T;nDTv z2KF*H?>%BmW*?x7r^eCC6OzcH3_TJhwI7YPt5JphNpM7G25IY_N$1A?hZi3>H@V|F}il|9~|D) z3mLsB^r(Og{o;NFP^A}xGgsjFp_!mns*bY+oq&Hf7gRRMknE|a;X~I5Ts(0GBVS09 zu9wB|FIa%SNx28D+P&=a0FL_*T15}{mVw)5OSaFN%Tie3asySzdYR4lLdlDqW_(ODx#X4xO+LbX z*Aw}4#BBf{naEMycWP9O>lJ@2O0GE&ZGacDTfv)s2)89Y$(&gLju(LArMknE4;&M# z?FD2kG$J3B^zf0R3q&5ZCilNAr^lRTlU>$xsObJBoR9Ya>Kh*hEwX}sjBuoOW!=oO z-yF}xD1Zj6%mD-0Eb!>O2;*9>QOw_v6md&HXA+qtu3p`I0s9K%{o7b&C_E=@H3M2EdLT>>bdwz)CPcYzkJ(=u(cjI98 zAqCR9+>$wHq(_3^BoUE=ku>ADDcN;yjB)4=Mn&!%Hle+gXtgnPf20&^t0PLX>f&kI zSW_{o}-NphgFWcR055u>&uouNl$&R_*a1%25y%7~qf9E4+x(C9S4u2rM zuW+|=AEd?v(VzS8GF@h^K&~o~3qiqT!?Iyk>G2rKJ+lSrZ_Q9?#_d&urqhscAz0`= znU;PsA}6HkA*Q7Z3`M5*ckC(p zgQb(EqLuOpJJISYY96oz7yBD!lJY%;5YZAExocD`#Nn@10QMn29m} zjj~#9iOdd(+j#m$OSMsOAvoW>wOH9uR8N%O_BV4Uo$9|L+QJ21VHqXel!t{m=TN)1^vSKCcLEJoA~a1u8_;B#rO+Y$gr# z3WJkFYw(4Q4<0R1qb?G~Oy{4SusX|&^UBmf%L8L(&3cOVO5IT0m<(OTvM^XIPmgNu z1U->LSn06`J@S&_lEi7~$;x6>MxS5}cZXVQ8-a7%j-rp0BninaK@06E%#CRd_@*I} z+2TE)UC8wCTK;m~0L3maPt~Wr**sX_piH*Lj4*@$9B|#OUwHEFboyRnJw4ZKO-kO` zpv~G4kc>>g$HV?KPkb%){m%$^>=4GD3dfCeobb!JC+yKl&Xhm-1X`>c0O{}g#m8sxo%ygP6VqUB?mPf4;w z^V&o9Sndjv?!27}`gfpu$1VO;@tt@B-Z)M^1;pqjiPAM04ekxIqC3F6#=uF&d{JJn4O*By|l_pY&C_?J&wTUJP zNycO-CF++clteUBDWalCN=c+a(|OlU6JN=hFw4Ud_h5puh zv{c57EvdcCE&OtUJCa+@E$j?tlVwsM=z$SECD5c3-^jE2z#?#+{S0D5csAB0JLaa6 z!#%Pd1rMj3Wu=vB?2ato!O(pUYTLZ1>f%mZ)2oHE*1AFG!o^f!-wA5`D;g_B)6v{d zkNGtoBY`)7zOwtoy)1R47Zl^bZ)^-r3?$T0Sx9_*7`)Yaz@6VYml}>a#2kKDQE81( zLGDvcdSqCdHf`)j%ay4Z{ksRYtvE`@u2rNLY(&`8V@b5&k0UFe(hez|nRs!kGgcY; zbMLN13mk&Ba~-Q}*`79eny@SYKZv=2M0Xhc7w-%5YmC8E_aFCs-Zk7j-2)cyv}c

2}D?2#qxJ!;Y@x9_vwum73?3ue%?01bjdgfUAq7!#kP}^5n62f^D;ae zJ(ddHwy>s;Jt!D8msR-tQYCd6)Eze-y>tJ;RB>Ch7^_Gv*Sy5|;Z;JjIm0m9T8f#D zlz>|~zhJ^N8-Bi{ONZP~QnL}`(c{u}qH$*s943b2wS8;o1EYC(*)@rbY&I3X-?@aI zvJ1LfFX?j|zCUzBjih zd<nQs6U6lSl2p54;S;a&l_T$;yLRNK;&Q%_)1 zYYJqVUg1uXL~OmRLL~}P(ObU{zWygl|E{yd&WAjerf3dWcv`czRh`1MRugHZ_cTz| z7hx`^(rNt@6V@?gMz8JtNxJVRGF_1>cwl@5rUmuE`HdIB#U%**eWj`N^f7Gly8Do5 zT_V`BMVzg0EigZ_CxJd%7|4{IqFAHvZSro87F+N|lD%DU6oY%j=+Qg&Og}hQ5M%Cw ze=>aqr)pz)r`#+kH*uhIv}$+@vK{N4Ur(y%|Ay=wYuaGNXPJ(K(VurqxtUI>7=Nt+ zt!~d}@kQT3@p(U-J9?bmOXQut?pAb?WgT9zk*0q4YPfgFG3?2L#cYXCo0>efrvcr~ z&?S>Y3+8uV@r6=u|Dl;Qy32=E78G#Hc0|Cvkyh|TayGlUHkfwY-ze<2FoiRT4iKa= z5&ZP|x7E!znaSkyR6BP&EIClcoTc)(ksCLeX9&yS%)Dy&IeHF#c76o7OdikdgZd$B z(RB7>+FAIow4D^H-Q{+QOeKHjiqrWy{AW>+h4@-;9WdI5{tcH@oSk?^Ey30WKZ825ab%vq63oX4E+aHiYZd|=JFGH?vS z>6hkVmBTV>vGf6~E~x^uv^1QYTuNRB64;?+gHd*a;NoUXo&L1IPqQ6VqTz(lCFcnS zRy1>yzAvDyM2?DCgkWihEUmKb1)n9#v~uTWF#j`w<*$tZ=h#?SBQ;1?a;;E4EfimU z>4zQiN-*qQg|B$u^U&Z}X8xZOTzNJL_Mi4cxw21W?D+He=+$>_z)=T{O$x9gZ9uqv zz?xmV*N=DBw2}1NLs&FD1j?Vy=A3MYG2df5)A|>IvZ2OwR){P8mK`DZQ$8E_s6Qp0 zdS77d#Adj#@+Ox4*}*ek2H~%dIW4d##tole;=vgcKqb%#%QHK0*P3+bj4>8E+6z&0 zb3868<6v-n4ewmN0xw7VW8)PwlD453jGg)Jg?tf6=EbAoO<+FL>IKMd-pHa`WW;E(-3o~M5CZbeDy$=L9?so1pH@2M6M>u_ z-F~qHr?n~5w(cEhI~CZc`6r=*hS9k%?UB3T z&$}@!ap@CKz14%;HkII!cO1U0J&Ts&@6mYkOg_JoiFPe{g1Veq40)FX(r1_98i6Bh z_4o${OMYXtPX^47wTJFXL*aBgDdw^|7p(;foW%3*q@c@_Wu4oAW`~XN&W~eUOCDEt zqVpFxsRg0Ri~`&!SpYg&slu?AYglWe2;!P0D7M~+dgunj0sfx&Sou4-bEy^I#Z9M$ zQ%1ufcQp*K(BhJ_c+a@TL7e#6xfE+mJVk#mvf>uYR-?Q01e$wGgQkTC(z^aEqJ*V1@4z`| zs-Hvm4cf5duFnJ>n@7@Z|9vADn+a_n+D@)+3dgH=oM6v_D%j)l1y>wh&Qc{8vN0Nm zs7t^GSpQ|1TWWm-W9~}v^Xr?me7ON?sOhskvoGSdl!LTqmn2bf5TS2JDc~|?z}fyz zY~aKLh^=ej3QDqxo%(L}llq{tt_tj5unopxA}-WCgrBZjQS;7iw9zyM%DtRmf7xuf zZQ_E*=Z>K+F-z&kG(O)GI~OCJjx!6is z!-Z<|(XrJ65AGKH|E+Q8*F=_lxe1@RQQTLe!+DA+GO{=WIu2r4xOF}#+&KeIDYtNm zjW@>n-Nk#!+Pq_|o3m=B?BZ)NHYwl$*#1irUeer&mwj~UwL1&w&x)%Ua^wwyoDutN zmWj&w=P{w#1l$Ua!$a+x@LD*PrS(1Ld+ynAYSBdceY*kkOHReO4r`dxRu26s$7s^| zS}2TkWZ7+bc-laUJ(ky|r==vB-+z&8d2T&7d#Mats=AER(4I!V{rXF$cErGeXhaq( zhu^3D!Yy~Mals$>yH>L{nR7Hp0PD>0@6%NL)}@BeM%mJDdw<}lc@x>dIBQyTb|nml zY!cA;ek@wOoeO!VgFT;%@k5S3%uw&=l$kiLUa^BsQ)ig|%buwh$}!dZo}l*Z1_rI< z_dQpBExz@dm}H8;Uj=FEKk*h;|F#6nO?Kqe)g8>cWFC8<-wh9WUx8_uKF&=JXWqRB z;T`@%*`VpPA0%P7Z3PK%+DtE>Z;XnL{`25(WG*Cx-N_#dQZI?-sy zG+{7L1ACO_(gQPRVCc%J5NTM+{rvm`V{fg7rqUnq;lT%dcg3GO6}F7_@3W(p+t)DL z8#XM-BZgUcZN!kt&8WFG5ASR?<~*e2;pW(Uh?55PJI{@ooOfW0bN91Iw~ef5RX8iD z=kMEkS~yP0fwgQHLC-Ifpu2*P(8AXyym#J^>YR|`3?_MD_QrX1@wLNf(WQdQn>Lcz zg7xgS@nz<-t)Wb!_%t{6g&(~5o`~*V>)>jSG<)N^j}9B{gX2F2aqZP(n0jU$4YX(> z!{!@l)c0dlUZ?eXOc(}-&0Kv0jQtkB+ z9b+0$V@MM_{&@-apR!73jYfN@f@7%w@KspQ|PD|N=l3d(LX|xeW?s54^PIkWswT6(cdkxe*7G(!Ej;7En zMJL}g$AFkAy#*h$1{qPX^)PV=p|hWXCZNp7XgAjIEOVSc3{ zC@@dNK3i2N+4KgyL|&uH%qp<=P@)HH7Yc$zuVL97Wj6XP&*z8bW1;`yb$n&Hm(P-f}{%kvj>{<3ka4AQUUNNl$ zk(G8d>8~t{4RE8LS0ACB=oGlYe+N9XSHkzMQfSyc1f$$vqi+@GRDb4%i-U2$=vx%7QlWoC-eP0lKdvV_6x{XjV!+a8P!qqzhjUDsx%L^5@^~+A~p2!=}TAGN)<2!M^{~g%xnTEHe^;pX7hj4K8 zY%u?zN8bd!$9(?0bD_tG9h|(1WKKxJ#m=3uW2Gp5FGB9^#3Q6rBoez#-wOtv6(Q|i zE>4$I;qn8>X`D%Y*dA<@Z8Tdu+R~_RCqJ4PR=qfDT)r0R{<};muJe)n2?;Q_! z;()asyX8$k*69RXGTD+wUff1YmVF?RZ7CRWZX&*q2_`qhAH%GB4k-Dy4RS^q zG2P}4n9J{H$1M%1#nJJQxPAi7zbnEP?Oe{fe&s;VAvHQ}6The44g)!84ZPEm~Zi_cq9~Gy?9{oh^MFS)p)*u(Jy~fO(=P*L6R!|xK z8gG8Q1{V*_qTe1~!h7HjXN`cnZlZ_o%Q<*$zua8JM~>YneoNTb2`p;fK`_`elbIfS z55Fb!c^{f0WGp#Dwp*Vg=blUi*}ru-twD(E>Mx;rVl-#-vYcF%@B;Nb5h&gC9$uf| z{d!u7X!BPAx^|7hF8>ZJIw6JEKd*%vQAMV}=QTpjbUR`LO!GOqlK@gHhxhM1B2%?tND4T}z>3hb<|V94lX$dG+RGDdUUKFe3Q zFzYaGJ~EoUk@e%bPiL@q?HDKs2!LUgWISgtL0^4p6Q1sj$B-TN9K8_&k}4%=W73bt zDPibQl8Mh$Zem5VIbG4FMmISC%$H1ps;T&=fc9KGLYaB zz^3sIl%72c9`>3L(5uBntgNZFeI`!aAZET>=)khgvf<JqT#4?s9<&2D7NI@c{rf?1lHvwaDlzj#CVh#8$Zn#5-wim zS_HXJx&8@O$$WuDW85+CNG{%Z7mXq})^e>Qk8mz0qw$HAGuW@nBn{iMxq*UU+_uvY zrapZKKkyz2|JYBG^M0Uz-U&`mwFCk)t6*)%7#6O)5-dHHse{RS&f* z{QNbT?Tv(bKW#e3-GCjAihzrWOK?lM9mG93hIQW;fTe;KyJbEehk8BnxrrvVoz74s zkI+@S9ze~uA~N7x4}MnHQOrhwf8A%`e;ML5b%8X$|LedT<1+G8vJ*Npyv8*7#kr?j>>HS$FTeO?BWYzHhwXv_;&EUrBA|H{il(P z6M=xQPx~U$Q|Z3B96n zao(osbhGLNu&IcH@u3`4wVdHT1yk_x2t~`u#UMH3BYMhu;bQ6iD9+Du#GbF4~11m#iaIUFw6||f!m>jASF@AS6yT9S=MxxQS?$Ujf8`@ zFUQFRuIB!Uq`@?Kae9vx;Gx_IG<4CSi%-ub;a)M&-T#biSfNNC-Be@_((UlX@(%>O zJC17?8B?-LoKlmKLhk2Hm}+s8JHAnrT%Nm3s5n`ky(^5y*}s(8^?7L!l4*nK3D41X zQ8=_GZv!Fk?pjzl6C9UMApS$S+%MAra1K*ulPX36<()t&;!d<9`8rXQH=v#H1oci^ z;CP;~dv8DiuK5q);MPYtQ1+dh|Lz2KcBkRWTs81gY=hOh92%Hks3Bkt`F(xq_ z9JmMYxTFUroR~q4gXQU|L{TJ?JXbwd8@SL9aQyogPGElw@A{?kUMT@w-<$=Z(J`2M zA(+WZ_|n<48o4_%AsD6`&OBar!1_Jr+}p|bc-G)tvYfxC)PCi;@jHK!I$2d30?|w> zH=2Z%-iOF6zH>2snqYIgKH2Gz&3i8j@YP0bdRx)ge49ZBT(r&^d#jLq38y@3 zZv(WfYQ@M*&(d*y<>tpizY>qL-vsx1?qK7>7qI=(B${Pdh@t5xaS@!wwIi~~xHM@t zIPW_2RIWpjW$y(UAw#5Lc?D#?AHhmirlVgl2eI54;>n+btgnB7w(8$x%Y@17u^~qe zZ~sDmHfd7ryH_DYkMH{Oxzx$(>g-PG8T9!s$(&yEobeggxT(2*IKkhQoETUJhPmw+ zP-b9mmo5WJVoRy+i8I{SbJi?B-kBKN_Q1a;5mp$p9I|HmLB>UGRyd(R@TBEDH{6zr zXKE$TwO5?Yd$u22Q#Ygkg=^e;fgE&wJPsiT7Lf}*7C0F82s3x3z^Xf`OIQ~ci{Na+#J#)m{q z>^khS8cBT@PUAF5WS^SQOh1k!x;%4bOG#*GS`TnqDIAi&s#m)j;$D z;<-`lWtr2M5*!zC0*{|qPgJAj*op!x*y;?E_3Keb=LsgY6oPGwKl$s)XJzMIg_2%b zI)Qgc)&EhT{q_9Xp}D9YH?K|6EP1(- z!2ZBubK&I-*cMXB`{l(!??N59w_G2eH5&u?8QR1yBG<7N z5*-vuJO zH8Y_w@^ocsUeYxXUD3w-)BRvbffK3yJ zz%4i7_ZC}tz3v_?uAE3Ue)23HxnsDc+YHoBD6u0Bg@VpsL7e`NRuHJpU~Gpgo!=ft zM!$c{Z8W`$1BJ#UIW!r66lu}N-M8_rP>=e@YO|}cBf)uf732l{7L01S&XJ-Hf#1Y6 zpdip?lMcUtbt^~1Q;v|}gaU~CqzkSelR0DG=kRX5Hq8!8#n&ZM@vTH8E)c813E|bC zaHfZgDw)r1dN-H3ujRc1V(Yk714G;^R}JX;Fo5eDH{wR~MshaE96#?(!DC1N0{2iC z_MQ#p`cxaRWZOc{d;SJ~Ui=!`c~@L-$rySx@hfMTHwvP!-@)q}XHdVeN=TAUz(>~} z!BG=Q&~}XC-;C5y$0HZCPlu2%Eq91^fg;-$*~&c)T@RC6Hs^Ufd%NU(>RBO2)uZKEao}{; z+&LSoC7wd~AuVjMD#pAhTa;9oMx{Pnz@r!Xxu2~e5HU2Hg*GOF+|QTXrzw$;QTYhP z?f$`w6IbwH(RrxvkVng;XHc@juuOJyFELQppi>Hq1czFA#^BFnXtwL-NU}HHoa+u| zE5AVV;nVob%7bfKFC@>_h9nTde9pgJVkBK?o&pjRDNAxa@W=k__u`+x+pOf-sXVy5-2L|)epyVg) zD*474zd|ylyhPwAu>u_1qcA)!6s8r-EGwP<2d3n8b7>O1V<}_|z4F@|H$KXRDF@|n z(0Mwjl}C~xv34@UA_?J~Rk>Rjes5T4YuShl$_15PG&r0@uF0K@|6!(!1u8a8fr0K0bDX4zEG* z-~1Kck1ilmIm&dVJ7LlfCP1bhFbP9HZk3`d&5YlPy-h0_b)AW8I#Ri|#jRl8bcWV% zS^&9ut#EwRH5|UamS%(>BJ(RoaT}CJW2<97s-E}eJ~kZYHnfMru{9?kjLH#BBR9Bs zwuksE(8tTiMspR`D`8GS6{L4<n*-vih4 zO31khgs^yz7MK3|2A7dN8>Z!_p$xZx4%_#T$M0egYh>6vv-4;j=tmAH7z>yR&yD-N z8w`EKSnEz1`pX}=&3kkOPnvljva|)cZ;*nfN*5rvVm{5>UBo4+=u)MYqj;iv7W*8K ziP!A})MQ&8C_n&-(tHTD+h#HIxQl{iXJerL(=uou5M{GHD!HJx9O3P&?rh!7@%-FH zl--G#K)>sy;^VFN(Rbukt~%Kf%(9%wk1}->T`?Z|%2nZqh9UM;G49r_YT{bN;0Mb= zv$->_&9^B_Q@w!K}=8^B#tSDvp}VNJgqlf3(cv;c;?wKyy`v9>3;PS{=x|1 zljJIRDs@_Dk)FlP{#IMoQ=r35D71u=MMhxa`5ykhU5vhC0;$Vk3pANoOdQ|Xz)<6D zFv8v3@q5oPI;j(w* zza%t)S z%=~i~7sp)ZytTsM#iSMZI@C$%LV|=hEKZuAcjj>7H!X7a!*q6K(;hUMe1Z6iPk_(H zRxEnSJ_s%wPa8_U^7otR^y}ikSU-0+&z5rmAMKZ*^W`;Uk30`Y-o3%*f-{0ge4g~h zLKAwasg~2cAxaYtore(z+F`7efD0HxEWf%6y#4QD;>{#Z6;I&xIDW5{yiBt53UKV~ zm(Y9jA}n0;0DexLsxHZY7+$vm3X5YlWJaw`BLCc=BlVQCP|6Leu*cxw4=0Fzf7e(Br4Ig#~;c z$3Gu-B@}c0S1a&dt3OW7c4JF-5U#cD6t_xKlFHV3v2DfAvE^M6(Xr2iW7EHzd8#zP zsYD5Qd%P0F;{EB|>T6u?_gMJ)R=}2ywWVjod9HeU6uJC`pye+Qh4AfT5FXIDit}qGP~E5)@|ou@yOW=J#*7R@pbK$Pn7a(?}1Y9ZqjP;s3;Zd6>$XRMLojE7i zh?j$~JO3cAv7g3V?Zx5Ytc%>SxsoVyze`X*Y6#k`N(Bz9xA9)Am-v9)1l>gqDDE|z zC0aRxwL&VK&+WjuM~}erkuPA#J{vDT^@Bq@A8{fT<|yB{1T<_HqR2xxG+1rTIPdd( zcVG%+B)%ej1&ze%Mly^V8U+=$(;&{vfX*G|hvRnY!2lS7bD;~28r#I-Qy1>fk{0sL zOo#n4Uk}&)9^gE+S}w0;GLubS1U^$9l!Yf9hvA+q{ADx@EqbrOc8(RRZHyrbBNS-n zDRIpI84b%T{*js(X%^AE65g#Wf{TJA^3dcxH~r2i*64E;D<&v_gZ)knU)GFuCXe9Y zI~90n-~#pkBA_*G6n*O7C+zNhMH+M_W9Jw#x?9eSTI;OdDIeWRKLiX$Ys!YwiI+Nv3b)t-~RYfR8V{0CMvIlxG< z8SE&}W+`542Zrk>pl;YNnB4N7^R*wv?b-vd_w{91Cw2xdB-NS8YWU-u(lRItk*4Mw z_h5&pB1PXm%n;Q2q*qy|FlT)hYPeo6oIU{S1}U ztYMU557xDJVxrqFvYK~$f4qGLhWfD8epSsZh&1rL|5r5%FiaXEFuE6aT zh)cf3aqiuFxY{N2ab}hc7{LIUF?ufO#if!(uU^hOdiM(6*gu7KZZcr>$D**2XHeNOgQ=R1OE9?VzXkKfu#r z9k5146~*URqt|XZCiU$C?{j>H66aU*=SNX^nYminH^mV2c1ok?#0pYA-xOZ$R3z(G zOe66}f^m29S=90x1^f1^VfT${7~5ULY4&Tv!_K$hU7ChkpWnf&jBF5D9fv>v+y}!E z4bZdSSvW<>4vzeDz#TW2Lrz38t}^a`-md4Iqy1~B$R0uU`%KxHIV!Z@#R9Z%NHX)5 zbDW*nQ}q5U3U%@nVoy8*FGmh$na?o)n<#@f?nRN%54zmt)hqc3Bmb0^y#p>Qk{%-9u;`}n?A?p_)8_tidl?0OlV*UiU*CE-HFAB*s+|7~)6 z=4JD&Fb>p+CgCkzj;r$$!~!pDp?Wo`W0>OLF+PiGeh zetoSM9;s~OXD>-Uk$52haLU_v85nVLBKn_KZA__=pqm5ai}0z;DxC&}sRc z-xo(?YS{<)kI&Fm)=Y&1Q%bQ*@h?tZlZuuvpX0f5OSpS$IU2q6LuYHJ%JCy~4{^#+Ch7c9j0PfU& z!i{vf4HQ5?bk=<}Sh8w=RnYwcW(&s#2m z?=F@dzXelV`T6&|S$uy>f;IcB#g7-yqh9`4$j~_l-y7HBvaKuOptk}DI$NM;hq$2a zCGQ{Hngnu{E)a7_hmDrzoe|q-!0D7CtiCIShO7R8e0mF{-A^ZH6IS7JcV&Egs{t&d z$AZkADWEaZj^EQW2phEnmbpwrb$$;X^+bTq7Hb5JOJvz4o6B%`u?5)u$Ivuc9JU_# z&UdZKz+-*`mw0C$_crNMfgXB9Yfvw#G4yz17entg`@%`cp@6Td~P=~Su`owGBVSLp5i*tPyLQGZPqpxQL z4D z$V8R5V71^Geozkrx9!8I_j?BI>&YbwNfU9VcPqLdc!mx#U%0OFMzp^Am}AU}`}9$l zjdGDe#p>%I!FcwOST)R%hm*BWi`?n5m6(BgAH%U+?f zn>@I{A3zNx#mXIKBjrM8S z|8o>~!2dhuo}Po2U!HP{zc=966=T?5RW;14(qpZ*12867g=Z~XhP*dZnbrGAf*rq7 z$>BD6j678Vqb6z5>L0cES$RoW*5OICLbQu$*`DR*v-cRYn?JL?p7$A3g+-4K2}7F5`uJ&%Agd3koy0JPl3Y3Bg z#W~Q|BE+u!U+{9`RLpfA#&vlwv1M);_*h=Yx0972GHo5tO>iRrN;0{sb$(#5iT78- z2NV_tu_PlC`lt2=SK&OBmc1On;?x2V6&hi{#0q>fa2aNrinGEX8FIZUhx~E=1##zI z*-G1tbw_t$a9N2HL>2Z<2uXn-llkKqr-bJ4_>(2&YF zc<|R5KHi@Vl^S1!g%SrjZE*#*Ywk55HRiC&Tb!ZqghTFDC(ubzP;Lq zrVHld@2$bqVNi-q)$Qcgzih;-j$h18b}7MR&Jg~Slwy4ciqNAn5$L(+_jxG^&h?dEQTV2fDT7ODl&fq%Fu zKCY;eWhl^@H>s?6e-Y~T&te^I25|06Dp$7jBL5aU5Sxk>j7@z0egF26OA zdn=`jv&4H*_gNNsIPRaab=-&oU;KIwFL`Cqj$zCW`KbzrXG6KO%5cDOD~g2ApriK~vb}fs zxzZYCz=2UzB`ab3-PG+eq9|*hUrMOGuRakWD7W|tU%ms~H$>m$x^UR%B z@a&oj>%SI;Gqf&|GKUYaGxZL!nb?X-m-r5c_b9wH!iMwz6bWB`J;#-|_meciW;9>7 z5KH&S(p&c8te1a_sQjr3Z55|r^i~PB=0ys0xYpnd z!sMQZ@cQ3OG>^WF@e%p>dib?LA6>r%!>?tFXk^L)=B8@_``wH2!t(d{ux1f!cq++kY~I3f z`+L+=^B{6-2q!tt0P1rk=)7GCTy|U>KGfHvGFuU?v((}3e=RuWO$+bHm`V@bkAebO zZPGnSjt1^KgVE8_EIsHRSKd5=xqhCCs-C=)AkUaiO>M^GQEA+XhSzZ6w>%dsbmZbi zlKHnA4OSiOTDFVNnS7cVkCT?&fQvcyKqYIry80|`@4f%9`ERfA{s}|kdu1&BIfu`F z@3>ApM4w^i!D8GgyA0>tOyLTD3E}Sa8EDMsf24IZ%nQ}hF{)%K^AvuAq~v}$E}~AA zq>WjdkrJsh9LH?Rqj2wmSor(nE5vP{%ay66VTb2f0hP98K^+ST$LDqI&(0`w);^07 zC9Y7`Gl$73Dqp`L=H5Wa; z)q-ATmchizJb!3->_HT4*n@krjr`Bk#|4t;^WR99MbN>OZRhxD|PTlq-Ao7Cp-o@Yi!E)^KkbXu1ZXo4N!a=L-Oj zTk^~-sUMwPqDk6>9{BqCJWLg=gg2K{upxLnT+uc{ZcilCeB!h0R~*sfg%h5LUBaZi z+ToCPFn+x!O}|bK0KbjC7?;zI#mkCNujK+aJql3s$3w1IEfYtLiQ>G6yNN{UW03q6 zfXiic(NS$2+cwX~yl>$O+-uo@$ERB1@9`yk9%2kk-S8M~%#s|V58nd9R zI~^ngcL>5BWOGO4GvR#Q57_JY5dLl23Q@NQx#k~F_biJkYT!>;EAWz|t2R;L7pMh^>?1o&6f~qvtf(I$=Cy%#|b0E_&jKx%VOYSr9C(pGe#Px}Kp8=cFT+XJIe~Z^wXTE8FxIFDl7}J`G?~k{D$-FBhp_hQg!OwVYQVFh#=jVRw zAA^CTGN!CbAPN_1xDheyQDICxToqBn)#ag_^Y1vE7x@vC>v(V1P#s#Q+Q2QF9+H1) zGW!{B0U4WDqR)^nnx2-WCp0p-8BLjG=Ymg?Q)1otu;3SWv@stH&hFu?msW$c!8O74 z9SdPfK_@H}E(g&kzhK4UtMIXZby>vl9MCj1#^pihap5gTHjkg{e$#0nP0e!bln2i` zEewW8x!2HR--YhClfZk?H!?S39^OtFLJ>t>l)Ti;9gI3l29DX%F+&vfx(*Yka3yZ3 z5zo1?;X5+lHsa_(5ge6i&c?)@BF^8nsb1kSZuYqv*qFToEFMI_p9Br~axGf0@mMLU z1-=2*$73Kqs{-W4^A64ad8ln8N+0+svVn%vAYoO24-}`fRxJaxdsZV*kDA5aBs76q z$`**J3gV6?b>Q8?nap{jD@H!v52iEEpkDG{oHosbYqj7#DB-?PXT6qHNym`U?l)oB z=Nt&is?fON2e$Vn!`^s9dO<`JZpggnG~~O5F$oHI<9#8TEA$9%%e}!enX%9&smhGT z+0b?NyubL-U0kvt3yx^y@fW;klriu@4SSv)=dhF8WfMTRdW$JQKzb!S$Q*MB~eTN;P(`p9Ckxx)(g#5bTY znLl?;Sx=1LCgFaMU}Q!USZS6ncjc`-W_rhBR=X9>aEV2u00CLXv(l_WFOhX^T4;al z2%d0iCgWSJsrS6GoR^>nTav~wuW~7R_y*4fvGL^|I~C%Xe_A*vP>B65bFr=JfbiwS zK8#Sf4WYStFy3&0Y~kHPIVGmtHnroVuIU4e)^dfzdZ*#XxI9dmwFy&v7txUmg9SHQ z=ds&SXVBs5X&@uBN#tcmc;-3}g@Q;fO?5IgvROzn|6b(UmQSX?1%=$h2eO>t)L80! zPKiDG7zVFq%*Ef$F}OlM7r#AtfMc!JV(nZNcofu(;X7>bxz;g3T4yZ$O8p9D%YFF% zoGc5xrArg}+4=FK#gKpL5U#mkK!3EnhZMU)tY0QZ-6wmq88JU#m*Xp3TX_L4URuV$ z>&#>Y-8M*lIt~bKtha}STN7>ADNw#Uh7j9cn zKHiO|%?(0N_XpquWni@(e+Q|$d zk`IS{Qc5LdrIe`r?$_@>ctp=R=epjX&wFUQ_y1WEEiQxaI5@0%yg#-G&BRp5 z;E_-a*%XW;+41P{)djrnO{1BoWXX;#UolhTH7j@hC9K#r4X+04f<&<;Rx1C-;?{ap zKduE%j*FO)HP7L+ToS7qwF2a%7h?F(U2y(pO$$N;d3zR=;Znm>WTxd0meKEEH>%C2 z=OfOLcdL%FBHLc!qGjr=)o*3uruU8sKkCH9#N=bRZ7txcsl@T>M0Vhl3MooDOV_!^ zv9aMEq!3L&BBYF&>tjaLpML}u-3`QJ@pNAKv=vnCyduf7Z$q8KXE4X%4*q?U$i(Ys z;p7htCQ5yP)1r%UYN8)aX}gB!HZ|jna8Hscl1+*p2$G-YT3B1{cJz4p7x;$Na9dG` z4lE8wF9T63?i7Z373#p0H1Xby7ozdVE9PF-X~>hi%qqJ_;*|DLtDV*i-FUJY9vU>W zS&I&$kB}^>y>%OQz1sxdJ%@4D?TgF{*BsP1Q2-%%fhcirCj|Hik~{YwFed-D!3MG$ zy}14F8wW18zdsktYPlSF`A&>`B8#>M`sQ8lXlHj%`vuRkWvJ}s>rkkc3MQ|fGI8N1 zV6lKj@xiOeHk@UiCn@6F-I5R)u@~=p9>?bEDY`-E% zULUyw(rtU;d8Hz**@En{h1n>*>NQ_~&joh>%TiV%GZGza2cco!CU9IBP?ErjXj?seLSw~;xF53X*B;!hL3 z0(rf^;oG9kBwk907s$;kUu?`_<~c~>n%`2WYL;jn;24UR?+MZ(&a=BWUmdqB+7A0x zuYlK%TsM}>Wae|l1;{qtfnT;Sr}87Y`09rtHT&w%I^5M^OYhX+&?n9%xqCf0Eu27F z5Bjp}n1}FRY%$9Id4(#iV=!fE5Xy87Fb)&!>DsQZtp2=K{Nd?J4#bY*?0~PhL3=il z7jvS`r7NN2Pa)iZ=kVC?DJFkB00YvO@VLDaT5kD>;z2%Sv%+j>UGW5EucWXlABWkd z1tuuiDn{SWlqJDs5&VlYjqqpXW3+DfhRz$Z5I=DTF)osX`q~M&C*&WiKN5@KjZygg zZY1k8szk*$RGe`X=Avt3B46;-KKRX;pGv;uf# zp;#?oT_17WQT6DsvMRtF!o@>=kHe0Owz@lq2V_-bL2r1*}9XI2EP=SK5YI z$!Q`qm^(wGBXW6>D+ykxRR{S6?Oayu24>Yv#Ww{abh&*W+zb7KzFz9E?1U&?dgB_H z*m7NfT+-C=n+XXB^k$a@o6u2@C8+py87OOskg>ftnc+iXBuRb~Mr^l*E{)qbV}2UU zx|70G7CgcoQ9-ywLY@kkh{M1N3PY!75*dAGbbKX6W(Ie`ag}tCvMFW)R66ijlM&=8 z-r?7Oqz}OYnryjklaaG3;%w}p^-usdr{$6MMuhO2zl@TYcH7*hg)%6 zX5ukUNxzI#{X6Uq(x5-x=8#9H)$ypb5w@gyVC2rTP!+BWuS-H1-a;+~aXf02z^8o9nYE8U0N@Dk!p2c;Y%0d?eDjZmTAIt-m?P zm^x{$l!wuzX)vuykfdq%GcWILr{60(8S6>zB+*@g=A7 zxdEtqWr)jXs*sqwc_^N?mbM+0!MzHmu*d#4NGyHE-`_4z?lqhvE*vBFtGgSPD}P~= zInUGix%0u`?=f8bDwM{NRNN}3La!Ff(fkKLasRpqc#y%zt5fCauOI8+UxQ+~^qx7^ zE+uc^0N2&fu}Pmz83+TLKQd&7>QBsXYl0VREa})|Y5ec>6-;!BhAQs6qHi`I{7zdl zg2_r4J1ZF0%?GAk`wb(!ejLJz?nAV5IyyNALW9w7Xi9j8B@?oFXEqnGsnif#c4*>P zfk$l7%4+7TsTl1%6awvPH-Vp01Zfqe@aOr6wx5t3{V^@=H(^E;9IzX zegV?-N0>Y{o!tz_JXeEXxDMT?UkT<$Cz#pyRoN=p0u+C!PNZ+fvO-FQ_}1|!X!SpZ zz8MrXeCM-UTc;Ai7-@PTaD>ga>I64e4I({S$M~%?qW*K2LGajJ$erhkk)Bh)QE(=T zWGft_gB#clS;8c=MgJ*oq}SUA3hTwz`aXXGZOs@^o9rbaGhZRV$(&*x;bv> zXYdQNUKr8)M@!j*UJo&_u!TKlBZum3N0=Gke{y`uxM_ILXAjJV5iDVo)Vs?a= z#O*4|#Ut=U_iH?MGyzssHWU zkfJpkRn&LER^M>;(c^PqKJ^By^=O1;k3z@|y*yScNrUVfHe_oZ8^CU18=E-P1~VrI z;D)KuXvcrS>rl30jDrHP?U(`Qs?fl6i$|FAVUXw5E5gK@a$LqbFPgtl6&|ZAF(T94 z=(@qlm_KYruikQIdTgY~gW@qJ^YL2pUA76f|0{yxn;rO|sR0WHzcAMV&f{mb-FRWK z2+2NofPddT2Mhz&p~&tT?1da*jIulmg%^R`pOTODQ;%cey2H>Dw~#E~)WXg0M}VDh z2CiQZW`5qcqBg<-%%ROmaN+Y-=Bw&tu%4U@rtACJ=z494%)A19%^Hw-$QnA{au~>? zNb+h&G)tdOCExU)vC}$C$&x35IQ!*2*4Sw!uF~S}3n`n?pUas06;$GP>tuemP8dGv zP@*X&UMRp}meT*YJ}tW-wlXFV27}C~;Y=ywbG;n(OQ(T9Z=4OV8bs4cEdWP2XSEO4 zKdn~_J8FOPLezG#eld~!{7dWc&krZM$itBRky44WLTdETgeSOvO&rtu`4VH@b`X4m zqnTi(bFe&(<9e&7(3RC?U>6;Vii4t@w>boKzt3j6&Qtn34bAqCrm1IgUNZ06`RpK z0@LcOVO3umlXUDoXskH}pXA)&m|6&EstMs&)8gIgwsF+IJVdyvG<05O$)2Nt(0(*2w?C}hK zYjq&194?3JP7%zLeGiz1MmbWQrbHf#$H6%+@00g%G0}_M3mf}S)3dH;c?O>!V*XrZ zI@(xHRd4S{xz~nxb;yO(hjl?fQy~4b@dW7+D`vE>9R=Bs>G=DvBY&jnA$xW|VSdeC zPo{=-gZIc{>d_!cQc|UGk&-a3>^)7yBUDM9A?E;F?8qEF(vFV>)48m`Pkz9p3@Gvr z0dn^uUPyTXx0Zy12ImgEC#wzFUpb$3ju<(hYDb@Llz^=(IzeXocl1uuWd|eyg!X&V zsFUL;z9pI7URQ&A4CG)zs4B>}%22ORb$lWe!*2|IiWa9tNq^EcoDty^>gWb+k33rq<`o%q(v=dJea{P3@j6tqcy8J zH($wfJn}UkZ#G_J1@>{Al4u$B%xz_>YWwhWg1@z8xhE@ z^+!&nd;fhbmAW#IM9n`0>t1SOiGU^KUde%-vfk9ipF7j6(rH)vGZfo3j8&XlF}-RZ zmR7Zb=ZB~8j?3~Xu9(gU4~3$^OFvq@P=MQ`H?kpTgQ)er&FH%`9$!XvayyGuoXx$n z;u^wGNnD8?u?zwG`1$m<H z+j;=+%bv$Q-TrKecrFyIM=+Ugi>m%YRP}2Crq4OScS)ZLe%qDkhJ)$o_oD%2Js0DB z`(#+N#S)X>dqch9beiO4%go*x1S1D;RWbz>=gy&N zdzv6qS)g7d{dWZ$Q*iQEzj*DCVGE|V55~7yW%m5PN~QG9go=e+BvY0V=0xy zAHu!60(2zK31ohyg5P;NI_yAkD8T?#SJv<*PE*0U7sBMfmL}#;Yy*3GYhXpmSPB34 zN_%+XqJYvXZZVoW$5Cyh4u75%Ay@bT;AMV-&Kb*vcVfD^%S6Mz$eDx-6S^O zuN_u9DKn=ZOlF=L{AP4?&Y-!>Gn{i)l%B|L!32)M*!NeA{Py0=x^3p6N^}L}t;ykM zoo)m5N!GOX&=l(6CP~eIuoz<~M1Bdo!N|w^_-@{2ti0$4aYgO8-q(e5AuOQJCiAH4 zCLx&T@DJaQ-ePb5vSy9f7hsY^I=fLzlzMSaBwXYB}wve z>HuuHqQ}2FuLzR1I#kRrSWLa;#^G4o8MHdt15Yn|fc&2?Omags=J#)>rfO^OC{u_R zMI2C@>;kcN7vKj!<^7VCg(H32$m?o%xXQ-B1LZk@H5V{5TbXUrx1tAjNs_brCbXo> zp11Vx>57cLb?jP^jjZ|uRdU%VkvZ_K74P@T(nK}RlbLiAckLWwTT+%_xlJcp4}Qh! zFSg|TND1(*71+#0HvEE>?{VMNEsT}IJM4<`!De|!(ws1#E}i2>ro|nEs0nIh@+@np zvydly1O(uf)EFjJ36V#)8}I^?j-PJDb6n?H7@i`?OD%cZG!`|L!UN^E?sN*j#>am7j@m{xSh z`pm2f;H637+~>QX`J6a?+g1j{@7mzPFOGX?d&--KiO}GpKn_Gx+?w?q&hQ1vvv+s! zxSbL6*f$x|qAsyP8ziXad13M{bUT@SGY>sJs1eJpyXnH5^VpSDh9@}QaZ6w%Jen*8 zka89Gx%}g`c*~<^q6$?ok08y7M~Up~Ure{UAbpV4g=b7AfUFod%M&Zd8I`L+a|xG4 zIeP~hEu&~!aU-NeE}&WuMF9u*V}^DTejT_5*S;L5o2Kf~MKie`m$9?V#PO1*@-$sJIKGCy;zQGsjAKt;a&Cn^IeF8Lt~xh@6%Ddvnw=Qs&3pvU9KDE~ zs{u)PDuNLE0sFtLpmCv|#KCq7-Ja$Sn|BWYGc6W>O`n0m5#jWR4A(W^{R*<3c7gKI zax$QJm^95(q0R5>=(K|Y%)Hx5^z$(>>bUO@+zfAJ(ziH~jtE^eZZW`52||QK%2T<3 z09YI8$16D%MChiy>%-6nnmR@ezJSE zDxrk&bCglg1E;)SFg@7{g{RG@!U=PT<+E%&={*@gYl-kjzZa6wTjBhH@yA5B6Nvq| z5A+Igu7Pb|Aa}bPx*spVTg)fafgmh;6bXI4_u<~^1DGjX%F1c+aiC`(m3g+6rf}!v zE6Wj3EsrP1Tesn>BeL|CUIFNLo+9d5RdA^G3VC!s1N-v{J(?3w*A#Pm9Wxs`bo2{& zE!$0xpeuJh6?7J*bLRRJlOvy5+3gZ=jBaLb1)pLc$?hW_ z8bfe$y$|?%1=GwMJd!$p3e~X)!qbz=mbVrdGoag+kzu!QvW+>j%Y66cdQuKM36ctnH z1gYd_%)5wu|LfZ^^==)3S1*WMMOD*1Z!Ox{IG(2eYpQepD8AAsJ32r!Fgktb4w z{d@AEuP_id=BC1{FTdDJS7uYMrfgi&Q^sps@QwW~sf(BGg<*Ig1ZS9WS^YN|RJZgL zZtu0ifY#H+31+LFvJP30PcH%j=7Z~ZkjvmY{WFPM|AR$q+$q^-C_Ii64 zt{ZViJ>4k2fUyf(-N-}EB|{g!KTI|x{zZ$`f@Jd0ENJ$jNakGTyWX5cL?m>`;){nN zCUyf_&vB-n&lFMUY&zB6u1}6frqeII*D$_8jqq!YFyqkyvR5^OKgZCOE_#EY*?1Sc zBaO%|XL-U1D3bHvCZbuiATfFQj9=w+nwluK!r2|pOmLzwJ;9Hq0R9#v!d2Fp%LxA0J%6emkZ>-`8wK_FNPs7j5ElpXT^# z(>Yw<*M_oT`81GkN%z?h+NECw4+|^d#bHN!X+scwXTO`#bl}`ecU#!QPD?p2f-3nm zY{~I9Ct&`rR@~6Fo2_)41>%=F@y{5SYp4+;dIokh|HeY5i|eoYlXr?NDAGsEv{lSL zjZyZi^(fk|zeI&RI^cG~2j1%wxlBu`0~>X9K3ZEy;1Q>tJmAvFOZtKn5u={hV<03y! zGwFleDK`n4TchFOrp>(g(OfcLDGaGjE!krAk-f%cr99n~(IK#uotu3TH&^t6*=tME z`g$30Hw&cBcZFzX?sYbeO~$RB9&}RHJR-668MEl08{YpmA2qX=ki-J6tD*BOCazyd zh5O^dSHp?4TK>h6&3Z`ogpv!x_9W}+1+uR5BzoW~y2PWN`}?NUAcV_%G>H&%k(Fq& z>>-SLg-{(WC(0kL;4eHkjYLW4;`AC(Y8X^Q{o-d6*M+*Y?AuN{em{d+HmWfG7Zk}4 z*hcHz7SpA%ZFqB2I<*+v!+!lEK&39a@YiRDV*kKNV*P3me7@ZxEq^52|f$t>2)5n4k=I3pE8KDxWF5~#7kE_UdW*n?J zUy6z~Q;-)Pf=i=$)XMWW?}qiRinp z?*Y86TtHUX&!dj3nR4Ga8S2Nm-JBx^;H0z_%KhT*@P{_T`{EPi>&gbk-0C{K=U6fQ zN->bhTMEk;^x#{g7I@H*fyS~|Ah>ruzRaJ)R&sOa@{LnzPz#R=`**^+{9@d;yqW6< zyAH)I?@-CM0mtms;hSO=-u49Ot~R36FH^jt*N&gNW$4POo~ZOb9Ebf^;S3{bs`}U7J*j?0>m*N=H_juS}oXq#Mr2R=#sNvBESXTBD)4Fw; zRn`7fV)AVyhMoA$PloH6`VZ%it$^j~S|n3g7QN!`vNNkT^JebNWVACI;qWK}H%Dd&|xvy&#xm8RpjeCfYNXWCm(Q;~O3hFSh* z5K_6W%Ubnw?Aoz+pxPe_cU6C*&9fUIbCL&FuPh|$T>}tZcZ8k4#RgVIaZdNoyQ%Y@ zH~8kqLA;QzMV52D*_DCYq0dT+c@#U5c6?T#Q-0K;4>z!`H1uL_>8yf~8wTW8^d!6k6(g>tRA!gk%FFdzKfZCfJ zMJ*v$vh5$|0{xlA-3MJrt5C-Q7*DRL{W$^@w?m^HIq564SkBnEk>z9Hq@R z(D*GEFilzxwqz-iDO%iosP-ZoZ#0M+$5psxB_B?j2jP*pTsZdD zipep^f%c<$kem9MC!!Zc;%9}@2@k%r_eBV9ZP`uM`-YOKBTbCAi3PplB#*DEYastc z3Odd)B@?cR&|~LS$%<9;$lb4zRCK8!QMrDZ?AvKbmO2K5N8B&ya&aQ}XDuT73LDVA zUxW(s%5aJJBvx0;3MIe)K=p?LbhWzx`#wOGR)l?LrkWR!Jv)~Wi6T|{O8p4*gqn~b z*8wb(8Dw6?@5hPLHqv36SpL=fE%fQ5%OoYC7%nYuWNxM0z)Q;El+U|KhDNjT)G{Zc zrm>&Si4h`^k`K}5le=}u(hZEwA|=*JKM-Y?@Q8`mUyMJv7}_MRGNl$)Y&Lq*F}98s zYxD&7B6s*DDMy;#%L6|k9lDC0>B1G-)Ys33Zmp*1C@_X)(YEAMdl39!cT%zaTn}N+ zGSXNVOh?W&z`2YdPv6{b!Dt*k_!e-y$8j$8t_xq znQEBLWtLj%(^kVgy3l9>F)9jzop1V}x@Z>J*SCpia`U;q$GS|0=S$Xq$v&cS{5t;K z7>74(Au-G;7bb5VU|vRbiaBou5@diXxi>`1pmJz7i_A@8-%kRs`144Q;=aO^D<9`U54B#3+n zeFkTF~wGi{V&Y3RL`)qynr5>we zyOXLNMiB3b;f*;m;2irLM_;LeM3*K>e)=nN0WUx7ehn{^Y7Vi#TR z#D>Z;n0>aK-ZeUmp3}0x>Xaf0omc^0o~^9-dlhcpC&$ez=HrWD3F@>sfw_6%08`%Z z0cX1{C&`L4$&*t@$g}?Y=r{KYF@9vhn|?1CGmMgP@9vjaUpI$HHb|4O<1S#-7*2D@ zKmL68y+m({B*wZOge@98l(PSeD+i9zpL=yk$G2!S)KP=H?g*N^Fn~F+d<}SW=Z?c) zaZvi$%t#8{#%l?RbZqN;{E}0{u&qm>z1WG^rn!(r%z#w}91j~RL1LG+LQkqYEGX-s zE{Eb^)15IC%V}qO+&jv*t#_gIi3LB4>`}vAXgC&{t%N0rk`1N%927;DZ-gp^VU<7XlLx zccc5h8O%)ob!-;*)_t2OK&)5`@Jj55Zj(UR=9mh0vrfUU6brPu7lYzQFS5g9N<_Jd zN26_yv-et3IZxeUHgn58Hg)Pa`^P39j^D6jt}f4IQp7SaEa(t#@c@@e>X)LY({A9$ z(F7Q4XV`01&QxaAEShWc04o zTsDdt$`h#G_E3Za<;;U;wotm`5E`9SA)j7Pq8lwZA1qVPL=~wLW3}(_!9R>jtNM^d ze}jLYA4JmUr81vq?IJFZ0%*{SWn55r0qOM+qsq)`s{U7!IAmm@tL}5Fu$FNA z;Acx`^m4AGJ3^pRu1u_ci$mJhMbx?ZE&E4&0|c5DQR5RY*t1C)*4J*Sli0^XbmWU0 zTr}-O{VCz(Wsx|k-24(o1tjRDPscF0Ct<AiF?JGcaj)MVjo>=$MuKNFpn=|HcU5=us;!JoBBm^ONk@lfDq?Oqy4 zzE?3CBeJB&H-d50jm00CTu)}o8Ms)u30IzZ3R^467@HsBG{@HgpYE7~#lKrw!A~-5 z_Njlc?m-JbK0F)^TITRm&MqKOwht#*ZpK0XE~IA#$aYnC_|ITA-8t2SoGb?F&fU$r zrBiTs6ZigEIv2$MDnsk_HFSo~EXdGErL(!t4$ssxU{O56&M)eOQ^JMJ5*@Cyc#aZJ zFhc?VYAX}zCKV#(_Z$~`+tFL2lj*!*FOm=)jl;XnBR5t+_m>7l^zJH*Hg9DOetdyz zzMAy<>=UG*zyYUl*|sieU4ECW5~%Bo5qIT#6v|35>~IL6WG!Qte*gwU+HiyHPPD64 zA{k0i=%9L|!s$2HtJHi4|Ec|j()ad6ap@kqJv|Ip#b0Oo_?_slcL|1ztgw67nUoty z(bbbPQCi82u82Mj^;&;mLrX16-cP~qhh^~IMUB?gDBij{8O!zI3%UDY~1DEl(ZZamD)$B1lZaQ-)M3s5E96|Yr4NSU}fJX6K zsK;PE3K`6y?nAw}#9fhiNIl2hZUWTdm=>+7i&`{W;N2P3ti0qa z&g<2L4ch`?dEx_fI@yHY(0CF?kR{Hlj-ut8S0Qiqe%kU)9@@N}N%*To-j#t4 z>*tI(b8|x%L)82ESqH62V+bEU{A|VOU<+nLr#1XAkftqhCD8FRm_%L*fdN%LnD$$e zG{&_<&o@Ond&2>AxX}k$Rwro88+WLzy#vupl-czO`|;Gix!kW(ldcb+Nse6Bfo7wF z?4lY4k{)xJ{xr5CE8C^1O37=K>Yh)>p34wlv$M>WmUyTR4MSX;K@McUN0W6`%!*=N z+%tNg`Ff@Ze{wFwUcm;KDe;<(DY!;U&yT`WlgqgB^*VZ}_5vyn=;FLj`@kd3hzvGS zw&_SEYpr0;(@nh$X?oVA@7*-?nspibpFYCgpYlWyv>|k;gh`$wL)S>1<2von6OVca zVlTUdUTWLKu1!y7CLLc1Zr^e+;vVN+Ul{sMBtv&C*M;Y{-20?Y(|WOA70fVc z#IgHfXfI(+x))2MLGxF9vr877mxe*KW(1?~`V2lVoJB0$9clHU$wZ*vsQlkwE(1c_ zU?Z($u4Vm#v*q8>cnd$8e9DY%yoYZq<;eRNs#tg?9-9)B z==-06cxU=n{QG?utoyT&l#7?Jj`DADeN`4AH{OHa;7c@L5=-LL?O65iU)e*(+-FgX z!?@$c%;d6t@T6yeS0Cg9)5iY6*Y`&3@x~y`d@0TfXDE>pzce=QkTawCBpx38=E1<^ z1oml*9y{0jJDN(BVs@trof0Zb+&^r>7iJ6TG@<3RA!Pw`;o30v3;%|ko4=UryDDL$ zf;Ji+@Plm}x3%$#AyLqgriCwc;9rjZXVjjbV2TSTD`Z*iGTOObhhgm&TWe^3sQ)59+0Mwb#P zuKEpOV@15ChdSVx(TmUjiDBky?rw0xhYn~}DOz>*AG`Q z#vDIxDXYO>zJII%82jf@(AJ~91rjf?@wGngf`d1BbgqE5-*4HlwS6E)h+10 zSq(b(L#elP34T0%1TGp(CXSL)jNon_O6aWOMb{p}+m9vT-iR+Lj!`4d7kgP{@g|Ji zoD9QjeHkw$R~jB?13RU~sj>K3%ve5%RWCZZ-oTGAViko-rGwyi;05ejZq--{tn!erRYcMyAjDz=*H>0e)E`#J6-I zjfkq{`mbH!ZkQu0zt$YTyC!4!HX(3t)1)r%xPEGzbId)f&Cs)AF8#eX2HFQpa4i2i z>sp=2eDOGh$A@Zg(U30v<&w^rs{Uqw2MEKzJ`wzLVZaK@a9|RF@r=ZW)TkMgX5w=1z5f^Urr9u25i zp9`y#W|P$=Bhaz10jhi>cxeSAKrBje8p9)|^S6-G0wx&1YsMe$gZu;Igeier>`!G~ z{OcFliQb3I?7*(hKo>QSQtATOn-r=V*RI$co9lpQehu@aUakKQjbRoyEtdctf zW!6FPEWdPFvl$+P{8!aHgJDmIma{+M0=T$H}`UrK*UT0-W;t31)v6x2VgK(jQ?LHj!uA~!V7c2_?~ zF~NU0DYu>!nm91k|Lh=+`*$ntb>;qkPJ^gPDBtv!8(AwPPx@UGF`4ths2D$iXMISW zM7_w5u^}d`(G>57zQtc#qS&E%hsm$QjckkM8_1W9g?r)d#9ZD+Wt{!Efh}j z-U3-wVTSPr^I+4>5d0uIou0#YFru@ap0pH%5VIckc1Z>MX0VpM`qQ6Yx|xmYcbAdd zK^(7A-ON)xxgNvL%_GOS`*2x;7EdU`gkzlfSUM*iLLKdi*U}hz>T4nd1f3*TRYO5R zZzcUT-58mHbcjjRAq~3@5!=gJ%>LzXP~Rf1%a~}iS{;l>s*O1=t&d~kBJfH7JL$cm8p5~L%@v+geJm?GamHgL>1~;vY*C_I?$)iV=(y-Wqedi znZX=?>VMpfwl53kk6#RdnF;@~8YUs+*^CD2@FWXod2##bvdh5p-9fJXmyUWPJMhJc z1ys810yU29f+P=feof3u##AU9k8kN^O2)Oov%m)~UvR|Z*=ppnqa}^Hnn3RTiGspC z;j~yTjy8B_g6>uky78qF`MDs6TIPAP&lmn?KexVxm#!v6C_)8YUc@1pP=h9;7WC4|KefkGJlQWk-6cVEn|1DsaMg}q3!^a4FZX=yKB1?6aA0awz zA6YREL2^O%3UjMT8^^H}7Q!QbW^N%gaV-76S_Y+#rQ?qM{qVha3TER^{NNMJirR_N z@4Z}S-=yhu#ilThgHoU($9H0n{LG3Sr!IiY&)eX3%#M0@k6~`;Ga$o!Ec%;@ug6P>$3S5*!ggap`I7AE-My)Re`MzbMag^XqARrbr|Nu=xR z2s3q8J=px}0Qc9*bV5x4JhS`6T)({pJ9-_kdCGeH^?M$@>`txeNHp|pu|nI^lH~q| z7KnKK4gYPoWW1xLsm%Tt*db&<-*i>?T{V-j@lQ z;|~iD+0f3Y928c01o56t&|lrb`%qBApU>vv*BhN^v*;UICw_sPgDG4e&<>32RV6!W zLhw<69kd@-B*gy^KAlm=iV6wJ>6+M`9iMG(vGJ;sZrxQ)-Y@? zOj9PtK?<6(k#io=EA1Dd`A9v=E1qF~`H2(lVJF5at%C1;$%$T>@CTo-RUwm|+-N2f zN}P?7q2&5)TyrUg-1Flcu~B|ds;PhnV`P|&gPDxt;up-ZS;iF(Orb_YF_sWalDw{!M|+Y!nbO(gaKJEcqw&{k?+?bQ#p=!JWPx>y-vdG@jZMdojZ?W z*3-fD)9L;jLvW@wAK9P(;anS6y5t^`AM-P?;`UWIxjB{yOz4N#3)4U_)q+lc`wr5+ zmcpZBO^io!GWqoTGJd}@z|22=p3?ojSP)KduH+exeU`*}0R{TjV>VWC+3{_moJYZ9 zkXQO;1?`dkj1?Tq^>2X$lyGz8-JOZ>`I|jlzb#8L4Pr>3Puy{o$i)LKkOx$eR zsCHT;2u&WgE|GR(1SjnzYn(r^UWd||319r^;;+rn7?q&dlkg zR7Uk^InK6Fpqr%Llt;g511mU7%^wz$s=gcS`k6xz6lzM&JIBLH?z`~VjSTva{KtMV zl!r|VT#;OqBH!!M@q1V(UQu2J^JSH(pKd0dBwLddW#Z-JUsx(ZJZOvXFYi%Tnjrllr!^Z z8PEyGoH6S5HF85P59&1HQ8113;=PS#U9x)tyG@CZbsYZFD8N=zRg4BFI-~tHZ{)NZ zUjCy(=j~WS7bLf{f}1kAoV)?fy!9I*KQCew$3lqG>)wc zyC9`;GH! zhB)%Udq})jMl=GNd&Bt83W>700g8vG~VKJBeZG99>%&SvaXD&U z`GOa0vf!rC1zLNXAroG-VEeLU+F+c?S}v19nV~?K?6Z_iS-pVVP&K4Wq5{zE$p~vF z>5jr#4)kHuH@5qgIj;Ekhc_H|6-w6gdHY4XtmKCFGeQ13r25xtsB@iyR=!*5Ph7yE zVb$!mjv_Rzo<+udx6n5aNBG-UEa555Rz@GCyHG`+uoKk&@ya&5gWxj_jJix9e0(^A zE;^mh>NqLVQ3q|Zu~ZjF7f&GkswoTNK4iGW-R(@6#}F-AVFqUleF@9NUd~hQw|qmo3wMg|j6+F<{~b za31v_jU~CfZ}NX3+bxw@tvrSJ%$mT;Y>glq7eArQK=P1K>G%3)Y|D zIGf`NRN|CsMfbQ5JzrnJ(`8oByrn<*W(^V~c-LeuNBatA?%0Nl6~CdAj3{gNcQH{> z6sH%bE7HwcTt3|QA#2sd&@|1(^kKps$bLBjPsp=p%j|9Tc^R?^|Pxv3<@ibN&LYiQ$;~S(Z-ZSeKk- zlVS9;A>FgH9%!d7mHXNT3IpEoc8?8RRCJ!19x;{dq zDU66z0+#wW!{r2R`g2(fv-@c?6Un&}r`XP?HJ^ou;PSJKW>65#IDCkD*hk{w_Z_(F znGw;4tN1)zj|wWd(bUNQpycBMqA!+@KNRL-tdlzyOyNSxsndo zXp*-_2JY9jv9G@XChfcn%VeHGhvG!~i`fQpx``<7vxaEJzlL?a&(X<~`#E)`=z32f z0^cK`*;tzT*|su%of)Xu$!B`z*FtG)8Z5j}23C(nsP>x^`X{nl6UbG?AMAnU`PPN+z3|SI&17%7FyULwg!49vbo;g2 z?DJ`I^h#qjs2(4Jhg_Gb(SCFAzm$)&Dwm?hHw{)W%bmKNa3I2;qG{3jG*&4u6@QoA zG|~O=n&hC$CZ=d#|dMq{O&4PX;N_6nuB?Vy)gX3Bq+6*N0yl# z!#9T~($bG|r0du#_&aJz&yHrX{Zsw;cQ|g!{f-65T;z7Mv5t)6w+ZBJy*h!U36wV? zOdfk2!50%bX87?Im~K_io~d3zj*ffa+~QsMV!=H~4XWj>T-5|pZ|9?IN;g#3JYyXi z%E2k}6H_za1$OSAM6CAPbAIwFa%sFA^R`AnEgM9=o>>$2s53ckAP)nim^NJ-|=+6}Uh?hJMD0&9Y=-N-hz-J&G}HW;84O8~b<#3)8nHlIVm4 z?#+9T-L#5h5PFi}wC@vM&M`x}JNPkO;r0o>-kn3wmAKF}Cwr7 zB02Flm~YYY$Gxe(&KR9Ic@MfiAW8=_tyJ~(@vsP6y3I`W%A{>*J$o4|Dt^L$~yV=2aL zSI2^T+61pAz|I9{h^);WR&%caEqRrJ=Xy+umhUBOy?&p0^*@Hr!yn5ojN>IM6d`03 zBBQcJp8GmR5~ZlOqCrDSrBqVdR!Jx!BBUje5v6#}b*QvNLx~2ZQj&%dBE0uM@O(bc zxX*LWxqiRzH+T(+ENjI)NfBc3obQonNl^DC2_O&2;5mhN7H=8>WKlfsoUcZ-WknbU z@JuzW5-#-fR8SAJfd|dw$@oueNZsuS*uV8MS`X%9ti1^Hk0{5+b4l>fE(R?Ue)E~; zPWof5tGk#vi9C2e{zf|O6n4H=&n^j8nyo(dHfF7AZ4Sq9=` zAsk8&!;fAkq1W>Tv@58PRP)bNg4oi}2Pk_v)s#)#HkqtCmQV9fo#t6oal~NyB6v6D zBGYk;C+^vKc;vqZ*zhh1KQ&2`NdEb(QBtJC5_-H-Pn31Z@1QOy3(|Rf7WvZ)+9P=r z;Qj$-%kzrXOtrEe2S7J_V4OpMDI2ty|RXTSbKwJb&tW_ z7e z_+=R59ynsQco%*4-$cUVuJUtIRnA2FEq)Z^n5)@Cc5w9w?%2qGTy2CVah>p)PFxv+ z;*B%NJ$nTAU?hd@%o2HbnS3_i6@V*M>=X-7;aK9h}wmvQ4DX2^`i zy00T6dQ)JER||~zG={u(4k2sIuECrkW47V!X86%@feP+Av$y{Jkh;j9--GPn?##a_ z^jtWMcRfl_u5AeCOHN=ccqdhI*#vCtNL<29NFEqE;wYxp+nwgNV_%~`%`{H?HgA}OjM}df= zB0RhuN+_wVq}%@u7zfwji%ZAARP6{lT{oxl6IGet znm4o^(#WBcd_J$Cj2mkr$96?#@h^E5##JAO7n3hRQ;#IJ%qqt}Yno`Agfj1A%E6C< z18khsJjy0_!|Avdd@wQ<@0lKf-kIjKuiT#L`DiiqaZ^~7trAh2D~knl#8?m!p$!v{ zV1kV;JH3JLFPx1L%&bksvz>J~R9T4cYR==9!6BjZ0ado@g%`{1FQ*stzT>#%0TA5o z0f}pyvG%Ap^Q^ik)cIZucv6iMz5NqPECjq;Gk~lf+>Ik{96`UfU!eJu=L7%nV?_mg zrp?p{99NzP*R+LX?9|QZr&vgP#SDpsu?Yq;p2Oy+&TjDQ*U}4*ah6v-7P@#-Va5?! zJFk(e6^P5M^Hcz-SoDzOdoWcDvz6aNL%0gU`ESVJ}Nh~iMW-k6g zxG!`&c`;KKBh$yT-r#z;6#tpa43%KVT)lBfY6=4dF=i`_Wh>t2qpRQ*U3EMNx{l<) ziXbT%{a-RGkhfuHPNfU$`88?*pN*24a1-T{PLQBk^PtF}4+rTd8WbynQ|=})oZ`lu z(iGV{+uvxaag(@KhtanyN0BkXF?8j*Q*7azzp!`TZ|wS;Mr>a!VU5o56|R=$Q?s0dgH0-{3zVPXCT5@I9O71c&48LDw1hfbZ-HhvGp=F4SXR`i!KpL zZFocHyV|giH{?mkp4%9Es|AiJGvVna5nSW0R$8fe6xEq4PJEP&+7H7(#%>et6HUeR zXd@Q?x(j}}zron*2k4klhB?a=Nb>y)C{q4}>!}&dsHzN`5ui!5FM6=LolYzh^f~Xf zv)Rwb5xDBE(%ozd_3ReE_M4*G-ia*3LWfA6buDY6aU|uUD9e@)Ad4Dn zc;84XZN`IErC@)!kB<8>3zf%e zvcng;VdT(!R%YkII!*3!Dc&DYr!f>iOpzwaZ}j1`mnk)}KFY=~IRbV%ErJI*CPeYy z8?^mh!TFVar)&7L{H~X&sH{^UnEhD`uKBGbeb2PX(-8-8f}1nZk@|~TereRK)Sl=j zZUS?s2AqHGA^z&{=4#E~(i#^5+3qossE=C0@*I}ZS%1%w2R*jj`?M&odmrI>yNbA^ z+aI4DZUWOTo&zeT%$7{(!fvTzkUlevX%b;XUvLJ}>h0K`^>WxZC6=+1zfeBwE@VEQ z!*aq3VCi5FM4oBF>m|>jN<9~q{xe`t-^UVlo?X0e>S6ls^IVd)DjN0dZsY5uY}j1t zj%VI++Duo{Ex31qhDLL%hF8ft0>F&CFjd zL+|0U5XE=h?mCBJuX_qT{Ol8K@I8SJiRU;!ufJfuMS?`BiE)to3r>#pBop_`!GxdT z?BRZjZ>Kg2zxp4Bhov!8B=se(Y8C^p6P@%}pa<$*&Lw^AxA30QJdzNZ3A_L3khyJE z^kIB9yIFTvP;lxZcE9F(4VjiKFwh70E;vqR?G+LJy`V*Qy{SWsiXQH5^AhsFRU2)R zJqb2`zdw-%A zraym-(|A6b0lOdP;cFv%HeX4M zEn4*lRllrbqi*;!Y2j@gy{nXNNl1gAB9$1nY9}6X*h7AOYK3PZ$Efw-aTfW4XCC=# zK~2niDAm7%Yp-17#4?_u(K>U|qwL56FNnd-s|9$?NsN7kiX(Ao3IL=UvCl2<(c~q$~M^dY8z{-DW-ZIA#D75{+{IV z8E!J~JtrGnpvPX1SbvYk`VvR{G#JZLPelo$x-Y_v@i$>s$vQQ+v&lPnG}Q;Sb%H=gmuK!99b{)$EMWOTHeg}xL45xPgWm8C;`ix&*;F?p zbjacF`|j0pO9!-=WrG9zedZ>&Lia5utSiEw*~R#Cg%vF@8BN?qDv&*=USa9F({OL` z6S!owf}J>b7!E|pv$gyr%RiQ+2$zWNy4KOV!0+p4oRTQ_E7V*`%=EQw6}J5UZZ z=Bf(j;OEL1qVTK)<;RE;WqC^F9k#;E*c*J2I1Mb*Zz2gZ<@;v4@%z`&MES=v&gHEN znKx}Yc^ z8cZL|XDhBH3L4YbL;P3;G9o@13|=mRR{^t@&vJe(9(U0dRG4A%2U--A! z1-Au!gH3!F_DRYso3D3nV8r{+@ZjTNE=h3|v5Aml5=EA9Y}Gv2V*CI^c?QdN|Lb(# zxjSe%R+jzgI3rN}!}}TP7s1Vo3hb9i6@9j2FMRE@A*X8xuud3{ORbHFt$7*UpC?9I zx~@TtlLga#Hwkx6zYA@pC3xq(4c8H7L{DDs!AC6;tmKOePW8}dI*LwgM{N~`U2(;{ z$!1KyRFdy@*wQtJq?xzUBSaNPex5&-{hKvSu<&S$z(=UY^^}QVqii7kvh4)iE}p;+ zE04o<`=e2f=W;E7kqQeZ3)se?49<3b7L_^YiVB8S=r4GU!}rgDT~CYPddq!om+m8u zJiiM0-warN-X9vfV?ZFzpUWQi9TPrzFp4}s!TWdi?ggKVDd3kJEG+aAC3mMkFB66L%u*`6nf*sD7gV_ah4#$9_>epVOV zcu)D6YR0vO3}X7i%P_};XXW}-L(P>*WPIs-{I(<#?UOcwu6F?!H|$9IlCHwF;2vy# zG!e%BPKJBuKjOWgj%=o)IFshP_|=h5WbA-~l`&=N z_odj2pHl4ccQrQd*a1#_L5fh!_!@c%Mzd2^(XhgOJn`0#hv+1ACVs?=DjrZLdSc_y z{Zu#Du9l~jFK^>s<6wMguEJy@FY;OER6Nctp%*_U;D2f}+0%lfaJfZ^nUy-iS$7lW z>|rd-ou0xi^&gK~6I3wojwS}B-Gj{X5uDwFPpIg1)@Iukz6TuXijI>sh=NlY8f-6s zezi52kYz=Zzk~pJZb%${6oZFB2t1mX4>$U@Q^iGP0^`-9pu6fREl!({(Yza2;ZPE; zt7+vf)Sbfn$BVgTM{;4n;v?_d8GxTQnq;K7JIES6!F}V4F)0V|(uwiJ=Ga%^i(~7+ z%x*RdaWo-gqOzbQZxJ*E`r^y!84&Dz64wn(XNwd@k}cip_)b^_QabAFdf+;&Thiw%o!OlUe(V`(Qg_Encpf&P@2;iSh`2xNbj^Jl&%WB_lcV>(y8?@#agYid`rO-fbm} z+c$y*E#iF{>fd1Y#_P04O^(*gzQfHy++BkF9RX8-mjC@Gr{n~CFbmzXEtnidO z)*Ekt)W^!aH&O<4t`>rZ%Omcc9PcJKSHxd8MA)qj;!G=xcT?+F5zl%2{Gl=&{qhlq zO1yB<1j0f@=JHvpNY43#4%p1rBcVKV@{EfKep&Ge_9S%SrF(XyZ*LvfyZyS*^n?;R zD_3x#Nd>{=4Hv>CZfm6` z8O+~=2hRV+PLcggt1kp^r|yIQ#8PQ_?*uk=aX7x2H5F^m&BKtXp*D)wKSNoLAC2Qxh zI^X%|>u$_O*c(G&$S%5k^dq$Yks&aXa>VNO-*EOM-YdGb6vGFV$29Q$)eq0lv)bE-o zLY|H@N4p8~0BqEW}l=VW`dpZSl9yYf8C9er48`H(~&autm8 zj$}o5dZ`)pWg91|6Um-7I`;B$@=4x^d~oqEt6#fVxO{0Btv&h+q7+1!g|-K~;GPE2 zvN5b?`7j!vy39&;*bzmuy+NXK#)LlFK_nA+OX6&bvgS)Z!2>`rrwuS83vZuSIa&Z!(#c zP!Fr~#k^dU@QChPPpfm?~kaI&Wu?iwY|nhI>lRloggZ}1s*C;tt5zfGFqG<7m9Vk(jC z(P48ZFJ}f${2f4C0a^(2pxi^6jEML_kMHZi@p63va_=h`ycZ*TuEaC1ts+cW^F22~ z^e1ea8wuC4`L4N@EIrfO1kDd;S+>Ci@c+_DS&dXKd(D6agpCm%|6>Y|^@}fl0p~Ab0uj0za5oo&dC+v7EMT^~6 zqpm6yv70(XI0-qY%Jx5SV15VrdB1!pi*{2b z>M^%y!)hmd{5>76>kLA9;zdEf%yNPToi z?M2R1c)*)@rLPhw&{i%k;V8HIR4+C06=$k{AHZsjN$jiiWYB+mkaLoc#a{~@K$K^s zrhWQ{`cVPT+n##GH z-Rq}de3f^ib(+(W24l$a+J7)%%vJEasR~U`Mi4ivJ9woNj z4aHHy)h@owX$}>tj=jv3_Va9(sy0+>FA&-^>#>EW47n998eCZMA!6pbkrei5}cm!VqLGpj}1bZgZNh7gR-AzVDJ?I>nx#bfCXK?sGK_RbHH5wyA6Bh&UVID z;hW3}JYUBbXFKK4;~l$ruWCJ~J2eqcOwc5j9tTNLs3Fx1@WQ%BAS?)b1*=!&pwv$e ziz*D6ypA(&Yu^I#YN0IZFXhHu`-baH{b1&$-9$z&9A2j%A$=L4Y)0cSJ!Q7LY^CD> z?N&Gd_gr36=l`OJQ~wJ%p&-S?P=?_qakaiX z-M&1E_ne$(Q48wOP%H+|S574UJ#H|(ZVmal+?C0>E@JsJvq2zk08jfDFxklh40sgB z`4~6Dw-F(N&fmXbU+!m4**}_Rsih0#q-4mML$4s(bs94`6%U`U@-q#e!|+(cfW6u( z$>y~_p{EZg!|Y&5;=}VNV%EuVPa7+!L{t#O=qj~ks@0>&h538Iz?YC|BOOp`Tq;+%${RkN|A4zY6XBuiUQ+-2 z5hQfjvwTHKmc(ZTvJ^Z?yckDp!Y|>1ubuR8TRqh0C=>hGS)`oL$Xm}G1%uWVn3QV> zXeq&(UaW?yv$J`o>KfSMAA%mvN7(KdNe~IkV09a|k;S{5nQjBm277S|H%XpiYO+^Q z;g%E5bjiavu1av|wI__rNi0=69RSlMN0Dv9osgKtk@??Gk}Vt0lZ2EGoPPQ;4h+m= zt>Y!wnhDyZcDxFkbmcQhG+2-^CbszH`bhje{RL+EPhbxQN0H-xe3qZnBs~&A%;~l$ zIIM4{=Zdn3%40)RGTF#Ae|}2IVmGowS_!}S?_#r8NwJR?+}L2oZuYyPO6a=#2F?4u z3XHTJVEySicp@qs^kNfH$$K8eAIlVuIqM~eBbm4~SeJX_+lxY>GCKdA3XdOMgX!!) ztXCvTIIYTyhRvCpAO8lLaZB<4w~mwqVuSFZAmLJsiP*mWTT~;d;<(ylZ^{ zb{$>I7Htb7f2=~V&A1Gr7e&*B5+m8napOTGEfa&SqUi0ya$IpxpUGdU0kO0o*n+C0 ze$9G18BDNi^dD;b@hS@HMiDQ+FL)zz9P{tn!`7uREO&~*BkTFz?advmfcGZVm`!C| z>{hhbO9!!sfc^U~GHFj>jj7wgSYMizehC&zcNRfsZy~;(yOI2TJd->vS0vxUu7L6l z1@g03nJm%_ff|QJOvRheHb%U|i+mL{jh|gBFLPk~mmd{8Y59s4u2V_yt#@#|nm>#U z|Ai9E4q80@0hFDohly^}@Yf6vrjfzB!?#GIm5C}TAG8H6kiuKT$tNTe2DEK9Qo9raS}n4e#FWoX;*TsKjqBYAo`QC^2Xc zVdf)Maa;63np=Yql$Z=hB7UG@=1K_FZh%px3)sX-6PS-lH@&=o7Y9lGhEgW-_ z6--HnIv;aV-x=+ou>Ji_H0b$Qme=|VkLuas zNIpld{L_$GW=5bx)Lhnb_Asg1Dk5koPaw6=Z=<4i3wKCv9GZl^rE7j%;lA3-@oUIR z^7&gC9M(CHL++EQ#^G>O4^5}wRl+IAZ^OcU=UA=idARA~k4CFM;M@PYIZ?+-`fllJ z@D5o_roMA#e!-bg{NeyUKYx%Ut?h@5IqoF7E|aC6b|`>xqc zbNI~Nye>hy43~1-Ep9Q#mmz5PP?`~%#qC_&!+Xp7VU)E5e!I1l+3`#XIaq;4o)ekv zzGe(Cil%;_IT8_RL9V-5VvCbKk?r3=O-(ac#*id&JUUM(_Mba(TYZ42btVuhvKZs9 zjDcqr>EJ2C^SYDx>;P5WtC$*kTnq)O%_e$gF(=50Um;+7chzHbU{Fw4PjyPetT9TcC{tFgh02}Gc#1HEhP zp?KJasV2D-OHD~$f$Pt1s=uOtWk1l3?}woNZ8aX+{6>gjF=Vb;3>;F}%MR#SF-xOS zX`S&=V{F8}?B@@#b*&{~+jK-4p@~O~O76@tsxADCFRGhxH8q?_*(qqMcJ{IL* zTa!he7B1nt!C%oiIvRHIZknwsJISEm4V)#ppTq~xXB|6s6Sceo;m5ey#6I{0`RVeB z8U-xC(ETYO@kxesEFA^La&Dkjc!^4Xe2gb#rje$)aMlpy4Q|b^;n{^?Hs{((-nTOj zNRbrTe_=9wAT}iNc!c1#$_*I1eJQl2o&lU(iPlBK+=UNapf{mi*wW(9hGzAm-~I}? zwmFPWS}H^KKA1^5#IA5+_pDg=wP9G8R4v>!HG_ECzCzQ3K{&nB6_YI2aG?pGp|j&7 zF25+xcDQumO5Is(!^$r1zsC5*pa^pRmMdi@>P*R%sV`e z8I%J%socxGynP~7n_}#Cm;*l&!|1%Fe$If7X=DJ|nDjjykZ!Jkp zJ})?4S%;o)uc5<>U<_%|WF`LhvBkxLy#3u_=Y?k&}M z@(A9KBJ}d0GB z`ZyKRS`?@l?>_qEV2s-vcpviHZtne+O8A=)13LxB%Vq?+!_z<$oUf}*R4Y#4O)-B+ zJ8OVzIvTh=JO^I6qXb%&YtYi+Ja_fiKTI5Lj4vBsaZlknD5E0Am}?VmU^HFdB8TcJ znN)o5X?9i4lJ4@;4-*7a;j#dxBSJW??2xG%TWH4GD< zdqbo0H#&Jm8NOO~4cgBIf%}iK?3Mm2?#Dm`)mF+AhF*J8rsHl(cMVFg$LW;r?z)bp zJwGvZN;g+M?HulF^I${QQ*ZW<^I(Togu^zu7BK(Ep{0Q%Cf*LiN&QAPHCwESkKZep zFgFaM??-b{fg53)#Vj0{Fo-pa%i*keHO>BXnJ!Bwi>?9k%XaC zU9!H`knJuBrhT)d-8c3!)RzfSdoZsiDWUSb2^*${!p5tT5nY!p+> zD&#VsjAkzbx>0!ZB?^WDaLLulWR8RaQxP{MbDo^Shv!DKRfqmy{g$~*_Hi8Mk35Ix zU0s;NuW&GS<1bZfZ^J{Y5$x37C{Qe&DXjH-MO9wNlFRd@(9P&QpUGcL7ZvG2=;3tk zR$MCXmkbu(h~V(TST&MU5sKO?y5ZoCbTs%T$9B(EAkW|Pb6#l^?33J&>AI)k+vt4G zx>J!nHv9&sF1ez2_cJIj2nXH0in#CASvvSU6D^ai*qcrN=w5vxj&9Z@){_n~_FIYw zbKi5>5jG(Iz@GU?sA024ESCOz38{5QAoJcZip}0fu5Ie$q)H}|*X2*jX8(I{^HM0v zEl_v@rl;-Lr$y= z5hM8isw_42ih_X6iY#WraXfmiiB6AFfyiegcvkCN;>4dD>#b^VfWMd8P@qn>=1gJD zC$D09s~&l!JOqYo)KTc)iM7^4cs6=H+kWC5+^hT#LKbLqRf0yVmEvIQp| zsSd+MCK$3M2o*fcKxTF(HSbP>i^Ic5dNrq$6ZGtx4I8HLppXN^e#2qT)_XHn`I0v0_&i1J{#K!28H-E1})oguM9a;`L z=KTDtVu;Ut^Df}9!>}aaj0=`R4$RZ%^SN zKF88?U6wdDJcpjpR!FNoWTVq#4rL9YFlqCAG+rvs+TSj~-Km9KQ{EWz^=<>sDDohI zSIY3Ok_O2)<2m!~w_)q5P*T*ok|_BTIB`xLe#xqlIW=NDlbyfw;2C1n!V`QnmXIEU zHKen}ku3DqVha-iVR{7l=G6hMD-i=$pXa-m%a};#I%qsF83^$pGoMXn;{)FES=8%g zvHqe=$<&%8$u`1wi2?k$Ef<9@+AMZdG}$P}GiZv$xRCAA^x;4(6s}cZHaSzU<9#ip zPf;h%4^HDXiP!k+i6{Q7;`^m0K1|#$oO`^6LAm(^Sa)tVi&-GbI`7$%udhv6_Rt!1 z&l=0-Ouj(t%+GTvT4JQ=k~Ak@C(f=&JHu+-IGSb`Abcv-4nn0nn0i@@<+sKWr8W&z z?>tP0Ctk#&x|2|~^#IZAa%bY-m(q+LGd5HI1Ad7KBklT$`0unEi@mN)3Mc8{tPP_` z+r0!9`*9Cz4|Nf0{^q%l`GgHuo3Vjk9)efTma);7-J$nDHfl~;1BQUFAFt@?AAoIY~edzRoS@2y9q`sNkDJ(cp_aV3T9%?>`m@{ zyt-pIcE<;;WPtmH*Ck40DZP7h+??51zqXv2otj%kY2nccosMSAv~+#*X3zw z!snZ2uUyP}K2!-V9W{iG4G!oNu?TiYSEK0yBf+oZs#wzg3n-tHs(7cx{)^v@D}HOT z-(9zx5e;NV;8w%wvmqiDNmMkJ93BKPh=$#%ji72e`Q~l7Yp`4 z8IAw>40bHEVx!N0LE*Sf@b}F)bT+tA8ZI>o;;(Om{P~*1Nv{GlkMrl>@QGwyQYNf9 z$!Bj%yx_6b6)Y{!0Al0@fl`;i%(0alap4})!$(-HOG8=53p=u4Ld__x}F`7Aw5&y4fJXD<5%=8@NN+xc?L?9vxJwQ?ov`1^%l zd{<#acr!KlEedH@j=&%JeD0Rz6@1t~jfl(~!}4B5kYM#R&gIxf(rLY%qESSutHbc^o4GDCp0~K;AigRvWDk~w2pi<8z(O?DAxU{mJ zTe){5nHHQ%S3i?rr(5Q8w?vC@O^hfuzRRaBcMb68qv^RSV73*jOk6ACl z?V$|$blC|Gnmp!mckkr%hZEpA3i0>bKUktT8g8B6j5ZM~$-D4JFrb%-&I+ygx;YW@ z!~+Qw1j0q5PiXK*iL9_!!zukr?D_I0$ad@$-e2y)Io=LuQ{q)wkd`zHi;Bb7mlA;e zC(nlT)mhj_QT9sB8n<@HfZ{C4Dp}813_Rr+B9aMSlP9*!|ew33S zO;~o}4nDl<#KuSs(b{q~CK_x`3Y13S-`)sfyR(J$1i>m@VSQEt*>l2_bZ4w#htAbU7GARDV6w^ii!mIBIxcu^1 z@^9N`ymDv(vnwqTa49Fjrs6Oe-kLIVfV&EE*JfblU7j~1x(A-pR?u9r3PS$);PVPm z7Mznzm)0o=Kq?$}if`m}t7kyp*a+~nI)UxparEDQJKUqi-*p_gE(~*iN%s_82K}y1 zZnKsPar;q${<9~L>0_JmlB*I+xTQ)8AN2@6TpEkoRRJt{K^`vHx0-ru=3z$0cv2_I zbNVAbK-G`qcr&3Dx^9?4oRkXQe_M)w?`D?u?@=OnQ5llL#}cv1ji|gbj(hY_jEtYu zNQY;*f!LX7uHwl>*vK=+f;b7>_+vUt&8g>B)@ozWuobK1p9zV5U(4#u?ZE4r2N-U? zB?$Aq1lyOTK#aW+WNI0ZIlB|^=j%DlHE5>_@8!>BV>%sn|@xbuM&7q&gg$et%{|X)S<=FE2Fro^9Moy>_NIY zDiXisSA*O%o_ShmOkc0FgJ@RI`!0DdhoDXvu;n;39cYE@Nu!By%~S5=FA=bHUr1Vl z&S2f`R9Jt~k*hs3i#qepFP}USO9qP?5Ke)8}NYA4S0R64Ig?x0X?uX_z*Cd3zHYpD~zurAMF`EFw&Gd5*sCyJ+*|VpLn? zhfB=#Q2${JKEGFt3$G+#Pn;Z^yUh=l%hzClq9d-f`ixE&)WClE8yH%!kY|Ld!jZ4O zc)$8C-bw9&gc&7hWp)X$)Q`4+)qv3PL$Fc-ehK7$hH_X}h^-MKfi$I(;V9@L6I zK~?xkJcRstc6uUo%1$8*>UKkwX#|wk{iA;pqOpI1FTOn}!LEgfk#Y5uijVE*bGZi~ zdD=F#Sa=g`vgfnPxD<50TLd~G*;qS}LQlTm0Is==%e<5Y2j`9;DO&=lQOiex%JptY zHWk3vz$DyyTZ!3w=i%PWBUIzy2VB`z2B=+7$NwhH!u7@L;qwJ8TIcr+vkcGTw(2sJ5nYFilS?2e zUZ3V&v&HlKleh+TeOTC=$<6YdBhbZ}Z1u=Cq2$X^!YI}vko)chM%4tHGZw=5tYN&z z=Q!LZl1($iHo0@8?uz_0{ z7?U>|lLq1_U|tLr`0?3fcR+(0(KqzQ-oP^5_;Al!@n( zRYM?RDbMT}I9E0p=S7>QsIZf9jW{`d4y`T@!&SLLOyoH)la__E8U>j!x?E@zgGh{DKV4|ERSV!MdRUPFhhBz zn}Yq)Zp`>uHk{Pu{a&@IkYvWsV1Mr==@r^|!>f$DVrfQtj^%?)uNr%OClvRHeuw#q zKd^DWEbBNQ#`5ny#Yr_y;7;`LslGVXZs!k><;~b=70Dv0H#g4rt|4xcR?md!yN7AD%>YJaNs%)H^YG22x1g@XXqv|u;c!I> znQ1hZjq%AQ598!nMVArT?0%G=jfs)1Dse3TrWe)^sN&(2X)yC`KTUY9#O8E*;-YaP zR4&Dad95_zT^~}+cNTvSka`O*iNB#PYR*U>J)nQ|`n-H*7h&%4mQ-wAh)d=1;1 zmVm+KEG{5y3+$0DL)G@Lu;)@F&eS&K_x!Ht_avIA&Up(F+RO1z?_Z3wiep-mr@8o4 zN#gfA9JXH4WpC&9QLCHnwAniwa`{`;$a@l4d4<1s{v^Z1caLJ~hHtsfTZNn_?+L7{ zJ&!J<{g~e9BAauCJ23vgV%+>jl)2BF!xVq~#9!a1u!SWraJ$<@bdqhRIimdU@Y`b6 zwRSwqX>Y*Q*B#-+%b!^J?2jNbuM&cOjfZ)Bf-&NI3YebHMciJ^ zLX_6jAibA!pvdAbZhh%MUv5k9o+<91$yNB`qx}t*#dH0X(HMF@dP*T z-+B_yGdj<{>bS|;wo>Q6f4=)ns3uYqB5 z_B%+nUj~D1v%uoPH{76jhubmkBCeTy4pR<0!Ho9^;l}Y4~%yaOOKZ;6YWJ#BK8eUkM&7~~O=MuJ^Lj$Efa9_F@1AVpF(tuU)ct?`p z#wkys`r>zZ)k2ev>L|d4Q@YT1`$RzjwLwPJm9!(9T9e zaL6twhI-!hcoy={e}Z5C@%Mkb?n9q$Hytvk_`KYO3h%$F*oq%{Vp_4K8o3T3QXW81WCC__+YC=mOb0RMr}e^_2(#D`Jn~tjgDYm zk39a(xK@ZJP^m`5OhYewrvrS;KQ-o=Sj0z5=MZ zZUW^OUATv5SG67W;PX85Y0^?vlCzWJ&cyr&;i7R6_<1J#sRQ7?coWNtjHOp)O5pIU zAFw1`9hVQc3x(JBp}luE27Pa!D^E=$S+it_k$*ar6lmbf7&G+WEKQzUeL{t#GH99d z7+!9aXFo!RANKJ$TL>2 zZaIw^U#62C-BR??CuwxwpMk}xy9H6MIWWKeyYOpwIt-kh&I;rNtf?ZLvl2fF2i2=# zs78h9g?)faKlZbUi3w<3V@~!o9R!=whnQJ#1HZ4lFZgCBLzIwWiCrQIIhl`QlMirS z?KcIMaofSJEv2bha%6sVYXCB;&ab&~Om$T(jpD{0VG^RbGs5MR$tyLoh#KD{8 z#wgo_WhQX~-+l1-iy2-!yb3<>9IgjN04>K7!|hoA+k0%Yu&}$b2mDEdC81e|O=|p8Ej3ls*{6bJ!l*30ezJVbbYf z;N~4e+i4cuV22!GksIkLH&2|gTbx;{y(>$ZZz)uKPz#H-bFt#m07mq6aeh;T5T>=5 z=O$S*nHVLqcKa>N&l*9_k3Gb@rxb~q^g}r3rAwL?s}i>$5i+ql0}Em#m{qh1H7-hn zRKIk=ChsZC|IirrH~tM2Y@Y}I&N}=top-#?xhOE|mtcmgN0ps;wMFRBZjRHXLh;~= zZ9r-==+#C;oIF>Nz2q4K5o0e?{Y^(DzBLZ&!j;G(;fFR=< zxYdyhM>Wr)LHAqq&ah;0`X%(w)+m8;$s3ynpS|1}EP~5dR?$aOmw?8(Yv^92!c@!+ z*}S`Jxf|AYY?(qEZW|opjPM9+{Be$Xgw#^gGH+&asE4-x8N-ZIeArZ4jqeUT0+wJ0 ztADDHwILh?`%i3g3?+#nt1xc3=6R;r>A&8IlCDKSi^LywHCM?R~ZN~`HOGHw5OTy~4k zFXY{%du}+eqWfB8^(0x?`D-DJ+?arc&UesGiqH$|Z_%_-eoQh}8XJukq68Dtd09W9 zjpq?d`AkGFp9pYih~fH;;y~SEB3L@;K@#7YTvY`s zRGB27eFJBwIHN*+8#mOaK~AK%Luj1=x#?Pfd6B2^*PtXrp$5!fJq7m^X>farPqG8@ z>7d~&K}0qxv7}2yu=0x(`55*Pw<`GYPM?pcyw8x18GVCi2WyfY)9OGoH-ny*JP6`4 z3=Ya>U`>u4v`1t>Y`dE9=pj`Q*?O2~Mm5tBKHXT9VhhLk40W)^Y<6U^2>Y|{C(c)z zi$zM=cn|YoP2>rb|8W-8J>tQCSQAI=-T?d8DPsTAP&{ckhpt^Sodq1dhS3@~xYD6H z=#!ZbjZbxjA=!L}LhKhfpa-5dR$eT@X`ey5fn{WRp5P zk~)UtIk1s^yd#kQA`oBIW7+J15tac`i>Z8=QlnS-Ot7ZB}B z(Nw_W7L?8cG)rTkymW zHFDE(2m1C)liAm%!H0?@&fHoG%^$wv^mb4<8xoF5#`;7zb{JgF%x1w`kh`UBz~F2@ zj-NaW9fjxc+Yt_gQahp5T7w-OisAeJySe$P3PkQ&AFLbo9Cz!iXDg;g2~>~jRjhMXYaLszx&q4+*^v6_9_Kul)e$JODy7|c+XtvBL}CsRDtPO?T5AH zzp1?1F^C^l2SfgS_(?kl^7E9*sP)Y_N$nH&{m*4CbFnpSi>L(iRbtpQ$AFb64dFS5 zdWb9ShmZ}Tsep{bWI$rT%daO)||yhxVC8$HIP8~IeoXJh+XU&G7hO8j!*H@3?kX>QfnZ(7ABQxt(s|nHlvlBiQu|@qA`E1f5iu<7d0YD6;DawAmSvr7}r) zZqS&`ZI&h%@A6ECnKwaBs6!Q^dB3%iJ(KP_j0svraP~kRw)uN8nfmeMUDZY=ZN=wk zCu)-W_PIh$S_4xO`K&{2Blm#M{W#n?h%>`n*_Uafn5$I}d0=Mlw>OE1R=vXSi;dav7hB|`62#3qggtz|8}6+VAq!L*g$Zqmjx!ZUGq)~n zcys+S>YIt6K|(mH@wu2GDNo$K!hyAF>k*f(N!&#TU%Z~0gN3DKJj=Eke~ll?3dv8vj}A}I97Z}o?SdsK=j7B!7`qmnPT9B$Mm-0#comb z3yvW(F1^ECk$JRprv+vulu|>PL{jf#M0yX$LBok&eCGHFw#1gun>+dM&Im$QWQl<4 z4=d2&_bQ{)_rsi;CgGTUD@c#U1(?=b%*{#oi{FexNWykaGJAqRxIf<<|M+acgiBH+ zyLBJ%3vJA``vxMrRxueVCkR^-f+cY-EKB7)TCK}Kd6{@ zkD4z{#I--pIfi^6Pb41P1K-EW*gQOv8SIQ=zgL@(xT=+`Ad^D>p$9N!8{?TrmxS|< zM6=;_vLLhi6ZhU<0QM_XK{4H$Y%J?SnJ zRU{T<6}fJfNOp`ngKOvHLHy)0j7WP2_SYuCR^?#^iKPUT!8{g z&G>*uhGJxziazoFJ%;@%org=K8-Y{XiFt;`?8>hgHj>i;4dC86IeuOwEN%SI%Q0(a%vREsMRbQ2cJLE8JS)2k{J~AXdA{~v?;;44CCQC73 z!pj<=TvEYl>RvYqdVX}{zj=RoCd+Q_ys0?c39}L;1)0&@tCOgYB9QmdUvTQEA7JdH zN9xt5B1_T|u3RL_4d?Uus^5mBZK@0Gj%|gXWB<}ZOI`F^n#`FVvBSG#rqRV`#*=N` z&(P^UKZkLw>f!e>CvG@ngRKmoF>}$OT4=xIrjL!sOeN+Z?YBS-|+7Wnm#YT+o>F4{S3hd3H zJ=oZ>7go;8fdrdyw$?8KbxiZQ_!1#ErsNjr$7wRZ32As^=}8b&JV%9(0c4&*p1@@6 z5A00go!#?y^8S--yoY>7aPM-#k29foQj(ts@*V=+Z)@1uU#a*iQ31DJZHL6n!%)*V z36CyJh7j98_UX1Q%og#7`Q!o4(4BynTi?N*;rSpxJ&0~dE5y=GqU^}#D4e|JE)1M) zMfZ}OurQ(thxiQKSSwRpaw?G=-c>;4zF4s`i?uAXLcn(9Dl@&#KyrUX6X#H+$KD-JpLY>z48tB#p@BWSr2P?5BaNJQFczz zjJ+B-hzmZ|!w|E=rbVhaZIU+GB+$kqL%*S4Gn*Wk*>O3T>5{^cap-4j#Qx7A8}jF! zirHV`u!1t%z2g*q=5(3t_=Dgbdy9Is>k_`WkBfCLVDzC9OggAemW|`R){Wn=E6IWt z&tHd=?_3rhI=q_yhKv_{$?3!;O4qSqtrhVZyu+Pu{y=^B9>elGZX~tVfL-u)rb@@( zfakvlxJJi^td(!Yuc;c$-|_?~Bx#cwvv@0*#dLxG(xo7K{2F)X%y*br{*DgX-vR}* z23nij1aUDg5V(|~ny>@%w)SK2`~dE4!yTGv69zM7T=12G3KO;KrZuxF@Yb&gyr-Q8 zIeTYA_fREfHY&s24fDZ!-F~F|C$g=T;_Oz)PYAv{3Rhe%p@m7)An;u>OL#Jd#*9{k zf;E3(eaH#e7~O=it2aS=E94gs{ zTDnF!X-Xu$%aw8Qd?tTI`F^-qJQdHs*@*}CpTR>f4#NzsT0k0&`?D#Y$(V$eF12tb z?}}ipqze`<&%xRc<-%82KZ4b;Hvf6BqXzr=Zdj2L``dm1?!bQfZdoB*H#8Nv@z26l z!VG9!a0YYbL%3Bp>+tJq7u3JPzb=j*ctZUy8vD+~d;1*mGoQ^4GW#wZoG^@6_a$&I z226{Q~#xE*B1)wo{;*~k33P%hHx=U;wae&GiFxpWI|;b(F_i^Hf| zSUKL0tHB>tFW`mJ4)`|Yj90o_@bZkw-224oY>!$K?#kr*M~Oi&S*;&$8jK;!veuwk zv>tJa(IJ&gglu|b0r##IfZMI>+|v{p_WFV~{G6@J);^G9+)i;a<&OqTd1A>fEmvW~ z+wXEc-ZFH>O7}; z4-4qlpM>wBX%PC@os8!eV*$@t%-3IlSt7e|hsq9Q?Wq`L*$O7YMZ)+?(%fv(7Qt7c zI=dH}Pcvqmgn#!}vgIjHu`uE>-}QI}jaSOye(!5O#i7Msi&lczq_bfE=othjF9h*n z6(V!KijKbDf~&8u1I7P3IFq=wEXwgTr*|q5Bh8J;vwH?8$~$L%o5j+FuQv)BH&2D) zu-zbX@jjMq8Nhd`;>=dcS!i(}5_DFS;BeU~ZoWe%PI)Rp&dwjnKK#q$hIBV#)cQ?G zH+|%eBp%>$Jodo;J!Ys|yp`taJ;4n>XA+5DAHn2C4NM(%l}r8RhL>k2l7MO3z^qr6 zdv~({{5vObdylHaym$H_X{ZcdXOH1KX>In(Ujj8QcVX41aA--;Buer#ncmz7)MeC3 z6xWS}t`p{%;$jA#llhQk-#D_*(V2?%R6zA%b*B0^1I`ZpMZdB{Tr?{Q6Xky3V|hci zKzAzM`0^H&6hz3hO11n6{dapkOP{4!jhBT4KeuxLdFT z>;1u%jAX-`b5QkdHCHCL1J7!7a`f#+?0z){w|R(=4C8tTDt-zPGJ*K^>sI(Zs~>B{ z^htX8W|+J75AK@$8Y?}&K*Q%G2y{3DtHflO+}RL#@^B2fJa!!%bd86-5f{1Plp?C0 zpw1)&O)z6bG+Xq@2|a4n;Min!CituhJ}0(a}mmj^lTkVmR!g1dd8!7#)xUKk_*k-5<)r{a$kO%{`g7v;oF$UJAeYEXJ)S4>~4C z4x}DykmiTA&@uBl`pTZ-ohth5ua6k-Yx;?goT_N*_qnXgG8yU`4AC<-2c-4#@&3Y* ztf#kvmVgqoxa1C|>(y}gS`|JsG>7`OccXOFc!)Q(Wsh;=5=z8YuSuGu<1dMC=A@@Wb+Mny|D2rIuY1EbZ$>>*qc|a+h;{ zb*ZpdLW;#}yoHBMO)A6hg80K5kXRZkkV0gqK=unexq8wEp=D zCw?!3@%C;Q=90;?>m?z6OajJaxU&iJnc(&!3MWJ_Bk8AgaShMYDr{Yke>L`zJ@R72 z-eMyTE4ZL%$y82eq7rk;*5+(4y##}qnoRCk9CI|C$zjJT^1C#Tc&&E;?^J!5eeDTM z2zU#(%@0GOx*2<`qerGM`vrp;-RRoz8A3mTzgWV%}Ny`P+yIf zA9zNIo+C5-s6vH2OQdl47AIl3mIj=1VolyosJwhMajTvO@l(dbhM*jr!n@W6veenD ze*S(wcBN3HON5&kJY8Vnx=Xn3{&BiH><3Njz9X37_8m&Jvr+xY4N$%4jrUTW$$Yza zsI>S2{M}tghZV+>f#&1j!!xft*Q9g(mIc_oJ_T&(G_qytaZ+?t7H(9$1r6;}&|mKi zdqzc(Lv3no_=SKAFWLofO8Efi-5+rHwhEc`CzE79Pvru_=Hkyc+FV0-4nF?D_x2lG zA)$@$fN#oyD-M%cptl@5xn?>}IH8RP)-Qw~kM?qo$G_0|YkR5j#A&?Yguk~-xWfN^ zYoaQ*=*&@*!EtvkiQV{uZp(;=o!jNHS#ut_-PR%a`q>z~^3)+VErXl!?i4qp&WTvx z8Ah4NWOB(fkXYY3$xU6*fil%X&>NYKs~)Ow5qZNn)X_z4x5(hhSYI-q-@im$z6G28 z?t&i8Vo#TU;QmX}64W&=Bi`L_;o*f0lA%0IofDE+uj5WK-fus%_#nmn4;@1B$PT9( z`y+6A-8y(szMuEnYLb_g+sLvbZm8qRq1jhiqU2wTn&%AJcPgX{?^a^Z1q;EKVP8g? ztEuwVAac@r1oKkJLwV6`YWlpG9&{r#q+!*%LizP_(BYAaxuRnK(g)g$nVUGm=CXR6%1B1(WgwdNXJY zGt_E^o%|jpoqsM(+dmf<43tA*r6G9DZsGzW#F^cS{oK|sJ8`i>E(xA|mb>>W3EtFH zb5)_@D5h7yor;>qQV!eGrw~0m#LCpPL?6P!`@}7{4xcM+|T@rSU(BSeOTtVBO=QPS^1Sj109X_{DVHqxw+-<{& z+`6DI;9Jp$KgWr(nmkn}sVS0RTQmyrpdIrG8O2_W9mjkn)rsknR1mt4gXgWg$jnbt zv5#IZxcKVRA&7p4m|gwgnpgw3OD&| zyr!1{8}hjaEv>I$N4z)+lcd>o5d{*-^K~X1`2T!Sf-J01VxwPWbIRSLNH!fwJU=9% zXV*fq@0KCFH;`h*Jkz!2Q8Q)^6yW>vIQ;C^3?2ScxRCcdQ2WbAVSJf3+2}r+eD2GK zp>Av3P~b)8Bz=UXI=O;@@+q({&zRNcNHF7L2`K&|h}oT)2s5+7NfH@`mBU_Sfy@OTFic!H42c(Rp>M=5nlWlN z7P%^eILRY|z;Cc<%zt=ZwhJ#51u}EVd~)RLC7|8Ah*5MT%hHa+7w5{k-dmTrH8l|= zA{CwD7G=Y*=2of(UMPA{g_NJ10)qqj+`pL<+2;NleD%!>-H-2qfx1k3NXm*^#&-@m z`x(UKUI^K#xgDD1t8vlJS!{;>1TyOEEfiD#k0fW$1H0z+WOn-+>?H^~CN zyo6>s9E(_#%_QuOppRTQwoUp5cVm>~oScqBQ61d3aNZLLLKs+Y%sV5)F>_N3 z%>CO(v+mi@rH?fPinE8P`GDyCjy zCz?__X_JTTF$6<<^1dbnnk(*qu zM51ggNX5W=eB};o^FL=C$|=C=P2Q~hq7-rc9l-juQej(pBNry&FK9figL@8~034sn zaLAG03C4ogI6t^~-HgADf*x88UX z?d$>VEze=z2ooZquR*d;%%b6gW-Oyz9Gb<&m~hqy=$19XFw0gPfB7@_x4aaNt=_~t z0kzm8 zHp~A6>aVB<*Y_2;!f+Bhf8iAfw%PGc9Z|^Gdkh-S%_03e#fXKa8d2F4iTzJ^vo$CC zF!{v_Vo?4Zhbp3ACf`B1Yp%eSgv4-@<#$_6Nc$^F?sp=L!DnxIyEZj-sfsJAUWf*{-l?l6xx!_WW5! z=;du(pm`E`zcn38|D1-*e?nZAD$Urr3O3&PIKEvF2(M!T*srb&?BAjUT#`4MnEW;- z!SbyFgZoKb-@6oU@JQW}xa;HOSg%agl|ASmwzztma3zgyv!FASlHaSfdX4f0C z;cL10+{Ts2BqCS*S5VGCiA8iJvGZ|P zv8P}Zjs6ls`egq?q3Ik(`zDb{2}`P0%X9iA?8wVyfKSIC#wJl05~`$3&YTJ(qtCCP zMPJQ{p^6TSRGt7dXeSmv_hp8D|FNYHM-vIpHX7jGPVe+OW8AmXu;$Wy^1W1r*wwFM z>n@tJ0X=7@ZCXKGK!QDMf3l8b+E1X#8THtfbrj9p9GGd|7x5gfFQzFmg;3o@$jO z|7jh9xje(C_0e?LQ+bUmOLZkPk~^qPd=LaV^4STy|6pbu-zSanA{SQ7K`k|HJ~!L} zy9KAADXf&UU*JfNhc?3w-*;fKO_B87C__j2(Nsz!g?^HL0nPh|xf;J_j@ZtpE(iFY zbBH*)#l*7#B`3Umq#XlzC-kSzS>U@~gKQ6w!P)hPpmUoqd3JjRSsyc*j+1MGf78lf zD^~($16ACsIz9F#tI0{}uPqz@RtTNXGeC6k7mPWwfS>DHkt@cXXzE=LA}@Nl{HaMi zzb%8MM;XzC=~b|2h6SGcXUATTR>XA2OiYxnrYbV6;3hE@v*i?6o|^@kzcC!&{}zyc z7mV2Pg=g`SV<9ecjpGuoyAtOcyjR)pHy$5tNb2)iaPrJfm|a$d`vQ4x?cp)ZdCO(o zXg3;tcJZ!1`%ZzegC;5&ny`+G*%-b0Ha6dkVXxP_kZWoD`7SpNGA?^Taj_k!nQ8Of z!I#w2?F>zbT|>^q)zG95dpXM^rNSq^n$S8w3SIg~VTni=_v_9i&Pk;WCp^i7w%Ib| z=>guM7(9ye9|!?e{{PQkT7V&cwAl};&T3TGV6ION*>sklH`k{?oU#QQWj>0?WK9K3 zodP%KY~cRNSjrwo&FBix2;gce^(*E7MpyQ7YiSQGU_>N2%dEq>D;4{zV?1S(L?*wce zHwIL&k(Rqv;@9Nsup=f1HtQ_I%ZKXVN7`Gev?!Oh?74#8yxV&ItRyHUez-$J49a>F zajVEq7(8|Wr+B>+F8pfF&jSZ=xqkt;4De2=SScL0?+uvYH_$Qt1~+Aeu(X&X_Uj`t zQ!f;68=i%{(W6Nw&qm0cas=BTLeF$9ip9F>W(iaMlfG3G&lWOX3E1pwn647F)pYHWm}DYE$;azlUZ% z;d{x`O1bLN1Kfxc;b>;x#s!*6u{$YRq)TZem^5T^IrUZ?wHlyhi9YN~;aVK#`KS5M z)*(;BV4;8B34>-Q;f1~cqL{fC9QtH|)93x&0%y{{_W{=){S}V>=K;s3sl(9A*LYt< zo@BnffGuBQF!skI(A%{hO~$C>y~)Yc{(d>k3z|=iXZ%MdxBlfIKu!pUjo33wif_i< z!Bf35q;^~ku3P2-O=K;3@MjS_o6(5E2}-P|PZBHmZkxi^ZOk${l6?6z0MU;|(;;~w zePs4h(DC~lsE!}O8YZQZOKClD|GX?w>kDKveQ&_OE$8vMTq95?8*M|!-c)mxEeKA^86od6jDe|)G7qIvI_4<$} zKcDSF8vGv8D)ZsxVii0%`UIG64H1}xt>vF}zXb1o-bBN5Bbbt`Exi_U4%d4%qxoWg zvUcWLawH~^o8R96yE7$;|KMuy^X-IU3uBP`U4)`RH$Y?CS=>;i%L-u*JAM2NRgQ{+ zOa)I|Rl5cBCcDvzRl8~8%jtMX&yK|GuESn92^SQHX!W0GoUhS4IQ-@@%{rffGh9!g z2a1!TfnDG}HVzGKMv^vSAHB@{)Gec6%%=< znH9^MImp@E9S6pnm1tha6h5yugFOzkBeTc0)E-igq^4EbPFIHS!h_mmE}>@ww{dE= zz%!F_i=W@XEdLd-T}gqpyi;Rs$9?IPY3X!#&l`H+O)6DsFQDq3`~i!psK;V98Jj)|F&qo4x}1>oWz_m)^!#1DD`O>J2p4;lC?l6NUbN zOF(8!xiDBko#-1%aJ_$c?^(SB3Cur&8mn61QN|@)?^=Q7ea$#0whnxM-N7|Yr{M7^ zXKrHUN$y3y85CI7L#gx%3`%Liq^bpwyR`zorYR8jA4b@5d;=`LIE1z)>4LMN75r>W z0V|?z;AM}O7<1AYqCzH+p^a+HdZs^-HQ0r^4#r%S=SDnwy$=Q!zXF*${v313ll~EY z<@X^f*l@rVbo*?``|woUf8sbkC|4x~wnyOe%mjFQ{t8{Od=qP|sD@Cf1kko zr;$gZxss;2u+!fLy`J5N7lvZcesmXpTqsGq?>@(8&vtV=d*taNEqkK3bT#Ri-Y;;J zDB=7xd|{yC6w_T>4TEBD@!IC`ti}H)-E4%|_lfV`EVUH+7Qct2{8OkC z-$PG~^CqSGS@hMqDq5B;&zg6su;ihNoreCEV{`b=vi9a}!j^NYcwTA_=b3N< zXADWPdP@SyQnTUee8g2R+o*=4IBVLG3rS)Uq{L5@t^6rQ-kKO=vutoJauU zyYpe7L6N-*tbu_*WzOW;R8r{i4@`ztpgsHq4BRirprgORovlVWrx7&5s~n3Yci^wM zT*0MQQRwyf2o=R*$i2&<7c8|veYls4EV+S89JEN@>=w|9d&vdIX+bYf>797#xHEy&@<`tfI%C&0@3rccJujTVg6{Le|+?W2(D0E>^QZ>6R>P zc~V5@CLYB<%hJgHh9l@Y#seECtCCn7cP>2q6n=E%-9x?>ERskNA18UD*G)*@Z%@$w zwh9t?-p%(N{w&Pl6nt-R#9t9>F}(Xfs5|iwPCJ?~18)hOZnG z9)qj?^(fLifvlRf9R8kDVs8%2!1|yc7` zco1I<@{Ih#AK+-7hstWB+1{Iu+>kQGFX3azGxy^l+(+TnB7L@`D;4HXtf#6xC!!^< z9^>Zkp@zzOWZRQB(0`>9MT+MzjiAxk^H7x>%P5EAQ{~W1_XnIiv>s;9<^4|iPr17W z_h^RCH*9=#8(qCpvCZlPhKVS$j_O>r-4#Vz#1c_BIE%bvwk_x0LoN_WIM{&zbFFdAS1!gbWAeYbCY4SUSgw#M* z{GS6Zx^WnmaMwW5ppkz3X26c{{T%K7^VIy0H@E86Ehw$BW@?M_x!ZefgiBpF!j_vn zd&q1z-NX{{@gsMPyRn#?bLtaKTz(q!_Q%7#A1`qaol8!6YryOe{({>ZFT*~YNlg6g zRLGpx$R*yo2Lns3?#X-6mX(eR&W(=&$nrtg7l$}`_IL)l}29N1-S23L!B z*!)igoAtJG?e-CnFR;drXL6|BlMBr`7dZEh^SD$0C8dAA;^*L{WV7ZKP&tp#y}1dd zv`-2{%T@ep;GL`&XnX23To@uxEMXgTe5gW= z_g1iZAcW=i9)lHUB{A8?kZF!dMLT;FdOdQF&`X`4-xNy6O6$H7!<@mK)5kK_EB5nTy<=!5`SJE0-m9NSE9N2=* zx5aSj&_SX4Q)}XIDwY%5=Yt&%t1wu%7{_U8kj`8I7V_`o>lcQhMk$R&PJ0gG`@V7B z0Z|aMYB!fx^d9`)--Tjr5i%lRD_W^s#D?<$Xxu!Ht~Rm8jWcg?aq&< zF#ZmUC-Gi?2zG~C!mpk?c&kBzGvL}C`DA|ymc6)}3 z^R9t-+bo_{*$D14_MipZgWqR!uqyczlJ?{9amfI7E`Q3kM|5y?PqI;GfhI_$dJ&1A zQ8cM=4cGP4o88a(4nMNw;pDGI>=`A^z83NsrThPZv78@`Gz;f`DEK30h{E1$2zz?2 z<4miw5TGiA#nolF;-5R8*-#c3zP|xg-IK_@;}k>=R??VdeNcZwldKG$OQy4au3t-< z2($KZ&{7BEw~Qi>mWx63{Ecul+@Ad0`jqZWpC$<3;7@LxJA|D(7f_|rH}LxY4tlcb z3(VW{3}*9puEC~Eq~~XY5+0`?W~^d^y9^-yqcm~dpo3{smB3KLk#+tofy;GsknH(R zlk3jHJ_ijZJ3*I~NA3gPS-x=V**LEEhZwni7cphpG-5WQluIm$BnOWfvkx8ngj>bt zg2GC5j=Ef{?UcJGY!BdbSK&s)eDE!;9g|L;gn!_<60MM2Xhs?b%vi;HK2J5N8fG-+ za&cS^X}ItU+yfeg4$PPAh&uywTPBg&SB=Q$7maA}Di3`sWr+6lQdpmS0Y180;=(&& zU^4GIcdO_dXEtsp+8sE^xLx%ex9C277$Qw?+CPJbyl2`yTaT=8yacoC#mT(!A96p~X-{}4KZ;3KMxp58kwjd@lx>}r#T+9FXv7R{ z+`nf9$>#I1lNX)lvzyVxD zmx_7zNpC8u2`a&D$9QIQQAb#MrHMXyr@|6HoFa>N_>-&egjnbM23s5BQA*_|?H7xJ z-T&UgaPWN8+;&iC;`|C;UrC0j-S#+Qf;PC9i2~+KAgfDVd9G3lTwCBve!p+4MZ+aH zB_{_pr*=Wh+}F@lb5-!Hn8!+}NB+O(3y1qx!fn3FGl@e(xoYQhZu3-aribV0+kimO zd9qX}9W(=*^VPZ8?+tKM$^=O49tpoD1Y@1CGA>^?1*hims-ks=LE)?!%Dh%2_g5Lf z-UW&D*^WoJf;T^o<3?&VN_&64Ah_#}A8$P3@*+l%TdOCL()4PsTOme}F!(@_C3{r(_Rvn~`^9te>n3AY z_7qz8z8aUlI0W)v5(UEFxnR<(N+VM*q4zSLr@Phy@>a<)nJ3S2!rLp5?iK-$A6H`Q z*TY!*Rfg?J-2`iUP4UV*Pu!b$7+oV8;6(gG(E1St3-tw9Ll9{;??l-RsMJLZsPU}-mr4b8ScVH9qyt>B5odN zrt5yT308p;DHP9uj+0KLLiION?OHlVp&j%mtU~jj?>X&LS0VOj5Gu}+fmweqz#qeP zB(hzEoc!BLt)%(fE=ho$TL!rhtwGFoFT~lU;+*F-V|X4U1)qCnaSX|li{X9EZ5+n0sd_b=ySOk+;&Tl(67C~8!K^o@NELw?E+Fyo(X5xTE2 zE^V07ju7}5%5e0Z4DLgM4)I)*LTdS)mg2ilm_9+0xz+0tdh;gFij;vPqaMK7$vr6h z`5~W!Nd;hH>{iBMH2h-*27zjvW}q8Z-%Z7?_)J)`bOp6NY69n8WpQF-3UJ>qcYOZi z56_Ru0q^9gtZG>|q{=OVs9ss#E9p^t-}F7rH#TCXeDC35?SAy{iNmucW!#Tx!R+S^ zo(r&i5B!VOWn)xt&=&O))UlbxFx3{xI%P7~yMd-9w$(Pj(S^M6!`$`^0Z~AaLr94>VGI06hsOC?3;{s z*S2Ag!gA=0x&<~5c%F>03>%!KO5O(bLePFM66oQH7x?|M*ug(s`gw7dqRP1Rm(6IF zDJ^_2Z;ua`Zij;dQDBi_f-Ubh(G5pbK`=@iE+!Y?sD6q)F%tx}V|P(azwgkmKMqr6 z?ZCmj5*sEfk)iB;xTi{;Me+^{$88#{Sv?T4Osrr~@9Y!0*xRG~Z((FvS9{QcR z3m?~sFi*J}YIn|>WVKy`u>-q>-4n#gr&o;ob$bO$cwUABJ!QBuyosua%E3$OQ$~_!BnvJ9NT;WirAs(Nw7VIlRar)dcZe~Oa3UVfL zvsQn_qua~4!tqLE%g9RjXPb?or*xPF{~bE7)|T9uIuhnTF=N-ZG=dI4OG!)OdDlkU zX@-Y@`!{AQUOTZ5vp*`c=9V_DXn8)$cHHMaUCQE~+}(knM%BW_G#gl>U5B$qdOB6@ zIwA~^F9fP^0o}?G<+^9n$~_}sP%D(Pl2W3lH>7bo!Ie1bSQ0nP?oiP;D)?b(8M?pu zOs6~)g4PaG(zAOdt&5oqA6>pu!{Q+<5h^pK!~OhBWixT!Wljgb8j^k&Z?G?P-S zBT=^!fo=N7useAH)+AX&@f<$?{gwAUg?e($1E&0ZXcAZb*ber1{={>+@#wfDLpU;R zGTRnFsZshM9DZ^e7k!?^`gSb_b2TX_*(!!>ioQWnSc}t5tsDp%`3+WoO5)Ugy)o~| zQAlV##LaqE!Cehs1MgFJvi8Dq_?Op*%g5Q!*^OghU04(hr0cK^GY*5RRz58C+`xA~ zc1Ty$CEm;ulpx1Pq55*W(0V@6f7tRZkDH>E}rG|oQ*`{q@k zW26rjdwt1sF$$pKM#DO_?plOgl>xV1^J*AD@ z+;tN{zIZ#NyQs7H1U^5txR0)IlA$e8lC=U01MX4H7`S@PpH4dHht=`3=~hu~E?|cw zC>v?972nT-L6IB2)4qxu+$=arHytWc@c<3}lOQTvj6v7sp3~%KVQ{s4CaZ5(Kr`bf zT<>^QvSCjOci1YMjPkRF4<9pe`eQfD3@?WFWC+JCJdHs&I`D+^02I|tAR?1v$)a6G zWMf?;e5EttV9!n{J=Mj}Q%=(DGu2twixS8)n#xQa@1pi9O-`O- zMZt_UjO|;T!Rn-hT!5|-1V2ea_cMvGS$iFXtZ5(#UfD$JMlw?hOhDSXiYY4WgT`5z zY@IQZgykFHMFQW4;B!_hHAgc1FBO-@dor6cMb;N6$@JsD)flU|1yI*FYU(sIX zRG>s;W*y=F3=63KY6*zFXhXVwi^0!3Q7quD63Oh=VC3E+cB?g%d$2=`Jzg*qT<^ES ztI6r4<@G^W)hbOARiDCcjl-a)aUTPl)Yz@fR_v^45!W_Jf*p?=#u%R8weWie>b;XB zb1&+{z14T9Z1WnZQtSn@-CsdxQ7ZSJ#2zf$bc(9QiLhNSN3y-mOJH65eegTVvt{$= zppI+s+p;@SyNT%}WRraUgH|PeW9;8r$C` zR@)RQhvIzCGcvRdw;bWw+`Mb~kfj|lo2N%2pO*?uKU=ePZ>`|3a(?YV$2V@cd?#*E zc?`e)QP9%Z#ZN))L0~e1khUUNH}Dj`C@DpdI2Zy&v)B5HDR&!00wSb z2IR^)j2!8Tw_7}DTX;Ga%jnVG+;Y%%kzhtSZ8TSM3x-Hvq0^=Nxxr1Np#bVJJ|qp+ zY~hFKkuyk&+9o{XGY)d~d9U_eNA!8R4hF_d1fwb~=JQ06J=`P49^R8CKNn^Z*DzC@ zBgJP7KA#i5KUsx8VyZ#s@L!D0YNxlCi?cntJD~5#0V2rCz#bl$Q4Ebnw9Rr4WuH6o0 z>~`6K%ZI*l27*PzSvw4K*#vlVwi!3YZevPes`yw>ntZl$!QIVg9BM}dvT!p^R+Mhe zopZZ|#~sda`v=0HM~{DT-^PNm&1Rf9V;8x(sR=IKUV}7Uj|vW!q4CM_WQWE>dTv+> zjXzi7`92ZQ#4P?Sbpqr6oyRqVe;>w<;zD;Ioa@{0t9{n2T3!9^zNG5{QseA@8U8gHo_M6Ze#2Z8w_XUqK5E9Q_fdIfiib z>lyk1`gu;pF7OoEb1(E11>$4%Y37&n^nuY_cDm>z_1+XlJeKebs4q#v-v!@6_eL+w zQQQa%11C~j*$H@}u@4{lYT|!AE3sbYIfk|$!1KSuV6ypQP>SWdLF0I5qUcSk*D)E6 zy6Z95!YO2&(R#X-oS}9lU9|b}Rg`MlOGgG3f=;j)28Xxe68uiLtkWYA>KCEj+Jfgj zsFOqeydQiE?>9EG!K(d}F`?Q79(bqW2Ju+(-|^+(dBB$>>HiQcEp{NSS!*Er+;teb zHl76SDuw*LAK^}1G|qEefhT<4aQ}XdA&b{2u@&ZbG0kT@S+eI16|H8lyCDO=`y7IQ z%dSyzlMH(D-*vo_TSqP5?j&Ld6q~7+!Ueje-xdEKb7wr$8AFP$gZp?T13u$-K3C` z5k=bCQ~TS_mXX~+h)6~yNpYU*t~7|E(jXO~X|xBz@A>@$UL5B<=f1D&^Lf8LcVhW5 zbH0lz&o-t6(lRantQ#VVX%BKR;e8F-KWah$L=M{i{o-Wqd?UW<%W1uX2=lzG%09ih zgm1k@F**CuwBc7I+z1*&13F63FRvPu6Q!6|ToPSfug@-ihfzNj>=tk zrI{b&;K#EQc=lWi)|a}nUafI-<4t9jHB}M*IPOQ#imH6NID$AAcygK+o}}jI1GMg6 zD6l#(kL$bRL=XQQ17{=pxa66|T*msNINTgV^K1O@QC}RfnrFhEE(^lKRkFA-@Fbp- zUI>%@7U66UUwZA{W5GN3Si!2pm!a;;N?3S!0%YH~fKieL*mqu&?>sutlQS;k-i12^ z7WXRgSiCeftP_E=_aDJkZD)v_^OnoAErOkqF*tH?H6E}JBIY(1xuaKKa>`cUD{Oa8 zMSXs!dj7NoJvp1dQ_EM7t0!}zI_ok-J?@9dNO^kbvMbx0kY?v9xfM5VY4J6J?{rZ*#VTRCtVkRVKidnxr})r40fx+>OBtTt zzsYTUr%cy8IF8OKF;HH|yAH@h`0vqiqTLzGS%!yVot`zdzncQpr=w`L=6ZH(i79kV z+e|CI*r9#YAa_Pwk&4vmL-+zGdTY{tYBW-Xb#HUW@3Qy6d1?xE{xX+FDyy>FDkE{z zQFHh&X**pjn@*Po%>n;E*SIa&&O~~Y6xqgfMHFf0D=`V6bn zS$P{=>mS3^4{kIeU4fNG9cPgzMd+PkB%Roc$MQB1U+xerKwFx({1?7^5k>DOUBJ6bWauur zqjcTiG`L)8&5o&_!=tM;@K%oz4k$L`Jl#lIG}H$-64ltZ%`>>w>(=rv;7Kg~q!8zs zml11W5^NdW#NA!6jY_WHhu7M+(Z%s)f}^uLF;+?%T5^BD?1t6Uzp$6&k8z*@m8mdJ zJOHQ4AvmXB2#bdSGi2f`WHga+;#&6Do#C%g0B$`hhEyh9d;WUi*7Z*zP1cB|F zc2ssuW($mW(3A!_uxrkN1Z@f6vanR zcJS=Q87~Cv7ly)D*#sCO)-*ZFUEqbFi}^JOU`?Qg@L1(l(#N6EHDOmVnN@nD+Mpe zK_$)QShZ4$b3Wrqhux~t#mNWqR(8V~u^JE^SVtZODKhIQ0bH%!1Whl5e5XRzZm1{* zmzFqy@e60VLrorphSTt%{YKW98&71SUlS2iH>UZ_gCc(I>{khmX>Ec^n<}d5Day+GM^JgQMd-0j2_HRGWA&Y*$*BN)(D5*!?L4<3u{{`l zxA>uDhY0O{y$UlI+mMVMChXq`Mc91f2+AysK&?@VG<4@UVg2=VP`whwO`oqpebdH( zM9eFk*PaO%+()v_gW7CjXb|klt;JKKRpcl83;WH=rtABtQSGOqtfpWI3)7jwBE+8w zdT#fE%(M3uz8|WfN&YhlExB{VWL>n>vy$|MP=%TGyB)3jk6=gy)a92dY$mYwsX?V?@1U|Z#BUERXpp@)=aoyM;VqrHiGdf4uZ$)#L%MuFJ!2XprUR0 z5F{qRy@%rj8rtHJo&kcna|U4PpeCytTFKRHR1h9E5yC!bf?xg-WI*`==Kam!Y@Y4m z&Q`{uiEkX^4!6Zw2HW@TJ@<#ellA8aTi}GpN@@i{$hlwd15QAKzh{g{&Be)$?dx@#pB^c7ke z&x>S1y67*i`h6%@Ir*gBhbxcqQREXLja>=k;h2 zHY}E<1)}#z#uQ!FZ0`jMqpXFI8M)}yegoFN_=(j`BLGsLVQHEx)rv_Fxal>LS)2cZ ziN5i|;<@_F(9o6o>R-i2E{g=YO3ClUbpp9+F47vO0h?+v*m%SLL?;lEqG$h+|0 z*eyAdtzGDVnt_R!R2Bo5f}?OSa5986U&f=7)^O-a8X-FtV8pCa_?R95(Nk)$@6s!- zpNwHrwF0iFtPwBNEaTSr`~{1jhV1=@N5b{r%Q%sF)#zXJ7gScBfy{FaXgwfcg~bh( zrT0~-)&x1Y>a`bE-Q9(ajxo4iFA_}7jiPorZ!jb69~?Nq``b_C62}cXAYGy&oVR`o zDrns$=FW5QZ}6cv93zfyI2%=3xxSrgBlx4DcF>r#Z}$8D+j(I98m7V{^l}p%2E`11dhbE-Fgt#+>Jfo4TM^;smv=Q66gv_)lUDw zu$&=IZ1@6wR(6c%*nh!-4;SHXsR`}n9mq?~tufC03A|kT8WY{Nv3;{H!kz9k-0{te zhVedjW5PRHQ*S}lnkNGDqAWPHuLm8fz1Ri5``LR@8TB`P5~TmF#g-e|I7Ot8EIDG! zVh#V_-;`yWP@e6N&?efuEuB_ma!qR{XuO z5=VdQhDp*j!Uv&8prHK>nO9W|(&ra*R`0)|S<_A2$$QFdu9dk5ZE$+a2U1jc6$jj= zarq&H83z2uvvYsIgKMI!J~tho^Bs^?TW7)L{rlm~xo?7*UlbU(>@A*a<=tY9Q>gum z$!y_VODesk5BjCgbKf4eb1rlF=bj^_pY-|u+>2m=Rg(-Iu04o;at6XkvyW&M-hj)J zrP!UI`OuXaii%2V%w>!+E(xr_6LQBuE8!hH9a0d^eO^V9f`8()O1>A-ngALqv0!zL zziWP02A$mjsGl@~zsJtzibq?(5}sQqc1Ipd&VPmn6&F$7vlTD78e`PWf3SAoJ-%}> zf`fDph<~(TV^>_kZJ$lpq5L_puE>a${K>#B@#ml?n}7$L^dY9w6CTfHmG-3#aO!q1 z*;(p}fd?mHVYeZ)?03gqSz{SnEz9nXh=;Hl(^%dPSsL+N9;;s$!zv8H2Qfxu*0JBv z;m!NN7g{qv?dx{qo<2g6j$Rln7z6g3|Dpb=tC-7{z~=)aKzjaUTr;S_-C7?E-;F}x z#HMgM#;O5#I{4x{lkqUhnBj57P57^)hRh4gh4sgRxw%)mx$lkkbg<(CNwm9xZ;WH` z*vp6D6k!dD#b3C%yWimDNCj$XFvtyw-w`$|zeLFnOLq8ggW&8X{+Vef$2+nnu&G05 zA^*@!lJ(DvSnD3f{pQkc7?0gHgUPjZbR&yK#t03 z!c@KE z4aHijLA2*K(hP0zsnB7Y1I{4V(G8Aai>PLkCVj1Fz`fMdMX|adkk>ti{{=5$qPcps z;l+7D;L3K&&3p@sk0=7qqN3+}zHs9DFJONDRd(~ybozIO3?4nKON+J?qkNPNRpamU zZx;8G2@9Ih?e`*b_SAbK?&(j<78nUzG*800S8A*lBZ-1`D?B~O@3G})vm(CFRM-^7 zDyB+tdS=JimUG8h_1-=VHut3WKZ(-1kn`aBq6VIbZ%2cT+iBOSbS!!D27X-Y6M9ZR z$~J7x1IImabhkfmgy5gq9gB4sHS&b~tR|R1-qM{R6QQiqmrPw!j~7S%q=h}_C2?5UIb29 zm#De{#`Dkk|00q=cim}0TgVtxH_>MOxe1kT&-+r<%U0ZJ(6*vzvNUkLG-n|FpklSkT6%O&6uugNlr{?pa|l_<0*n<| zitGideR~FL4%*IQoJ0A2$YO3pa|l>XTY~||{&36P4C*gw$Te<)Kwg^@@O3>Lq zTws~lLFC4klHU3J`$i=R&IQUbH(Q?1w{ALjWI-JaW+ZUQ*`jn#tq9s!w!*7}+E_3o zLZ_#0gm~##(93yGtd#>{p}sd;bJ`0YbQD0|XcM}c&o2H5Y=MXW@m!bL7I43qXHr|3 zvRd!uY<-L&ty%bc`m-zh!0cPMP}T7@F_k%nRuNC&_YIz3%@kNigT7s2dM2)_dk;>y1OnG=v)?1GoNPuTo5mixgwxnMP=yB?gR+B&_M*( zPGH3XzJnRg&n*HTqp#tx@VeJpsOJ5W<<-rwqd|uKQHd9}Z}`R8O*E9y;egj;d@i#coub_1DCww6&Y}8f=N7I zY?MSN`1++n5P#=ux|z)H>^BRNyA<)~F*WMma1BM4eS&}azql#Gja=-K1TYZ43$sI) zu^Z95QTE&^+?^;#pO5%~g>96*8i;~F)DmBC`RMbd6u!>aAf2B>!OiIxik|r`xLGg9 z)R*ymIvA2y?wM4h=S6bM9|AK*~pM2w3_1QnCtIQyPD^V=0dTdbl%L*y5{ zc_qeE1T=QLw1AR%iFPAEZ$Kj4FG@jk$PD>;`<smH$x)Onk zlf_|i)k`itb`UK1|JSF!UeNka9!r%W zah8DCd&9i$0ZM>q&wO53|u{rU6r!&I)Z;RP4Uc$oKAi@&2_u*t2cDaCM~* zJNuciO)01FyHgHGubM(oUZ1u8$P#`&`H(LCxR`k?$;Gxw^O=J|0Dd~0%q&zw(c`8( zjmoHlx@$Y>t=?v$H24_Zl2foVc{z5)ycK#&E3)TZdvVH(om|K65p;+Adb)B-9kxDG zrmM!MLbHE8jrTUEZG3urzDYb5=f-2;_!{sz)`s)#PO`>r9ZgjkfK4{y>{2=Z zz50hk$Csf(y>FA?T=5pp_{2}Hw(tu#npoTEJ$TOVthUnM4(+%_#TB~^H<7T}K5*_- zJd7ONDb&YR?jD66Wl_IMY;sX)p>KnP4K`$e{;s?iHyPJ!aBcXqpb?~>F1{~2M@ z5SM%dtAp1N$urmR%fNa(b1N3gKnL9oK2`kssm02jPol7W8&t0Mg^{0pVOrrIJiYY| z%>7-Co)0A;PAx$&c>fI!Y3gBUQyc!7It-23i@BpoQuM>R^ZdSbElGac4KZe7^xs7p za8I{jLBE~x@{trWvo(unR9{7vJJTWGUk60JIGmqfBG@+m5gZ9vi2q5Jz{0ZkFjT_3 zS9-aEhz0&Lbn)0cIevxzRcT*77h={m&dc9UVSrU z>)3(vY$2FWPU0$8twcEy3D#~>3nN3G3)V>j3z+_f`*f#@`@(l!vNs$RfOix$4eR3% zm#=tj_G+lOcMkSR%F+`}6WB$$3#cdV2rE4!@n?G<_)I+r>4y~PiyQ@FGGhjq`uIcm z<~V>4^O(UmADrjv4rBNpll0PZ+^`*JP~Z`+V`4P^*xbh*A`k7fZls};lNyoQ70SsO z4sa%=sbv3cDe`2t8Vfbc5gM7R(%U~9fIQ29--_9GZC{u2bH~lNSUm#F&Q#!`(*|_- zp$s>zEfF-$${@xHiDyk3{JC+Gg$2prk7WfImgNpxi=K0SZZd4u*&(d4+{*2)Kq98z zgDQ2rD|n0~(<(azm8-?cnwNSkbWseHRIi4Er9a^T~E6j>dg3YEYUzOuI{VEo|T}(`=}GA_KBJ|H7v8Cg5Bjjn56OV5`(y2wD1! z`!&TAbnGrd-()wuB-kt1!_T$z1zo5RA_^YyPjH1HKYyASg9G79=>yvyxQ{MOqjw?7 z{fs4={#r0(RzO}__6pYXGw?ZH%9M5FV`hIPw^v~?3v4dOqZayb(m{+?%}Ao#_}^>F zKM7c`&F^h?^XGE|LlBrQrQZ@1==iG_5DA1-91>@2-Ln zNj{T$uLAmO#?ill9I1UaEa)(LLndu1CL7!KpuATDND9|ui|A`2rTD(`?z$1|wW9`H z{$hq(R!?TiUyg%{+a!D@Q_5uxbPHmq%F}0~-U+qcdU5;pX3P(jVEzBuuxV;qpkZ2$ z_M%EOx~+&?GAMw4-(lE4IG0PLjl;p~`C1Tay(>9ab#Hn`06VyEw1qFA~ ziQ}28*fvTOG=F{u#}|G?lizJTk8p*m-j#IL-FgUV8-SQ4PcifMQGwZKe@Hf;&lO1K z!0^IPguWY($rbykvbR1Jxz>&LN4|l!#(1DM$@pF2IUG6?PLd~9306EwW=)a_#HX?zd~X$UKgXG~GXo-YOLZtr z5i8;%Jl3LB0MFiCUjt^=_qo1925d{?H2$2JfP)uB=$rMt6H{}TRGe35j_Px{=O-F) zQu9uDu5L_4M(Z=5$Gc#((Q7EXQ-LGaO`%sUhhat1FF5se4s4mT7lOvcad&>7z(-f6 zvU7pk=(~~cx#1WJTzRY=Iwy3(s_|pkh)f;u zUm&2VcOP@rbuG{uvWKi4e_nWc=}4OL^B$?cUxURH`B`mrHyBB#<9iiN}mN#iM{dRK@ zKZ-ZN%9?q0wMC2Qs(*&e(`6&99Ggg%IEykLg9|v&(+7LT7ol$>hldqQxJxH4Lioxe zC}?>EcPcNyO2+RL-o3!0o;4sUSBGuo(V+b&f#*c3qU|Z3FHpXV$QL<)0^w)Fp=Yt@ z$~PE4{1{4QeQ4EJIqtzgGVEjpd>Z#bTu`)-9jN|F!n>ayd;2ku) z!t)8X)#II^c-UVjk1=_dz{uk_?jP%mg~)qc)=G1yWAmU_=>)c3)q!x6Q853h5)^K$ zBVH-T(BnZM1l@}w`_^pbX8H;RdBcy%Z~1X_*2WyXRkDY8&={_4^a^BEcX89yxuB9O z%g$~K_Tu-p{k~3qO(R?{;z;l1Z4;*9y{!Tc9ne2TqM@ zgz3{$@$WMOZ0(tg!PTj7u2qy}%FENLg#Bo_q8U|h-htYSF(5To3|-Bm(2~!Z+n;X1 zSjT+)6QxC0^EonEw-=-?_A@tPn=7|=z7RYTkCF$8w)BX{CP*w-WSX#=h&*uNmcP+Q zJryYy+ucik&+CD%DMdKGPK&P9?ZVH|cQ7mBE~i6+NEbf8GZDn^iLtDU z;@m0KW}I~(6fv%l_h`Q2Jq=^AdpJnw9}|WF^1(1$<0E+R_r%{88Z^Q43$_oe;EMfT z<62n@LX*w$Pm={Va=;V41Nn~A8Z8F9cxL=!M}8+Zmd^B8i52q7EN8qS{ChJf95JpF z*ROj;d~#}W`73{VMR^vHHkZL@zX~)-8|Kce`XtoZ?FXyo1`9{eevSo-7r~3q@~FvP zBLbNtmuM3oay<+z7bt@Ji$gqvrWNEX`(y-~O13MJ#3txC2k=OBL zIHxcTn~eV9@`a)_?&(Uf{@ny-N~5^4xyNx)&0-vIQJ|;(+fD9;ThQ<6TI}S7GZ4UY z0OqMA!>*Dk;9{E3eK^GU??V^1akMqfR4>C9u|e!<#u=QMgiHTDlJFN|fw_I&Z-l_7Te=+e@FNyL73B|P?PA&Yj$gTkplkQnt6 ze*ZTS=HCp(lNI0b(D|n@r+p-iq3#vkKMlZg*-{d}lF;+v3Ov&|-U_<#< zzN_U!7H(d<&jK5W}ID9BL0ig$X{*>aD2 zyhGpsY+I{DYnBVp+2Rl^GP?rXH+GO)hu34gnG?J>x&UVNMfiEr81`GG1e%N{P|4_5 z=&EM}mzT{VCliZ_VDcFdZ&QQtkh7fCyfU0|Bn3#>DQ;rB3yY3ZXFu~Ln59T4ocz%U z6+P8ly45Qz2~R|u%1kcd#!a#^Gz0d<&nDlq9-yqmE11471wBWsq&M?;w&Uwjs5dPZ zS8dn@JF=HzG5e#>c5`0{G8(y*u z-Y*|i8NAv92NoU2!ZV6Y{mg8&h=oB2$X!f#Ud!=>3dJn!iPtp$$KlD)!0r$E?BVU;Z5|Rg&G~_{}$EK0;!s_ zI%dC~!q#+N$FCx4bd(D3s&Q_u?3{NF-ff z)R_BHA@3D=hoWQdlK8rNAQse1?0b`NS&lb+`~DQtb3VX#fjG5&d<1&;y236WO)}*| zA+XL6cw@4eji@VyBASP>UA*TbuAcntRL7WOaZHit65i+YO#d0JqApoEOfTptTW>s* zZg2Zc%$5mp=a4RriRRGAlJ|)OD6^L(#bwzo{%L9E_&(8dPXhvMZZC;UxE3XD=R{ zbOyEt_dxxS6f3S?jd_ngkq!GR!RC+vw~arI5=qZ+%8F_1%$*X_xJm+G$1iTHY5_dK zE8ur^B9m*K#`jH2Fle`_AS?I{20LBhG>AkP?gVrjbWQ* zBhYZ#QWl*qPqY6srT2J_$!8;9E}nE^dUy-gYK~@h<4ss^*?m%UFpu9Cn`26_5FR^B z;r!zxq1WoO;LJo-?vj}U-)CKmrLt#)ms^~{W=Rd$j*fspBPYzQPeSQ)18_M#0?$3; zy;|QL=xMVge3o1z+_OP}-ZPV-9h$9V@rPZQFE_K&a?C!ozrUpNT8kM5jz|E>FXqhq zZ~@GU*bGL=mq|v30}f}Uqu!LCVDKdf=U=JEeaHBm+UF`#tCP%~@Z((y_Yz@3q&W+C zyOO;7y9(OJ{o&@9cca3b7A{Sa&qI2j=2j`c!m}|mg;ABM81*CwkGXntO?)q3KgWlw&A>iM9e?T^XoeFaHC=aB=l{C0~<%sq6G_x#1dM$mWiCblODIs?HG~VcNb054)ZR$Qmnd>jN842AulVLGdV8F$#<%-sB+%1k)=W- zZt^|83zXGHO0Xt5dnR13PkX+oVNa|rN{p>Tw_?h2;OA-XSt<$Mhlq3s1{0p~@QsesyA`q5fMs1hq zvn}Dj@nqdqTz*9v4xe&^A{`kvJJS&34yJI@uMFrB{w`tbIfi}MQ3`n$sl3zZA7Fkl zkh?cHmHsip%Sqqy%ByRz#6BG?vN#OCRs?RVOSy+8`9xK`91^RXX^>eH9v|8Z<29WG z(fr;}!ss+!t&QT$GZl&Tc|~y2Mp%~F1lyB#gGNRi?onxmMEi9xdvqCi@csMTb`kt)da zx8mI9j9_+O4#BS|Pt5t*%!wSBLvFY=qKNG~C^9fY>mAZU!xjlxmvfBVJv|1~RGo-n zt1O$x#Hd`*NP53d6>Fm};o(Ym7+RBtVNi`ubKk(M!kciXQGkg*0|X^%lc3o&o)t)xtT)O~>qM z2JmY{F3S3?#^dpuP%m*jcdhOb{^uk?W%r-r=3F+12Jvvb{UpsJ5a9o}U{Hr8Ly;;Hwu+aTN(!!;-$!Fh^OC^zOex+~3OZ`=5uVAXbx#%s|pI|i`w!cm;; zd<%zm+$FJkS+KMyiDXF~#Nl24gm!n-v2!?-UMRcESoY~rX^pjf^hV<+Q2rq?On=g?W1wzjK zemm(;7vd}Zr&xYUl)|2w%P}w8JrQn$3>7uD#z2jQ#HYc`lVd?_Qv`mL zmx2JLQvgQ=*!t=Qsx+>J``=Fq1nGusbp11E`67xhSO0+V>O9-Yvs|$7)@B@d+6nr( zcVYUqvp9bDU9P>f0w4JYK(KBvf7a&x60%Fk@7Wiy-_8ZNGe-O!xUEuFW&|B`I|ND} zi{Mbc18p6QfSSs4a9ey9yD`HGx3(Iwoa|5J{V`G6u*M5LPKmSbbt+UU_ZkuDJqI6W zR923aug01cPf)FVKb{?%k7`FIK|i!(KVE>1HS#!h(HPoH-@=XE_w0s-CcuqMD=Ith zJSxQ9g9pYd!MmY^OG-_#`@St1gu-JGsBsZ)b=6~!#tk?)OPB5)h=%`kf-rc`HWD%Z(|S*hAob=v}9^NOzj`Tgfs7R zt$CSHUy}M+x2*_^E<}+i9^lUYuLZ56L@1bta*743zPXh{JQyE zdTuGi`uVXu1u=HgLzL`ueg=lgZ-m|neq8@&OISPDCD773iXtQG$aRGo*tmQIKD^-p z4HY(+bY%$#54E}FOa5T>5y~Y>bVIf+$Mf?g=%!IaDE5WGYj^)p zMYdzS4=bxwYh zsf-ibcoMao-%No8}`5-6|@fzz!WU@tiW&fWV2B?oSCj`|Pb!mJo9Q_y3l zA~#e%)$w2|B30a0>3Xy(3+L)ORhhP36)bx?k}HomDlFqVa6-@-$l&+=^+^mgpWF_-Kxh0Oy{s*yDCN= zH(@e-4tl)zriwl-Gl+3%gb~ZMIOgYtfB&YDi*z4|JZQsr8%p5Tdo4(cQKGM+3i+J3 zBCTHEj3!z~U|e?)jY+DHxMD8wRtI zQJLGuT{yIn=y{ajXvd#8*1H33YItvD<~vx&dkL#0y0Az6G1sY6i!Z*9#`jkO(J{Id zAIuJeqTSk@hF3B+_^lO+&6);@e{|Vl`R$zB1aW%Eu!PH02i&kyhT42TLzH*U28}H{ z1;5sv0r|&(W;u_|Bg<7T4g6B1&=KGA{H`Muo-yRfyc_Co9*9(bk)faN(8? z?w9ved@*tbKJZg!u8VAN;cstTP}Ks~R?0NmUL2B=Ea_s0E@-LPh$)?kP)wxofbJ_e z-_cLzR))hF{tVmr-$)u&9s-hUn=x#$9jo+UB=n29-Ajibc21lfvpzEg3s%1n7-WgD z58e!Z@%iH5GvDxN+wDqs4Qn(q>>wWwUEmhDJ_oaqG0a*b9an6ci>V#QvFL{z80|fc zGn%Z}to%mMZyd4{X@3R^)2xIlJgYw{#)=IVYOtBIAHgcBhV(D(ASB9*L|s|VrjD$I z1fJ)8@TDo9_Sk_(V>M`;#(Qwtc^6vJ(+LC@5X;Qx{8>MUu!B?Z+ir6-dDQ~Zt0scC zcQ2$E&k;@xe_|IQszNLqZgT6UJQ953a|zS$@l1d{25i}k0DNA~GtBPJqR#o2(D6u& z7297CCTMwbuJVNDbrzyeZZxi+)QSC~M_^Hm{Dm zn5D<2?&b|sj-PS<^eeo-DVs`?+wA6ZCt5M%Guty^HY=VXBn_3DS+XeusfBM~Cg@SY zPepdxQj@*%-G&c6b6JvnH}4*@+!dMzU0`!iiWaX`vN!%Q z7DZ;4U`<~p9Mp6qqGn?3R9Y}Q*>{{dz4XBg{&%_JJId6?*B$)lR^#}|2U+L7CoGKb zO>URmPJ8#i!g5u6#->-Wp{J9n-Fq+k#QiiLdYr;$87`nVZk(s)d80tH=O7-)DIjAH ztijujD)72bg}oWS3Y$)dP?!`9B3C28ZN+NbTXqh{`yT@DJ<9aI;AS+~@#!^$XS+7YG9x0UP_CM@H?dz{A*T3Bd7s&)ne{vdhovwpZTsS zh9g^6bLJyWsae5CSievUmqlu_^#1$Myhj6muDb|s0gGUb#1V8Anseb**DK9FjDxs& z{JBdc9^V>>v-Ybt@MfYS^?LCH%$z>*tjG1ZxB4)wyEBKg=euaMRfH*+jAwQN-a(Zo z0q@o|aXYXAm2SKwU1w(CmH5X*Mw0JTe@(*0`xgnUo_W(2FBvBF;uCR_7|oo!tI3(6 zG!{Ep2U?b*B!96gMpU%o(}xkX(pZlbJVsbK%K|f+df;j)?_5*MAp2YDfJ^^@O5Xz6 zoO2PNqj?QWpM`@o?=syq|0wm9w`H%SLh#kq2z-AeklENkw(^5Xc zzuN;JcbL-a z`p=2=@s+srrWVs#+Du-iUWdMDLwd+14G!H;MJJy^oIC0;nNd+lj4!J|qHiwo{A&&K zr+*ZNeElrEUA>IE6n2G}=kBok^<9!FtA@h6t;u9K_XxQ<@uk3BAF*Vq3ETPXmEBFF z&-m2#BDO@H$55P){Z{MQl?}EmFgqGfe3N7**+V4gfDMu7nbmg%+H~ZHy&N|o#4cn~ z81x63CIN!QB%44CrVD zH@**L8A#&c#W)TZB!b-mFVbl{P;9$F;qwCBFMLLCr3f zy^FaHBJmbXWae%xc0PugemN*|VLrYd>p|}|%w+Kq9@I?E3ZF_>!ROUCiS2`>u#f8? za~_^1`d5-MK2nF;&Yg~*Hl@LuFJHN+SB)h9D@T;Jmtb4jXjb*GT+kbKj{6(0OEc6z z;Gxl{p(C^x9)FW(p2cI}(#Owa!p&%a8w#xZ>Q;Q|{D}*ZZ3IulEVz8V12xX|f$6kI zcsTSptcRHD0Qc7^W?KHNA-$%V%|BbUVh^LmuQs5Zth?UhdxxRi= zlJ;#C8y6l1X8ReKMM=^*mvxxyN1oq*D24np{tNoY1hhjvmMf8(gBNGIqI$C`UFT!zeqnU_t=Gi2x&sT9!q6&Hgo)-UvA0{V!nCE6(fgAkm3&YN zujkw#s<~mPL{5;^5}r(ddI~erP^In-G_d7+;D9!Xc1;1(Z58m*)SWs9FUFF8K`;^d4u^RvXc8mxTyg;RnO(pOHm#6g;0V937}Gu7 zhNR@!EzH<#2VEC?;q9kC+}RZcq}Fda{t!>cNuDQ(=h(eOSdDf~IiH|B^CdTNq6WS; zjD&aZdH3Yhv-sC0mN=51IBfXw@PoTQrv}d@mG=XPer{Ir=quIZ{`*=sxuF^FB0nAO~c_X=M)Zy@KvP97V zFAl3hMdU$D?LH1Cn(c7OhXs&aK9<2&b(S2g&vYAKkhlm5$l^OT_iM&5Wz#_D-)fE5 zZluF2*%G{wF`Aot)|j;q8L%C$bLjJ)zmUQ%g2?P9OxAb-yS3kuEjp>-zA+LFstURE zjWSf~r5~L--yJ5_^4xCT2WPYK)itqab5U<9*tt9oBM$ZL+?Y5~h+FNCXQLO=;(v13 z+4GB7zqO$rn8voo$P21?Ve4A2B*;6#fzx2@oGA`o8w>~J!H9hDg%o@o`QWhD!}vL6sj(Ag}uBpY*yPzT86susgxdmS|$kIekVb0RT#Nn$im;#R;Jz^I8{~@V&C&r^e17h>)Pqmp8%k|Hk0?x~+I^r#bKZ zn?p3*#1Y4hG%;99mRThD1U+ycnRNChdoRlq8g|&x8NVJdI{S5K^n7Q=r}Pb!Pdk4z>c^E_{kR|7x;hhXZbUx=-p(z+**#N zYs1OVSABAoV;3FPEkd%j0B2+=QQdI~+FcxpZ)2sIlerdjR>dT8UFt0K>2Mr%*CEt< z&8IU0jUi{lH}or-%nEbAS+|%dTTz?jh| ztch48{T>lY<)SK>gT5Bz?9(QkZ^y#mK5bsnOHV3rPlP#HodVGva#Sr(1xtuBHFtNR zKYy>G`SGv#$=xwbrhE|B7v4#JaQumd=I0FFBi?-U^|_m>iD1i2h{ zikJv_IoQGfbUFvpow(Wm>0I99mP>fV?FVdIa2)%!646K|mRYNIl8wD|isL4z(H7=5 z(5v!9)a5wZEsuh^Q+k-kT61XG-*KEL`3A4aZN*=w;_&s4NIdzc35pk_LHw+_aG~`) z3J=d=29k{-YPC3-GS``|o$w0N6PFO>qwCluqOaIv)8wtTrV3Mu%h3=bq(c8l+=j6_ z0V21im7g3sfbwiUeEF~rub(->dnTWY2MqS3dqFjbJ6>V8@0mkKM?Pa!%^f&lGywg@ zv%&1dG&*(OWO7Bi4|_(f2pXh;-(VZ7eCRV6D~IEnWg_%npFVvu{}=n#zlbq4nniN2 zJjFXBQ7C;_p7vHx#>C2#&=UU=++TIFGLj+e_Vo|(Lre&>=rHGJ)Jp@f2&XyL$JlPA z7}g=?JG+raqJhB@2rbBky2!owNO3(geCP^RH<;n8c|Xe@mvi?mkIyW3?MCl+BIM>C z1<)~?K<{1p3z<3YJne@B1bmgDdsz;vr_AN&(w$+vausGRP=&o^%CP(spXr|Wo;~5& z1DgV~Q8Pk@1P*z_+_)7mdW>@~a(BwEr?Z)#Pff{gi!q!sJ&R{qT*hCLTf#Adt1zQe zxh#14AM^{d$Ek~RSkGJa>{VB3A}Os0mGZ}NpfSQq+;s)>Wj~i+veTue%;KA6{18#_MGdU~Zul(ORiSWG-;N?e9ZuREaM9_j?;@f1rT{ zZnv;H;R9n6JA+!;t-yEoSK*+94tK6P3;tyo+F-Ct&XNRSYONDotcl8 zUz*_M+;QHSsNYDq_tPci9dOD^0_(h_(KewS2NpbHR+*dv8_lNxsVOk`QXCFm4F~lr z{*ZX!Gdy1O1S}p+gt4U;`1P(eXs_{}{W++>@DgUx^X4VYCL<}jVZAgRIQ<`>5hay3P2kXCF012f%Q3ZAQ|m`kh_H~icEwJpP4i`bUVB_NelQF!*WJUX z`c7nf)j>x6Ob{H+dx%Nvc{pS+i1;A`gqa9bk4}OufpeMczZ4o&b=mfCDKhYu^9<}X zB<*&!=rX*ODkO$rSfwuQ5$?yxg%!-c=6I@ZVSpV65YBMfOQVbajL=34_-}a*uSYx; z67*ichwmm#qkSp9Y>+HtwO8RR@oskc7X_;%r*VF9zdRAQWbu-KH2JS#1^Z`kCWcvi zlCM`-&W)PONCZx!Tii$adtXk%+a?Ir3dQWFH#c$iw~4fRXE+2)9D|IbR_yz3VRUY6 zg-K!-b`AZ~@rsbSYf=1+tOX)_LhjN#Xip&(3OEfFD=Lnj#B9fqt^p)_$GK8~M-8Zi3Q z9EiQ^hNl9TN@X@QR#l6oU4Yp_4Y7(?14Epro4kl;fE}*NrfCP_9G&jVqnwl zA=q%Fkk>eN3_q;j4t?f2bc5%5B38l8QG+hGER`{PQHUyJIe$$oU!r=qI!_ONYwU^5oqbS=@2Vi0TF> z;M2D)=zTmn(Dva*=icR9eq_$F%emC{OhFRsXqDY7yb(ugvg!bcNQ!6^*{bE?5 zdx9;wDnXyD9$@n4`@)6p)s)^WMvrqoY+lWGUMTk*`#~MqvBVPg-8Zh&bnPd6JiZw` z8l&KlZ7Uo-R|y8Jwi7U290J~wDl z(rF^a!bo16LM5!>Yy)i&)V+kCv})z1uNcXyFhRtj|6-3fF-XC<^QUP+VmPm!dr zn&j)d6tXN?hS;uMO0pW#cwd|P;Z^ZQ_%RehbBDiS!j*}{sV;yX?G>VpAxTtnvp4LM zxk&i$WBF-I6p2s1H)MOpf{vFGG5UBPz9nQZA#Z}P%svf0dK2(>UL=`w=PL~STunD5 zi_sHD2?@-&%WLa2VS;}!bZpN_OnJ0~INv=-eXhI!vm36&b#*FCyS$WM`zHl9mrUvY zbFZ*Ku8Y5Ug#ZoIjG=wXQKTqp3{S6)A?pXl=@P>+`tji|XuqC-hudz!g5NAGi5P(L zkwiL_d4e8kq-5}r3!N3PizWD@RG7}{f0{sq zCrjfA#huh;g(Kawr3QSiFQwg{r|9WQ;qp3rYrLx_O0G<-2GOAi;KBt;-lNV zbtw&?oZ^eyo-RZijo%or{*UQ&Ql$3+(wGy{e7N^moiNX&$at<1Etz6Z@)RbLt`EWN zG39G)s4+5s+SQ2vvc;&Cp25m)>u1JF?C{Kt`)t6ZB3AN23HCH<(VB`hP?5G_pSdr< z3W+t)9MTQ$+gP-8HJ}f#UWYy9FSuS=1?t^;0in0mNw;nYguQIUS*zC4uAd_0SC$|V zh&7~SF~z#rpTLw)C(kEe;T)gK8Np>&;ra2G_$`RrVbO^QIvl4(S&$wGt;W-O6G8Eh zB`$b3i?9V%*sD_wt$Hj=xqL#sj}#slXhhOuhqo>rrBgnvh1%E|WSLgST zs(a6j%AM~5FQ++lcDp@%+Z+S2)>8?8MiIL~Y$eUL)FT42Lc}qNk8uUP?Ask2-~T`u znLni)QuCIA`}ky1wmO!vnA48V-(zvV5urnxQcV5gc91Ib#m=KKRO<2u*r#MiCC&O+ zmy!|icj*GhO%Jf}X(Qi%xd2&NB|!xDb%6fjGt7h|0;u1jO4q!}gHNqT=!q$2bg4%r zzA!$4o2I4m%DCvRQBMX;+3<~7c4a!fm!1xCF^W`%%d^^=r{EsT-%O&OHtktq$y~5J z2fhP$xM#){n7Y%M{JgC||K-$y&aX@`*{DUkdV+E z-237SG}y8nEBgxjO)?c8wk3dHvl(&KEashCCq?huv_lnJgCAoANnVZtIehRA>bu^f z0Sa+2Dmj^eWgZG2D#p+YnP}L{v3ZVMV;h7{p;(?6DKfi>BeNb-%|&_8c`=?*@`&R7 zS{1?l9wJG=8fW@LrGSc>guu1*)i8O*VK#NCIJ1?@Y?Zy+4gG@m@w@6cr2n%cs$rI- z^;;-oeQhJFctV;AuH3|E+_IuCg?F;*T<@g5^a0Mu(WdDFi?DoOJXAM)V&`5Eq$V8; z+3~}|bhL3dC=T*r(KBUWzh(2cuKtPxQAMofVc>Uu5+{b`b7}6IQ1n{C&ATfnk@Ew< z7ED$GEpab4Pk0u&et#1c7`25;#W^gYO^YXhA5) zQWrT0?|jaX%j-CY;FvM${nrjkGsUP-_D?i1R3aWFbtqsqjS3z+NsleIB=)D(NK3u~ z|Jb?dc&M_N>HpZm(UDY0;+}Sp+;56g(V6cl=goI&y38Eq{uc)W%z{5EcJj zz#O+7@X}p?{8=vx<+ABut-6lvGf^NU%z{0}UqGaW>sU!?5xRGeF3H!jq1kf^z*&xv z%_m%FLHrBwPEVSDX0;?OT)PLHuG-L!WKCiq*N;tq4e;KrEZF>cBN2YN4$kgWW7c}t z!ZVFMBzIvOgm61glWS5mK+_$CE7#Et4#8}>(qY^*|2KR(-pqWvmdqTPl>>wO@8jr6 zJ_PwIk}WmGkRf#e$2RoiUtuj2UU3DTr#@m2ep*1LHLilHk5O3PX-y6Ik@#zaCrK@@ z#n?4V$+ZAqXp`9so)c${oN2#e zJ-?>J0NZn3g7EA_y0~5mij@4Z^Ys~M+3t-ma=QT4%~2i0of{xqDr261~iLs49KN}A3-;7KNY)WOGkbJ%ShS4mRG743qDF>vn~ z9+&utE;C|TK>$O3ngFUEfDU0fBdP1Y8jWV8jXiQRsR%BmOHoO4;Egv%O5Dn&E% zUXP;LZbi^KlLDPy8bt4~HK=O5M#s(m^p^J^4DoIS8jV;5Qw`|7%w<0|?4^C2EqA5H8gjDMnEkL! zhu**I1+((sF@d6-N6J8&?p!i~{#OxAqv(9lPo2*+r5Vy(;rFm@;4r?~ZA9-TC&2SB z22hkdi#*-#&zeUXkcba`?6gu(Y8}vz9lk5j+i(GVjLU*Ekyo+RUjtlwp5iXfsS>Gu zf_R<@h9|@8h;8gOl$3Y~a$63wt`1{Ryh@0;tto<}dy1s%@;P!f$%4v%Z)O^dlxTPA zH#8k?#mEDPAR`GM#5$C943RGxv44?2jrF_y|Y(APAJmegBQ#hjVo z{dztzIaG}vH&t=rmdPYWdmfdAv-HS!E~9a?8Rz6pfDDsBn59P`$M_At;q8ahnvO)& zdnplm2$-N1fE^{yqFs80Qh`>q|+*=SSF|Tgth- zXVCbBXq1}t31C!@NRu%PbDKdc6}kDIr#Fqekxg5@4`JY}qop@*JSQhN6C$agK(zw5 z5&tFT_&p@NnRAF984pR*BTRhWXaJWG5p4H|Mv!RJ+os?Op@j<;?r@Ac8p9S zjmy-@g4Yx^O#(}^%Jr#({SlgCCPQ;wJi-5T9QmcKP6bBNNphMHNxrMfd4JrQkhzm- zeS#&*a!y*tTAtpVzMIw+aA(lV*QjG}4hc=tA|oHykr@U&-MaEetpf z7xbr*l^tDZ*}R!~x%fSdFD*rz;AC3m-Uy|Kgy|KIJD?d9MYb<~jw|n|QOn;a*o{5S z5NxSTl6-VoH%(g-`NWbbACV?&=I6tS$H_!1@HbzJ_a9q-<1SWem%$sq6A-dfhf3ET z#Xkq$GrgMHR8s63dF8y5tr;!ioojst4)^kCw0J$7kywT9Zj$)-lqZ?glS;f5Rl()n zBy!*3EBqWmP?29pR}_0;b}fYn>n|Jw^c^}}R;P+`bBHd-F7%3%Bj+^+p|Ro$-m5PucuhnYtyKOnH_pn5>sAn8AL6#;yEa7%fesq7U99}Kpc$PL|L|$+< z6trK4E6x+>J|+>m&sM?CT{%o{Pb8z~A4@%7&7>q?8+)O&1Jbzs+{H)f_-U#hoH{^I%%ev(vjqZ15hC?Nl*6dk_Qgkg_rV1%rO=K~k<+NCbGOs59({YKf!WO@2) z%!}@R?+jbx?n9_?1RKFQhpKi=BOwP;S(^pwW_nk`l@DQ{w`K>Lj5?#R>I06acM*0d3d2#eGuSZyImT}n z1BUYy?liF{&Yluf?(=NdFSh9Zz!o!gGZbnHs%q3)tg~v^_lNz8Q2CU!!zMW@k{(H^O8T}5JnoLg6WUp z6tr3D56T6RWL(*T(D&l>cSbzixI#!(vn9|n>~ib zfz~u`>Ka-a)WSAi&xD;l$+%KYktCdc3Stpqa6l{qLnM^3^W$NdwOs~O&OC)hRg$oN z<_z$@qKP+O2{4Wv2NJ*P@zj@bKDTdk=smehtV5IpiOJi;PWsl!{BHb?{E-ikG<^rt z`8NS(3p|DW|E;6em3ky~M<&FL?jt>M`ZTd@D>*pR4xbLUaU6!7jPb=KsO7ks6*(eL z&V6^rcYjPUKAOdIk0i-2*Kqh2B}q?7%E9~m6|kc4GM;R6Va4X=^8X9(hjTA}i+7cS&^!Fz57?oMKMw=;RBBg{T--s!g4igkN#Odei7j^5c{(RFPyzqaWs zoV^eLwu>^*GyXT$9hW5kxV*lv*gO=fjDmFjUocsuM2GLl@EQW=F{?Hiz)Vqd`pxYb zXmWkLrIu~z#C^VWZ2@a6p~5j@rxSzQs>Er}3OcfTFSa?UVV&zXxN4+8?-)g*!1pP{ z<&`d#t-J}-orYQ4v?{P(sY(RrenIynZ#3%^B+8wdxX8R7=TuK2H*Q(bq}$sudM^Vr zHU!Y9f1ONZ=mJ=JF`54CW+B;~AqoffaV%*mvYibjyXxhLdErlX`MV58R9TE!`XU{9 zlMCTiESI0ok)RPhk$hL{87LjP6X?({%-fQV51!2Cy8DjI;McuWe?vLzn7E5qIg%FJH>DrUV*Fy8MM zq!OFzvFd0R&im?zQEwDD4^TbZX|Rb2ths=*994;k)H2+Y`~q*R(q%GrdO)b#5QWeB zqr!JRaYo(mr5zv@t$7$adlZb_A7GAi zJGh^Cz-w4q53cRAQ0GuL%4B7NTlW&)>)3Peorss zvEDXt;~sr_vws^{Evu`0xyDGrI8OsdeDe z^Z~7}jAJDeNmRG~#uU3q#(F^*wj1ArR`DbFKEWEYN0sQ-gy}S9ZwamqqKxb11#Fo+ zk7~?cNY=j|go6&v_{BySM=X{2PNLkdGb9R1N7v9%TW50OLmE~GtznFH_i&j?KF)DS zg>50yR5MtZEP36F=XW(SKQmf*%XPlt3c;Q1_t}a>RpA^nWl0_$-Rnl5AFhI#A(^cC z297t@dw{&<@M1kL&#=u-RlMy_G)c{l01!PNi-*saV`xwXTcqs;sy`+YJH8{TOUqHK zr&2U~mO3UWI)kdxTyVb2FfIA<=m(laLVhlNdqM_u6aVn1oEky-i#_0Xa{_6!^k>Xt zFM;z#5i&PuH}ZlSUPZJszSRvN-0)hM>)-3q9>b&1zs`v~=H_2FRA5nLcOgEU8Kkh=B+DB~SKkMt!> zo|6-5do97_+*B+!?qLVNt;Fc(5s(^u9#O-H9O{`(T6pEmD~`uJ9?gKof1bd?G1&Fy z6t}O7hM1ISJp8GLU=^VW|X{=Jo4j1?jmGZw>ves|KpVFOY9HU(t% zmEo2%S82ng;9o(%T&W-H#WGxT z$UVG+NpGrzy6g+^;f)DpOhcKHn`XqyU=sRzO(tJt&@nPWw*Pbv`E#7wyAS;v!>&qw3&b6nT=3X1s@q7%mxk*RaUkENek zo@X<@c&iEre|*IQ*FD(SH(PMT?*_xz^&A>_zJ?m2r7{u?G(7jz5Xye#5J zOklC5>>HDiE=eveo&c`>xADXHZ?rmW0Lx4waOpV-diRGg*=(ms0xM^LPqYB_{hoG^Z8BL6NRBd$+Ds^AL(7qcO>XinX+`IPgok*g?^&nPX zGbd)h2z@9tonE;#3 zPSqzlRoPgTzZ4HGdctPU^d`Z|b=btr1%to>;9E^)HX6U=-?Gu>mx}md_6r^vyc>fT zc8tQ~kJdzk({|PE*hqA}=kfK%gK==FEv6ejg2p52G_!3pc5;2jxfW(*uV*ilktj?z zo1A7OQx>rr_uEi6K^!Xa25-n!h@42CO%krO;`ZskS@{pLOw47VTaTI(+m)Q>CqMwU zO;AJ&%gs~#N;Js)6Q!N@6lcb8zTNeYp-A1F81CzXmV`#^|LX`vVGHPB^HWs4ISXzvk1@`Y zD`fl{Mq|6*>??mc>iJ2TS*fH!Ql-4H@!7m`U zYwW*!cVTsHF*C(bhhA=8hJsN^jG?3_9@PwI4APr{nXOHhSPG$3Oc0Jf&t|mY5n63i zq4RET$D?jrse8~3*gIniNfF~1<>yq0daD`1h0!pOcax2|_61YNHSla+Ax4ymksDLB zq3*6ZeROI8w%_0~Ad9wu<%=OEu~m%h?Y_<0d!K;wvb8Wt_!_M6*JINjD6neBJFz34 z@&;pEN$V6X-mHLZ^l@{yno=l2W*bG2*D3ONz)_ydMz4kW;brWmTPEmud^y}O&&3)) z0le#W9Is1@fUzMj2tSQ5)ODv{0LxmTR)m*Imt%55Ivko)2Nf;|5GUX9?I8#mMh7 z<-OnMj`~trWvxA>AZ#!b{>%Dd6{sx$yKi4%?6jiU;s@zqxSGq_&X%Kk7nak5j>crp zmhH@hHtvko)5vvS44BDWe@@Zs0kbgmKd`*kUpBPiAIl!Gq-x!BnI;<^QQq!I+J+ig z@NEJM7fJY%{tOmrbn!nnoMi@=Yr}`;^*G6L6t}fkz`^wwAT*w3)+qRcPU$ZE^s5RB z=J?^3gcj&N{Tnn}JZO2PK8Su*C5vU1>EV}G!CSqO{Sq~kv>BN}{c1i`gDg>fe;a~q zS{b1)8eAr7HqHFB2mAEQSdrX6o$*s>`mh|0Z8EdE-1CI}asCTl%Qj|Dr3t`lZ;q4R z3-nITR>VyvRLp(|j4y?Q>+#v7vrUZEjcLeXAjNl1PW>L7!oP_I6 z#tprNFzK5pWB2$QaCCc6OIN2QwT-B;xrynWG!;gJ4ujK>5RsasNJ(`BV=nT7S@SNL zL8reV)J_*yv=-;3XY%1gPBB3!_Fm?fdm&I0BTUj|3t2~)8O3mV|=N&ofd zz>UmRp2wLvOXDtmT%#wS`Kst)j-1QUvO&QN;Fp4jyopb zW@HxsLFoye%;zKOSm?Bfs%rlR`ymtBX4?fX(w9TWWkM51q>*tlRYJhJ*@YZ z6|($?P3BjbJ~IV8@cB0MFHhl(c&z2QWq0DG0bz10REYWq{m15HO(2&0j$_6rab7a5T$kA2O!;WEozKQ zF-fa*=QnG{Gc|Q>>{^_CcU_fpX(oEN!JJ? zIl~7J#*G8N{{@zwHe~Eqp2gWZ+4#y+o{kQe!QJ0nCVJ@-TJwppa+ixV8IPQnpx2DRd>Eba*y!kxLDY~wKx=CDyOvui*eErJ^1vBP@s&znuf z-B>DZiE~uy$MBsrWMNyZFe(1<9$EVbZ0hbL-beRph*VfWq&EhW z(%vd~Yv#buoqiq7^=G41{wROZ=RjD(Tx5<3ETBs24C&Q0q+&9;_*MBYG;I=vnh(ve zh;sv;)v|^C_ESmL+(%H>e~0yZ5KO)aaIT`7Xza?FKz~Jv!n`^MnpxP0kKfcWPs|rF zr6qR|r8}YR(=>WVNSgh0vIRdTnnT)kJ5W6H2!fxglj4wW{?u8L^gpH^MV9=B1&jZ{ z9Q_(}klsvVDo?PI9k<~AonLIg$%X73-;GdJv=!pcw)66o%Ajw{7l?KWfHYUmb!@(k znR%7#=xA~dk)XwJambIgJ-Ue=Xl{X0DKj!-1L6YlJiI!Z%y@T9VdW}fP&wNg_8Wd> zni(P9_TY)sUi&2~|2|HAoI|iB^d$2q?q9_KM3~zkMFtZ|llDT;Yfc*^X2%dp_>DR|8*+R7pYme$sAr9-oC3vq>4; zj^pbQ@Te)od29V3;ao8lyl3hvj>5{8KMBw}asy%-e8Lx09 zhPjb2ChSYsa#{Se|MtU-v_3o^)Wht!<3}^Ucd}OnSw>UEfT|vQg=_xC;{~nvD0m@* z9b4*5@7x{c?O*+k70{Z1yTvo#+M>exD<9uv)7sS5-zadxe2JBnWkG7xAm${y$ zq-W1zG-Y<)qgT1(DKi88tQBKXfF49rHJp!&si%njEk z>}A(0*yF`z`hK1zhkm((vCBTXa@HSQr;$dt*GbYndRx#=?KGmnGJ11D2#DIu=Q$Le z#8alT8NYw;@l$>vPJDj`ic+`GgGRqpCJi_zDVNc1D1zaFc#KIE zh55qksY=^(Y$~^)<~zb5{nH~x;n7{Sww2o@V8uT(!Lj-GMqi;`SP#x=O@m&sEbd*)hb`3! zuqfS!9=_HAW6#Q(LBw)n>^TCg7Zm$oG?A;U5j~DNg!l8nS{<>N;@WS zmxjwT`J??8aZlm^IJsXz-Fg3zR@N~)?&_g8xrk-!OQEjm7@k_AM(iuR*~y%pmH$v3 z>+`cAePR?25i=^jOa zdLlvw4ps5}y*u&6`CPpG=Q!(DDhSG}E^+hr68LCSW_49P58JD!Vn}N!dQPjtaE@K0 z=dFoXt1t3i78YYvcaEKkaDf{kwdPRvFao*T#u5dXQSChBGG$(9SV0_JwLT2z?I6 zz(4a~l;eLLTIzyw%ocog^fvZ-R5E_6a^TJN<2ZRKL3@LR{Mhqco>wOoJJTHS@YQIn z{uB$#9_m1ph!9EPc23$)#OT3?nKy8Qdh_Sj_A-H**s_)3Slp%$^el&4YFc#)Ma%X)cTI%m^z<%uvw6F?byO>lJ8>r zHBQ2Fj%~MLX1>+drdRyOQBm-7?I7;5I6yDDKCy~1tcSLui6o+W207o?#TJevVU6}; zR(!8L4L{d_;~vYPOI(qx?=EJ~ey&26W6sAaMPkuCG3wfRj^@Z%5bXdi>l^98goZC6 zpG);YFJldO?=YrIugjqDyLHsXe4dp;R9TtQac5>wWCl|fzLQQ|B1r6hNz+Hlf1oAE z6#s;)VW#bM5U}b4Kd)0LxqSkC_;n_*PDxlBH^zo(h*Kk%g<#uh1Rpgf(za7YI3P8N zoc^21oIk3BN!r{y_mMm$zf++-s0eiI-olo%`K;F99$v>K77nV~5xdw(hDtob{RaX; zgY|xl z@9$-vt_Pa8aVEV}xs`gQ2olXZ;zUg(uFURs5hlh6Q$2-kaC6>MOuwFtGis(0m&wVv z+2Aw#vcQ6F(yRaz{u~;Rb_>(PG>LzJ9`OJNHY;))?s;MWcXnR`GUGbW@`nidb2bJ( zC=Nqt_ALybX9J#_1!?%-G?aYe0=b`_Lb~*NJU9>mMOLHOtE~XvKDJ`r)9>ii{Q}$n z7NE6N4*b40%=hpWBny?KX;i-n|5P^DS9o|I$8P^&&3+%o<9o$1otqn~f;a58N(c4L z6Ud`aCZx9NA+w>yh@7z!q|G0bScAVM7_qE}e==?%t;)K9Gmq?mYMu9NvfnQl2~j2~ z>g#y7%M|JPyHa$*{sNX7nUdP$cCa<(9tH+>;7mOoCdDiZ7mqJyycXSKRjtIRLZKCt z&t(T}7I_m%PeYpdCL4!V|6=BKFNZWoH`wf}LbiCC5|h&{=s4XNbQRpWtU?8r^k0LY z-zs3?o{N~cG#(FXTTp|AzM$t;iXZjgpyUOPU)rpJ5n24F`gX;>L*r}IiBd=W;&z_!-4k{~<~{S!mkEJqbF$Jor;I-w!zxc z0qj;)q5}&qfV$jdn7BZk+{wI$g+`e)=zJ+C1gS#D4OJSjE0+AYY{Y&oHK%_2lgQvL zXDX84KreLJXu;rE8iGXqr0Ni_>w$%E?ooE-ki+1wU=5S?_=EZ{7J0CcfB1+LITUOQMuKx``uDFm?j3@%H40e%co`YMzZ{Em7yYV#jm*1J z4P*KnaA21`-8ka`)IHsZv;KauYEaZ5%+4iD(t={}7}x@3`nvSWhFGfPZA@qBZDDin zrK5=6Ca~~1MSZ)4NR-bBUhec>+%;8%IsMuPd4c)V)^<8sk)lhNs5O9Jw1w3)=}qvT zybC=`bI|Li2gI7GQgeZ;SYIEAI%5=h%_4*~SixqTG9(&Kvgm7O0!Bi4xcK3#XYIC2)_;TdSGZ%;90KAn|(n4JDi6aO}4-wT`KWBDxWl zFRuh&XMOC4O_=`pht*)O58POKj2$lAL$!I{)ceL;_|(t^`Oe$uPiFGvW;!-1C~#5lPNBU0b7UcMaftmq>+KS+czUwgDZV2)lJ`xwW`zWkqiWXZ`R z@i1#;FXYd<$41EA0gu?L%$Q~o?yhu!i_?CC&}C$5E6m6NH0E;nK5)L|6(i0$+eLGN z(aW|JTf4N#)pf~?mcl4A*cZ#lw8UZ0NHQ#Watn7hEKj9a;vuw#4ZSsyzrr!e7iE&y7 z>vH@zNa)t$tq&Gtcf~t=dgM4c>pX!JiVNd+Hh^?noyNu%Zz8<619F`0Aj@+W#wR|8 zgd7R}$><1-e(XW|IyYjX@^k#HSjOb}`%s(m^1qEp&YwDU1?B(jtwPzD-lEfqjS=X^c6*%xnd=XN>;E> zqjp2#(Pv;I(*~Z78c?%vKa9WPoH6IiL05bST{$tB9&3w*&vmm{h3|H3Ri+`SIHXAp zLvkT^{a~5VaSdXUqd|J-X5mqeXB(ta0+mj_ke9R!=FGAn_C;3YpKLN){`n*b%!_1H zI@a)?YlktF^2$!jtxE3#5n@5UjL+E{}?@-|>N4u_{$A6Wh7;Nqc z?93###&Z_C?DQE_vw93WYirT5b0P0o-g#zs!EqFE)gvossF0`8o?t)NiBDV;@z>$= z(A6SM#X`>D^kR%xP9uTX;?S9mii2Zf_XMK zpL&uF!b$^pp|q7L6DY)w<5iIUc@g<*l8QFElSr~i8MJwNlJ$%e6ZT*q7!KKU&(dMc zl^EgtV;^zUg^w@ASJOnBCfLdKo(e?LVV8m+0g1Pm{Z^D7|FVNTKlm0jy;fmfwihhe zWQW$Xb&2Qx+h}2|$Ia3mF+jQy44%h=oxvWq?pXoziPYmW^DrtYHVmb+J0SX zS0&BwyYO=V5-b$dpp(uT5V3n#;m&74QX=I_(>83Qr!p#_Qb?Ids5`*xfDCH3#F`{= zPW$L*Ui5SG26A+yk=-h~1&kecVd8vs4Ah)MV|Fbhf*(!--(ekwvL-Y$SeMiv3n%(h zVqp62AvQ$m7vns*j@h_v6yA$=;(C=@use1flf(WoH&BAkO1{Y^ro}Mv+;?Aj_cN>- zm_Z9-LeaEB6sHV(;t6M6c6J#z<=)CeyVHNrYjO(c*m7=(CH6RbhdlGyljBDiOd`9K zo8kJp2ozdc3vC{&AbXJ!yZ7qRamnU z*??q@VbA$3jduj%&aL9~&(IR`C2R`{sm?lQ{BTufbKzG#)@~5;F zpLGv1qoS8!_2#|UB`V6y=&r-Kpvio%qjzBIyQ%0kFOzi~tj2>YFX8j8){yS0hKo5d z#PQ?BFq0PwQhigYS&KX!P}HJ#GPe&^t?e=-|4>8+sCqF$oYSSalI7m6?AyqTrm6yr@Z_*&UK2pBOY zdym|~nWe_`{>n~PrEE35{lgG8`K-qSdlOmJj}xd)X*z4g@vT4h)Up2wUgF1G&1Zvy zf^pcvA8oj2Vv^7q`lf#cd3!4#PVX6p$=iRy>SZe_j!kEHU#1h=&FwI9?L6!9b^?@1 zN28G2MYMHmfcSSe+1KZzn76VmIJ(6Qqig?y&%igHm82y7>njT1+vX4>z989KwhE1k z<9IV~6l1clJl+Yj#f(?V)M9NcW}IA3-wvBYW@QU#j#`j;oslS-B164yaqJUgam;wb zhkYjqJmYw$nbW&5Y^n}<+t&m>EAlaUH;v0(+v!(O zrO*K~rY(oqEz7Z>>m+$;T!G!$i^=a@S#%^`7cH+#!<4%lNkU5=JzXIV`I#r#6{h+y z_<97VT-!lSYm>41f^!e)rz9^D427IpCPMhsRoKZ6;sR`4#HY_TLnoJLIvf?+Kj zPB1=(TjWMygF1oLxgEITQ!V-JX*HOKTEXlQbE%AYIyr0{$@oCe!Ce1Rk*FSs`rPQzDw`9!nPZoq@*qa2%VRj1#nH;^T^)G*#;RH2=>h z@pwMtg?yq02hYpWaK$*@&R+>v=#Ay{7U^-1<&R?QsbBB~Dd1e5s#E(>Lfjr21k-yg zsCE5zez;pOm>jd^TPK#mJYA*{(&fXi^V0+~d1o9OWdTunqOYztPT z$qPgr=`FzDn=Sc{h9213pNsP!{e<6^`h#+c3;5kigV5gtA^-VWI*H+`Vm@o~+fGuT z1M~PWUmnznmazB!NGKl@!U>axlUvEzSgWQ>^$q3u$i|6$agHSYE}=;u9(9Ec5)RwUv z?{<8~uRTRzad19o(Do9JjP?L&k4mmCr-k#sT!OEr#bB_vL-cem)K(Xsyy74zK9tK3Y9cp290nav6TV0@gWxsNqW<1DWktBoi)RaJ3U z7LCWySbwtEJ{O%o$g#b)bD(x<2~4imhVrlrP>?ymDL81N#;m8J2(_X7v>X9i7kZ&X z!EhLI+y&}NKf{}F0%j}a!7OzVzN(lcdSwtpq*M07vBF^x^ZWp@KUxGV$qd+Y2`?}G z#D(}OFu8~Wc>j$GQCrc65-()<=7nc*hkpR=dTMlI_k9hV(67WxM5U6;9qCY5#kl!es=+8(2JRhwU%s?;A;?GO z2_JUVa_91;cn1k1eq(1cR3EP=-2>8O9wx!9gq6Jc-68b7g9U!MCL{^dn1;!l_arfq zRDA!jSE%}XFP4@J$I`vMFmUSxcE_clfl?w^{fNcj6lt<~r4iGVPDQy;9eQoYZgSB} z0f89v0i%UD(kuPYQ>Bk8rcwJ8q|Px^uU zuhV$Y$lxEpILlRDl4Tgz$Z}~{C2)4$2MaXZ*_?jFO}Audj6n)tUerbmCi=r`8IImk z%f^EHd+^zz+o-ya$L1}0!Wq*GARuBl=omE$HQ5^AUJZeW#XGofr?nvKLn}@N%Y6c0CEo-G|ewxpKUeUkx^S@8eBn4f%^2 z3o-T&XX5Rqg#|h8(D&GxuP>6MVOkxG!?ux&+th;Z=0AmJuV!POuRM3YXEW&4AHmQ_ zWwu_NN1n}$hu&x_(CL=M{liX>n>KlzLCRt1tIWg=g5R)N`5kv=^3No{=`x176r=o# zHSp4ADh@SjB5M`ph_RVV#rlljF)>RXw>g%ub$~?0dUijOJE!3JcLjL)oE4hXZDcXI zrEpC747A_;!D%^UatCdKKv~-Ym(Nv%t1QMm*GG$tdJ~GZx~I6>(gpbA!b5K5tU!p< zbm7uUI)%gf;&5P-Gbw66h=Iwog`cH%z_o(IMDTq(J|Q8(pTu{7NxB6nd^&{MmuA2( zA1VQs|KNI@>qS{lJK*rmkVbzJL{8#2JSe#@IljUc@wIgeA$SoaA_!fu}(_MM3&^0*z>0{LHRp1^jTg~Uy zl#B9?=73&bDyQ>s4S0-`gQFEpxkxt{KVN6s3V9!)?(=+M*T_RSHZ6p=4@2DE;zmOX z_7kB@Jo;)Y@GjpN_wuX`+%mkXywWZe)$l>f;vU^nPGGs1c7`nATQVH!Qiiu}FE_`9_0Nb%sTRH5 zBQYxU8N|+&qPJx-hF*Jim7!^LR$rWw@J^*9DF z{M4D7E?D*5i!6)pBh)tp-Llg~DyJmiNwp-c|EPj#-CLn`{c!3J@i0a))&Lsu@_O{{Rn6uSAat;+mT3I1o><+{-@j9sJNsR+D=b{ zdH2=%)swt{skhKA8&}hjrpqz6z7=;gZ^6%YvFKq`NA}+8L$T>|P?Ad^jxV>N@{+4) zTN*<52hJd!?D#&@x>+^foyyKU1_`?abXChk;#OFXZ&{92>B3OF9>cW6swC*@#}d3# z>@Ljc7J%v00PdcnA&HM@M^Tj;wa9!SOnv*2tClarFb5^lw_KA(uPw*N)lbkwH3pQ^ zgR#jk45o=G(Tc5%|H^R&u3~pzvj%JdC<;l#l(^2 ztbet6gItT2NN$J|>KQN|_b3T!s6K^0Ur>gZLf6so?F#s;*?~K~N}k)GD&S8{TZOOZ zG{B--rbAi(2=kMiV8iQJ++kx3&CmXecHxVfj-G z=d$Z_F;*786#ncVN|e2_tPZsuh3?HY&^6Ev62*zwQxyP(<7z9=;>t2H@YqiTWYunp$~{R zPvOQySzkeF@c06a< zz6!_Kgpehp_TpvtBOs1!k9^$&+<+p?iMYN_!k-%AeGa;bH6&9%Gt^HA3zqVGRVM>8EmTHp9Ytm(9hg zi;QTDTaIw1$r~)m+f7oXb?NGjQgofNB8_=_9L(}HxvrPjP}-><_3y-zl*rML&^Cf5 zcwE6po(WW+ABrAq&DY^wf^rugp+4hQ-Q(cI`LZAPu3yE;#%&HPuBuA&*c{%wG7r|g zcA`{r3T>ykB$>si3mYtiU1^i)k;qQG82vM(sOJ*5!==!3(wwf?`W3ZrP;5w%q*@oX z=mRtMd{q4*syEFcDt1E3huuTv=6q_mC75V-xx@x&YrD^c0Og!#}Y(?MK({NY_{Jb7ty9qvr*AfKL;VZGN; zV(YH}SJyt`W;^JJk(BElyYiRfL1I`89PRnGwu zACL(ZFpDKl^4e` z4$8RaS9?yz`~p|27mnxqG%<5eFnT*w?03UGT^NG&qOU@EN$9*6}^gsIM2!^ z9Cw#-ILPLZL_bx=k08&@2wy;xde=ik-FO-iK8826aYGZGU67c>_Uapw2)>OK$*Rqx z_BIhn1NG>hqDHc8-zm{0GPp~&;bBd$bwC=SuRjQyWRpjx;OT1K|ufcaj$ zd!rP81bpHS9L@&!d;vWdt094OXSW6MrlJ_Q-d6=yId=Yu5hfXlF&) z&1|4xuxkEo5TnVVXgn@*i-G2O)6()>RSt;$f=5Qs4gtp4550?Ger$kfba z>hx(Q|7GZ}v;B8Nx268KAq%VNR%TO8|I?$tPMYH1F3rtY{Lf4K*R67Ml9Dp~SEte9 xQ~vDEeE+}l?{?APC9=}UObiw_PI|D_pZd?^!P9O|lFUegHMndSI9Rsm_PYcpRmw?psi9qA^vN_0)qT} zLj5;y4G!@Mw~*or8yI+r^FOm~tEVQfv^}@V`>z|COTfH$~BPp@7(582ebR(tP{BFv|a6R6@C` zZrm|{OZ%5L)&{KE`d_*j`=20c{|XuRH${SDCP16uzEO!x~h@gKmXQ0`aza_Ff)|v;KrTx?|{G7!<;+&KLO_aD}b*e{@!)u z1KR%vIQ<2f_Yc51lsn&zyTD79ztz|I`UHoC{?`%XF8uHFThEwoW^Q9{Wo0vsyXar% zUp(JFlU$-d&BsaJ+_z1_I0!uD`|yd?S4`)u+LUGtZf;jaGsP_8lX z;*Sb&*Y4r^{%r)p{X@3$EezLhzTj3bVg8@^XNc>+hr4dW-y?q=_kVQ2UB8FB;qSq) zt!t;c%omrC(izr2$v1L;Jud%Gb2nZ ze`s4%ncNU7?k0`j?#6K9D>T?YW~xnZwJB3SgNNSZTG2Ba?X5%!}dM^%pz zsVHgcZoO*1YMC?h`7~{+zCDURPHM9C@#1XfA1n5_Mh>m|x!vAEv!iMFdMkU~thwoj z(iD5c_lMYL#zFQw1;v{5thE`f>@)VAHmUaUV}hG(qYv57?wi_lE^VCshD#ZYvbeha zCBF~MLuQfvo6k$^2U~^gg=+iIPuZItTNr2m=u`olAhL-4OTqixse^zZe+Br@e^T)D z|DoW2**J3@EdN&p|J!ZzKY!URyPNq-hH|GY!e3v3zn=djgXRB};s4$na~(|oe`T0x zdY*GgU;$2QWohgKX>6{zKU2FU)Z%jXqm_zA@n~iaq?Zo7OHtikGBzdz|qQmMA^dC*eIFBM6_e7X= z|Gb&r-*Ft1mhXbS-f=K;wTCq6$HV^34;mc?)mXtmZ`c`e7_LZd1LfBd@N;wu70EE- zeS18U4Y1jVsW$qozWO-!AZHgHSnbNbY^{fZ5e=|#ZHCCwGho}DOpY%}KdxZ#`( zF6V8bb1m!8A-0U(FP%+}R(Le_ij0Np4_}hCB}oX}9;$zRAB}o0!QRb?U|SX$v17mO zV+Cf^+79ZJqm8UB9Jb9M*}+dpRpt(el)OkUj*;WsUbq&Da^zvsO92oYcmoRMy7b%q zDx8-#fRUkUc(3p=R(yDbXZl-7m+Bby>8U@s^ZjW&bZaq&y)t8UB)^jT9>_av+dH2}+UIP9!E`{Fx9uYtuPt~C11x3&pB1W}S_knG00%X`b)3Dt$ct=$H$bi2U_P>$E z$f9H#^re}aUHJreiocLA(kf8B^)9hDQ?(OtuCSZo*p0805@6EOGGbZXK;|EBArEtZ zakRG8!#mkfoImRrYGfLstw=GaZ|E9tqNgb9YEeO|WM|_!8x^cD-b?MbMe-CbXtP7c zpTSjp7KC-$LeSi4aA5QSdE9UtW5OO`$-D);593zTJ^gB|O2#7MH}xQLT9*;cvO>B+0vHGf?bUeuum5H z9!1eDMH+ae)f*q3cEF)fC;S$Ch1xs&G^Td`h8r_aFf~7)@KVdgn49w?nC#6AnK$nW zNSJ)(W9cHrQmUgA$jRB10J;v}NKUXn z`966jh}@n*BFlP6L7*4N|2n|x?O%(<$1K=ub2hMb-LL7kce6o;=S34&n2^5CLK15p zhwcr-B;w2`(z#BM89lcOO7s%IifcxyCl2xMZ1QE;ZUbg&UOg1OP-Qf|-h$iiC~D&% z#+0n~hQ5^wU~%OFyf|sYc9I8?|oJ#s6Uf}>Pyq$qxue55ICOv%oc^4>xN0yRxL*4bS0W^WDi+f!giF)gCay_048}6sV?y@J;TW%U!Y`;zCe%Hg`YY~vUKO1*u-XRh- zFW|=U(~YK)h}UNp1MkWo6qr}pzzaACU}X;Nsx{QQ${hBnRM~Zyc94lHf?-lk8Y!@s zCu&ZHblFHD2uQS^Hx!6O!9Q}mqy74{+Q=AnXC}xDqgVMZIy6>_nU^#I^_CHE+qDl08fSz3#D&Bn zY6CNPA`QhQh1fAmb5Jzt5%Pvo$Sdb6x^>B0Dw&i^t!(l!TvQSF-jW0lH2bZru6W5%}PvyHbov9)CwHCBYl-!g`YjMrflv`mv```Sh04a9rGKt;HmXbpS7nb$Tr_^1Y79y)=QA%%GTHsuxO$g^gzroalzGf);B3#;yD zfX-NHs$De=Jel%XHS)x(e>}(U}i44R`w9}l1gyFDFwW=q#vEjv(S0|dlFJ9 zz>dt&#j$t0u;kbiY%nNAC;ZNH*6o4)D<+Yr>K^3#iZOKR(d}4$XbjC~q?rN9?PTMP ziO}-F0nVpRg6+mJyccJ6*|=p35%_y>b8sO(YrKL%C+jHcYhvy1tGu1o6%BfHAwC(W zhp{&4vQ=U7A~@+Uv;kG)jX+&?MFY6ue$`8Yhx0iZ{QkXPQNGBy{p0Idn8@) zW;vAaQ^Ae1ZScu9joX7xD4Rf&g|0IeNheOsfrH_qhSOW4$J`yMhPXH0$9}<~#CH zARD?~N79YzQf!Dv78D&1rs;BGkmfl9vM1l7?b}?4@BoFmV{byt(In_P9Sc9B^+;BZ z0_*-fpOgy<+ikO1K>{z-L!X~6NpJVU(`|=H^RK;jXKp{Brs9VN%T1^MX;!Gm^@!Lmw z76{_AeTgLc^nQ}-+DVIb{}6$d=A8QiO0YFV3rdnINZXfo-t_huw48q(Q)=Y#jNvyF zPqV;>^5MkA(Gh~(?P0vk68JQ@2_AL1!|GiRs9OJFwD6H8gLlWUVf6>l>RUVBnjK5; z7e0m?>t7QM-d3V}Gy~q#1jse0BuAGCF?AOupknkc=jA9vXU>TsnMXPZ%uT}eVkhCq zp*b^*Hb;<>%ZB8-`%;dc&_?#afD-4e;!NHeqe+mz{wfiFlXCX-(hrE?+it8&>8CJ*fdgAPZZMSg0xhY*{x`D0C{tepu3OP3HT4*A>6Tn#HE7m_cMZBd_z z(E}@T-7=AExg3MJC!|4Zkubc^4unnb-9fCShve62vl(H*^p)m1oPRbJv+dk)Fu$4z ziYMZ_neJ?um zt@D{@N0u`;5^9)dnr*zv>sDegTC-{c0_^L1hHTk-P4?E6268L&2EIpe$a7VhO|B}8sPbG!vSk$$6edE1u3G@pv4;t_pU6CyPhdLC7Bab? zJIGGmYxqUThrRt=mu-{tVuhYBV~Z?b5h3YkxJ0fNw%+n#Jm)(zwqn|hi`GoKcUAxz z^rbR^vWd)+kFAWm`F;j0?hvtj5m=nmL#oD%l1FT`UGM2>pmRHp*WWjd;d~gwiAv02O;<)u^cg*src9r>UW3San;~809fVYv zFe_JU)32YtL*=S$=I~EDrlL2W2|Rh2;S^L89p!7JZuxR1Y^)&Targ-gu1IIB`F_#h zve)PqV8+aN(hM%c2bhFrRc4E@9GzI$2;(eoBHhHr>^Do;30LiK(W!gX#@30wdh0lo z{iBh&SACbs?rdSk#3vHxtH0roVlFf3>jq|uOFNUZ_$>48jWTDc*GFh=?PW`6q``=K z1Z!pF#46m9=h-H9^Rf;|GUe-?n80}}ncd=EOrJ32)s>8rWs458y%De2Bb;J3O!61& zR`!N&8cO2{`kOJf^5$^mu|BiNN`z72ZlnTp7t^zM8enVP5hil`YR0Z6f_WD+fwRZB z2EX)knE407I4#fD!-gY6u>HgW(vhc)Z#8_FegkVJF(sPuHw<9}e9IdoJJXT$s<#+!>L3T77~4 zxG||1*M5r4?3>Oe-8jw?mjX7$;XFwe{LVSQGo4*ka}8rEV%dfBo!M%`dU|#DeGHsk z&bAojve%C^u|rzT?1u}_seIO3;veM8p5@JAcQ21-pM<5eKOc^gm!sRDuv`uL#eTug zWfo9F$HDm}H9Yga3ouFd08=*eIU{=f1;erIX2hy^q(a4otO!Bo>$Og1c-eJky-6cu zqcEGio)e1>EoO|8*+Zs3y^MM9^MJt{M`&`~Av#ax2HP#`!!A12%jzsx$O^2U!}0J~ zf<@~Jn8BeaCf>h-flr-G^-d?wxgRT`W;6qX>mRV1Q_|T|w`8__Tp9Vzy5QC;M(peH zmF&I)CG4({Om=&3CQzh{1YJ4%L%WfUyt$j8X!$&^gm~R>A z%^bp?HXU@Qs|eHogvSXPtIwvL5W(XS?Ks^0kPOMX!>bz?QQ_Ja4!!D#&CTh!+Uq5m zZ1)qB{GwRf@<7(*Y9cEtg>gXs~YlN(J0<8?UUs3;}~Xp z-6Y1ic{_73Pl?Rl_!aHW+cOUD^cfXFU8Z54DRb~+B0bTW1u^@tu$9NIvmxz|S;O-K zY<5Ks@oc|BmnJV|>?{7j&nOpW;f@K+LCa?HVXYn$Yx@Yr9UrnyH@aAZc7dkS!##8~ z`Y4EJZ)V4>a$!|11KFeVjoQr69>j9zXKgo*v( zIDD}iQx%oi%$*-FKHw+01&djQgR4-^_&VzUSmC*1@!Ck<2lnNvPIUKv)@OI{bBrq_Q@Xu>9%hWa<7dZ`Tmd;OLSwf#&KLDc!xGF zH$A{L8d)UmUQS8B|Yl+p}9xV0Ghr7w`@ZMXO5s+>MwFVQ~p|^!t zyzro-R!4Ed)_huPauP3VKB2Q$+#ofvn|KPd$KiwVO1wILyjYy|fW$aW=Uw%5!00vc ztdq73o4q)k?VFUr*}7LB8*~fc>epECG7N!Z&OVS)x}NCoo=qM_X)uMCdtq707syIh zV$7mtc{=s^D7NSlW*03$YNp0U8&=?yK3{Sw_AZKAIl(t(9^RG}=BRql!iTTwXn=$Z zGtE%|OfGGv(|I!?a$_R8O=_ckqNa?^v0n`f)N&h>Cq;tJ>(7ndf#vjQwLbqYcxpHP z;AJ#5$i;EB`*DGA6Md_$4scWsXmKobO6bGQWG8g7h^60FTe7p|OV|fNi!d`Y0Vf2e zvezZH^8%VZVdK?{FoB;R3l;9>oq57Sby_PG61W9(dsuo$SQ7VpPX<|jlrl*&ifYIm z0J&0mMt$a6D19`SS>Pqiob@#cTj>!fRf!^Pxl75idt=zU`Vx%4!zej=Dia9<;x<8cjj^ky)}ONwF769wjyJi|0a=hC9* zwHUk8hvvi#(R+rwiJxQ)KVB~(cldd)jRJqrYHkLZ`~566Ei_@LIwVqFvIC}nKS&zC zY2xsYUu3P*0(5b(<(Rx=q4&ukW%Ki~B)F8GTV}?db$CR^vcRnPfly_A8x#vqLY1H( zv{c*@giUGga7ySE5!;?Bb9C{l_+wIZ%nhBI_fkR6rQ}7&EZAsLL3A3nlCM_c5brcdPB0EwTkwV2pQ=H| zzKyPSzk~vhPS9_e5%^GjGy8tm8@%8$hnqYlgW#ET~0GN~Y2HP%A#e)ko@q=(L?P>%jv8o@k3#>#rO$!B?gj%n2T&-;;1q7PVaub%@a^fdJTJ{g zdNlbd#~zF6fWZZR93;cOTsEH;jyaE44w>T(l24<@1w)0A1qhB8U=q@kpzr!EqL6Wn z9vW$c9mg6VpH~mXtLh0O83@x-&X9hQHLT=9Wyo@0hBHqJ!t`;O_~y$g@{$TN8nFQD z-)_LH6UA_GUlfY8iPBSvUtr5*HAchiD5&&FF`IsgGE)@3Q8hPHW+TI)WtPG?V!+by z&(8F+)=whSt^!mk7k3ZWAXldVW$cAf;PIWtJG)eI&o+HF;RXlw_M5Qw&B|SprRq4ZuO+t90hnt>F156(;14Z#*U2$osarjOtk3=S3{iVO3g;*_JQ9*mt8E zA9l#Ge#Zppb;EVc=ejVI^NK`;;Rz^xSP&I@X7XMa_u#^KbyjFhM^5F&siUb zv~_r@;^{<9;3PRQCKAjxup~KT3mV=rLXS=ERA6%sT5feBW^$_tZ@`VL&*Ok-h$2TY ztsWATf*?j%k^Q;PkWGo4iEgl+YTjD{uQrIn^EYxZefd|g&g{kC+(3{!o&hPM!0Zi+n0IcJ*7ib=)p%`-u54M?cc^hm3AR$B*wvWjyus0 zFT%{v(XJDDY1uG6<{hVOzA9XP zFa&cd#h7e?TOmkIXn+@719EOqmKA#gfdGQf&@F~Z+S8jr@ zi9CDk#71=We@+@cMB6!zF%#iOJ2n=}XGp1Hx`0rb=vSbfj z_o$;`=FZS^Iw#jMxc-PI1D*y1&>mcVVZF!y%Ki>d^99T z)RGElcL;`KA3Auwwc$`P;S-VeipNr8Q5Hf!Q#qICcw?CstQZodc3$DY*%1!LH?1MN zv6Q6Kb96`5OYDmh$I_0scyIU;#!j&yDi@bPg4snH(Its*9Vfz!lVx<2I|bK${RIY1 zDd=Bwj(&LR&X|7~uv<9G3$(3}W4>MvA0$#*cySruPZeg%xld7Zw+-YoI=E>>g|mG} z5id!59-3wh!FbtCSSQ*^Q&x%KKwdE#tndamFLh8Tat0?WE~xAC=yJ_ea?c};?mm12 z(t5@*rq9=aONW93eS%@&d<8PCTK8CP-i7~ocd67Y-_aNi)5;qxF&_C)&acAEl z>V5hV&igQqhM%#5q8=`(~E%pht;3fQbE%lw(hLSFGb zT$jBS9(?*qLuER!qR|6Zj((dFxl)|@e%}gx^v^)*!Ytyaq=@gLPr{?`nPi~Z5FNOy zX|BF7bMLkq=-?GlF7$^o*^|^(VJQgDizU8wa%e1epNyHel=>w|fok1km=RDx-dDKN zS^9^-_(KbwUX?;}+P$dk@nU{Ige3FkXc&Abv7m_$%JHTBaqRc~Y?rv`Gp!QPK(ow4 zKo(d)K}Hf7u8+aG{rOm(w+zqi$)*;n_Fx?7fki1Hysuj(ljftMtb*Zn{88&nw|7b&$AI&{(fUw|3NK?qb>0^qJpGL6zx%wwWytIHNZxz5ky%!%0 z#sYIh4|x`e;HNFYF3O9bLjAs2an_P_?Tdtm#UkiE?*{HxOu(*)L`VqI!)EG1E9Z36 z6Q;YcciU%}Z*&u~R5rutNmo30a0-0?;Q&qd>^f8_SWJ&+-!`b z$}G7Q|Cy@6hp*qD)wUFuSn#I=!6cio1R#;FneH==b;) z>=NvPjb{$Rhl0thJse|BT`a<$Gbw1=zY9WaDH%iVK~;21M;GQ)adFYCT#%JAg>mvaAn@uk&isCqwg%0`*f(QXIzyh37B^&W z)%bG4_jofhk8-JKt2G>mYa`=~pU}%IUlY8*uc@?HfV5{kq%^qlzDL}EP^C9^j+(s? z?{Wjw(j}l~k1A-@Z=q|CE3snZ%P>bY4b$ATI!WzjWYl zqf8Vy>C1BrcVvVd598b9`()t?4Se)v8r`WYhmwkmaPsZ%oMf4!&^BF*T%YxXI7b!n zHnmECV-=u*&;>LzvxhU{i$OibifLStfGvj)60^d4Fn97PNK$TqE`D#pQBDoU^OItQ zvx+c0QxZPE62YLHeYh-PgzjAX83%_GVbD#84sALLlkzUm)a+^4C%1-V49MfbQwxY8 zslbW5^Qq!CW!x#O&iwq|Mt<)OMkQ4{+$ffYcOoiqByki*Ri5DZhq_p;z6Z8uJF_NV zrnAWj1`G@xBx{}-Fd6HtfmhVa`)x4{DKaLE+r68-KACJ@kY+KBnQ4yqWeX2z=cyyFS!4dBJFO>E-1Q$$Qfg}r@U0^8UW;;}ah45N2I z^AZJY>SG|!^y27bNCpdK!!r_dX2BjDTp zp3`<<7j>6urOCSAIE81-86)8;ST8n${-V*?nq7k*tO8Iw#tUqHUFki{~Dn2-}lq3T3wv)Y7c3*O7ZymOtNJv!{M~G z&^q0Gu$dGCtV}Yr`gp>QZn7E0XoH703OQSB-A?!H#N^BbElV* zFzFf?JNq(<)D!%zTm|I^&!BDZaTu{&i_5NLQC!`g>%$?|t7)4PzSKNzA@lm$Sp zUWRp>>IR?KVq(8{I|}R#z_FzlaeSB#REs5&@7IOMPJ3aHQn?Is^ktwg<{p_5Hy?#f z*MMeM6-TDkiM&!DYK)5&1@8MF5U;(L-hc2F=f*pcUal0@HyFV$b_@=_av*ab7r^!H z@;u?!U*W*hPU`x590n_FCm~f4xF)onI_o{fU%NPsPOdB9fY&nK@cg&*xq}{Y`TBt8 zG$w-fENhe>^73HrE)z?TpOrY*;wc6;9-vYRF`Is2~JnbsGA z#N+$CuCt0n`KBLUnX;e!5Ew+4jb+&R&5D`Z|CX@NxdiT9z=AC~^humQ=6oIp4L8j} zI$VLwGeYe1JWtq3bLo5)dDQZ30M&*J&=nNJrLN~dDd{s$LeYT^e{ck&H?gonDv7S? zRYZa2y&%5N1QUH9L+y;|%>9&0yqxX>c*jryhpcwc-L_-k<;TO&IXsC;eC3C6SA4Pm zTpv~$+@+TeEWr93Ip{XE2i^E~aGt>yD!6AC=n1~#afB?OXp zjoF`9U2$rHGZ}k(4(;dXw!aJ~K(S&gM1=H%k^Xd?=#fhw(KuvY3$oHuwYL#o+Nr$>chiE7}n}PBM2w7|h?7eK7 zll$0q*K;MZWnTws{WyWU?+XBFj)KEmw_wsOG3I{wSzK%&%u3zg2O=vJ*L1G2MsEW7@Y&ul{I`7UycwtHQ?48IlMnRhW;^njn_m%X|0Mf;}su| z0aFx_r6-BTtsayMmxEn-%|y1eKgdfIVfqYqz}Tl_d7YQiu{n(>0CT)!Gz zy&ZAI+!r+Rvh?QP*KQCI1Ud%BqHr<|lykS90R<4K+<=821;Sy%}?xBY?<`3HEU z#frWCBaJQ*o4`s1t3uP#W3Qmj9(Uez`DsS7~Iw&Mak+sEpp5Nr?9oLXj~OAp4~Oel2h3$6*D~rTvyp z_~3>rXN1|Wcdv0CKYN9yna6;8erdyDX93QHo^r^Wph`3MXfdRJBMdcsA8@t5j`s%tbd6=~1*6%s!3YTvE!c3-ly#L; zXWl%LVuvHnL%!K3+S*b?rIV&new7TL`j2HV#NWaTvf>~uDZ$>d7o=&yQ#qHc!%>Vm zZg*NL7lfYvpr=nRW*-ZSV*WUW^_(G(=Nragi}V_161Nm;KOBIOm^A+Ph=2pL?~&I( z`XF*38V$i3k7&XKEoO#u zG**T>FzrV=$&9goApNc|79N;SkK4SZbwNY$I@_XKjJ$LjiP`Z5&-k_S3?NIiTtp$WEwu0Kb*CK>i*foPKZ|v(Pb` zx*cb5yTCbAf7b{P3typQq!k-C`8A$CwTp;y)o6*r0kXj78*Z#Ir_I&Rp;9#(rv@od zo4zLUF#0D{C`X{I>L_RV_sQf&u{KABB++L9Cr~p`j!iePVfA+$giCo+)H*rO=Dn=} zlWDKZT-oo+962G3n-7{`OVBhBP0zwHv(9iLjJAQ<`${^8?cquHr_zf3AFIKqNgXn zN9^V>wW^)T6S%V$>C`MZs#3#?TDuHIODEvG6_jqZISifwl3?w}!{qT@RMWg03fvar zJgMDqX_5k*9*83`{&GZgN(OzjCJRC{VqvL80rZP5!|A<9w!#-ES*4E>%8DTP+ytE# zD=<=~J3-I#8plM>5mY4RftTEN($JjNqIxUxY7B z65xHo8CpDE1X!)fQ1IRhp2RPJh<1McXWe*qiDVMxrXB>BBo+NhB0^ADpc`r^ab#OvH?yUruU zxPoy)weOcf@RKICvjbS5^(q}U8{Q;9qzQ#Jk4Lcxza;) zZ*?=BJMS3h@~koJ&Y9IX<%lrYDEuHNTr_B|Tp1Ml^w53nUuei3bND=c7V51t!p@${ z7#9DBJ{jwab60wkUiCAule+`XZ|er1#JyzIlNp##_E9zbqR$BZ#+WU zCANY1*K2cr4zD0SRub^Rel-ZZxJeIh>a+8nvLAbAR-*maVNS|(J6L_m4C)O9VO-8k zCS&VOdLlrK`W2sq@elT+WAtg5{D|-04n@(qs-ygVFnNx|RZ;eLH9z-vH6J$XXLF8E zOC;_Chv+T`O}1fj7q<9?k}awIXt*etX1!hxf-996_f|znxIdX${$7p!aPBMy3;g0) zNjPz4zK9}1YsZ1j`m-=;Uld(ay8~8bjzNoV8CGfEOz;+6j$+H&urg~bq;>M^wA^j5 zOXE7NuWaDQ$YrQ#`HT+hrozq(2IPm~IM`Uy&&$4<%Gt2X2Oa-RuzS&C1gcXbdFKWJ z?hYr~_Ij=bdp!fZW*mty*1lmSI%^_uQP;4*6uhpF^8TI ztm7#+*2D5|`yub;BZ#*gq4)MuP?D|$namU(Ne?6b+6=xpdJN=Rgjl9}0$X7v&ZbwJ z<0ik8XwnpeF77*VK|wPN+U~_XA1zFbw1Tz?D`Cy~5+cZC&@>YT{CK_=b4%QK8U?<% z#3dSbp5^zhemDlo_cgF};x9YzU>lgRe~5Qo)(M~QI)Za+i^0iN5FPk2)x>-^^q3`r z&!ZU`x?2>Q3$8|e$$SoR^s+u6=$uDfUH9btfk*?qG6kk z)^h4Fpu8Hh9D0Fc7Rb6!yF!o^gBL<|^sBWk<38R1-V8qmt^TF->HX z82;V7SPLX;G{~OQ!(`d?BD-zphIj?5by=T^tKeqfPG$Dp#H!dz{92s^VadmE&AbSx z@$9BYRSv?6F+ylnTmw&cjbX1kbV6GnkJsjV8RAcwFk4#{*>$-F%*5Dw6kVAM!U_Bu zXjKm`_I^9J+!k$kw(*EK(W^IuE)) z@Yy~zZM#Bz!|y>|(0OX#w;CNHW)axC8aAh0r)%#-&?^mPVDNYq1uH>bYT#?mTz5kd zR*%7+_8F)c6pV>)uG7hl8q|ok!P2_1`UX2 ztpeEC9>AZP@mMS*h^4!Kf<27BCK&sIXyV`C$ajlk6d1YAhy8} z1ho9HO!XFrTuH^8ndfmdtrGingRm}Ng56s{>4>}#Q|KuRz5@Du-@h7_(+|)KtD-SZ zw39Q<{3Sm86h`t5CgYRCWuz&wAG|L|;l2Z|_(Mb#XPNHCcXgH6t|80Vt*r(5-XpNK zFbPxN%VKlJ3a}|`rVV;IuyQeBO65H8<}ALws`FwVb0wM5Gp^A6`#RhS%tL9rI4~%^ zjY}$)lU#leg{DO@A$nridJ>rL1{x@$;|t@~se*0%TXI4v4o0I7c;5@f+xC;!t5i^W%5kt#TZxB8ccJ4Tg_Uo8A>Ss8Ub(i5IA|qMS!;7- zf-cgHAr>G{KSD-v5#cn7Gq0}%!)#G6)LOB_u6S!C3RhW?0=+@p+og(UD$3#O!xs4Q zGzI0%%V>MfPI!}f6JkF`!^}m#5D$Qs+ippRzPiA=IihT&nh1W~*$*?T#Tch&b3kra23eTQrOeYq zoXm-RbHM_U%cnV*O0^RMGzZ}?9zGcyFi&P4v5p@V8iicX)u*zH;_$!Md{-5Bw?n*u?64rPLuUTF?0vt zS2yw7NLO8ay<%MwO#X00{Y$D0=WMI=;Ygql;5ppZ| z`7*_yz`gXE(`oyJ%ns0CcH5eQ!6z;iy#5HX+sCtcRcatBZU!EFA6c@Eutvr0!tI_|7XB79`m+QT3Hn+h_|ae|-Sg zhYKM8cnn^OtHEj4E>clfzHdG93?w_{80QTUoQa!Sz@e-GzgPN@%hG_apC{ptgfTp) z#9#>2xz1ZD^9kNcTSNWj5s3CYjK+cT)HG}}dwpLM3J)hToeyr{(b-jCGk6~KzY4*% zi#Pda^aPx0m$z%F8$wOzo3LU2d3s1b5i@1V=#Q)zPVDA5vM{dOPT0MNW{h9J7(@<` zv#L4tjp{7AO*;!OcWD8Waht+DXVN25fb0ux7-H+_ycJK;JfRRCuGozIC8GQul4!Ja ze$0{EDa9PFNJW>eTd3dGZNy$TmK=X81FakP!>ZXw&^w_Gjypu)oLibG-uMlY`2F!a zcWdHd>-Esm%0R$!3PUdv=nIo+SU#B_3(maEd0ke?c{b(-{n^Vv@*95~s}o8J`=uau zL;$-~8)#Sc0Iqr>!M>0m0Nb!xw68lArIN~^ve6d3`Mnb*M{m`Vfe4 zqRb8jbgH=zcKKE0?2$pD@TaE%_t zj4|WT{Nok4{A)bxza)o#k5&Y~%?23c^c=fx_hZSF0FG+VQk1zY$~<(vihcJYv|nSn*~&C%!E81`6OJ{?|MNQGJx;6d*pSa9SZ z^e!lbdAp`y!FUSu+$fQ_nnjcK-*DnKS%Lw(0$VJv;H=4rX#sY){aqQ+D0@g{{r?X` zXZn{@7lq*_l_V*eQ<8)zB&xI5L4_hxLR3gX6j4f&2BnE6(Wp73G!Uw1ucK%#k|IhO zUPNYvWO&aX(1)kr8P-|%bzfVLP6__OT)U`(f~XYo$kraC@5q5!+f;T>5WgRko(&ed z6F`ig4|l#F18sMduqsH7y6lL?ktq>ibioxfkJN&ko-YI^EMxyl6$3q-NrHA>G?msD z$AYjEwCgWWC%1Xz+002?%+8zGSO1PVpC69f&c)LmzTvdSeiK}kpTK_8G>1CpGvsSw zJI_c?$I@0c;su|W!taK-L?H_f7R+En8&|`x&OS(cT7z>9m8hFm5IObXITpE1q8ehR z?DFzrJZoJ}Z`!i3HftNdPu)lqx588EPm*}DzSxDo=ZQ{k%x()=D0v}DRrs-1lm@Vj-*W^4`0U+x0R(p%^_=iT-#TM<`|@Tm%hscFP1ML+>ppT%@fqE}Fc$)9USnBZ2lg&|Pp6HO=X5G$ zxcYiTKYNO8{_gm02TK-)%ff~qQjnPNf-bOs33oSm;*%F4#AsCxd9-R44USm>N2ce4 zy5SkPb|3-X{dWwR`~T71OS1&)-j5-_PfLOI+ZarYkirE=^J#DScjm#icVx<|8o~5S z1~7g_D^+^q$iCmn(czI9Y-#8gVj1;|32yeqiqS$SpPp)x`PCTeQsRl6n~I=(O&{hO z@!#^lR6wdC6|V)`z^|$L?A4c!_$DTSoK$hc^Y?Cp&vz+S+c6Ok zHGpZZjR5l^Te9WhJGeY*hPe;RsjaRx-k4t_=r-;Hdip7RShAiGWE-&`M9Oje_H0vM zjn@pZe2URcBHaBlGg^7P7$xuBCuekw@bGzKP+k#2?{9cjy)ZtWdd#>&Tn2x@X4`br zKm~1fz%!Pd_+*Fkwn}oQS2Wp?S#F>hy9-B$MH_wI>&uPNV~pTSOi#qVy4xUGK!-bjza zux~9;w)TXeTuK;U2(Lu@=_%ltC&A8JIgcHDPzqUpbpW~yvE+D0^+y{&aEV+4{(FwX zck9#er9_t8Xl|mv?q)!dg&jn_5Jurl9SAI3gRb^!%rR3H&ZRAqoKp58*W)4~b%z{Y zuuq4t;E5@UHssI#25kJ0ga##k@KPW_Zrk@#$4w&SDPOKrlZvmDWKt2O9){Drb#2QYB&1k;_Ft3f8%04%S{ za@IPEFvFk(FRr|dOBb5Mr$q}{$6MCiF%5N^GbkV1Idk?Wb@Bl$Q90D{o7~TCI zn0q|}H`Z=Kw|x!Zt)aqQ-M1I3zk0zNlWH)rqBs_woLiL5K>!iaR5fA$3z8I@ztGCZDEr9owYHWB(Z9*^vNFQ_eF{i(2WPCk+@S zz7D0p0yi$(P8Lo1j#bOmIgy%2#K7hoZG1I^)kl|c&-RP4(axrP=dYbEJst}U<{9YY zg-nvM6pR*Fp`x8TpMMWR()4I_{P2wkFP^}b%&LKP>PZZK=4Zr5S0Uu*LeTh+sg9aI zmc5fLWE$r8mdv@WjFry1@GbKy+}qhkB$#aKP%@j(pI$?e-gA(<)MZ+!>WH&$?!?8+ z8>V%d1D0ejfhRxg$f=%Kkjn7DF!2K3<>E^uoja*>=^%b@?IpUe#35^6I}Xi12}|4; zfUnM6sM6eztusVmfz7RI`$|z$CBs{!^~gK2UhEErrnX_HcOjX+#}9X#))0B}47zT` zfS5xT4PLm4!n8m5E^L5g?WxBZAItEky9AtcP+&7|+0t8v%h0ZI7u+~-fvQ(XL5=(! ze6mZ7s=OSb<~#f$%qNuYd$$uLHmrc?Z^gLt*Hz+bV*pknIb^~(HOBT&851I@4*!)~ z!fef1sO349+po47zm8Fd^#0ut5mn0b9Im85qXfohj$t1(yMwCGb!u?92*ch-VED>( z@Nya8XMP5I>JMD#N(ITDWwB6rcctOS1w z-@tQLn_>8Z8a+1zEiAP#E>S{`3%_?owZ}rE`Gjh;%+8uK2{0;bE zJr@^~K)7}13^_Zs02NgF;!PooIsF-02Ug`!I4h`h~vT^vbpqWgbITDokOy39R+H48Lk8p_X_RQFi>u=Z~v!NueDa+9Hhagf*zPwlXJHGYYr+qsg6y zQV=`+m3)v6HAyro#s}kr1gl!o@WS3>@L%ISvc)hJCm&Zp8|NXiK`0F-jT<7u*5|2h zy9Zflp8;0oi{Yo_R+Rp-7$xJq1R7&y+0&&duu8HDox7sx#q1x{xt68dgt{Tsf^b?Q zv$=zQPE^Ce7kxS`;r+}ca#M6K)SK;xX0@l}ddE>Lzpg+IjXOx@M9GpzuOsP_g{{zV zhW7w?>%ipM{;=ED9K0?WW2}0XAo`Rtv@8{2Z))UYG8+Khwu!J?dcbrp0QY8R4BW3W z!`m}$;N6iB!P;@2;AgA{dp0U_$szIB^64zoz0ZpKARa*%o*9c>0TrZXC=}+q9fqOz z?`U=QSZ?j$P4Xp9mAx#z0)~S^$wuZY+}wBp^zUTRz3#mvE?k1$)b|1hYU=UYuN<_Q zJ(IgzHwDcsJaM{G$TiJqtmJ)BK)Teh*Zg(81lqFY&+{VfNpSa#-2Z2Bnvb z(C^w<_RY`@$e%4kCVvPZ%{t0}^UvbVmP;7vm_uAA24nQ;e5p}?X7#GqtG7c{rpCc;oU6?uY zCy1oyYtZ#x#7-*_2V;eCtb%6{%xMjze`h|RRoXIS%f(p6$>AH|TqQ1asT8q%y#$VR zKO!a5ngrQASKa;ME!i;;1j+0u`SZ#L*Mzo0<%bSZc($A9jC$gIQ-c03qY$@uJOu34 z#ygw3U_6Y+2%8z`x}$>LjgKNUD4m?WyAR#0f02#?3%r`YhU=WH&yImg_`X<-ThXTg zlVmbsMnE!l2trX#{4ITRUlsM9nSipb7Hl09W<}ocyV&bZFejssE^l0jqCUF#uk9F= z-8P2LifUAnej!07E7=L+`e^1cLehO7<6^6RDm__1?fkU3TOS6|LUJL_>2Jnk%M&4G znI(1otP$6MJRzJQ$ReDb1TTxe>UgQGv4$4Eqf0PWZ5$2vuYyqkA6e#_v$L7}c zfmdV(i9b7@4fYa3*-io7wC4qtzV?7ryAHrM@nxu_IiGt5^@6^+&+zilBl2$XOx(6^ zA>3II1q-%6!vU|AP-H$T2$7XzC($_Cb~KPMgTknBCKs}K@5ocX9QtQ1hYBq>h($>; zXjcqk@E0{~Yd!%-eU`CTj`l)oNC`R*Jgh!;R1h+Dd<%lN6#!@ zNu!qhB{oA>Vb|YV==3iLuFu>>Z4DA}riC$7jonSj!#^UErN@>b76sgir}nx zG??rU$JfSn*g3Wh!+t5@m$Geq7sHD9>`&w~vsThuSLcSYHQZudbc0b_D~q z8mcta>^=X#sRYZX8}XIUN20&I6y6-lWo|Ce#ijW+c%@r_<3mI^CBX;U->?ZE^SSns zb@Mrz( zbuUgkBFwcfcmR)&`~aOL{GNMv8ohk29s)K6@O(xFtY2{pzc^h%C3XZ|05MZ zdpQ>xo5;o=nvL`Ab8t=bOOmiT8Pj+CM&((HSY0{+50cebbxs)lMn2&1h7+)L^<_Bf z$sxDq8B8jt_~qjlZ1Foyj@Mekj+r{_%_s}vJba7h6$!Ck3DfbO@@ZVQK^L#^*>1qS z*=*F_bKvjz8W+`xvGUrN$=S!#!KlYbV3Dj3fB4_xXnT_8H~)j>+umaJd}X%bjuvjc zm<U8u5c(= zw}`zqL6{zVG6}B4*Al_+*_eOvJJYA2TAle|3F_?*2G!hq@Wc5mjvwZC(-S9P=&Jcp z<9QLE|nS~&&xDo&JZ>DW4DOmBj|C<|sNL)iU$o2F6uee$8+P#I2m?yxli*3Z; zMH01D$HJpIc~q=x28QsixTMDtc*aAQ^~qgEt*t5m+bc1O=|>qyd5&szFe8NwR@4Q< z>ZgU!^vPO49^8cZPYoEVaSbxYkEh)=Ram3mh21rUR3&zhCY4-A?bf%9uVD;Xo5x?n z&3>r;%Y|AlH9%x?@m=0y5ZRUq=26BtUVIBN+8;w#yY}H;~I-Kmjw8Ret@E^@pu*N4l zZF#@fA8P25i6{Ba)_!RxkkMWVYNny!Hq{oRCg|e*!zOI@iV5uPW6jKDPa*DnU<|sN zAI3D^qvn+Vk|r6B<2L>HfIq7@K~Gu(e)6>DeuXOuWZ6t?HE_ZH$8)juKMQD@cn8;I zx01do>Rh!Y#Z_-gNNAB5Qm-QDj}T|S>5XU4?bZRsKo%3;+n|%8E<_n@5Cj=Ya^>yR%RcnKDTp$Ag%8L9y;?CzZ@yUl-^QE`6G?7 zi{`;r5qZJ015IRlxHXm}Zo*S#)hPAHmb8!B@p>wT{R0%;Qb^uZR}fap(q!DlwF2GKlOa!2 zA0t2S2F-jWHn5I&Z|X^4+=fviBeNX_+-mVY-|mdp-v^@C=YYnrGQJAkhoQj@g3Pw> zRCf0-5NdS85|IhG{>?223D8Ez&@?`KjU;E4G}xtS&LGlRMjg8?v2#i_7Oxl!<;&{u z#5+m$Ui^L1^+c6kHr@{RjwunhVq5mdp8~oQVsXV~J??SWJ^EV99jBEW;?|mpR3jl7 zXH`8!bKz$&;#&>jQ|5zm(QXj!I?4vUcw70=s{moUBNVT@hrO24EKMDOMJn6iMlc8e z9!-VQQNOBRr+#IEbTjZ_-V~mRuAoip)VMRf;_$6!4t}z~3^T0bAa{2s$fO(Ce$5oYEpUGBA23$e0LCBtXRiCU68_tmojl(WBMS`9(3k`R#FzX9I4xwE&f z>%zMQ7SLZS$EEwqfza?#Ol`45(^(#XOJhht@eFS3H$}KJmvZ!05mr)qZPrF z=QI@IU&sLZbob%L)6tOk+!~ivoyNaP^U?Qa0+q?zM{9q6u8t&{+?!8*)W@uri1a4m z$j6Vk%D4vqj(9@-s23B4DG)QlKmW1@Y4U6%{`t0)yKUvgT~c#I+szl4ZbpdfT@}jw z-d_QhZ=OP6n=#8i&Vg+GNZh-mgm=32<9T;OZsW4C^i`u7DxTlQvlR%cywmqF|O(pP-x5LvfuPJanrE?~eq5 zyRQb@@KlHm-FO3z{aM6(I;M>3hU@V^XB|jy-wCzTz0g8l0zUcfLw`O4Uo~cclx&=f zo%7~lmCJn4`Noi+(bd%K^mYgc-bB9YMbWao>EuSkSsXci24DVEgzjSpt8;tv1-;x- z?7zPgCW`mcH8Ub{+Po1u*YYm2^yoiWow|&K&a|czy4<07j0z^-+XLgT=E9*MH+)~& zKu^jCVbvU-v#ux+c=R}e^Op@E-ujyi87R^(HEw)wzyOUld&0`k8RWgIHFm$Ag@2wU zkm&s{s0nwO&OB0sL)Ye@>d6?em6qiiUd#d~6>Th7Do>7{e@Fx+5%Bs+rAf~AaIjc* zobD|?%;3XZPryb2C_6}xSI>u8rK4nj$PW_u;)j6r{UcIBEZ8{_ z)Z^z)w*VzP1Y1#c+A%?+>|t8CCLZr_bMTL!1iR;um|)LxG2Buljn@_Pz_@3WPARO# z#_gYI&sjCpcw`JkLx=H6b}71UQN}kBYvK0s$)x2=3Z9S2$1W*PjCe*&>|<3(hMkUS zr0Xbsa={*~3sMDF7fE1;OCH?h_mA(FsSuH4_OLib57tLcgt%5dgKBJoU4Az3d%7_` zFr6`=Qe3{qJ_VusaV0r3EG?EuK| zY|)^E4RSmSB7E{I&QDIDY~o$W5s!y)k~!5nujB-+liF$Whu8FUg)L~6@qNfyr?6l^ zfh^$j6~mq$Fl}56vo~FZI+b3S5Eg>s({|v22|CzXHjMrzIrO-O6=alNCev6aa9oo> zC-NP}HE*ZEkX;n{Ic68yNL5jb&>%3n5{m`XAJhAv{$OB<1c}<4iS|yXaN+qx5Y}uX zF;0~vMs5LUEy|-Cs5BVJ*6+Y)x`p8T^wN1 zxI9{?n+V=SMhMzS0uj z4keSoeG$-RR*Z(WmuaEWVbXM|miF3u;Q0FMVD&y6`l8Rm=ShnNd$`+Vcj-9Tq#;FD z>2Ja@@C;+Nc~JMRa$31z7jd@{<0h;vr^dON&|xM8FK&iXfnz=>8*Iei(c{2HBZdim z;tD&SxY2PV_n7lXPvh*glVr^gIXJv>9DLEa%e((3agItai1%q(qG>%5*Kb`8J1VX+ z)89Td)m9tNRi{0`%<%)nfIcSez8lHUIdOR9=M22y_zdPW{v@~m(}K@@&t`K#IRCPp zWjY+Q4L@zqL5rhm)Z&FJyN`FAP@-?y3g^s~L3_U;-J*59YD3s6SU+C@e>~j_<5Hr4)A>TgWp0=bWNX9A-3;}+ zXAIM2y)bxkJMqlP#ZDg)csj_>FgzA(AT$>o>w*n?-yG57kT#Mz6tzWUyU=k zqu}w_9vrk{;N5;L@`mq|xkewuy+6g##Jq;=GF?qpYPaA7OCd<|odwOaJ77lT74mj4 znHCPIz=_A!5IFFF=v0K^m5Y*DeECzbq3{J}k%PnuA!iy8{oZ z9Yl+3+v%A<23YpV5RLw+LZF>G^k!(oz|b6!f3yO6R^2&^`xlE2YG5ZUVr zLHW-?UDH=^UHl&Ny4(;(#k4?X>Q<_L(}vExycB)~FNT4+#Y~8}2FyD@8IBz}kKSvA zAf?-eUadHYOM49Pi%JAsp5FvRc?SiPW;&4#`qEs4V-Gfr$kS67)VR25(pd4XfUF7- z0>?K(;HRO1&2A?#Z0-q&eDhTB?BrOO5O4vl<5q$^ttPV%EW~HwN?crV6WS^N2P=;20Dy=LDo%+ z#}4DuAn|U9u@4G|nMFscCHfuUWc5CrmU{|hJ8Iyx=6rDZG#{5oN#Vj@fv|DnS|Gd& z$7|{{tT`%7w#0p4E>5~bTi2gKvvoeO?oJQyZh1ns+qapPZ497SBp1O%7ke_=Zvc01 z1VM3|B$}^1OguNY^BQ5_m)7eR; zV7_D$4qoJY!ksA1{b<)1xmxY49Ge9k|10 zh!#Eukei=?sZ-C9B5iH@;lUxYu6hP6N(*AdPe^i&K^LHyf446+5W(*{&18q33=6SQ zXxi8a;iUznns>|XR-FWk(_RaH>x{*8sW+hF5K$fGp~6XzjKR=}mr&*4D(v!c#1g*= zY|+YScre;TVjjML@j^QE{(fPUDL4xAZ%d+3_Zh4`0rbniZuEI}0-4Q!LErp5zOLkF z;ow;idQu5L6n5-d$M5X;x(jnY!NM~P2;nWFgmVlzhHgC0x+=f0}~aB zO5df?;_)JUCznizgo79(?+FmPHIkhCH<3#|DOP<>(V1$m9RvEeS3&FMOLRme3WDFM zL*ugXwA3^TZ(V##Hxy@)j?KG~DH=qnDOpHQD%pse4~$z~3!Y6pchi!I z2NZj8_p3+vLm^o(4T#Pez`oKP5QnXqb5ht(^dvxuGvZtY&l0I!@`*7&o&bE55jE18U?(O zc^$o6H^HScYqWFQ0gdrvU`|gsc4)VDm6o=*=u2&fu zs&*lgezEd}+?e~bB%`ZAfZ!HPh z>qbvdxQWdWJfRJ(Pfd%k+u+<9Ya3dn)yi z-;dEV7U1IiYotx-CDlBsgJW#Wct7P5Vv&D~4oHoV;*F2VteDTl%rX@Z-c&=^i)Y|) zRVRFkzd*it{1R;Nx1lzMCt%q$ak?&019J`4Fn74ybZ}1&wAHbo?y&|O`cIIvvV6|a z7D_i~jmIrdFH$ewW9jrHzDl#Soc?%qmfosggt>O%_{!D+4$RcTbp_U>b?&V3;|AwGo zl0J@a)FSSh&iLwjH#yU;4{B#m;*8RDXnkrdCl*}}l^%ikq`DrA{CuE(^EmeA7!%T` z7eZ5y>!YWQ874_|k{a1sqB`pwtgPw8)W$ zX*+GEcG5}MxTQwW1jh8?^}}=tp2T6d3Gm@gI(Y0!#kYzT@H*-Z9(Pw}4h)A-@#E`I zoag2&o%!=?cD7K7=>{-xUliURzm7JUa=2IJ7krv*1zyDg_~N7j*BN&KF-e(QG05kK z;mH_v>^WHE{U>&Y=qu$qYK>a5`K%oMP$C`s+b}UN9E=A%9oRZTx{$1BbTpRY&W3kVu zoNxqVeyW0ZvTq^}@0O5Ak9>S7twcVh9>Vs5U-Z%CG@Pnk4ccoJx%&P@Je4y8WezCA zrvCYOZc93uzNio5hNNleIR!4)`78B&KM#L(jU##qnY7=YQdqJ9cI4L*KOuD}*sH`` zYpa6$T8Il;g=o*}WR%zzjep&`LpOg6HF$uJCi2UmO~wk?6NpAZ_7;f zY$T^%^V#)}^*Hou7Q}`ZQvcCf(okjrPp58W%6`s4Sg{UY zREi4392mG-EQLQTTS?<_dw68u%J>#723v(LXbF?xy_VxZpXYnVJl{e;EuO-4lm?ME zpUQBdz7ww7c9U2S$711{=d?4k8~5)?#vr+?aP5N+EZf$@K!PawWSk8lPonWl`2fLy zIP{zCi-&`k(ZHWc5OFjbEFujh~_U{UR8Nn@+C34iL0` zn}+Xanz0vqs;i$Xx6)6)jPTji4{$qePj$7*Mfx?mkkk~v!fca8=&hCt=lzL&8w@j~4$yh;_Ctc4JSt7Br?2J3Xb1mo7(8bRG1kErp^3r7!{lcM}vaQkjF>S@YD$-Rx-Y7sjwC1C+2c@%G^h@i)A7tXXso(=3#;Px+{k3#24amrnNR>&j{5=uLW_L47Hy4R2k93#iM zuhqrF28+OXF5U&fV}2=nejZNS*Vr+^dfoIzGUmNJX+};~)4Z z(LtNiz7y%^b%HPQ>2z`O+3J4}vthFT4J=)&3b7Xh(BG>>pf&y+)C`|P_q<0?csB~F zbDxp3-_GFkFairLM7WUE|6zlcFuEnClO;PR%*!=?^u2{FdoIp{&Y6@& z>ICoM-rovb{??y_@4SV#AC%$Z`&n2nWoptM`V(aXw-(KG>goV2sQC#}gY|+0}E%W<9nW~P7D^~oNwh(JQKDkfJBFjLHXK!@u3W2`zUR$I99`8af?r_gV*C+X>UJBUqfhY%YjIGVc$;~f8^7c1hKjU{uz@WB|A z_nd&AQc|I>{RHt_^@&_?F=G8CPT+d+D8Y;49W)UV$!hLAWlq;IhFg@`h#Qjl=3rX& zYvEC%apDD|b6S+^*k1>lx|;O4c{c9aa|}w{b5X{)9T)sKMEW1sFbeB7qv`6SnC|Sy z`;Q48wlnAVK#JHttD|b~g%GCJ>@Ds2@e@DJdyXc9WAR_CEbl8Dfwu>D6ZEjd$U}=E z`^zELh=m0c{BY6M z0p7uQAFGyJrvAO(VU}MC{xzM$_B&+LZ#p~BW8Q4&TPMv0H3vgef;W48`%T&|ZjMo2 zA8`gn9;SkiDjpe8BfpEJ5+;$(v-7_l5 zyL2Hg(rPwmxV#Cc)(ew;uf;*VBMBXY;=p<0SnkEHc>3~;0%mNv1arczA-gXfL#~X) zV$b!|zO)}7Cx^l@ldX`e8cTP-+QdHhmgmVY0*s#$5BmJhvbi?{O-%W72rhmjD{d*1*{Ojr z^4tp_f)J>!yi?6B*h+S~x04IJkF3~Igg)ebc;y~>_-wT}=VwOHvF8rsRQU}~FjG-# z{Z4pWe+TqdN`lGyEL)LI~P{tT+}pDK3Fm1nBMN1&-W5Qkz! zF~@E_b?O;qw6TnoPu9V4yV}uzUOY1-QOrb1m^1ydmr!i;a-7x{3@hz|pe^A7UF%dx zEtf?>qJAd80T+xbO2^h!?$k&`pW~LLf?E7KU>;b3f%;hXRi7#x^iu?vo@l}M7ezRu zwh13TzKj8aMie*iAalw+5HIobkisQ?rZL2xbbZvaLd<2g@Tr^~oY5?!e;+90k`WJKj6cwyYxl6 zA{A_ZLyOO=lcHux)=LT?+Hf1JdeRU5t+~WAb^|u*XVd$C0L11Q(ZDChkSr_)n`}yG z(+mU9+-VNicsEV?iKkE??N5$dpGJ}Wzv(WGF5;BE9YTh#RR`ynp}`*;{eU!}GbICrdfZ7+O)dTTW*a$qP6G5o?5IqL6wW_A4v(2hV5f{BXkv9TcyD0 z&H++a28JW6=i z87n0vilK{Uv!1h>1hb>VQFe15ty7r=lFNS5FBNvA>99QRRs29iR-K@eeN*7@XFaO* zUmrcicW=}U^XQ9idkk4U9rn$S1Lc`wBxn|rfaG4N`Nd$Tq$KL@T8up1mLYE8xnaTaNZffr ziG30Oi7E$pGBLOBko)?l;qc#Zcrq)7+9)_N5qARUhZUiaJo22`zxy1X@(Y1aJKteL znizR(=nC$le@K6sJ&yHQ$b3I54$oKq7E~U#gwazs=*E-TV7yhC?edt$&THBS7o^4M z4ZU96Rj>`c#iL;Rel3=dm*AV=F6}Q1As@O=^S$pf*y`j7O8m9fI&}}u$meKW$0#^_ zDI}kVbm7D^hMm`!NuIqGz~u7)ovks@{52J=yAGhJ=TGW0{=T(Sf z-jDK*@F6UzKS@)IZWCv=TQI!k0tS1DVN-4vaWo#m&T1!oIcWwgxIG?attSDu`8xeC zPnMILHX^WoF@&`bo>QN+v-mf8Ec^MrIz3UxV72@-x_{)A|3yIqaZ z_qL-(%2KjAo56D@QfNzIIW72R2spS5wf^ZtOML-kGjk#G&|NZ5YmuqLCR3QTav4tk zZ3>4jL_lrlM9wK$0c727(ov^9*nIUB)f1(#^HDIC@Z5v@scPK$MG^Jo`Q6AwYX|sO@*|=aVH+z}^{&@L~@!Kv>X7T4MKOQay)8t1uJfwyP=FNrX z)KQ#RropN-oq_SaJ~**43!)m*V8yh3C=uL%{mZtYgjN$I)Qq6^+%DLp>4jcVIGggXFb17y-d^TW78?{ zlJ7jXZCpvWrPZ6R_ASJsP7^F!GZWrv+hJ14NoI$nI{Y1850b78nDNE|wcT=1HDojP zn!lyNGk*!bW|=_vAvt(tC;+bEE|FQw&nveUqp1SFZ#GH7-}^TS1ZEDPV=RHL{+aZ* zj2Zc+dx}`q*24kb!TE9DWGda_#=P9D2lHYriNek6@S-oCym+}9cB@CD?Y<6@Sek5F zJTMMo1A6H(gBh)y_Di*`Hi&{umau(V(w?DUufN<2?5CUp+h z?y#cEi}#|=kS|8dZ-CEN%TP+y3bWRff|iv%ZF9Lsh4@@&Bw{6K+%kmON`z6nD1!bD z3J?)J7usu;UKijuna+4u9waqgr6f|%2wXR$ zQT9;^EIoKdu=q+RoZKpoJ+I?XOJF644LVNOF8^J1xNQo~Sd|1HPx_)}SuH)PtiyYD zg^}aA{bic%bJzH9JKZYnfT|{T*ulFFdJDxl{#_5fezRf8lrmHbNfx|&t%(NHrD2-i zGkQdFn zLxcETNcWpcKha5GS>=H}qCo1p_c3q7Zs4A-7P@O7A2W^$fs55mT=XgwOv($$&%awp zOL7@55jjP7R%8k+o}5C#WIy~p$xYGoxR|0Dou|^u~`PVQIl|WUnkE`a_!cTj)Ub z<3GgiOqRgkLj_CkSyJK4{{K^S-tkzyZyb+E%HGLWMMhRi8l3xjD4{YcDKbhLDlMf* zO18+#UfC4c4V?RWh>A*E%BYkmCE8oR^ZVC7yq@Pc=RVi<`MlqRzY%d2uM%cX`47sk zKB5l;_>iBa3=eow_|tAGlzPm<>K?8S6Ym3e3ob*u{XTrWaXmd6eh{R-L}Hr2BjRn4 z!fpwDN4#l2Tw}TU^~Ha5+ok1@?kLEemNh~3hyvW~iGnXzZqdWPpV0>YcVu#vE4;Wp zmA~n|Hf_9OLEa@8P>-Z)RzjB|e%T5ZD$%$y(h#NR3G?G}m(zQt=ctu~HD;cJJLj6>P5pk_XPTwyMHX$-4DWFw&0o@Uof_jX!8W}wSMaem6;SbX1=K;*|}gVO%Xj7_vYvqXpEtCU@)d!Os`rr!KX4vjeSMZV6! z;Gc=)sgE*#%arH8Oppil3}d`IyA!R$D7cRlqUfiqG->2GlpJTNT0cuSDT(vh&)?xu z*=2YUR1Ht#uVLoq6KLV54Y$jOY5oThH0E9h+=&oh5M}V{3CB-EB&6HGT>A+ti=I!+?lBXHJ=XSsiFeH{}qEh;f|2>JC4g2#evUZVSefL=^%FI3?{bamj=I$=^~zX=oHrm=s|y`}O)9(d->80PkQbJx)^ z6q?+F@pspfyK*l`xK9l67)-)Zx*NY;`iCPj84#2yOfm{J;rp2a>NI$f8CerSzwH@k z)9lkAt#AYBvP1fBC+C=QZ->h1Q+a>qwbA{N!FXb)1YVhFPw!1NLD_EyFmRV0wK^fm zd+_u*U0%Ni%;p5*F!Kv+g7Y}PYcQ_d*$&&ccw%r9mF3Nqj;1bX;R(8BHM&T zBReo!tPOrO)PTE*6^!3OsJi^X)Yl@L6c|~8td==^;`#!gZf!E(p%k7u2Pjn1BttRr$1+*ayWvW*}^$MPF``0)M-IkXw@gi*HN?zGM{XSft5|T(A#Q zcitzdXocOwIUp()h8c+$*{oMu@aaJwLk%k%rY+2%O7pWoBDNRZKbPXc(F7u=^8oZe zrm?ShYRv8Nuk^uIJ6_dO5n_=sg?jex1;vvh_+qLPdARr_*N?i{puud1K7pr=M^^UY z%$4Q93vH)sZ13QL>~fl0po=|{ryzH9oXs!|qPx?=@r8OjY5O37^A#78zx(CUYi=r# z3ku*Be4oq|k)yh?&UkIK25z_dGeQz4v267&5bLn!tvP>zdiNV+y~AH>F?T-oXbFbT zBO4(`U5bBNkz(O18`P?BHBoOrWa`Pe+3p#g=JGP_7?dHgG+9lr=Zi!<&hbZ0@@0ywK^w{Pa^n@aw%XD)hMFfzk*#8#D$sn`*Iq z+iTFgk;-Km58|zV${6=B6=sLkkTKcQWLa-4$GR{AMWaCS(Dq#8_s(R{os2MiR> z_!IGt(-^8{-l3zja+oWH6X@T_i}=}Atnr8YXL?9ckhkE;I#Rv=Gj^R-;++hQCApwnC`xZxxn>ZF`TqXg_ko)5&s)r!8y&hq0Oiv zI@*uWl;uy^my4$Gf5hZ+ZlXTau0055t93|M-Xwm?!An^ERTGU*C18?uI_~i~PVehj zqK1|<4c<}&)64AO&W)w~`^GQHjGX86Roz)u-Ej_bdPqJQNNI&^G!}_BfPw^DHYeg}c)}BSJFOSh@@-tYvxCGzD zjFYm$lc2L|A;&V{X4)Y(5Y?m2K-f)qS6xakXZJ#AVm48`HxywWA(WMc+NWpHxb8pLZju2CAr~QKf-r6>kixCc&rx$6A)a{&^kV8D zitRQ89oJIYWV#4juJnLc|2n#Lz6}G>$;fv}$NVpYbdA{nQLmNYyG+f7qkh#~Hhh@= zbrj=Ogv>*8=W=j!Pi(9%7bhce!5oa4BH3^h1FZ|FzPTq!^y0za>NC{lc^f`UJd84i zZdmL8it0;g!Pi0q`lXuNTkZ20X2%@EdlII2L_dc-_Ddte6M|Tw>R^uHyB#m?GH2Q@@ad6H9%wIAfM?4} zVfA!AeCJt{gel=fgEvBYWWUmu?kd!X-VGnRC7{odd-m_Uh@Tc#Q&}Y^nDi(Q4ytTJ zse^L3Q^W|b?psekF9@RF9cvlmgN?Ap!XJ|+=wV;?TK1I_)^v2#AhlfUhTlVjwd z!S57YvdO!E7`eSbhtE;e{#`71 z*Tr*utTD*y9iX2sYVswv|0SFH@6)XjcgZa-zwlwCj5Mz?z+G2`$kpk=pimjadH8yW zy@8EsnL!ek>iU7s7Gr$&bR)CnM-Hh88SFm=v5#KMm z2JdB8W7LW}m{(#3UtZ6GuDHE;Z+#Nn{b7c$-kL$#NgDxOoSR; z{`AT}xZ3XuE;}<5H(Yv;m*tgtk_$&+*F|ZbQfvb>G;w}0eMA1Smc_86`X+|)wNcFY zF^oy(;f8Q?_SWcm=4_cfZ^Yt0{2kduZj8(0Y)vm-EAYz;kmB#vSvk!SM4VD&am4cD_)!K`29Rj|jb0fK zpeNB7K6w>FwRQmtx+J03$@Q3BEd`4<=rl%jwUUJ$%i-7BB=)S?QMy(p0e?w6)1*d|{qK1VWl5 zKj-i){);OqczS&^91`#)i)Ms!46v8ziRIs8dJ={6h zf|tTp(|r#gqTUe+Q1TSUAnkB?Z1NRDR9b1$u}WsE;$JW*Rm2iBCCFV3wR zgf_iq1&V55G%60aoPLW}M^bUs+A2gx29L&m#0w9X@b<|+VP=*rz>u4Fi0#&7lpqUH zrEfBwo?VK^|7Ae5WG8O#ev5rt-I%3x5n|qH@~i#1e*CX_yrGGuc%}XiQ#807JAc;0 zkR6Y-2^m0~6W0T)ETpC1bNRzvLFS)pjT zPXN@egxkra zv{Y(s^8<(SLbsz;F@|B=NS{iJN{KyTT zcX<4q3~K0?gTa!SWb$NR6m9;Gr;!x`t)m;6OH+JdV)iYJNll@DMI`tUGSm4k>ts;y zQaQDH^p#zFd^P>}Y!+Qhx-m4M1N+BIiJbH$B2$!3m+olBqHjDXxy~`@kL9pm?kPe1 zV=eHS)-KAEVDyZMtV3rj*7p2>KORx=Z|h!^>&T*E<7K2_ ztPRDq2f-q0gn3ZQd2yG=z?K&7*{+*~Gv)@O${SPEk3Y?Gwa(>Vi+&0UH|~I*vmANx zVlI@Y_k(kxqHCq^c}Eu%%y^Vi&48` z9v$bIU{|^lyzZU{w%4NIL3TgBE1$_M4d-i~CgD`l;HE}L$ef$^I ziG{swn4==iPbt0+!o!~Ypw*hZf(e`xm&-K%Yz(9;`?ruKo$R;{1DFO0lWmg^ zflL2od{ZWhDYqw*f5t`hil8X$707}|G6#79{9ewZ5DW)y4`HG(g~bo=<6|PoUpD7B z%;=)z_|BbhxAG9IbBco7eX5`^wG56(c;k^FbN;#-E$~)ML_Qbm*d>?5jtMY0GgAmf zZ?JH-eHfg-N1(I&1+wu$2wp$Okhtbjrs9SNI9=$*#K5_Xf>j;zT9(0BmI}lMoy7Q` z&Dd~s82{LwAY}rn;A6O&H9VWjbiApd?Q9e6&QOJnS^@( z6YpFO^jlR5b8C)~zS>P#Ir<*g)<Bq z$W#jAhHpz@g0MKeXiLMle(SvmyfUWhAqQdJ{+AeA`~(Wu zeZ>=V&!O4ZYB;sb4Q;t>__Gay=+Ys@Z|C~~z44IUw{Rs)5p%+S4TQgqX`%0;GnmI3 z&zJ~PF`j)uDrr|}Lir#BemXv}x5Hm;_b_(O05FBmsPgi& zm_A$x7uq(_X@`u-LG~!=e#P<{yNJ4f=#>U5pk9t1;u-XV`0Pk7bENI7##&G!=K_ z{h3l|buku?l>^U&C;SFb-@Fe^LQHBXq7;}K@iq$ zkmLOdFyJp{Js?Dl#(yEsu*wISU)Nuro+&TU%Alj ztU%5s?}ek44K!xMGi0P&=##`anCnUD4-7z)=A-0~vl#BNd`WGZ82obLBVBv+6Kz!2 zrz;m6gZ7inu=4d6#_sQS@_jiU&Y$#wcF_rZk$?{*drtx#dNF~if29D{aZ@1P>=C&m z_>4*{SPzSW;*cqdgtw)^sIKA(8$ak!bJm>qJRurJxaY}WIdrbbY{8(wuhi>wACu}EM%P|R zK+hpv^fnSCL(VZU!S(>16{kq1-s7{w-V$JLa27w!=A%w(AFeF!A`Y$Lr1t6vEx2+Y zVm~PGup}1N3}k>2_rGtS^bj|E7KA0Y1lV=^=Hi|%2f*{;c*OWC=nrI)nGWrcHR?*9 z?ySP6*20`OVFK^N^>-x3QGoa5PA7F*BL}fLqjb)<`DAvV42J0xf^o7f8+Ya@DV=Z@ za$c>6H*rx={9p=rSYM*r{S(l0sTlq?ze}=SF2b-6H)*|I6R6!@0r$gJVOEq4tdE^Y z3#-ClLEAYJzS9fxK2)<(iQnMt?>;=R#Td+0o3Pxm1md~heREAT*Sk_{l$cY-jD=K_ zqEtm+q270_sjek`67wr}HA|CG2&@opR zQyv$PjV>F>rcDa`bk+`zr@bQ|B)!S#r?<>0|3fHx@He|gzlv@dr!eYMg`KxM=mKdE zdO*R9==f;O6<;@zveqAa-ns?NZC3e|eoWY1>zFS>-m>$n2mm z%6`(IeJ~Xw>4CfMKO&{0+1y_B5z1uh!C06rEaPQR51Exz&15%o>1i!7 ze-MOG<~y-P<1UfkbO4JQjzZF`+h{+zfaWA?Q!>vG_RkcArMaFkD%wdeh9xipc#YQX z+l3P2TQOJ95p>c#Q8s9RwC*qiN6}z%?E7W1`L{9s{%`sU-^W>)F}n=w3lrO2}y3QJ~Z$JnxNU6e)A1c&i z#yGp~*kUj|IRWi(au z4E%S@hx3(fAjMU2XgbnQR3G;DE(MZT`*~0h`<$3rgu)WT3yht^ady77AAbCtM+9wEKrx*2n>hNBu?asp#@#l? z=t&Kk6aSESlsTdJ(zM3z?HVvRqZ_9@7l0g<0WvdR5~n(eT8MWp7P_ z&Gy~&yGjSuzkUQVZfUV6`t#w<^%Xk=icZqO3{#U?^=#e}4Vdtq2b(4xA+6i>iH}|aBa$!Asp`RL#gX|)VKLI87(a&b9_~JNoLOQPE(38a9u}|-9*qL&jxnu zXcN)6wFEX*!pqy2n3{tVNNjK!nUFig-qBe~I@NsXx27@rD{~zUir_)|^AoVPFBJP< z+R}*VA@=m-PP$Vyfb1>nBXKWw;7Fzvd$x|^zkzuAQ)VjMx8ky2(&?aee?DODSvW*i z(w<>+{Fz{cn#+Z7mb^1W4NigSbG+cr)tS-hunM z*HG2wD21Y7`a<55lt@=~&*~ggxrV z>8!A|aBuV#^l7f{SgYr{tA=Wx@84-?uGx1;;nSI~} zv+VnIm>F{dPA%bDtNrq1*2pvzHrB#{eZe3is09}KdK{137GF$0OV$h~u^$E&!I5Wi zrvJFlwM`C*)4>h6Dk71(%$3Gx93x}%KTX=cyoqK#ub^eWp3%8gz4R_m8r)9`;lN{S zQqMh4I(7!brM)qY72j0R^0`09=T(B0dW#8oECv6&99N2_Vce1sI;H9up7pv%9LO9D z6A{GDbta8<_Z(qwej8Ew=K{Z!!iZCz6bX!SCd;z#kj7JH@Op0}n8{Rv(#I2c)8H_e zuGS*$65QY5`iYuxynN*yNhlWnhj{-SVmxc#vNp0I80In!4lY;$CqsXe$q~P3?A^!s z?D8EVwMCrCO|!w_J>QAPzfdX`d>lFigfa8aLRi`;%(vZNK<^YKQ8%wRdb6w(uezt= z50ymDb^e5?#~lXsQ_ZN8DaBuZ#vjJjg7DwwXd)_X4t3&}8z;WXAX3UH#LlgpE*(q& z$t`y9&LtHmeRxC7{JHmqudVR-nJ1m7nnUs$%Soy8SF-VsI{*#h>NjSQ3!gd}7gt&8xO)cn7t0XFkKpOQv8ZV5MLp*jalMta#+=GG zoSQy{!lXCw=1mB*hVMy*CQgPvz6gXzzGNYC8u+DtqQ`V2U>Dadt{RMFyI$VL-9sf* zR{Iv`m2O70eaW!e^)a31Sw^mCT%dUyMLvIlZquup(@>*gA=NS{gOtH+_M49xX|uW3C}Q~v_NbnQ>RW{B#-GHSf~qhns1#oo zO=V;%{2==EN^Cymfpu-ol-2V$tvwnEA!UZJ zeJ^S~^dHO`uBGKIX83KwCTy8`4=3*0jFRV1=?GXzUsEdRsQ)N)i z?I!hS4lpGmOX)nDLb4|B2BSHgPvvCPdD~B4CSsOOut2bq-aRowtTuBT=BJxsO3F;& zTC|Mr!lT5gu>?biC=TjQ!}Ar>!87GKEOYn|m+iEmQa);s`r$6gj}O4guH7ir_S$rE zTLn2k`y|ekcuWga?QmJ}Ct7T82xi6E?EE#{9&)lc|M|RCSjC?X?atjG@a#GrGE?Lj z`xDUe(KYJ$Bo_Q56KPCtG&jf7g>Cj#G z1?<*K;oXeM(6H_XTdu=pa+#_4eELSlM~0i7%1c37N(j3*ySNcgE789veCXEC_lZw( z5DlCp3>xuwIZvcKI^LNKmpb&YRpVr1ddedjYxK6!(MFk@VP3};w=cvai(?dYoG0TE zeAr#lYWi%M49_S{1TQX#rh40_(ofgrFmlQRa&O6Mv&k?utKvT_ziF^td@R z%~a*~#1GKlI~;ZG-_s7AyQb>TC85fy5e(L75-xB@t4lS(dZP-gzgvr$`_3)wU~j=(lwWbn^m36fId5J|oi-eV z+2)$$oA@BrUz|_pq`f77e~5zdUU^`aW-z;25}Eiz!KBub2SFh<^x#@ytO%Qg&AxNV z1I>3#klTLBcV?Mqb9`vzFFBaX|3GJ6NCxkfSGeqIGS>ktfTO#I$e{}&m|hS?E8-KG z^XxgauvDd$0teymT|r1Z4KQfZOG{Gqv8hCgNm9B@Cy1}bSw6|={&6?st@MyMIzA!4 z7K)LKQjSsYD2ZFw?ty{HoEL7XFs85Jaw2Rw#iKLmH|Kvup`i!0$M(RkH@0-uxo2e5 z-G{)16LiM$%|zm;6Rxb$qZ0KYR4U>V-IBT=`v*0tNMj>C`(uC%J05_2<%PIQNC&h& z%!UBj402W?6`Ir-XpP|BsSHx-yxX~W`L_#pWNxJPsn%qV?*o{AIti`{r4xU_Xu7$2 zGdmb|i5*=s7X&Ws#A46;Y}h+>(rSEz9{>J@ej1jB^3hGmKN|&h&qeT?Zaw+H){&8+ zi6r{FCNtmB0UxUVhLcwsN?O1DtdOzx!fxyB> zKknqD|NADLqU45c4NvI(=M2Q|bjPZF{~GS3=itYrkL-qRx%-pLECY~Ie@P@J?8NPiN+6(TwaYgK@Z=T?{YA?xLu^PurB4DdW z4b;t&K@Y2i;A1L^;rZXll~s;-uU!RCTDK5Oo**PxN7Ak?Y1s2!n(e=%#d>lJl4kCD z37FMF2na;`JoUE}TfDP6tW zMhou`*uc4~hl$gMG_vc;B4+VSXIxH?LuWoJDyD z?VDN<;>Zz8JH2nzZZpnl+mxZWQ z?hIyJHzY562P&qBg5{GPFw{orhm|dOSkw_^I^sDmo&e)3J%gWcBAT#)-nj3~UhG{w z84q7qeG*AoJHhd9YHuHae?IR6-GEVY&_WgZLO5T+9Rc1fok`@s&!^d_OBV3?R2enQ z{(^3+O+l5urU&j##_9MIwkgfxE%Q7qb z-_Y)6&Q*Yp4U31)k;NUB#AoUwbQrRwE~DWXS#lkvnv`&7xEu*CEyh!u=0m`80)gH# zB%$LOSy{0R-ZrQl}Y7py>85bVE6+|TR4e&yfvK8V5R)g>lH z9~qdbGZVs|C9|!QJt1#AocWYm27)DPz^&XE*X2F{{OSX&j0-fS9%7&AYtaB7ZH`eb z29Ksj(9?al>E?egY5wI->Yijs?v^YhdKwI_!XO~dy-Y~t7L@jVNE^2oHg5cpMk}^i z&|j_@v>;&?v+t}a*l}Hmw@>DA9T#Cto<0Lj3yz~}wlSi96}V6GqEBsZnI7Wa%W{S@ zNMzp`)Miy^S;{XQy_$=9MPFHz7dPdfybI?KDq*zBWG=ty0}@>eQFhij8hHN$4vQ~i z*WRB+tjf%3{liz}w3R(9Q^;aepqtFJGvvVt)nzoFx8x6^Lvna8=F$d*1hRu}B4rtbqq>55;c;>b( zo_)Ta2ug8YrBf5I*#{ft3l(stqc4@+VoK}x2=ZS=N;W?H@QC(}3Yh%f5QXBegTU#g zG^(~Ru){X1amr6k+9_eho zM0~UyQE$B|nwRdUR);pSwlU#2N%uAPUZW2w(HUgsXI;1!lS6J~%bS)y*w4xdUnA0y zPFQ$)$TZKZpPue=rxPDp!_o&aAg)=8)*;OtQ$P)0=oMf~_*@jROo0H)3Gn{!6#9E_ z4E{)61|1%I8si_fQvKzhXx&~7H1s>mbT9{qLAE0D4oB0sM#WUlM-uuky=GQ=+++@q z&&ONsE||K_79Y!gqpP-6!qw6%Byd^|QFj%jN0UU*^T$H0&AUe=4!;N6Kilzlbqy?2 zx5X9HR?&3dLwHDSIj-!_WFHw`gp)(3IPQut_1MgPj|&B$NpBWbt~f%}uD+&!qG#e{ zm!{kvJ6xpXuLuUGdmWK~Mk<6g&rVCIi@B(fjy0|Q>2iP%b z^waJp6@MaW!PC8X@?{5e_vH&y?M`i~SfWg4W~Q^xm##yRBpV`L#Ap7s7(wTr3DSvKLxW>*-dKRM&sk!W&R6Q`U&>}2R);eiMrdeJG}vCg z0M5lLz*zG?uyvsz@kJ2x(?Vd9bOiB8R^;Cn@`Y`$j+0O$0kE(hqw{OV~cP>Nz*d27!TL^#Qe<Swfm(-)%*jHj(d=Jcn40z#k98WLoDlOIH!zwjXVr{e(#a!c| zX;&&ETYwsOD)e! zLxGeB&NB)nI!Xue<5_#yqht?rb|k?MLkl>0TIGa1Glno$#T{eDcaBkMqFgk~!70QDc4?Raf-Gz(>8*#-F8* zHisZ*Vlosf7DJkQfT`qEj<4lih#{9%i0z)WP=DYiRybSIKXS1%_Qp=-~7ivS-{8R8{#v`-MU9nmBZmXS{~?Q=sVyXMoey2coMY2wuLa!=zBEuY9H(n>_ea?za$l-|&Q_hlorh)7 zfjJFpZg!m7z^D)rVgs8U3qJUr`*?r>;^ZTC!j6O-j3mw8J;?qyO zgwkj~SA`KoHjWFDbZy^dxcJ_DkQCgPq&on+^D zI9<{Z#TCt~iDp4K`}%r2nJb=xf^ClQdpt&G{b_);^8aD#=X?l$b+K{E z*?uw**-CaNr{LtkJVv=l8`nG+K&55&u%_FOc1>T4Azew-yk!cV^(w&BVTvbivc5)5 zu7tq4)#+eyu8BP7It~in5$Lj1m@e76nM4bA(};wd^lg19J%8s4HAvBeA?ajdbK^Z; zJ#GNg+>2p}FAuH$#=)}p++HXD4v{asLv7KX&fDM(*^dK2B7KZ$-WWx1c3NYIV>!Di zIvTc%*D)7uN?_Rxj^84w0l$_RfVpQVnW-fNDaul;x*sKb?!2I7dGe(CSO~5W<>Np` z0(w8DOjmF?ZcW%tc=uN`ZMIh69q_90tsLi;m{trkOzTM3vClN(eGXn}DS)#B%_O|? zEz{-DMDMU&s1kXTdfRtWZycP3V&v+3SLq4eLey=Zekja& z*3?9W<0b_@CQYMV7#|&hmTDbj(&>3{HR&>Kedx!U#-1bzTxYE^pcg;yJ&8t9uZdi` z3GUr|k&HbT04@(pJblg?_C3`{JK;`ro+OG# z{Bj}EUy3d>vc&T63OF>knq*6SZM-ONhBSXRp4?YKy%c}aWBWdnJtsSuBf^@nJM{`x zKio=OtvJr-oYOG)RuNp5E=A#gR`8|Ro`%T_z`VEekn0};0UJX}Qq3@EoDhPJZy_Ys zWd($2q+&*BD^^KzZVT%Id|?%Y%hP)3nOI$7Ru=;jyEVW)_!U)-Iztq8U1a>HTw)hY zmE(Ia>cFOzRqTGb8T8WiC0Mk?n=XAHL?_zR;H^c|aUoo#4vqOpcHAe~F&bpacrpFv zdWha{+|Lfiak-JQxzs>)Kb-3NOD{bMC$~IB=^`aNXnnQ?+b_jY)sLxEO(FvpUv zyLBpdSkI_Xli^E^re^5(MOJKo&5_M`b-|=%LGvM-w)GQrAhF> z@f0+-A0sV`MDT4K$H1}JOSGSRko{Ua(Z5WQXtY_7Qy=rOK_nSqnIEZgn2a+H@#s3I zdYo-igFeZ5(32jDF0VgR_Fx>ih>Q@Eud1A1aW^jH@;q+iaqNt_MRcrg1O2hNf@(aR z2W7PqP$L=6oiCrE`VJFD?yM%=JI@kNyBXmZv6Yy3X%0F6crK)TFU6|8aX|jLK$&L{ zQj>c$MkNF+R=g&^WYzImqZO{~%|w}dJ>)!>1v~^CZBe(Fc!HK$NAw|o}7 z_Dv-NIlFN86DiVYD27Y7l@X0DX(mYW8VT_=B8CGc_&hR?Xul=Eew{$>EzW~&DmsW- zDwv~xi9YczWsX^T(-$dmH0hcl)HUsF{7Di~U{M9l6FN+kf39VXwyZ>h*g~kCRYVn6 z2=ZsBm6N?6N9liYi(t+t9ZW0LAfJpR5O`@^-#QDnoSX-#awloQrxEDUZGx~1g7l@s z5FFCZ<=BoqydJt1EPLM2g<7+SkNRmUIn@?KV%FHbc!X-%R7S8JIgQ}vi@ zt94OFB#JowgL1;A{(Ey>;lsr zrZ{im0Gm9pl+3ES$T)a=pmw7%ctrYfxfD$}c*!1D|B%4UyXG|UYzaQ;b0azbM9?Pg zGUIG8NS|=bQ2V?XG;c70!FE~PD{P5<71^Y3;2aVBA4TWkkJbCe@yH&Lom5t(La2o2 z+}CL-Z6a;7Xi6o0rD0@d?~)mn6-A2Y+}9%|TcWfyBxy>UO8w67Kk&ji=f1D&^Lf87 zKjX^cwXytQ7xhb@4>ntu(oxwfvFE`O68p@6jGnsy{gb5e*`fX5F=R@Y4u#O~K?O7- zA`0!fFpiDZ(asTx(CZHcM=@-$8KxhBc>k; z%j?VqHP0=%n&VL@88`<01$yAS`X)~JJPY0|9Z7pCG%#u0aWd6)1X>%J| zlKnd83AleQqTlZy#Gp-&(SG%9*if$oCu|$Qb#pS<)O-;-xSxkbvW_%5_yk!qbF;Ai zjuMrB^_EC|+(ONnwWvKxm9-fE4W3VNrWsW$F|=_#Rv-U`_}~{eyWS2$>&2@A&!-Wa z{D;gNo&DtF&C$@s_Y8L%xu6{Wz}_>xG~h=%{Pq%qkx`#;f{ZTLq+17#pLwr^|2weY zBH&SbH~-va!K?@6^k@N}g{zPz-bSt9xyb{|GiC9ZY#=T9BL=nhv3O9UflHdg|Bn9h zeb;0)(YuqTc;cQD9Och1smW9DjzEc>TEb09(!-XJR zv(^Ashw8x2EoIa*%$YiEs%0$pz9jttee}^$(TpAqhi)xvJh4yR9xXBukj$qu_}#`EuEDx7?qsJ}O+%Fgd9Pg#v*W^~O5 zKedB&=OQVroOB8sW;a3qtovM1v<$O2Pn`W;?nke8*g>#5-wzQ!!01mSL|3-hgPK-2 zPF0H&T8VkX_Q@f9hCGHW*joWt_?-xouoN8M1meS-P!c6xY5g^91r_Q2#nymc>S}QY znl?&6=Rp>_b>!fp!5N|%F3&!EPDrb1D2^=T=qjFTRB(P2&Nbkj!t+_~LIlJA9-|3O zkH=c&86Ynaz_S3QL>;6X6>|7&+>GZWD0>UGu8ttPnvL;ed>=H9Z-p;$qu|{?dv2DA zw8&&%DvdZI4}rJWGNL6jaZ}83!l@NvuhcX8$9f#>d!t8x|2JPW@|J+xqjw!&@i`Nt zfiXBR&XKAgxIhZLgSaDE5qSLc783NII6j*#2aS)Bx^=!G{i$KFrE>thrCmw%-7*^T zkE7{>=P*<+5Z8a=`yaDzkd~0iIPUOi{M|W<@B8fs3nvGhjoL7zJr$pSok3Oqs=|vY ze6DZmeSXg<0ppnwbWZUloS>?JnZg8kX>ywkru&oRFOjgjR~Z5-tZ5qy&x^>7AMn04T&BFPx`r8fo9@ENH@vlH7elHYeF%k@pjw07aq|(Z7qhXs}Fsk`# z!{~(9bWd6#l0-`kT&V_rmKJz!(E?&<I((qxlOA&=K`fV?wh`N^#qpm=ll1JCfrxJ8oCq!X-Sp-wY3x`h$0mGD#+_i#>~Xvcmvn5f^;thj*tU@D8?L}3%16ku z12){f%4?h(qlV84-&23jE@r}I%Dv?la?ds?@m%BkXl^?KjZQYw+w13&jcqAat)fi& z>ew)eSzS*$42-~a34wpro^anh6d%f*#6`O)IXK^#-fy*qkwqC~ldK&tW1LCd%p$RH z!bNVbP6tsgGNNtwPC=o95u9H2LAdK6&%$r3r22gKNPAi&_hix_>0S|tVxAs6%RY?U zDwlxXqFBaA?I1U@>?$5xum}FC%cIQwJ!lF$(7>;lEPiB12A>M({&SIBqHaIeQ~8ct zG1rDo-P0Ss*}qAd$n;zXT2biAT0_Sr?^#rb-4_}NbK;%hFR zTelj*?mnfX{3%znV2CTUbV601YXZXu9`My99YO0umo0qk;TebSv*oJLyW|lnNTlv z!G*W`>58ovF|K<%4DPKaMH%s=%WO0 zO!!0L$vHk_=MT$PO2EGvr6f##wxFvjgld)WZkk!!;ZQ~+jXNNu>$>%TwYf)frT?K9 z&vpr`yGnh&Um(X<4Ka;bY53>ae9W}6r&|u+BFwHGSoSB9ij{vPYqJK4$RHLRjvrx! zLLJO2U&589_6q#7^T|)8iNX_;T=D62b(*fAgKwuQFzd7SK!-yZWWsS&thhoJh*D%0G&*pn&_o-pQ zQ}Xxk8~V>{2Y32jJLj@vE^SxH#}^m>LCe_rcyHAqG+BESbHlIF-ev`y-F_E8-dBM% z6(2hIWCy<|2}I4@@9-im28rklmR(+OIq(7)EIEZm z^Ii(w!>gFuxMY~P#)y7Y?4)!3l1P4S9_US71irRf7wT#l9rbNONZW*MupXwHXzTg*lm9Oj`&mZ z13IMgr8;q<;N(&*q$Ti5e@wS~)2HXxY(GA)Nmha1Vx_g2uf|^Le9j{wzhFA#sLVdP5gq9!O=5>Hegv_C|60;&<}7!VgqD zKppyj+$F9_Goal^29$z~=)!C(v{<_h7cQ-$Eyad#W!Wlz=cq~k>vmzT=Sh>zw+z`I zyZpfBniF{K`bVBMD~Jr6bIIMwYhju03;O$t1D)sW4?PPRP@WOXEVV{1WkU>nyQ)p4 zqj>kos0#qTWo_w!1PxfU{U7NjSs?wZfF5wL;Q6ivaOC_Y zc%u4P*d!$jtBZ2Mc)dTFR%ML7D}K_nT`qX@nH0Ibr;C*327;#T48gC~N|VFN2c zU+O#*2p$V)MbRG;I`RVr@mSdVlwEQPaSpQK2R>v{mtXD&aZ52h?YMU@F zt&phssf!v#Z!vRb2OQPPfoofgF(@jJZm6!KAM3U0-cMs8RN99z8$mHb%|;}DMhMr^ z&q26_I^6!&g|ti&HW|&t;7f_{YF8cI;CYWG=~@w!$@z5Oj}y!{NePHb9Rm}jgQ=IH z8u(5k&>k?Eq+e!;58usP%BEtEU^G-1dBYmzl^CKYB2MK6biYL;ID#QY&l^ERFdb3&B&8zykK5?{Za`7bBv(J%fm?;1)@GyM`Fqe5ZpOCXkU2 z_&f2KnKY|Yj?`Qdl7)IJK}~mnI{lo14kmxe(hsG~+>$s!>DV56^y5SF?%)D&nmU3; zM%2@gpSI{L>xZ@l$FQ2?b4)CYSA@@Tj$IYkb)}C!mOBlZCChn-?nH>Z5JyArSVFeN z0qC#Dq9wnI;PnD=cIv7NbcV$xG++0GSozJv)=my}!_;uOz9%+2PR5MeN9actcT5et z$2porV7k#n(5|pRGutpsn$-Xo&T9xKM4aJscnWxlzsKZXScvW;#YN#!G3cHSTy%6u z<syV8HJz2eIu20K}#@ScM?rQ;PwAqPBM>SGyW{A6- zBxKSa@50cb6KFJMG%dO&LCizXfceE2RgE6n816ho-K1usRILxz=c_`^?;v#d9*4qB z?oh<%UxMwcpcao(y_u@SQ7(Zbh*vUlMGbUPr5~dj>cRKPIoR2ufKPwjB;eJ9&vTFR zSwtb;uy;d^qZ0gFK?)*H&cf;~Sy;>epP%T{V4hAB!^iwLeaGt>5^_iyH$xWm{S^Z; zCz7tvBXD}nL3BGD43olzMA*KR2;ZNCkWH;TuWdAzJ{d(`ur76Ch;t>U-PkYIPfpH7%SkJN83PC zLXJ~+%!0l1#jx>xE%g}W`SpcInMP9~onhaJ-EXt0L$Dzu(>xyRjy)#ni?XmH`4(g+ z7s3U_c`%Sz3jrldMGcxKX=cl5jJF*RUC-OA8vkA9a}7qY{@!YARg(a*QI}9qpoxx) z3z^1h2Ri+4CW@+C$ndy{V7n#?`pmaL@Jb+^<|mn@Qd$`6G8-P=^g;b;4dl_(37Gp@ zh3;$xe8%q~(%R$Ut$QL|3Y`omkLi;CTo0nuE`3Zp)LAq-3KhEUPI@sMT3IF=K z$W?=Ud?p$FW|7h$CXE~+v0(`A{iUd#AC6yY-ZS1EV z1<4W_n9JXVAw8QscyXTWy4oTb{q!&WU08@gu{q>)-FcMw&hIIC%t zdhPN*VYaU#9QSs?fknI%&RQ3eeoPg7edZ5`OOckR8wr~p*+HN3S~P3p{VmDnPN3dku;+q0FKoD!p@g>m=kaL9>nEYSnN8N+`JJ3 zi{h`rfNcSRX-UMaJ5x9_;3InMpAMg+Jm`d5Q&D>VX`*rV0XN&lpD4EMgC%3V!CONH z&6X&U>g}UJtwRyS9!8++;T3p$4xby^e201a#~ydT_Q$|u+eyve*(5SE9ghU4BR5|a zA36PHwut|L)f3QkZ{Fn*z_q3CdR)Y;Wu7@tk!Pa!hg?4 zHQa)nRnpkq=?KrM4Utu+&>a*=WIx0}#PvvW^!zkP58H~}_gm=S)>};C7!Bfz6S2Ws zAK&dZCc4jJtA0)G#OOiZ8`7n~j{m5Q89_)+%pKzW45#o`=nJM+ljHZbe4a=r5r%Yg zq5REX8u>R!xMnKv>ftuQg#b<5SV6cAN>OC{n>^vb(RzCDqb==jy+ggmOrn;{s-Y@d zh$Z0`JXc~blNG-Wrq`CEsh2E_KYas6H^xD^y92x|mSg9+{fAEkx0pX(HDFwu_LccW+U{*LF@X9w6t`E=wa{yI&;()nz=0%D{891r7Va!#=5|0C*C(V z_6&3D=oqr0)RibjxN|MCsZe%uBV6to&$Co+S+{v;d$1OQ=o8RPKB_&w}5o2o1tDFkinH2W4)d$(td% zb`S3}`8$EcNL)c(A6pPiD}~sZZZJLF2`s&1;Q*;Yzcg`hk{pZEE-6vN6pk62Xoi2z z-(tK&4T;Lb%f!@U6rVXZ7oGi7M0cLgqYc_YT30@`idF!&m!;9d z%{MV9^$dwC_kq~?8DL+n4JnK@Gt}w~su4VU|GEeHW5B<&+E_jZtb&@ZyGdjc&jxUq z2Yz=E3Ywzl<$o6M_5RtVOHWoC}cdJrxf&O0nb#WuM@5%s|eair2WRcM+f>A-qjPhP9xVio~ zY+R8?mT$aC7N4~vwJ++y^idFoTYAD{S7ivht|mObG6`pOy@C&(D(oDeS(xH|h^e`A z4=$WgBUfr;xpRXl#Lui7`o?FX%!HSqxo{mAu3JtHdON|S&z%rsV9N@&+k^WFceFZS zN4DfqrvEeV63??o`Pdf0-%x*of}bS$9na)YU5yJ~O=aIB-z$ze2B)WQ;C9H=WHWi)or(@77-lKMY30(b_%pAX0fKRvY5_<2s2+t)Sfy)_L zQQeg>XuR?qRb6)-*2|s&w~eWgANqpRTYCZCp4Gq&28nQJY7>@?%ArbAC&2A9m&i8D z&HP?PmaXefL{qj7S}yNpX8&7_bSw&$3glN ztmp`~L0~CA0wO#DP-2}PG5_<5PP_05v^LFzrOK9Ei&Gi(@92P$TR7%|dIad3Zl|ve zUD4p365HW03xCQ?Bv&3WU~}~=woeQ|J+<$+Mv3<@+D{WMh_toozG+Bi?{GtNK??Nq zo#(8Nad6w&k-ZqM4L3(?V$$qUcy!AK$iGleN-Wdi)J9cy&^`pE2TsFUw}nvPzJf%) zcn(Jqw$cTpHjK?{5)80 zW{fgZBFM+JlA;!8S*)EAfJf~9(WaZZBvR)mR6WyYty(U?R*$h%Tz?tHnG%}Vj5bvQ z6Vc^lacq0zLsz@S0@Jt_!W0bXkG|s=w_XxARjY#9r*hc8&4$k*bW`VMO*UI+HhHCI z4Yn#{$)lAl>3>qm#GZZvbZ;5c1D-vyon8S5@4N-qvxthouD=aj6>-^3TO|=Z9QyO%uAzJ&T4X9^;|$hRhM)MDo16 z3&F{ZHNG)lv~(E2LB5iE_hlU0_Hh$*_Lb9V-<{c7`?2iIC;FlcEgg(`mP5VwQ9N#Q z57b@0apbrvtJQL!-YJR#=2QsGk!Yf)$a>h(nF0>k0xYUp3r;@&;QnG|=9jq_$WAnX z$XTz+Zf7;X*K%B8NIQ4$t_(Dfo`e^k`qS}8Kaq|VCy3ALw`AI+d5~FNik{0tASS$z zihY?z&fPDBSC#qn;*)78XIDpWz0SZMZZqAm;1apj(Z)UPnnw==t3TikG!8YhO9e>6Pc)jHE=?Uzec4f1QE?j|w8`%E5uFWy~`Dba?gW zKNO}W!G57Uvuv^eGasBrvkMcjXH5y&!55FTn~wdT&qB51Tya<~hKw@^01u;5Do@?X zym2FOoO&hP4t_)*jGKwsi8rwsi|HFa7f>5F04mve#J4z%UJN#b`F4Ns!RpEu6;-*_lcxZ4A{JwDjmhFrrW_oS3M@E81#XcahGoHix z$K~|u(q_Ez?L9Q}_m4mP`zb{0AMr}}W7gZghDEFn)WJFMnLdW4JQM66t-M-dX5n3j*Spxhs%qGdIHd-?lhiCX(J12E|Thh&*Aha zE7-Wa(WW$ue_!;iz`Nc0m>swe%ss~7_zk)!_Jt#T89uPg=QL-r7M`p3c;jUlH*lZ0Eq@L^mFGx>Y&20icbq(l zl%b>aa&X7qKpd>zCQO-i4#(U0qj5zvUJuKHiGC(*$&PKHn~=viO%%t%WgGGA^k(pS zT}W27$G~O-O_Vyl9cJ>gpXD`%xJt8{G}}*v50lEsk%;?5zAPUSzgN+0ZCh0EWbr${ zTKJ`0M{C4p6OqeRV!hReOsx4wj0pkdX=lJ!5D6hV%A%B-X!Lw_6=!E>(nC*6=?E(u zlpMu7Rdfc>Z|*9nKV|`o;&S=<>?7P|u7^>cKRF4XTwVU#R8V?;_rWLw?W{=pKgAKfc{Ml^vGR}>+Z{lzOIyp6Mk=~==*e>a=4ESC%hIs zdpu1P9d6H*j?APR7tIi9wJEZ98^n<*2w?0roJp;rI-55lnT#EjgGWh8knUN87qiQm zyjcX6?z@9x%I)N+r#X&H)M5YpXauc%oAtf|38r2{ZV zArxCD-5`QFOTg;=ecHhDN;XW9W-l7mfNS$*aLd#a&ADXF{wQ3?&!eZaP1(w@txgjk zJKZ38d!OLyvSd6n{0$=|F2k`c7csY4j*}{G2Os;DP(LyrjI}GEM-&609jnls-G(g% zw?U`Ujmm%g%=f@lVA3cH3@%%UZ`2IPPaOxSpJYsH3pGfJQZ5p~|Fj3isa}I38AK&`na`Jj^-w1i8zgG@t^oppc$^p3jXX)^Pk>KJk zOD}$K!`XcAF6`wPG{~8Z7Q5FA2N%yFE5{a)G5nsk!|fD22>d~MdU8Q*x18wY=2@ux zv7b@3tHMWbdB3DYJhf~$0j-(58pdu9JZmy!qP&hmo0l=g%10Q}e4qK2*~v(ChX|vc zPms&iLg;!gP3>x*62@>XUMK2skFlaRC#{1F8=i??Q3Gc0x5BzIb(C-T&9QC!!A{>0 z?_K;z(ro(4C3X@0MWmoj?S(L_Fp)cJG@qJ$TmYi2a?CE*PrRc+lAU5PhW0%SL-AGl ze1_^XcZ?k-hn=Tk^tTDbHTy6zb^bu_rfxtNQ){rXs==drq2#vVeUw~35kPY{4AvNt z6pt8g_k`!v^!-L?N{A!l+*C=x%_U$_H5UCRt%uV6w`ql?FUY;t;J!AUz+L&!cdOr7X|*%@dj}39#9=I1%3ZhB68I*Wo8WNBtO6 zhq^m|GjG+`Vq=pd1nlQ$)W!df&QYqzRdmZ%7ZU*hC`R zt6=WjIJ|wVjk>to!okY3+=Rw9Vq9s?ZPCmjYG&gZUusErwFp2=V;SkpE)x_dG0;fyfhWBdD{|gxl&SeIU7DWa`ZsNN=P`l zg(1teagUJaFnU+hhd>WFAcz;&A8t%w;r9Rd?*VfsWjQQ)0ZG#VJcjQAdlPf?s zWhtCDIRw8?+6r?!YfyT5JDp_1cW8Cx*vQu}h-uGsu&Z|y-1>kx{dO$P8u&zAj3>e& zIu@h%d;s-H0nF#&wfOg40@)|t$@E|0UF05lp#Ja&j(hGy{Dwn;TtIaAyOr#hH-Qoh zZBdclHJ;=78eYVD(kD`H=!KkEykb&I@0>e}hu*&;|3sBo992&`@7H3zL^%|MKBUgA z>Y@Yj-{{1J-EhVs4|_YJk-1vIGit$PVcXI9fVT^-i>SuXiE z<3AMFN~T@AyYYKT6nSEoj%OTMY}WdZs!ttDmDM9~ih)0^*z$lz{*e;-vChnw>@%MW6%EU1%Zr5|!gSE= z8NrU?oriCtykTpoB2MovfWY3{SnrYtf=v_Hc;7<2zgV6K(%s!!{MVr{kzY;Kf);RJyE(T+EkAbx% zOGG)Ud*}|69cUD}55-psxz*RlK>Xkj@EphQcV#w1@$q@Y@A(#3vnUlmHn5^!D{{coGil^ti<#1KA2Lx(Jv5!oA z(8$#b>))!A>L1_fnF}qTJ64PIEeiv~(kQTESKv)QLsqcf2@Z}(#7Bvaa7sZ53vSP+ zjc2{7+NvZlUAYj_?(lbH^nh^+O6;h4Ekrr^HI>%Q1|(^8YuH5!e+zNPH97pMBp@mp zUA(7*?_M4YK(EH5P_ey|b}RbRnssKN(YF%MWE#UJZEt8AXoKXr;rMUIVKiFp&U=uw zL~Tw_;Kz#^Fse!i$w>mZFe8KP8uJ43`a3|yHy$M9Rq#JC33xx+4BclXFiD0o@J{hD zQGKjN>NLtJPx6QHB6(5I?``nJJP!U{)ndO`=7Nl79-f-I6fehXu%AA5gFP343$90s z62??h=~@+3h`Y!2c=n=Px-mPiYL@6rNhQovo+*l1Fj5qtb{!m^k4Ba1DscDeqsu+Y z@XM5Rcs`epoPOfVE{_Ho^*Bp5XzXMBb14&}7yhQ3-|L7XbuUukd?k2me-WPyJiuH& z8yJu~Qe-$i1~b@Ypcog-KKng^&FZUz&V?swzAzE)E-3-i@_5+ptIsN0jwTakTC@2= zDU>tVL0bKGq%lz!~Lg7?obAQCms)5(^j!Rg6@#r(G#_?Twc%Vl^ z^mX}P=2K@D*gh9whh-$U;PEtEyX!t(a;k+0TN_Z@{|C&wzL)0+<)S@gqeQAHnVQ^5 zCPl@e#-@5?orKiv)hn3oDkh3PbcW9NX(sz?_tJGQ2BGl1FS+6vP20BE(sm{u zy@$}!8H$5FRnQNw$&{unWIsmY#^FhDd!`4o!XTV1Jx~U5HWhTkVJY_45-*{T##`>} zf2sI@ce>p;9!V!8B*P*1C={cYKxWSiLERlMXrJ~U)V0r`PmL7uvDSEUdh7?@%@u?b zyObHnxGDIeA_nWPA-zm^4`g8!_?H7%Rt8{wg+3ITwxG_5>FBOLk@@tK=Td_{Y|-=; zjI7#^UHej*^;@q})_*;0&6$slR*CrdST6TX@fLmcFAxh~jRjkAA-NW+Ozy7E;8K#E z@P+*X;>XXB3bK=k1v?8wA9j*N8A+nr#Jf(Wu;Bdd4A+#l2PSM>!erlwA-yL;KyIj& zt6G~wwYOC9eFOo7-E^mWG$%vC@Jf`Z`a>!3`JIrUiz zB=Y?SVwc%NCpH&xikI$EX|YDSX^lDhF%2Y+XFCi0w~$Q-N*RyMV))u^6a4p+134cT z*s{o%%r8~M?4N1qyP=-gtA;~F8b?-%m*TIZGBEC63Hj9Xof|P>2=3i1z=%g5ao%b# z7|CVgy_to$V%0T(3omhPXaR2Ce4i#vP3BBh20*jD69@VC1{WlQRR0vdvaEr}PFmQt zUXr#&7SWkutBA>4mZYlqaA9+=g2{nam^k4A^C`>&7e3exCzsn1?Kmk?eS94rb(u+q z_3je+Q#Sb4S{m-CDPa*wg^(x}yq7oNv(Pqj&PYoKl_aq#i{$k<;xl16R{oNe|!Tsbd#Vo`#0pDIY~`c>X4tg zZs6gYhUbemLg}Z6)WquxqpsM1>f+b2_^kzOw~-S)Fl@#%+jQX4VsXfMzZZ2u zytghDC-+VgEnBSwv!@Gb<>ljtm=WUi$##eXOE}a3aKp5w|m#{gsOze(sl!V zICP;442rfAr^ctZJ5b>POEEw|pL6;@m!ubC-q4R-lxHmVDN*znZ zVR0*%7w}5BplkqiXF0%uoLt!BcoT=uPJ`nK8^F_e5%0g0LmQQI!i6_#ah(i*K3d}r z9gBkT$)Y}-RF?pL8^16I{U6Z&*kGcq-y%4C%n(m*89`1fhQsMLInk9BDR}*s66+rR zj*7e_*w@30nOhfPKrAkcoc|n5<(=Qa6*GRzX`}JP!mlPM3RPZf~kS( z7`N0Ehtgu0(AS0JjMS^=Y@dx=lFGLPDj zM{hybFnRXBt`GFmtQ0=en}JF>vCQ8^B^dP06q7qFn1AEPK=r!@KCgF&&N`VzoKEj2 z(b{hKvit+{s5KBDj~l>-C0k)vygWU!-U4>Z@5kvm&xw({5{&-Xf~7Nq=%oj)Y}2SW z+^1$Kk-frH7Bl*x`1($?nNdKu_l|>op8d#AoIuf|61uq%cxDcG6yzC2Uk6VV9a>uixYz=td9}6M=n?$D*Z^5M#K_u{oFAdF$wz)Wa20f*J5LJ_J zV`Y~%HF=VYXDTm&bbki#29l_{?!KF3RR4sfGPAJIv4I|PKMqv^29P@~m$JMU*1nFz zJfCswg4!lb|E)?sO9!y0MlK@B&JJvqP(kEBaVaDH;18X&QwhsmI!JnpK8$_0974Cx zB>(#709mt_>z~?zJzL~OvsEt9Zbt{w&u0}+s`Jme{uEp&`2ak#RFv+q#YJ<#CW=rC*7;Q4YARiX!)4Jq7=iX*{2LD!PBo z=YIXpC3CB-AfmMuPMFbdk7*6!@=Z29|{+ z;)8lmym;IYT>_@yuBabG#_cHy43g)BZ>HcODRZIVY9PL$j>zCiViS6cu8)f+68}0$ z|3o#=c__!N@W~YJ{x|}k4ql?V*{{f+9VbwFt3SNkt&Epf+mef$caS&B+o2+5Gsc&M z5O?(~m|*>$XcYKCqTdQE$_ZeOh)cuCBO_s3_)dsKbx!Ef2C&zfC_n3#&&5fZ=)?cASwuCG_XhH66zfHoYuf+|4zsR=ZA>5jrCsg5e4vwFb&FKs; z#})i}efiKxZaBFRKRs^b-eefS!y8x0+~`Vb{lFL_oh!)KY!Nx`_LJvFw4>&g9Qu;e zz((_p-0}0aSaaw)adb0be7p@r>SLTyyW>2XU-=G!k0Rj7!#@Hk^-x^lS`V+@+yT4N zS3EaNhHMI(!G8X<4QA`s$lf1N!8cdcaD=np}7_dCVi#}t{>Q$FC4*@U%z^kJF`D3J>An zm~${DPDrwZX9ed9t)U^N7ApCS(hiSs$O@eWMrHgWkdlvMD+X`>*ld_od0;^4bJW8V!Krw)bSCZwJhI9Kq*UwNX)QJUMyj zJc_HfG9msi(9%o_KD%TxNuIT^?xYUakT%Tc^)gv=N(YD*jiAG^ZH(A77nuD=Cp4OHQqg@Gtq%^L$GUtxceC$8z+#lCnzxv*yr zG`V^%6b~!1j@qNa@XrwXJ@3TaE0Ne5E6$#bxPr2R3iu*177Wki!rZ-I$vWPpdr|W= z-gN&0^+iiiuE|+sp?exVMIY#N;de%Ff-I}LTV6!oE5Pfci&)opc`$8!EL>WkCz@1Z z2}SjZIO%04=!!FV_UQ-koR|(dz?eap;wdlj0Muh3p^6ZCm* zA?ldjY_n~%5VkJf$#Z1qkh`6%XnbiUedb|}pHfE2>fQq84UubHzOd zEoAeq9*Ah}1(h?8A?Q>-N{#H9vYp1Z>x18I0rW)eg()X3 zQK~{hG_v$NeR(jAl*~v3yttB?r&2(g{G~*v)>!dAs7&A+y`bgqg=2~r!QHC!bVB=e zNV{K%P8EE0Ra1-9O^*Z5-cw|k7w;{dlY-UL_d~JmCr&J+fV!H_#j#7m;N^{2G@SYh z{}gGV-swM(yLlE3wv5F+%kqfGBoO5r+vt(qo%Gu4aZuHJpFAp_$aDX;FllybXj^^; z#3jeVw*?X~->H`|eqe?{zGkFEW&jTNU8U>#ZqS9gAGv9L&S-M16C)EUx&FZtGQ-rG zoRy1!`KJp&S~Xj^W=0;-P3G@kt()OY*9~$}`3mtFTR;!K?Ce^8ro@*!dHLDi-12=RF;nG?AOuaQWbVp=u+n9qDL@o`grQ-V?&BI>Y{?u z04ZD%N)w|lkos^5mQL#HyDDJxudZ^r-x!b$cn#XU zjR1p*Fk@^93@zDiy?24SVpwbU8vY8J7gTm&156~ukHDy^!P5e>BQJl`pIVDqohqWmwvY52(} zu=&XiGCWkmX}XxR7E`OKN3I;_EIa~_CMXfxctzIf7>6G-AJLihLLxKU9D6e#@?82h zSp2Af94-lmEfWoKO-vkZ%1#4?Pp^r0eg(QFq_d)&Vd`Wn!zRQxLD`#RZpzc`^v+_! zS{g^AbxjGbA^CXeS_&53Dy7GJ#z3%%8#$i1kfph%qFav^;^u~0+@=+Rfu?QX)_xWi zADM)A*DFv*X>od0)fgHH|L!x*!(G?)KwNent1Nbc(R0-xzt=0XE#50JzvmWN5T!zt z*rlSsTi4S#`SoyhK?WW=WX^`gnvst#i`kuhny^o^7@w>yMBV1;U~{?+R*8m%(+;PA z=8}=D`?3c3yJH0@ZjFInnG(J$qakwH^AUFTu7|g8kja(V3ggXAz;JL9IlZMB=cN{r z8C9xPzkf@!4&!{Wp?VJd{ggyzGxxwsD;_!>E~8-EbkNOnV8Yk^L@0`ck}o|}^OP9Q z6I}y6sX~~h5RIH)6kOTxiR|ZR1D=~bK&*Wdc{12Zp1R&8)1=x7n|=W%KfM44)rSRz zO&39_?K^p+F$zaV+M)AUBfj4nP9$%Akgb_aR(kwlK0P=G6<=D( zi}F!;e&Y~MJpa2Y+LPz3weP``{U>Plip{viGL%1qX+n(FIPgCk3`!L-oe+rmv^Od?elwi}llk_(KZ4VfBg&m*Ta7XxKxcw|2bSEc4p0gH^1Jxiu zwwz{Uw1fLhZMv)W|Cy_H%)hlxw9DHR6Llr<<5j+|b*KP3L#`uyk`Rr_YR8*lA*8$g zDNRW}N{&?BfTHQW^u^{SAh|sO6H0$^wC*}iT`vpf^A+H~^cE~zc95!YmAoVFAa0$# z8{Y(H(+UYwu5|DxG9{nDs_vQ~)Tj@G)t+(zQ%=DsvCCk;*B5#GB+ACZrx5BWVwqzicaoxo@d4oOZDX<0Fo8PG@CukMYVGJLjg-)PX-RaP0xexP%+ zf~cs>?xRE(wKk$fNL zTsVdbYnanhw4qLU55%AB7Vf%af`5B7I0^gtxXMnJcf&^E*$uT|E|-ZBdrfgZ&)}|K z*g`7K-Nf??3_#%G%a~qBz}|^t@tDO%jM?XjX344oDcN!C3C{v>d^=vWC*~U!i#m?4 z%aX{TwIY1r$xuWb`>dte#K|tSE~(y5^I}A5C0UM?HMgxkDUF z!clas9(xkyz`AReD0aUdF29ybpSQiElXq@l`}ekTeEt^&-6ElLwB) zJ@9Yk0PIci#$m4#_}y)XozEv>Pqi=e#I6y&E1MCW9}w?PG0|QwH_yK;M zF%#H@l6c9>3xB__gtS+}9PYjXRy=r$o5r@0ACbmDDoxNpPnPcG_n52L9Ym`RqCwba zs7rqa4bg;pEb$gS%V`tbL%NV)8-{tZ^+fJBrM(LCLG$W5lBX9<{AAzanWrb&+=?V& zPQO{STs0VMR8{!#|B_HyPMc>YT0u#{QIK$|hbK`h*!#Q7h|8#a=Fnq}o?kAY;;;93 z*G7TITlx^Q(W5XfNf{=$$pN>{hJYXI;Q8BMq*1AaRj%^G(ryFXR%#Dv#xm4(-a?eL z9!AsKmWWpcN3hye*H9cg8n1Vqgfhp~Fwbl?1g}~Ol5OEIY1V7_s-TYv8>chd+7W!V z_E9Ev;}u%P97f;#Xc%Mg9S&D+AVv*^D6OMKn_r(ttJ8AaBIFVoyKktleUDh( zsT`v;d=T1?ptSuySXB7}Ud-_nz5R3*l`QMA>)U(`IWkz}G*yF)dT)uH3r>?Ad!_m0 z^i+~_bTpAX{}}lp8J_y593t-Lf_c^K@0=#zar-oTGL`$TOWH_%HLVavH{ z)PCAZRD85qYLgrOxPBWPj$Xz5XQRpKW$#E*ZW2J&XvkMGhvDOVy6i-w(I|W@T8AJt^5j%zV_|mM36<7X+&a>HdZJtq( z;F|@<=guQ{8)Jb4pT)Vytn_{vv7NUyA zVN4A?&vr%!!Cy&p9yMkdX8-&H>pfaoZ-NyZ)HntH*E2*x3xk9l;UBim-Gc=0P6w0p zz3@Rn(U@IebyS_@)fhkIZ&$nTBZe#E+0R@R?{*Kr8em2oAApTd?)70a`mR zaxtxFVXi?*c=6*iGGXHrJm()zwsa<=$IfgxIc+3_Y&C>&ldDMkR%g1uFB4+46Ty4; zP}Y0*Cu#Opq&nuUFwOG-e3eI1nA^yf7M6)ddtZYhoWUd1Rk@w}6Y_oQb~yPo3%-Oe zgd0cIpvR;Nl%@&{q%J=kU@AkO>#l`{H9_#``)+h=KgAMbIRp#6fy=9p!1OU|$lK1* z_+L>Xc^5Sa{*+l#3H1i7QEkuB~>0 zUwd7dWB+~FlN}FT&08`3Rke8K^kSGR_?ptxbkQnfFb>(c7tLl)#)zqZh)v*IoH{KU z*S1OFjidu`U$YNgYvs86&q^4sGJ=0e<)kSpg_NHk$g6bMlbh>lMl+gJWR|E z57gtK;pOl(D}xjkI?|g1w5fH|TX>Fz3Rha+ zCBLBAw;nuO-j1~_=q6_}mQ%j~8NoGO?HVJI3oRm995_Mn7v}Y_{gr32&aV?j$^=9G zi#C)g7*1dK?jr*>EEAo%qQIpL9U)gg7n)a;ix*jqW_2>ksQ>#mq<;BI)Rp&x-r{$p zM|vMN?=8m;$9ph)|7YA2TMK5bLMONHHdFfP!i@C~fzmGS}Owv z@wPDj(L**QbUB3IIE4Av#H6Uci|q){7jhitIO?;&NG-mDRA(MLqdXPVdk(;JpW~!4 z@%{O%A4%e=6E{F#(i(CssTF!~9LCv3h&9b}Fg{5h56ZNF+QJ-c(~QJ(7N41?^;3ia zd6>C89bF_V#eISAzvF_Hq9Ulv<8B@#WdU_Pl!lKj%*{Ic;Vu9 z@S~bEIaTNnkC;iGD}>`X^-DzTY$WtNqETDOfc>6tM9wcYVb3zc`3DswKaB{mF-@2vA6o!zVY=@cE`FW+u7F z)ipkw%{n!S<=o1EX+>RVZ!r$E{WQ3m~ zi(a)IX4`xrs*?mq!M|ykKlm8HgUe!>uVcYc&zU7wJLBTVj;L04h?(D8;5yNN6wZr} z5V(IYFzC`Wob{R^9d?AxO}xrHC%wfXzfwr!+Z4PsYBIh)(8~Px-38rYGU)wkE&JK8 z2O~e4ksbZ*;?@7flMt=%%p%(iC6E0hMeh}*J z`_tI;P0?`sUm12?jD#geGq5{wI9nookG#hlcFUT3Y-b)Mr`%@k7>x)tjJ zZ(U-eE*XM&7AHy7+2V@xtwgHspZMzCEEvAZ6X%Rw1rJxc!l(2RcmkV6TLZs~5-R^O z@yRs76W|IgD~&j}XiS(e&qQ%o`g!=-bCUSV_~GYqw=iHrILY)t@wqD=;I?%$glykU1R)HJF8IZC zFWN$S#wL`n9>@=ToQS^5*5V9df(MhEkg8WOZQrZJtt1~Zk9`vF2(pGD5uYM``IoqB(p#K8_qpx~@^MJ3EjaA;p@ zJ%CB?f5CUbN0wJD4X1ZR;oRmBbg)~F9`?D=ws#GLb*RJhf4VR-yMskEB$E9hQ*pt< z5|Wu*1ZRI_K>S=qkiVKK@-qJ<*6;sSd*{aqGT%50{yXe~by8xo=9C&%HOTNU&ihzy zV?6wrX~Eu0P;tUtF%W0K=l6V=(kg|#-k)f{v=8#8goaz8%K{0zJrl8NDYUy%hFvBYzi zu}QxYEDIX&b*PYGd3Xip{|v+nqt4?G!*pyr(!qKR%(+@q0*q5A1icZriHg)GJUc~! zTWd(2Rkuxh?T#Z{?Rng$+eGm<;Mh%k;*i~fC*WWV& zwc62eB*6rxDFlc+y1L-*_JQJ8a^_&46h$949`Z0yGsxZEz5O@Apt z8^lBSpuT~0^YdsJH~qD%^SK33(Iy7H`aCe2I|Ks@J1~4kGN|txjrQVxXfoHJr*Bjd z1^vbFWlbMD>R!tdkG-t*OxuL(@6Ccs_Z9go@kJaR)CRs<+W2sr3SG1=NL0}{9PC+! z_|xsf7}hJvdn!MYVSRD9LD2>p-S?2;8>DbS(`1pO)mz+lb_=_hvJF0~RpRCYQgC(3 zZy4*L2ti4TEV$+Yn!R!bZQnc`m%Wc|9U516q+u|)D&K(0xTTmIKMeJBLvh{s0pelT z*1}H1L?|uT36Iz50I$@*J@KOio=yo|TrwGFu6oRDRev&-NtLiQsI~5|^(@f=-?dPw zkV3Y3SF0Xm zIO16rRriV_x+u`7Fo8by5My&Lh|k5!LeZnib#*OQU}%H0ctqa`togZ-8JcQC zK>BB9G^ZF1n+EU$$>kVwuo%rBW(&MDfi<+cgiVy$$U4W!GWpGGNSpB_7=`OwFYg-b<8|&|B)%C5qwp zImu$@M#QqS$646@58!eCEokem#}k7RFjdovZZ&!aIUz}~x9Aog?REzL^RHlbTQmMx zQb%Il5@Gh1CrG6$F+j6YJpZv0FAcXM`?Hg=epwc}ZkmfVYyOJksXSgjD$jk~7h&)w z5ud+{;LvPMTJG(DN>9u1bK+LmG*X@0KmCT==BRTm`7E5xmtQmhLt2 zruRllQPIwFeA6}zWs`^TV^Kmr=;0$$r7?)lv>s0<1(?vfoa1o3FA`#2?t@7>YZ29R zX|j-SnB+eT4yzx9S5cOH$`=hZX%l{nB=6vcE3tHQPPQ<2S^yuVZ;{vLFJZPv3MBb> zW6I5HsG@;%kckH=^>UyCnt!vi_7k|_BMnGjvz*PlE+(JunF)8b5z$ zI6~c(9R8-ut@5j^uxAZFP-=TA~Dky_^m@L|a)e%0gy=xK5swQvF7uU5~$3|PP_Lr%enA`#AA zV#?MnPT)Do1qmv%hj4sdG{S`rb%I8 zO%mSRCPPh+%hBiSx1!?t1lBpsi8tox!aM(Byy>=rZ*RDVpXIgKX(J)`+x7-ucPmki znomUa{%&|{?Su;?PVxmq2ystHW`XXLAt3S%tA3(S+zcYqq-Z>QAQrz~Bwp^8LvDHwK!e0XctC$2dZcFKTIqMpV#6Nd=~)SG8iB+& zv=V}!#R!}TD^~x)7VYKdpz)7-j47DHR<&w_j@whRW!VSO?}$ZglJf%WN=;$@!p_F@ z)?U0k7%;FZPc(3y3BGBUL4UzB*pht{zB!$S5P@Z?ChQsRTt6aktA$?tmBp}rz70Mk zr&)5Y6%|F{6QKQvCpog#1ZdtD zR1I(@m+UfOqkMwE=9H#-KE}jmU>xMX4}xulHZWz`AZ9i)2-tufxciv}mzq_EYc|y2 z+O#)JwnmqZT(Ohst^SPnG_0}HI~!&_O~(8cvMxm}4sbDlW1V~YDSYszoH>XmgVW#l zP~0cS6G96x|MLXg`>F=-*r!12G#B)9z7D0&ZsEM$QsCh*jP8F>k7Jyg*?%XTSg!gg z{@_I>8N~%>!EkfXejm+RW%cOan?-eVnyu)l8I(?K`o*Hhte_jeKZF*QOYk^Cj;5FR ziCU&dvwga=36^}q&+XC>xl9kmbJaB(fC2_41=8rRM1@FTa z_!_d3hNKJpZs!{?`IQnh9UXvL7Iu6_of(IZMtqih6pozv5;`1a^YCi}u&Ss9Jx6-5 zRRsf~8Lx;`wj1!8TbEpY{H=Kee~keLbOpA@7~Xt;s_VMMtK8~zKbSXIqVDc)@t?_V z)Z4t3Z5j9oeyd+ZqdPZA^2KHF@7BP+PD3((d@vaQyo(Vcd04M_ zUNr4pB$VYDbBV>9u(1CUYj|V=E8o3`l0KlS&*Cx3XgTaZ^UgI+%aXhmIMEL_E#sp7 zF1+S<4-1@ilJU@&;?$@KNhZ9xs~E@i7Lw}s zBiYX*0YYEj4Aks&>6!>P+G}V_d#}HT-$H+1J1mi9Med;X!=K~sJtinw9sskh7VTI$!x*Q%P{(Kvl2zTs><=;&<;K@<-U{^beSFhbpcWE8L`O3!-cL>?U|1Q8b_X9XR zQxEqnI?5NNoI~ktd+^P(PTbR=4As3#oIH69;#)l^^XoZzDzu;%?;b>tURC9)eGaho zx{JwaC1Sw% z!FaSa2_NQtXM;yDIO08-Zm3Kbd5>y`{Ou+vUbGLTr~2@!Eh6&DB$!RjsAsqK+jFCM zJDB!-J6U|w5SB&@-o68&s8Eb}&T$B<^9d#g{++-@Z@kzM&l8|9GX=vJd;_Z!738|_ zTYRD(k3m@q+|sWIC7!FZ^5ybqmg!98WF|lu8NtL-y42%X5c~2|k80_C7ON?rVGT1P z1x_+Tu8BHzULs8cKb46034KndFUz5H{s6i=x*CsKIH1)-V|*{Gi5kA4xN}DlUOKiI z9?#i`%Q_EYeaC9h_mSm4?gT=ftQ}kNaX2UQ0UJ)AV6G$(eLu`5>c2AC@xNEud?P6? z8{5lzWB-tugD-t)kq_%uX2_>hjgdHDDGR9w1Y4@(&|AM0Ayl3m*_!um=Z9JhHi>^))2 z5AU6X`{hmO5?v`eU+An}_kLElV^W4FU6>Vcw^AAJ%(d{c-3DG7aFu-c4HiS?-hqOLRKUA^&f(Y19;YZIqLoS6uUHPIUO}@5v;%ALE92P zGtt54Ok$Ef3x1u&3?&-i6l=o04o<>e`w=i_Wl^-D3|FrGO1>(M2F=~260#`vH$20{(K z5Z70Lc2`UA5A}z|F&P5Kc|BO^rbAm)iCEmHKoeTSF=u=(jz4F|xSk@+d+J2D!)^%2O)#-obMS*$Ve0oCk& zCjU?!JXKS4$mkjdJ70jpnM%C=U^{M8xzGN)Fauq47J$<6fxIWm z2p)u;BgN%^nI(;AtjEWfc0jYfE_@qSf<627Xjg75`}BDn zDa!UkwH2Rn@M{-#dh;&4XXgqY(o0y)kuV7UWx;f7+rf51KY8B%joo0SU{SRRdcP#$ zjfflgQRfkkZS5rqht^_WrV5JkYw-N|XebTVpp&<5B5}2o`NZL4(V^W%G=Gm5*lhoW z~n|d@m+DHj4&ijerwfdx_i|f$z8UARBqI6Uyc1h|K01L!SLe zx^bN`Ee)0cBd2=2xb7N8`aUHyJ>4OvZ7u%!5#X|TwFkelDHU>tO4GYXpTWnoJ5nBmOsKAZ)rMi$8s*Fy&?& zrl{|Ylgx%t?esp>_j-ovGMg~`_W>NF{~UuHGFZeJNiH?&4a(=v200jZV?N3}&Z}v!xBK z@XW~p-79S2V81uD4qAzDHX1q}Rg+4$qhkk8@L*~AHE{cbAy>`JfPg7iVT+XlT~~b! z!YkadYxrSw%6&?_!pD&Om-5u=Lk~$Eq{t`e`iWJCo3Q0Ng`#W68pw|9t?*v0kyLL| zVGI1CT>AG|L*4Ahn5UKt2c@0agH%&GNaROrwENI~Q9Nwjl1H*0-N)D42Jj*%#`a;~ z#J9uiAWbS7v}XN>x5H9!y6G!%$gFv=q;n>f-Txiulzt*O@tNpt_!#tDQ6_LoI>eUG z`PKnoH}t*8CMH+#iZz0H^*gdvRSlyO!^yu)MfS90KVI>u zz}%4&;8SiRj#FKT%T}I&Jzttc%T2l=-p&Lp9tiGTdJcD%w-U3?eI((k3W@y{%w{D~ zw(Cef6#iXGi#v*OlW8$@^!vefjS$l0)r|w*l;OT{KSgPug>!xTDA+Srl1^`#C`x*C z1D&r7qS{Z*;rye&5Gr2`&L--3dBS>Vdd;CnRDx+6%AjbdHrn?}@!*wz!9!{}{P`@) z=T-Yd)mmfnVza<8`0N3uEmm;s+f^1-Y5=OMwu&o<*y7z!6#u@l<;hwSaPENyJ6w<` zo{^$R%?%&2+7Bt1tdW9|i8=7b<|Hay)(4*}j(p*bJdw7x5y`t13OadTh>c4Hyc7Gw zeglUhBlR zF-ssx<0bl!;$(9BMf@zB6L*akVN}yKviI&BqV_a^q<%}qRJrjqpjDaQSw5T|kvI<@ zb|zwt^$5Pj(gu`<_zJuaWua>_iEpvnFLEBG&n1<;_+9vg-m<@dub1I1gE)D&W`3Q0 za}MGWE&9m67`&v5MK8A$;BFQU0m}->>;*@$#n}Lp{9cp(pJJGK%?DrHy$JWkE+DgS zIZYYxOME7v9JK^Sb$9D0=6S}`)%nLfbd&RgPP?i2`GXtS-C2nDx9HF#f5PC)^+uMM zGmsw98%2wDhC)N9F3r9(lX*#uqxz9m_&W9msW44KIlDX}?N>*}49vzoCCRY2{UzC4 zeH-E{@8XfMvM|3bxvu$33lzki!Q=h|X=8&0H#R&-tm5=>*Z1onS#ut~Priba0w?gb zBPNT7fIXHTF=A1_Du_z8AI*Jj#l71*#Ji4%y5sd@qJS~MG+Oft}N zX)w<3e^L8w&s}tnE)e!YcJN*D5iWck3FGx5Sj3$y7`842l|)LIcvllgt52tuvI=y! z#R(YE+X)++Ht}^eP3V1m5zX@`#tRX*(d3&vCtCaQR^b4Ydag#T9VT@(gob@Y$u! zi$CrM3kkt1vLKeuKkG?f+~^~Nmmg#X<{xpwlSJ0^t_Quj3~FAL<5Jl>sLl5`aO9>u z1+yI5da8sy`C&rs?9`~&`*P@x_k~EcZQ^yUo@9QvaF?(>3ZGn)xr*SL3|agGYvZE9 z_^}Bcxy+l6(wCr1ZcgI5?aJ((vkz8WDZ<6irjt-vNzko~7dmM|Cv&<4zcNx_LOjhv zDV1z2?g<9=>l|!TPbL3Jn}gl;c&2g4nE$z#1En8l;M|jAxW$(;)H-9&`7$qTE?)zm zZ#(1VAEq#4ZvY&1RK}5Af{T0JVm!I&2hNwDf*X2-{_vS3blLoml}JWHNbDi_`*#Oe ziU;xx1xcUSauB*1^*~+L8P1 zHJ|1E7P9qEHQ>sXOn7<6i7xOx23g0;p@=4d+_N{LkkSEkfyEgfcz7+4Rl&4Bdj?z- z*wTG(<@wbQ?X3T#C!3B!O_Aw6G8e_hS2QABILq(^KwHgrrtS+3F3R`6z~92X1a+b#@&UkwL{_7 zUwtm`-wGdQuHXr)o{^Vo$I}P~byfIYw zhAbb}w--0B*QT|?zMy(f7FPd_$M6rSWYqjDGBeH%6CRr2_lMTJZC944q5U4Ycr%>+ z3*Se2@f6gl=<$rUQX(DWf=j-UZuv?_h?=6-WSkRV`ylO6Zp2)!5_vNhfm^WOmRi(Q|YSCY%2_Sv2lY|Nk zg9t3ZuX{GgELwMLB}>3f4ck;USe^X|foI|J+bH~R@0qkZq>@UhG< z&`yfQ#C0$5TuLJN&%MbSH)=s>LKCchl?6vFCgUa+$sYR4Guto^bZs!?p%v>v>Gfrt zIOGOo#+)F27v8YfW->g``Jm`~o1WkRS4Y2b@uYQiKfWl_z>6z$@%qrA_;&hR<{z^U z%}$MEVd02u(HuHE>4|IdcoPV6G$aSkGt~Ya1arKEZr-kLHtmn2*xRT9mc@UDPMfjR zs`n=-s2H%Tb42i3u>wCtUZtjAFX1gdi`r5Vjy5X<=WBo1cbNvX-n^9L}w zr1dm-gb9_r>JPUh0{JjW9e)3ck?@%-#~H!XX{f*|FqO6AnjeSIH+@~8{QsVUvN~P1 zNe1?wslY!=USZVsjr{x5i99Iw2)7k)0#m8hlKiejhRwK9;2Oqm>aL_fr}z zx@O?=wc%veE@3thHi^Xge`5cwREDxETOjr6MqZ>NwJ=@gD^mzBW-GGa;m3sSV%6*_GRx~d z?(y>ng*Er;CaO)K-}){?c9k@D*y@VJ!-#r4n$3^)`_soihl7WOrKt0AHKrM_0*6(} z7*c#0jhg2`-Iz+YWNoI{Xy|Qbb!`B3>6N6r&aHz5AIsq7VqH2qyoA|qPeF;0cD(*` zIx0&!Vyr_2D4f1QitQ88+I}`$Ht`rsuau_28{aXl|GI@clNd_o@ zRGOd2o-6O-tJGUy#D9N@+BTto@k5jP`pHn$btOdGXCD{am|^LtaJV#W8x;i);~Hsk zym;hoOe-#j+{@*#QY-W${j;8$=)?R__!~gj~RLla)0{5;@oJe<`@D)Jgh)R)dRLH zUP@{MgmdkX@XVW(3Klz)SY)>myi>M-38`J8FTr`B;kgZ#jU7ZSN_J5bn`D-B){Azm zOJ|A`%D}niF6u{=z_ZkR{&xI5_B7LlH_G?p`LqY{X%eB!G9>xKtk+mD{tiSP8iZ37 z_QI?W#jyJR0_dKb#C{E97!mfGJ)E2h(^X`k<*=d9GrbQ3N^`+%mJy69m`25mtHo2( zmSe*@HPMtFZBW^D2y5@ohN0bWu)}2{9<;3ohm1Z@L}jip^BMDaP==ycNw~y56ef;e zMl7el5N%QUB9e&Mg{c*HiEVKLCRbi3OLavMb?F?MO;3gAhwRvsDXy&Yur&^mkiz#N zJK^BRH+VWt1U*BAT=KIPaCrZXm@M@W_oYgai$7IBcF|BA{Cx%b_s7Foft_LCA1?7HkW;1D0Er_=>2b(EqLyWc%dNdcXq0D_;v*?*PT~VJ#rmJIJmIH*j$kKP4Qd$0S#f$e^J=&Ut&c3Y*~YO@owXJ9t9P*Qa~t>tnFaLO+|5Lyd@$P7 zra{%K>o^{cz!jeWtlD+}CTiUya!ocU196!OYr$+HOie^!Y5p@r1w2lsNpRgys=mK9{LZVf14J=SH6zknJ8qx zV{=4tHp*2^P8l`SLnnM3jS-r@As zf8Urv?K#>Q>WIIRVqnk>J^t6SmlVC%qN0pIe(|d&=Ouf@^6lek)TvV1P&JauJimxX zr32`JlT)xr{xt@QUt!!-H}36lA9rnf1d${2$j{xBs&0Nnw96&<&Y^YHC*otv>?S;IfeR8~GMeQf)=b zQ*uT20^60Wi^m>`5%gWWBt2Q#je34-=RH%T2gaPhdC#+INyY{of`aT>~rSe z8cudzlH{Y8oWjh`_4sCnJo}-g4$mE!?~C*cqm$n`u}$ePb5!6`B96pD|0k@D46r3>vo}=RXp<=a2!4> z$Dv_H8E%{X5kAkvMegwuh=PG^U5lzcvdpWm3Q=EP(!!R1+th zcA}~RA#wzcp-XmXK+c6oUNPx3ZEm@QyZY4Wjy1c%sneT}eEXE}Pb;{g6#>QH%5+`E z0Qx8D52&eL2e~{idVFpRe&;=CeWV|wo_KME)We_|H5M0ZM_{VQFJc)gyzAt>>HgQp z>7ozO;!AhsXrGk@KkPP{3p6~8tQ}9J>c{fv_QPUX(+%uZ?@1`EyUiqnWB9qRCiL|l z8NO+YI=9Ib?tfmD{6qU9u1>rOvsUyOZYCBxEwtRA}YV1d2wNwy2yae5!&Y(xAKUDH4sFAqGf(5|gG|L&{ z`h_=1y!lU7lcfnA^;y{5;e^s1;p~T8GTFXD1)}*u==$~AK{dYx_f1Si$0Z+Fq0lQIdO!}F!?I9E zS0Bo6|E>GeU4wD&Qc>H!jrE<>rb7#MV^u&d^LrHmIlBhd&gptW9N)c!bJw=B$DSrM z-K3MaR}>(g)q?}UBglVAPO#|HQTW*4!2a&7gank}AZN$glyk)o_ygiS)r%~Rm_@XF znqjI&Kgr*OY(vlhOuBmz#vjiRG8@ZT;l(x#doUQ>|5N1uOnUx{|DIh@iRIenZ<3YiXdR|0rXmUoJOIJ{&aef#@6oRiiO z?px{P)?O8UY$aT4@;XAkjOVZ!b=U#T^$QvipRBDQq9D)-WPjw7$e zK=18KsNmAUKJ0%Zy6|R^;P|d3YmR8M_h-%U_t(ETw>FanEh~j8;mtlY?gPrMo=tCgRZxy0NQloF-pEhIu@QSeGFIxb=auVsg6?=re zv53hwPlTVx0$6-@J$A3v!KrsO$m@ebPOnRaO#SnbEOM$MdyKw-?SYx%)QQu* z&A#U3TUIVyx+uH{q7I6dJ2Es-I=_RFG-y%AKunIm+bli{jwE-O{v$}b&V3qRf#P~-b2Q6(Xk zOD4Fo^>zC|c}hM@c%chbkuzcZko8RJcPibmJP{7nS_<97K|=OUnSC2nOeWm(!^+1$ z;GV1%9nVM8_{25BTd;vA|CJISxDrL@3J#;F_9&`XVnAoT+f386dYS#jovhnBmfLkI z@bo!j|q6ql7Gm;AsHWf=x`OySTv4meyc~h2wCcXFA7(#nZ{-F7jVUT%EcRh zphiIgeevz8c=~lhhXyNiOH)rS=RKEp^z6eY(w6*9udo|-k>}#CA>6&pj~5gT*7CHQz@HJN6vhxzYZ=zRMifl+vgeR;YOLLX!@eMdPc zC#o26eJvbUup+I2T6q53SaIy|C$K-g89X*UB1d*?#;|!FbYZlY=;W1mY@Yf;d?U3A zF3x{P!o-I`G0%*fKH5HZWAHhqeex-Z(K7(Y5n)jJejLVxIPtSW-*d}vRl4xnKQ_Zk zlh%8i)5CHjasA7?nAd*`)`siDLWe!{$D`Avxbx$y9XzCv z$F9>KaFL@t-*svxdGykPraU}DdODjxs>u!?&32)cT`%!bel~Lqa^chRyAU>yWFL3O zkRxXeK~Z@*EYxX0jjUW0Z733jO84N%Le^zsUArs~0AoxBD2(^!DCRy48z-?A66t{JP?1^d? zrZ^J^)U=`7#!{3%{Q$b=+OjDVM$(hx@}R4GH*JXUrq#FC!qmNL@bt%5*m3VVX!y)W z^9mc`&Mg-GwEjobrs}XwvI*>#iY2xG4Jh$#8Cjuq7z3?NfmCNFOdY4p1MV$iue{&D zxP!y6bLdh0HheuUEUU%m=5OJiTPtYl>mlx6MEg%0vDFIF_C z*sPT0cOU7&i_dGZNjXe3&g2F3%~>e8uj_G=(6`-bc^!;pC&1K~9i-^rAe_{D1`hqO z#xm1bEK!KT`pL5VN!b*XYn2sE^yBQEW-fbuM1l?s{10P){e{DCWH2?PzHZn1v(Wbb zF^-h|hz>gx*tM(X=~FqPnFo;9vJ*uv1lHp0~T<^1ni?pS2xzH40dc+EUn6JcX3|%oe>%2!Z-{ zJMl_xG%V6ohQHr5p?YO58P+(689JDg-ckivHCh+CSsnAgu>nJG3=;h;8Ne=#N`lM( znR4IeCro8`J_f#djgMy4;GC}AME<7@vy3?jqYeD)l>4r;4wC}$8mBKr>eXFw>7*Kb zS^W@v_unB87o1~jb&*_KY={@DtCIP1wdhjtj8(sG}FJmt& zB>BR<9(3T_AXe||0zF#Ch`|F_k&oIWo-1VvGbQBN?oT>g^=kukxO);?>!H~4CyQ(* zZgjTqPLcN*8F;?Fo9%MaC-P0E)P36xjBXf%J<DO~Hw$WT5e% zI&JJe#Mknk$Q&v)VkccGr(HUn((!8;O=Bo7(X_|-17~37V`28uS%%>)iGkvdC`@+K zX0DrrftkWpoOMqf3q7Ua#1QxHW<$tdu^?QuE1d|M`;c#OYPK>piz}iRmC+Ve3NJ+q z&`(hSb`RClkG2BLYUfZA_+kni9g%|$HwDbj7$i~i^YK?~8`aC4f>1e%lPb2bZ>`;|mmCf>pI4s#*wzZP=nxH+ahTL3fLC>XD5K>IRIACs{X1EskfmBSH~ z;aG@M7NrnZx{R6b&;xtsXM^6ar6l!vEtFKcvQb5EP}q469Q#xX_c_*kmclCb?1(61 zB5;~)I24JUFoNp^rVx0XkJiV25aTCvI4*$)?ZGXy=cgXedzBjroA?aJiwRTT{1-lV zZp1Gs@1RfpIz1+P#6WzFFZUaoC(mwwTOj zZnlKShi#c~;b%DPQA%oRomlUhzc@4@6Srl% zXa|bcY$n{K1Zw6mm@#D%W*_WNs!&dl#pe%7VwO7 zF12?b2Agab`nAM}S79v2UTnUJOCEm3+jU9s-@z^}_bSO8f?OIOR)h<7O=I@`x(2cj z1tG-9i2XNJ5*@iUX34c2_Ey!^9n6glJg0ZWg!N~giF&BOtFpmdkpzwGjRUdzDTk}_q{gZuz_HTOzHgT$K z^jb}(=;nS{{5ypC_~|=rbXUafTl`@D{%wqSxe8;;vF<6`_M(lD7^&@3!hZW^sOwq? zul{yG(5nR8CLhNW?YIcF*_$Eugb0Wk34v_Vdz@yn7^337qa(i#?0)I9k)CO|_P=o$ zzjq0i_G)u(e!8%uh%r0fh%V|b%viY<^i9i$oT;j8#kx9ZnKFZ2pHYSZIdafg zKES*2=O*v|8468TGxJRt!Nn?BSd}b#}LQ*RWPZ!v5-=%!$Fj!O}t`ZDqtZLD@sS2Fk zpW#eB7ImhL^5^!?Lv=2r@!0nn#EvbQ`z*5x9|UB>e0h$|ah`-nWVA8W>jSZ0B8dBx zrP*W?M-;651XAizm%_*???>o*Gl>w-@0S+gg!1AfC z^zXu7`0nK|a#C%H?J>>^{goli%M-JrGl-EdoM zEo)Gw#`xVbW-{42av66q$Ag;ioXu>e{%apb)%o#;zbj&dU_FVn*u+==?=~-V-)+t} zFoCE0rk&`{?&nsM#<Z|bI>qL3v17Qyhd+xTw= zbkTpX5|(|tMn?LRuxb4aCUEn6eo%x6UWl5=EY)}jaX!l#329fp>3A7EROZhYxKK@R z7>z@5Sr#;K^Pt0R`?0Jz2D~Ocg~Z2G8L^zN_~c_YKJ7P!0Xrim^OinSAMHY?Px0fP zc}sruJ8rF+qzrAUmvM1|F9@D5#HzaYe5G{_uygHSXsm4m1LhQ4$Vl)Xk@1I+|>iz!bKM>jSxaz!&fB%*MtM1-594KJLzQ zfXQkq>4gsnIG$(LW1#4oH4tIba)!F3U*GI z;Pe;2g#E@P>Nh~i;|v&eG!v(Jo7iaY9hV0Vf=EpT*o=?LZ`bd8qkc0kz53fEV|wV7}uXcxDm}NtaU4_xI(w zk*}Mf)W;Z)3FM)Y%{!cPLLSqE4458aEfUyu16avWIJ`U-KJ_I+kU#>6-r0mz_GvVL z^9)SwZp1#$%Njkr9W#61<4Up=l44yew3><`v3w#EUZ@K%)+#e6^d_;E>;@Q@UsoR0 z$FZyz7C}vM0y;UC@WssLpu7q}4pwC2$62o2`i2khJLW-cr3It-QIp~7 zFy;)tP3HSe=3GpD zjvv_v0sH{gb5sZhKIMY%ZUt(6-WUv<62P^`3DfqZ65f;ZtpDm+><^rV^S#Zv+)e^i z)VZ*in%W`OVK?5kUq~zB73gch3GlH|khKq5!tP-7>1`?p4?mgU-P@Dd_6|XI(S_w` z_ezktzFv+w`JxSE2Cma@^Pl29&(omUa2|(EZ}PIBlS~bXq0zPrd8>I{M4<2p@sB)* zlJhukwq-Cqd|esMME7Cd<5mp(q0SEHhho0lI*9OyhL4Lf>8V{0z^LgLj2X_sYq#C# zQT`or`M*4_D_D^qu_Bm;#20~)RxoT3W#C-OD#q8+hj+fvj7%yh#s0tRAzj0sef&y< zU3+{xNU4_d_Zwz{olyaT^ed>p9A7c6&COPn7Tfkczesx)31DT^2!E!*BA88lcp4{! z>8qw*^7e@pE__f&KcXeb`3b<0z(P9Twg^<}IL30$Nf2{#p!3`0sQ3d7zMrH{`E{c( zy5q7S*D>FQ^+r*=pTfFyYx*$HjC*HUi3QW)5o>DPP(tQf?|YTa1`hnuaaltv2i`XdhiuAKw1jQb>a+8vbBY9-UTGn2o$2J9E5d*s$hT@aQ{ zg!`w1Aie1Vb~`tgODzb-zqfngyh9ek+ShP+LK8gk9V5H#m9S4sl68H>ITNxSn2?OE ztet2LWhp50F9efbl->#u=z{{uYy#Sx$Wdj(=C!PI-% zQg}K=g>ek{1>3v-;rm-ta3uXSOM_Mu?KuVH>kKa(nDz?0*I$N`d>1B7xCd5BC2}qu zNhW9QBD_3V5U~|;r_Mjyr16QMx_>&;EU(P?7O-TXcL6i_<`mgkF3sfS55c;PmvGC} zEYORd!N$dNolq|>NzjMs+<`8TjDT6ONGs)&@ z3(1djH;AY1ahwsd4bBI?w=LuO;HSYBjE;{bLk7i2N3y|QskKZux5XjLX5V(BnFovJ1(l);2i0>MPBHHvUE3o3z6{9hEtU z!^&g}$h~?BR~)g1pPCj_yHEj*olA&Hg*uggo=KmVaIWG0gWz%OE~c^O>_dqxTD>3? zdVXK!aCdQ8P37(M$WvFU~k9{3tx_G0QhWdyWL^BpNY0?Ve-r{JBg?ffj2hav2P|PvXVw_xS$(G1e;OG5Upy zGdBiO2szaR0Z+3@{N(Fopl|0aaN(N*|n>36Kn{|7tb4?1AXPQHN{H%k_NN62F94MAL-_mT!2KLg*~ z)3~#-dZ>>V;9Yy=ib=6rd_fOcrmc%(AHOYT)fA^;wqY8ImmZ{d6LcA=={fj~HsQVX zUwO%{7R=I_0epqgc|<8t8Oz=Zur~B5@$yI~d5L#uW}*^4GFk=+`(24WmlvEnLk{0a zf5V%h9&E7BTAUQA2th`k3bY&oEO*P$V5<{~N z7?P2#Qml5r7OdZ@f!Bibct5V?^3OlqikD{2!F5~i5&>osq`xf$?-k#889$dWlI1yY zMcI&lZNe|===&Ajm3v_CxjWP@hKJD&wbp@0>%#<$4xM?3ZQ+ zALR2JONw#tWoP-||X` z{dV{t)V!Ak^DaNKQ%03>^;4s3{{`c&;lI#gF3yPb1>^1OC47f7`EE8-5P&Lytq_q3J~3@(U@mSOy&h+29b#^>+N10!z1LLZca%&lOf< z9>zA1bCnrj@#jCt-*_2L?VQ8(=Y_$Ny{dzlx=yyM$OL zhe)uQW{mC0bC~>os1 zI~dxux8Z%~6|76n6nLE2giEL8qm6w##I1FPR64}3;m7meS$2_q*A9_2TyI;O?M$Zi z!0qzTtFuwD=rQb=kWI5Lo&&EBdoZa}o)O8pi+(%H`9J=egP@8g(C6{6(MAQo%~NB) zY)GY^1Lt7y=R7P3jB}fIG#!ed#-jqMGpgc1G-rAeP=$oT)6+4_7Av|I=YgfGh1cJQLHQI^>CAi|3 zw=?*3`9?Z(TOEk>-J_#hxVeiFHhf2GfRcYxW)L!N{Xj>{uc2I^*}Xo&L?z# zH69gA0oAGn(EH*Goq2K_mp2v$>8@NnR6Q5p^va^JND{UDu?G*UcN0|20|iY9C^`R! zh<=j6gW@H8KIh}hZIOqq#xK!lZU?c?kETKqCrL~DLn3mL&p**453{rCaYo4xenjzM zJR|=XD*0+CF+m5Edj(Kw-d!xstcU2eYV^ptUSeCR&XgWl19txfQmysh=Gywb#-hb0 zQ2N9jx^55fZtXfvWf$tgpI!E_)3T2|KJWz_FUydtt?M!G;|o|6Cd+yW%d-K8OX082 zYsg>yoH$O9pjz?E&_O?f+_HO%Vt-h&BUqjn|78Ql409R&JL*t;@G!<^Ux4(or$lRh zKaKP%zorjV(hAH<0bc#JCnGM?C@u(onlKC6?Vd@wc+?FO^x06A%|MDo`zQq)jT7v z=hjJD0%y8AfZxOE@YO|<6`lVJwMPB$=cn_WH+L1LuiFDD%kBd6^)v`ij>oH)mZO68 zCu)9rH6ETU!j@-sV62BQ^Zvy?u-fzyCe0{Eg?B$-%JL}eG!!D=nsw>0#w;3k*9opA znz6e;fYFeg$y6w;1u;BhD}k-FV_-Av3wh0DRKJq5)+;Fg#S+}+QbQx2s^OX~7l@~% z6g%uwjz!_EwB+qsSn87<`4m^#eT5`z^#4p8|* zHH>!tiw3Ku%1gfwhfTxb?|K1@Fczouz*4rbu^VqMy@MO^WI-O zMcyy}&G&46Nw|eSH;&9s*u)63bsoE{gRC@eOe*h|0x{p8A5N#9ZLATbBsq$;~&b6Ljdyb$7rtQ<(8k4#OZHE(maxn@z;{fEt%2VrIzT-kNm! z?}ZSn9Oel9wUxBu*$Am!(n1P3XXg8RR-|W~mA4+iw)GY0 zcWNB-x&!GPxp*p)Ey>sg6hO>G0VY&65wt%C!#873aJV`~jxXBA3QvrH6rpE0w{0(k zeV1l@o+zMX{6lJ6a2A)3`GcXZJPZ#?v5hqs;bJs*HnYJ8c~2UN>k%P#fA=6Lr`{)a zea5_z`xi0y1=qnhw2V4*`4j1dk3i~(D4B0sOBZ!qv9;Lqn#x`L39qdJ$@5oIz!BE8 z_RlrcI`@V4o&Ji$>x9UjNiq;LC<+^f=aI7&e<0k}8XF3giS+&egid+;@CK086xFeV25iD-Nx=(VH5j+o5RI<=(`WE`z*bXj#=M;!f zP7=xc1^D)!6J)nNA?bW;=K1S&OgPcBK*z+%(5+0zLfHc~VTatuV?Q zoW@#C6=qEhrmXUO$^y!7Xm}CE~8lT0PUO? zLbiI2Q@KTNsoSzqe$EfBSJn0fl*~*7>Q>D?&mydm9_K+-;O>nh`qci>e!5&Nfp!K& z(GM67l?%Mdobd+OAT|$p!|SlBnES4up9i}-4Nx_F6Ph;9z`L!*IQ6g&Gb+EAx;@f{ zouz=)k99$Hh|45L_f@Rtc0XUz%CP-)6qR2S3&+d3Gsy#YIp@PQm|4c#VG;|ei~>gg_(N^AcF~LOXZTxl!bw}c5AKqdr{g&{NpW`+ zKj)k<(>^?lJwBcadeg%pXUZGQdQ?l~hfZQjMxX7IzNOf1at6d@<6-@;OGGsLHCf-l zc_J%x*eij$urOAKzHYh3|7mm?GB$*8?9>V(N4hE7C5i7={lWX2eo|g-B-1}_5cxk`ZFuSU|Xa)1sLuOUrq){w$`U*?_^nLr!!J#fRx z2dMt69v9tCq?WHQLz|!`Ts~%qdG*HNkQ~MB#EXbhNox6wqe^&NuZLh@H!$x5pkwDc zF3Y!&o*iw+r=ok{+tFnFm|6#^;{IfM=6BkmuFCU&^Mq>JAA)@SWM<&I=Pg>&69?^kr!S_cOCdfw3duQ z_wmE<+vOrg_%1=|KlbqUfg(iAP2pWUm`eAIm_xpXAFgeSq^pl@gY-*Vv3-6T*Uxkq z*QUzi1gm^rLdXT0f6x{ta@jA{Eq2iTS_7;cqI-}4h;G{^x><5_-L~M&Rm0xk^ji^at4PVXM@f>Jy77K;1Y=+*sOaNPo3Edd7Wob zF3XNklV`+=dxp;}$hI9@_Xo0FITq$-9G==HiLrML@MN+mrkreii11>C%~#Qt@oc6|Nh2E>kbP2d>+##UWOh`N7nnM^HVsJN~2LiVx^Cu@vgd zUC+oVc~HjolaI$L2%d4i2eY=$LhHpAkn?FK4rfirwBs+RwB%Y$-W!h(rT#$2 z<_dnq(O>khbvRxql)%-SHq%HgWqf+*5~OzJquk3JaxgptnO*a2YfenY!Hyte*rm-Z zNxMy)Zz#~vRj&za?}Mj~+F)G&MN|@MfU>3+L@1>VUhXZ$^6m?uzbOP&`D@_ynIaU= zm`Iwo+~m&o{rL;Bi{Vn81-PzBpw9He~tNH$25Dvu|oWX) zO`V-CBg-l{sNwOyI(VrkhTPHdgSm58aqv(8wdxxLgLfW0xdE;dCF24;mNu1*+Hsoi z861k^?qMJ-q6lFf*;MDkb`sjv06OxIsNxI+e)=LTTd0H249$td(GHZ^S3oyCRHkin z)nFgDMm2o3mmGfik&MpY#9!y%&hyXo0=J|l+H+5sWMnHcY}ZeCzfzK^sc46X;(G8a zN*=RYmcz3u9xA$JL6Z0oHBQ`y-9^ixq(BF>h3YW>^al2@bR3*MHb_k?g_(&7H+cnW z%9wF~h-PG0p^axKqTW?p&}V_i;$(OiHPLCZj`ifs)a~^WFmG|CmkYIPrSxzlZlw!`OzpypF z0JopLK{`Ll!*>r^EI#B0cb2GtM`R^ZYYWz4Un=YTMT6~B%0a8A-CU1^1pD}mB#rLa z%M6T`6B!6&!fqx&W8@9gZd8Vb;$5uz96?q}NuL=x7|wI~yPW~6Fc@8t#R%JqGQGwz zXp(ReuDoj?t9sH=dqy)Qu-`w1yS$@|g3CA$h;X7*u>eff2`k zo@@!>deq$T(Q-aXYUxB5-wsfC*#s)beK1;$51;h6vg+-Uke23Um{HdNqSNB|>zZ6~ z!GQoQTHQ_RUM*(xPp5#W^)6WXz!6J@XEJIkm1M3(29DeG@}>69MAyV%Y-+Q>YkBdo z_k{uKiyZ~GqGd$aNr*AM{R)=2jPO*i#=z^F(b)c07#fx-FbaqMBLdBjaOp2ceoAgG zMR!%~uigg7V^;7jbi_g0Ux-wPr9!Zk8F-x)MelNNa&np`>#=Ssn9u4#BTFQ3-O(O0xbFcS+dc{O zk7jaCRdMiMbPny9V#pK;qG8h3pk8}~aCZ~t9=wEodd6&be-#=noeEhWIzeme2#StQ zW6HN0F+qxV;o~_E_1zo%3rxYdt9h_gRe<$bJ)I|U@(T%j5WrkN zQG&Bq)luP}7ir}$cjnn{QzmavkIAhI#WW)k6n{6Fw*Qy|kumX@xl56!KK(I@%J%V6 z)`&9mDh5DyzXTKS@fhFVEy3ZKej1+r6i&Su;I3&r*#2~Y72KL6Q$7!*ZheECFNP4^ zsmmT-B}2uQ`eMG?E3CStL(lpb;inR3^xT?*b8Uu5T2>NXb6SI4ol_Y5kUTubF=ozc zJ@`vm5XK=I+mfpJpZreKsH`;GKRujdS4WX2c08JhYutk};U(x+&CMlVe}?d4O$^U` zPQIC52N!!vWyS$+XH)woW z37)k|Bya0aqgl%)-W|~h&bhG=ey2vmSQxiQ9SnvlGe;ah=Y}I%PjIvP#)?m2lZkd} zKWQ{Q2D>J|!AIo<7_fE)E~q<2LY7Ivp#nQ}{ZvEWeJ#ZP`(9vKQ^ea!Y@u$*6vxUG z;b`|GGT`h7*UMw6l}{Dzn8juJd<5`%L^btWTw0N7G>x53KJcP9KjB+lJNwfr6_$ZZ?06)vJt{rmGLIc~UOsOsI!>R^@nQ*qhaKYM~J)4&kfrDEO4E z%3Pf%Pd(kE$o{%Js3WL`mWtQVsilcrXg)&6{bIX+D|pM#Nk1i zApXL8t3krNi5}k7OdUm^5($@H;w<%yq%_1}XooR=RQ071XOSLs{Ev6Vz6_kM_LAY@ zH0s+R#DoS5qSbx^+h>Qu>DBEpncNE}o`I9!=Yz zNcUGu;7a3nR4%s@HTK=a%HCEgV)~l$pPquD!7V{A=+X#us=!5{PHvy1FFR_p8$eCco) zy~O;fkeeA3+b)AH?$=N)b}CjR|E4_!<>1?U8FpLG4Fq~ z!{yav&tNpISyqH;t1f}|a$%<4D-M3I>I9tFjJFn=(=hGxp!4b%iPud6|F%I&8{%=Z z{XCAzT?EQ zi|;f@IG2Ujm6G7_!yokh#Ve?`5&0s*)fmum4a0A)hV~v$Ff1w{mp7TP^Qymt?2rc& z$Yq6=Xv_x_39jQh!xkUf=@U(hZz%7b3SX_{m?>MIfW3hq2rfAdqJQ_H7)ii1yJB1= zaRCKYztPQ0yim~W6cptz1r@JO2$xc4hGwba=G;kWGg%7*#PjIflz%wO(;w~_tHax= zBJAdrQZ%)BPBu#}#=;>rW^Ba~v<=NcS-mcp5GBCg?>+z_Dihcm&BwU!y1Q8~e?Asj)Q?-7;vl=V(IwS-=>wDnAqfA_unvcBs zTz2QBJ6dx6AT8XU;>W@BNCr)q=M|rD)z?ydpt2hLM<&y~qsmNp(jHtMbraVax!_tF zg$q6Va8^+`G}V{TJHg`YvDItvnQA)kP0(UynSU)TtPduit1i*g1#7r$?0(YF%}2}W zGchKDfaNDA#zb_O%l7+%cU%!1_Z+5EW>UUHU^*=C;LgO|E`j3I*YxGVHmZ@@NpE>p zLQvc>R2><|^OEm)73&hg>i%0=bKwb0n%Yg4JYL6cDYKzL=AO7^c_jXkAA+xw5^?EE z6LyYBGubo6guU5kNz4p|#+*;Ad+iDO76~za@R5<}QG$`T|UOh3i@__9c1t>L$ z@Z?Mk1o;TEm%DTswPrJV(%c_hZ7U(U!$v&u1J`%x>hQTb)_JL+2!D~S_{%Ta2dG|r9ydSJN{A?d1EgsUaZA%aEu7z?e!ZC1tJDbZhsWB5$ong_zK^QGCM@2zl zM#XR$yxjf*OtQYh=Ax6ZtvVIOH|zlFbRBz(yD>fn!1&u~6_C?1}WE&3kfe z2S_(shK^y?k)N2~;>ZpQWpV#i98_KKC*uc>Va%!~j^T)gIUNR&@Si7$eZE1@SZ(=17zK4p$EnOfHh3W1s?07#m!pV z^5BDA5@gPp24!{ec>QNEJ$n|I&2oA$N85raKmDG5m=cF$mqST|qCWrg-#>6zw-Gm$ z8bRutiNHI~dG$3u!T>h|k@9YbJE?Ja-Ebjm`e8ShiQmkg`!ta)brHY`v-9D_)?B*K zAcHSqozCTB3USi8PME4BN#1^_BljN9hS{3)Q9&vQA8^@}=E8Px4=?qu&B+@2-Mn#Y4nZxBv|ws1sEW zZQ7Y=pw%>iegSF0(yT)c&|y&EuQ=>m9`8OnHm z9il0N<#;3Z8NP85hKR(YAjiJvts3u!w#~J4`HhvVz#$pt?}V?w5pz)b-5B^@6CuJ! zjVYW{0-4+Xqr2;$q2|zJe6y(tPCU2_<8K7nT)8T&+_Vg}TRLI*@)D}h=!K3T!TMG2 zq?(#O*p%EvU%##9vY6IzLnQ{+z5RnTRvWShuM2~BpNj1Z&eatcFhJM7oq@ZZ0`Ws$ zGv~d#2~S?|VUJcgrJJ*{fY#E1FV`s9ltbTJSuy4wlUc|AlIcgz2^W=Y3X@j~F#k%L zVXoc|w&%bpCh2DjoZ$G6LrRM=W4!>=QTu~*4;AvP%zEgtS8Avh8Hb6+mr44oMzV6< zN=9bLlD+&(pV>Rf0exbH=$t?yrf2pHt{^5GT6PD)+DE(b(54Wuc`l&kNYk=Eq)w{I`|Z-xbPcD9VuA9V+Z4&Of|t z(jAgNEQUE6ECd!QVx?jNvfpK>jdUrh53OhH+BUQU&;9k!e z$lO#36MAiMvE5|Y;q?I0wkbiu8bS7`@gY#nD*;=LB7Rms3y!~}@J+{WV*bw)mzkA7 zEkBnYzxE%_lpVna`r9^i;a%)-pNcY}2{;z$Pn*_hGBqDp;_;d@w)clqp!`!lxqDfS zC{5r>tXgGQmB4CR=K7C6I9m=cerce)lYnSUttG>=Be1^99Cq|>g`mbXu$hwqZC5Um z0l62Pqrwq3`ciN7I{sU5D^;Mgt5)zJvvyt!Q>z7-a(*ZTIC&WM^{E zT6e)`T6;B-ci{Uwh&5cqNVIsu-24b4b-#s}{_;n;3UAaZ59C>DMB?G;>Tn-y*{XhJ z#(HNcb6IR9^PjpsYccx|*THk0dQE)|v7$VFlR_=rs0y`xbIFQ*^0}4H+a(Om3JFxw zq8FY%tp|w{b3k5ZJq)c;W4=DEAsSpOL-8bV>&%AYvQIW?L>X5jO^ zx4^CNEP6Hwv0_j6qXQa&x&9`0mi7>SzbcBVixyCoPch^;+(pfoX{0eD0gD%N40KZ$ zQIpq0E3q(;y%z$)Wj8>0r8F~5=MBBoD}gWm4bTh&S z{%vvs4oZtN8#AlcRSy8Y2|I{SR6R zpr}|%w(4c^1WkY9sinEFXGRWh?ZqO9bbJBtzTZWYtLC_8-(0-$wG-Mho5AN`7VMNU zAd9+JVt!vWzHdH7wdQov`R&qN@-P$QxqZ`Lz`(0VMI6dG&?_tHbKazvgP-l>BMp(_}dmNHO@%;H5|Hi1KX+ZnXamV|y@@~MPt)>ENnE$f zb(}Tr0+_kK#TezutdE%}V{e`X$Gu#+ckgyaE>IlBrDV~d{0y49O`$&?nS($<7RL!3 zW%nF^05j+R0>#lUFoRnYXvF(tbILU4=nWH4R?kPXU3K)FMJdu9G5GYsV)ib7Ik_^q z0Cw_2(Rm(&tN#UJ)yGmSz7I+BM5E@vfa{NK?0J`^M|x}ToyhaG7jFxS=Kf9 zB|aGpPfljarx)`JjB}vT#T(Q_V(62d`>E#NQF_?*AC4KS!`Kv4c(gnOOJ-_8REh>` zH)B8EZ!+gq7TgDa<65{Dd;)XrH&SJfdo+B5Dp9?-5I<{QN2B)dROC@43fJgi=e;tT zHfukcm9|pZ^f@ribsnV1o`Jif*T`?)Exz@G3uKmCKb0MbfsgiwVT+sWc0c^kv;p)3Oc<6 zcvt+xFQ+%tN`?=}UUu@Mp`{DjP2E6f~wq{>>Z)UEeyaQ9cKUJgHEv#oh-ct zHM5tJfo?(Wp1DYkm?_Nrk`0&<5ehDLe@XHD9I*R5PLw5H;@=GwkS}@;jH-vr_qK@TpX9b?I)L?q$)5u}Zdejy3C!g0$Ve1W&=}UwC`0iE|*(7jSX36k1&8&bz_VbR}y;&}1~xg?PQ?AKmMJGLHW6-Ka4Y222-eF5ne z>x1`dW^ki91KT%=Vd#&U(0aZl)M ztUVHq4kwSm^Hz%dYhAcUyMQj7y$BvQi;=Gn^+CQd8fzo2qE^*r67+5&`+-}_?7g9g zvqvL%o3r9U2v((_<;CAMybt!SaR|dl^E_7eyLud->fa%4}AXX|$gBCu* zxJ!Hxqwys1-%2{q-i^xt`%MFX<)XZ_AKERN2QlgEsc#pL**9$+J{i7-;#Fe{ufc(?F=vbwLd_|Usu8o7k zLI0tzq!~VJkA$%G0Ck{_B;5Ep(LCTx3(F*1qJ^CN;^G*O)xzp=ztw|RKPL<45tT0|slxo7pb z4)k#uuwAECz}2OdwyS);y)~m$g&B95@ z-)K$jGB_SFY9sJGX0w%EQq=cQKIxrq#He-}GOvzaME`$WKL2L~I1Il)JwJU`)NLNm zvwJd(#OBiJy5{)ktP6SXYzu8lnz(Q9CpJHy2$#wz&U2hheDMaFu9*vS=8w;fjsHM= zcFWOaQP+w3**NYR2Y{tQ9c`Q?z(jnX36DKLqMf1>SnyKeXV*<4b)vi6*)|oX#wy^; zfORmcP=^7jDbQv<1E#8VP{|ACaOHkGzSFM9%}&DLexnSo9p?6@yyLVr%mz(&YqKWS z`d~UzfU9(4;X~wEGP=hVP6wXHWlO)qrBOLlKXC+Hhtt6}tOOr=ixb;ry0qn0AAasj zoUQG2hHrWH6g!|=k4^{wVfe0witN*0Xo386R%G};{*a^u=BjLf2S(cbclEY7?MfvX zKlhFL)o}SP@Av#T4K<(#j9~ijF?cmrMOOWNj`P3UlHqo7DBb=LwN4b^qpNjfY2ay; zTpmaR6KnCz%NDxrgaXK`ZzBqRd%^sr88f^q5yq%;dFokE6^1KBHTH-=AR&R zw66hq_lfvER0iEY*fZBpDscy&-L^Vwu5le_+hMe&ip~mL0;l?&NPoj8x@)YPkXw)N ziA^t>NABlY&U%UQonLA2r3&0ubC4+Q5N8YnIUnn@G{T${VRo3rK+DVptl#AeV4NVx zE;%eq?s8n_lCfO$uq(mW(Prep;WcEI)8x8)3-%>&Iqa2Ju$al9D(nx;GBE}h z14GVFKO4f&eB}@Kcw@cYtvjbP=*GjLPe>6gQSSeWhzBQBo$GbB*U}UPDBGzs8m9; z29;78)O+4U?sc7PYv&21k!Q zfhiNjh)cA1oiKA+op={NZ+nF^T>mn?dS~)_^aEHC{FbO{QhX?QH`N7kb=|vS{Kpu} zIy4(1Mb3Dxm*c(M3@qtIY`F6o{se|1nGpfM%8wA9_8t`Oq!Fd@3h-^DIoUZdpA}80 zVzw?#qTprCz_cZ>C(ek>U!jFt&E~8_LlPKIe*G)Zy4}r#X5AB)o`pm>xJIV#yELE8auv5kq+$>(FYZS zaqXxLbVB4-u;?~HrD$KSm83!?-cN)HMO`@k+(=%iJej8y?cv+LZp8Pc>KM5G5*&M^ z$z08|S@ahMAvJrTxNtgEc`%gE>edG=L{E+npP@&e=V_AL4^G02ih;C##R*n;AQO*iox`+^ z=~Su2guH*7iXVf_Dc^dUIGZyv%xfl%xBn?RaXFOcOniu6#z^wU3{x1mVI-X|S)4k@0+_)aaVGWH-d@zg_cY-NSvWfh@6 z{D&=Aa+Mgw>+>aJ^nn;_@(a?5I5JVlhJ<9|$lFz-Cv}>rSXhaf247f;?o&22V>3Ld zRL0Nw8*s+>4YHYx^@k_isJmWv%f z98rIGJ*2LD%W`F1ShKMSZfQ~HUG--$%en(rD0xE0(m{M@Zv^O#x{mt6#rF1VFT;@N z3!=0FBRX-DJQ?u$Gfwa@Web$n3kkCk0cck{iKW;opiyVCpy+_8d6E%wZRCZ$83) zz&+Ncr$&cTRU+={1-pI;?kD&njS}|4g1#wOYY`=WQI<*8D({6nEoXLlvNOnU{6xeP zR^uC`GP1u#9>IC|>F-x5K%?r+3M2eb1g3v8(0j%b;`tHA1m z=g9!I7)Z@|LoV*!gdWcxu@g@j+INXDT1%bHx!u6#Z4HG@VsBI!S`HT)vawOd0_SJk zA+NHmF!sfBvSE5HyVW>=zFeD+of&VLx9)f1;a%(=gv*yJg)0O31f)N27nZZ9D_NN~`fJ517D zzLTrhUlEtPp9Hqb4VIMJz{V#@v5I+7IL}b3AVA*zm7qGfjy;7;`}Sdf`-o2o4AGVd2LcgZkrwsj2Lzwz0Ar{WTtY%__^n;(i> z{>>J;AKM`s^B~Q^oV`7N5vO*qg(bII*fY;H&^PHdhF<6lxK-|wd@E(X!$ATrf3Ih=r`!-f9;Pj@ zLS)cXTmZ|`{js#`D6~cD@Vh7NF-1dzH(V%UK2wT`;(cX0>01R#*~g>PTQ$Mi&=0{9 z<#_q=9R7YCuamuqocpBLNv;1xNp`L{_;=pK@BB6!D7Cj=dCFaz6b{J`tb%F_{ z)8V_LF)i-!Ow|l~t3*i)OI!IKo_j9H^G+L zZZ=eLG}{-pfuvPC6KS{(s_Ke(=w}36bjU=^ZU<#eb!wv@hw}Z}P}-->XH*JZt1-o} zxim!JeoSQjhbD+hwhOE$9SaP;F#^0A=c4kaK%8@M76fcsPTKdhlfOGp;hY5@SaPK~ zoZ=e%Y(We_$ML?OJeGiDvWGo+g?t&7iTsT-=`dojo&}%gBhotYDJ1 zXn@WE=zsPRMn@;$_aLj)V z{u$*B#|2lL@k9_h@a|w>)^z2L5j>g-Eli0bz%lU}hr=pq7BZ=g@Y%;||kG}BwM!aGu zi|mSncY`P3rRr?h8I%h@_0rJLV>}jY%0})M3hVqkAzpk7O>2aGgXTL(Elp#igqhvU zGkGnUC#=LA`^9Sod>jNMBFeMz9 z9!mw={Wa|5fBW#X-dSd_Uz1NcEd#FMCqxN*e~`(OU$J&Q4cwz|35M!s^unL#@MQOO z=#}Ur5*xEHH`C6Ud3S!_%FB`iQs<{hrUf5r5O z2Q0iRc+O1NbGK5syGaIurFP?8iDM{lpG#XWIKY(CAyAuhh3Wq2W-q$Wko~*bSc{Wi z`T0vS)bQCH@~}RdMBdJZsPQ6@7i-d`r4QlyrdOEo)fY8_&awsZ%JAR)7}oT2J=6D? z&$hggdYtR~+Zbw*l|p1)csqxn=w{oa6!FRzoF{sfX!y_}w`{zf#m z*O9J`iFm72Of288A)_7-#=N)V=q?*2s&?lW+wPJB6|u^2{jL#}Sr7$d-0j)w!dajf zSBskFqiFi0eQ>>FC|&X56exHH;gP+6@#pVtOs4M;hK`y}T(mo2>Dn~r{PZNoS^KbC z3nMWj*9IQyC1KS)fhjd<4EkIOM71nuSZ^>30)>9$MZsH_t$Rb*eH}bwwHtMDuuT4lL4fWSC(C;H&u#5qZ*ne=- zZ@7?a8$o+dRaA`E)}o0C&S>-4wxb(ZY=V2N3DLpF)rG7O_idv@5@N0HW_^!Xw*9BApCvahc#33F?rBRe}iK z?q(T#t|w1Vxw&D0;T=}BQVBQb?PHsgwYk*xaOA;PQEK%LlC($YgxkIW+dcC5RPC+k z&k6^eJ^vmPdHfbZ6YEu{6k<`jWntnT%LM*d}pwE?^*nMLk z6jUsQ-p(>~^6}V!TdIkCqwZedgSCHCo%Z!?AN#d9j@bUWsuKv6mXWe>&d|Pe}Eh8WtEmK|}s$A}h4p;`c13I}Ue3Mzae_1zZHJ%fe^! z9zl)An4x8ZBWy}k2rhnq#>4C8{W`P1f-JwpU8%MxMmpAySXoA2C z7|&fMxM9imRru7iM*J{oC|=0X;ep-x5HLJKJfTtzz4Tjfm(~=qey9)1UbzRd`a|*2 zngnr0xE8;6#){YJp2GY%O)wZ0Pb@CoC-MRt?Q)$YDOL>78e>rvZ-h!Wx zmczclbJ#zTv*3erxFfd?=X_NZXG)<%3cR{1 zPcGSOp>q0fRvL5|F26~@Uc3rU2mI-G$BATo^8kE&<%<1lqn9{k)-=&{!Raz+bqdbi zron&8Ea&G!&$Gs`lNkQ+3|fD%gbMwqcu08_KUdJkF0J2-eGla6yr8$Z34i0A&AKQm zkmi9d`558x4%gS1gL!W#PCD;O)BDw_uaxlI+>dZWJ0I>xXW^-PZ!qz56q%K~99!2M z02+CaUkl$s`XySSY=nT;ZWJ0judcLly+w=FE(t4b7VCxg($#*)YNBw=SV zJ>Dl=i)zvMSE=o7W{rL0_Tkw3vayzu0YZyR*>qAx9+5rCEk08 zMxq~hFr|`>KP?t`&t_C2_#$(+*T!#Go5@p$YPLRK#5W7Pu~1KK@X__aw4e@nH}@Je z`g|o($1G@_xCbVl)IqNeTD*OzEbr<$k7UzbNHa<2hMTmh_{=bBZErxAMT`)8FRBxp zHIG9-zl$(-r5w7vPr^YTWAKf&BzKvd2`92%!~F+u1Sp9z&2%%wAM17Knhs-Om+2~S z^mJk0Z%YUq8B0pueZ|lm1>GURo!JK`#TwYO%cy~(9ayz`f&fmBGQ|Y zjtj7X~dgIG@jd;BW{p+ zgzVTq7Izs(_4g;7+&jK!f`iC;D)X=O|(nGcZ00)?W2?8I_)>2=QAB} z#|2+h{OFD*Bi;C$Sys64n>Ne4*khkrwO@2xIQKS-(jmF52Gf802p%Qp*ez=cF$#>v*&X480;?o{*KP zlkz0##;Q1eOER?AdC)_8fmAi@F^PDy1(ocy1jdv(O|IR#kxX9A3W1SxpMlbIY4XlWg7<0u6>qW0 zf}?9hgqwjR+XuT8!@Pi%x67=_13%cA=ggX{(M3V(4ps*wp)s26E zS>P4;tZPMfxC87^%dJ>M#`C=Vy>M!s8(woxM8n;x;J#IZjQae7Ts|?KuDdM>D<%Gr zOCj>GL@yO8bCN){Hw06MJ_jQmCpu%l4Ma|qpw$!K@Ke9K2v<^}8(<{Y97ka5WD^?U zx{#jVeh8{lwu?rc(&Y<>&A}Cksp1)$-t4{CL3B|z1+BZq)Y0`FoOMs9k2TaVrd5%? zPUy$>`LntD?msx;YAS0y7Ao+Z62Q+`fv4mrquldVg4^LZZWH?Mu61*mN>L~uW+Ope zZ0c}#`v-FV&T9ToTqp`1@le>KRiWmDNT}BpJa@T^vHIzH$eC6PZC<@%?T>z3tFaI^ z2N3e9%mI!JSPq^c`=M0GpuNpZqh3twuJgiZr(_#cqPv(BOa;yzZ z*CdnA!y4?TRE*(~-zDfVm*4QqJe_GCcm>lhQp%23fn_}r`WupPFjb8gZfQlsBoV(p zbSN4bTJZsQg))8qELyrb3H}ubT)2f9tl#qoLDUxiuMd+|=D9^Rqx{9<)EZccfO85W)-Z0A8VIOWKr zPw7LN&mjE0MV-BfRfK4dOR(N{BA)9Lm;zV#k^1v{VPi@mXY`XN;AD9+3f;lSBq9)5Q(?mLhLIS2lOq{c;tuTl_0Y7nZpn5MOWW zgz+Jlz^(aa#fs+p=r0IpPu7KEld$i$UJ(Vkn;w%NCCy+VArETR(_n9@5h+)pA38}JOF^CF>r8(6DUz1g zLOL~;if2jHqJP$Rc2?;GZctc($U`9aVhc+W zABK^U-|^s#b7(Jl&Az1eIEXeh2=h%%^!X5i+xoVHz^r9zb$3Bmmk$6EqfC58GKrLIaf2_X|G;stKHS+} zgf}lJ;Bfn1D0LddW#3ezGLFac)*u@5;Rk#%(4yhz9}5be5Y)=G(svz58L^ekN1*NllTw}=#HI+KOpM^Vl9oiO2)jga+!2{ohS>CG82qOmOn z*x=NRUHNIag^!`s=N-=8oDbFkTfwfR3$DmX!OmCB>|oMkOw`^7r?w4%JLy~TZ*D!1 zxP#ce?JVB6X@)t|HF)$FS-M|1KRrIN8tPwWft21Gk~04n*a~yY6xjxHyyF3xsuX#wWAHH4Sio#UY%lw?cTIIZ1vz8-%-`-Uk}*i4nf5h3=|3 z%v6Yj{mHrH)#^glp8Nx?O>c^>Kgj|ei7GNc+YtJ0rJ$Pjh1PuP&6oT_R+0e#&Xe!J{1KxhZala(N zDZT(4#gm|OQM16P>S2xgJ;b+g2KZ}$y>wU##C%F7Mhb^;_+BGu?VCWSo2X#${-f~2 z*O(tR6a0d&p0eOW?YQ#GSlkr2jjRs31&@+V=t`@h2((P-PUqsCr!uf4X$PFSDZvMJ z*0RNKmFW#FHMsZV8thwe5rUitKwHRs9JND&|83q&I(^Hb-^v^>z6ym*l7o%Dk$8Ua zQ~PZa$64dhT48590d`F~g3HMUQsi=1{ND>jlH?|20W}pa1_EV0-oA=mT*^Zgoj@+WF93y`n!ix*F z`NAiUnaA)eXpv@4qdgPw@rcFzrh*!el&Z&w_%*a+izcpLx|%2OpAfUWN#H(B=67QH zFw-;>7iGN1>LpHe;@w|R9QOsPB=5tbEyh%7nlJ;JvXeV_?B*>U+qp`btI!oQ;}cZu zY4Ne!Fx~kkbS;d6K|5zqW!Y0$SY=Ho?5`)fIo?`8l(f`;U5EeP6Er zFoO=W`wW4WPIPbbGI-|W3}ciJ(Tt=UDBr6@XBwNLW#CurYW4!p+W#=;D&gT(gK4Lq z1IuWAh?mBVguK>b(6TkH_-*J#%1u2XUsLG0Z~6ji0T&sbD1;#o6PWEUBl_XA933qe zfqxp$!Ml_Oyk%t3Xp9oFHMz*IC<6JzjQEj(98eSxASbEv{4AAY&fR=o4%20%{(uARDw`#y7Ib|w?}6k~0c z@BRnnbA+r>zY<@6st6Ch^`j5RZs*7UdGOw4QmlKyg<53h;-;<9e8E(cih(KN zd}gj3f7%ROEw2Fc2kLNG?M@wAXVQ|ee3-{=g;`NJt@(Kj?*=X94(^G(SvrPG=+%K_ zX*8E_pGY^0B4~?WJZ&&+gYB>6*ksT3cq+n%{t1^O)AuV9|InvRz{xwx&KJhY#OmvnQPvof%gJ`pcGzRPB7>zpFO%;%Q2aB`3kX54M7j z%#MG%q5?N-4&efj;u=knurgK?radWR)i0gUwR#enG`O;2fr%X!J$y%^CwQ^R`ElU> zM{s;k6QTSz1$y`KT*z^^ptDjIvAb0_pw3waKgezutypXbj~-qliNdTm+UPpu=E{Sa zL^gQo4(10==90nAzTjOd@c$Fcsoj}!a_z<|H2YZu;nh~K>%(R;yiuB+_Ouf1d=dc9 z|1F>~vh!HSk;(MgUm3cK3+|W#Gngr3$;)3HgNH5?X+%{f$VC@2>9R;(!F9pPU6HsD{ofG^H_S;Kk>Ft^m?nnNXc^^gUmz@(eWIDfS*Hn0QtNjWsfbr{Xf zn@N4g_7nf8IC7=&HGZhPiUBf3Xce*m#^%lw8P%PH+cS@1LE?SfvBiY_=V*dgtKz}h zJP$-Q*4%opFqcpt#5eEk5-m&}z=s@ukJ)uuC?z4ygYr6HdzU&lf#>Ld_zUyTGN+5n za@a4AgM4AMz(cn1h7sAx3|?E)){4>8dz(Lg3J^X|Iup5HdqP0tFMRM*rEb%_u-NVg zw$)d{%)gW9F2{CMU1&qQPu<4n0_*33z`j4&`AO*B#6!MgGDPU<(#fk{L)dY7I;2H{>}e+BODEr4&Y6HtGO3eEny2+pSL$B0j|yd+MU zk7^QpspZw;vIxqJb6P+%ECcjA=EB?V2Dng{Dlh`a@Eb|XK~jGQkNtuI|8XUH{l}R% z%@q&3IS~hGPll8+qcH>xMKj*oi$b5r!{PygTdu-@|I=25g6EYuWltjhjxfSq8s}i{ z?+$j?X)Tl&rbFb0W#nJP4pblioJD6E!xu$OX5I3UtowV6S@s+e-W~T4vo94myI~J3 zZmD7K+oq#zs}fsvJrlZvgT!Y{52N$>9t;_2z!NXji{stS!|cp~;2)ic!N0rU&W+b( zUgUj$h~;VbCa@Ri(CNFxhI4&#``SM(GvT$UVAx^G6iYdFFLdzTXISjm4=x(ktU}mNhQIkIx)_&-{gz?0=CLBt9qz@=GGs8xGLYx= ze8S6{{5TJ4U^hd)lCpwn+%M-R*!j=Jm=oTp=wigb=N7?#b&I%6^k%+s*gP2dJ{N?4 z99HDCf~=i6T_P`>W7Y3N$S7snNNlNnkH9Ox+bD9@8%Fp09mLeyQPlie5DgBu!~kMZIYYkc)Rh!;ftAtE$KPajQ)2o~}d)4j;E zItj4eC&7y(?}1&_YiK^~gFdTwQ>hMhK2Ye_PgyxYm|Imc-9bx+X}{RSEXN^4H{|)CIA|)J zTJsmm-G$6^<^_l@U&Y7Isz%RG$4FG8KK~*3L*hba!|pX3>B7z#w0-?vI%L&UjC-d{ z*S%TAzlbA+=frnZx<7~cMA-54b?q>IUmjkt5%N>EQgq>e>fGF<0h}IWiSA8X!S%c& z;OeI!zWwG*{;~8d?|0pgOE2wWD;`Q?k>e0(IDZ5`inB>gc@rERyOQ5?9Zvm&S*019?X5*BLCBR0D7=EryNP3&CWE2+!NFBtMM_IAAJoHo~)o`uto_rl!K19|Al zYust@b1=9#i=TPsPwy;u64kI?$&Xu75P0zTY5jDKcEeB+lp*m_|t?Jv&c?r*0s;PW7H!9{v~OA!oCdkcH4 ze&FuWN9ch-Z?G@uM40v(wk=ENo#j?M{BI!rTCc~KW|h-Qjg|C(UMzfll25Z1ln8mo zc&a6l#*YyTD)Dj*ySA+i%dAtuYL^Yk^c%&ER~k_9i}i4!Qh^&yGobGk22!*6au|I^ zole+d$972NiBGxwq(IVlKFzqk(e63<9mv@L93`33TP z4&lHlN6}{fdRUOS!oI9%4g7fSF7(8z$+f1fAb+lk^xQMV2rC8$3%W_vcZ95qDsbFW zoz94=C)Gox-~-@4}7?aC)I4>Nt6a{yEUGML<;wrs|VSmr#oTeiz~RW`YLM2Md6-qEB^1Y z89yJPOZ_%VP@}jCD3IC)L;D`0cI;XzXVxg1P^7{Ov@6M%By}#MbPETKna_V_1d<>b zCsaS2hGPv&u+#q~E__oY8epr8YQ3ANle8_5R@=hW4P9wTWgycXo5s@O?P-g-EnOCs z59$W?RHcJJZHp>B5wwQd2B(nHR14bnXc?d4IgpM_acAUX6UTHs*s<^N{t@Q<2H`Rv&Z_#kRD zEn71k{{B|wm!CF(^V~8%=&uMn&t{`@(@CDLUx<@!PVu$teCbLs` zg2>G{$>Rz-eVc=$*2|Kv2NgtH(HYLaYY`3g7Q8y+eL%YYC`>C*qXV~(fUfisOn#|J z*_$tTcdrUvD7lS(SoR$DUNFU99Ygpj4JUB@HIoLYZ^Gpv%a9Iu0=oByli7*0$bT-j z*xB(JYo&Ghz!8-d8!sk{RoBD`xnbd)v}H1_(mDp_ua#)iL4ADLST8Wl_oB8)gJ=Jz z%dcI^1z(ZS$+|s^@JtVMe&Wq7*nbe^m5hxCBVEgw?2Tk!HtqE9{qfc^O)JfvVe9yinx^3xo? zJ733P!ENN{w4Lzm=_*u;6}&aKh5MNK3LQT2!C zGU;gebi{% zLWlSLy0yZK?Qjfs@B)92C&G+sH#<~(0_>fCVzBoZ{yjMuj6Bz3%Kdz(DW4(QF`yX3 z5`95p+$izD%p$mNS%crUrV+^)F`B%I!H3q$#AwTX+}ohYTQv0{%exuPL~F6@rV95z zQv^%oDV~mv#2t^SU`~G{t5ZD#KGO9NHFmW~S4s-^xV;fytEdpN%1=o1yF=_+$3pmO zWlK!xD`>ws6Q(o~cxyKSC9X|^KW`IJS?w9i52+?+l6PRyKM`)&Q3SdA*I7kaEnA>E z9xB(CKuC8Y+hlqijgzl~X;3}uI~@lQK7p@{JrA9_@_azG2W)mtfjgG>SyHqVx3D`T z60rl~zmEp+*=gHxS8OrceZ7b62+hZn2inLTfoZ<4^aUPkQl;v9gE8;aShn@R9Qb6Q z50#TX5!u(p_#|!!vav$euB{Q5-+Dw0mz;ubng+2&7e&5Sqs2mC8FnOp#?gx-z~tV0 zIN81m^+GcsW~oZ4)a_E-8~F`>Hksj%mD;p#>;sXSfg;@}>;}%{NN_6$9cupdHk|(G z0V7sVgRm>eR6Y!WU2YHTpLFN5hW+oE#lj&*%!CGyMonPqpt)Wnkp{P(3| zz$K1q(~{X38x6j%@;q)F*N*o?O?Z6a9{gAN3-hNs;;P7Su#0fOilH(z@YwF6xw3MWWqg>$L z^D)qtXhoO$Tkz_|bNK6ZO_)-v!_yLuipv+Ypw$BlVfVBf@&_Bya@iHQpsW>Eto@7; zE+=8Llbew7n8v1czXOTR5!_v(UzF&gOCJzr-u0{tK<0B2ySDlApNB!AW<78C2 zp@(mFsiMR43Jh+U2-G1Ib?IexX?Z(YzW)NN8ghqI=*mYpbZjhZ@%4g^#RXVDcpzJz?GEi< z8(G5&CEg|Xm%aSEfhpSQ;pd@|U`3D>7LQ;vL&ezjSO&gqzegJL%JIeQ98f3{JXd~_ zeAm2}uufzJKPubUyZYUbVK$k@H#tGa)lm2r76&8LUx80=5(sNRHguc{&CCCTbNk95 zz3Cl<7!BY7DQ7{^`!6nYv7kh9DkRTq7ad>~!tc8Vy@}T#kUj|SudO5pv9Lco@YxeT#4MwdE`?!UT*nhU4oD~UEhkq!pzQj zmb6{H7M7>~@{xrwF*?epNo`qyb;G z&561itVC1&6eh8u8g;twq2JRtxGroaU3V%6a?+33P7&PdEvf5h|5g!3IVtc_aS>vL z;_Ep6{b7hZdKf4DmkW>EHK@&ZD+;laJhmo}*;(I2lo2>%-Xm$}@?>;sH3N^{dUOdk z!jf_Iuqiehpa0xUyEnQ*+YeuI+;lMY-Bk<*#pyJs;V}0Z?LaHTGsvZj-jFrLfKPpY z9{n9(z;<;JT6|jr5|Ik*jLHReCv+-%WPMl^2VZg7s@b6K`vhN$d)UaOOQ6heF}fWV zJe1?>@oa88+I=fx&XtN7HsU+H>Aj4m7Q%VsXdEp6bOA1fAH-+h>M-=`PIzZ98{#Ke zL0(fjs7)C`f^#0T!h=1>rIhx#K`8V9Ls~69#vmzdWcIfF*13NaQ!PBEp1)rxh z+`2Ui9C~u`)1~<^TxTeXGZ(@>X(y)0va#Fdn5ZCfKd5~SX0fvp$fdF0@Ye7=kk-~G zInF_FK}U}I3i)gob78l4O&0T`2BOuTCY&rS0q3sIqCXN8xu=H>ciCx*3WMdi(Zw1} zR_$X+YHnOE@B%r0?=#+UlgEKaM_}rZU1;RwMyGe`@&|Vo^D`bNaOTW9H1|lSDZ^5j zQu$c^PD5FknQZ2J;^y##HfNri)trtUU`G#(HzPJp*Z*oF%n*ok4fBh2X#YWl)^-1r=F099El1lw$Sy!AEiM z?VK;kZ{GeYV8V}K>*5b2{LC|m*(KzcZ!1E%*;xo%A^{<*(lICQ ztEkh!6!Y$A2`u~*pc5@amu;}`58mN$N22X}@*)Z$DeQn}DE`5#sm$n(t-o_t^Odi(5d zIqG~==s}yk$8H=;r(UZU^{cJG_rG4VAU|P#qL=_qxi(CuNQsu*lH@Y;8=Dy;ok0*g01N3mf7GaS~+ z#BPh&nn%{`{)GS-J6&+^d>O%C4RaK6fd^9gwTbOfy@=cXYlUBWwrJm!2CrUiCC|4$ z!GP%x@#ecJqRVH*53j(>@ zHXFRZHkX*m`Qa(csWfWGERdMB9NxTBr*q>^g4FtOTu^rhC$CzDcQ=jTt1qOpOG~En z5pCIE(&Iq&t`YlJ%kD$Nx*;?c1?JMQR>C~p&|>&?a!N&l&loJv?=H235d(DT(n|__ z?X)|jE43D$7`V|Jx^rmEQBK|;lz~ajUVKl|5YmpaY**zG`^iU-;Q0phFcj5W=sn{Fr1_R!o62v)@mqwhpKF;3CW$cnl_c-#~ML zSozOQ8&#fd04;&t>2ACd3tDcli{mpPI?Mq_YHCuqR)G&9+7I7PDx*sAP4Y0^0#=#G zp~=%I8157aXI-DbWvhMI7Iq)bt52Z;t^(`!wH^O?9Km4a13Wt{iT4GR1D?xA8tsZ# zk8i^RkCZsIFy*a(cBAg{9Jr*HhrgH1L**~ONdmUvcC{7oV#W$QJywyw+?<448y0ZK zMb*g12mtd*;VkQ#7QfYPNpGGur3PMOK<2(JHLp#hk6!8Xhqt3JeRB-@n0{c<(i15w zu;)>`r@&32n;5#~95{Bpg;CGGK+EgJaDH|jeE#T-SMAJk+AQI^{vLthr7uL8FC?k% zz*d}7zK*J=RDo&l2XN_WgQa^+`RMgFbaTLcHhcV3==(f|7YUu@@bw$qY1pY{j1cdk))5C%~Fc!H4E&Bl1(7ioeqLiT<;&W5=abv2~Iv@B6KWbm9qCwX^|u zcZ5&h3bhKC#MBj2j#;b*=U+h)%PudN>R_Gf9f$aojvrzo(D+d)T0b7;K9*&-wgi+X{Z6 ziB1U_ZZ{W(m3YJApUq58{{brL3@4vH4&v6whw&QE6uc|_M6_&L0xjI-h<}FpqS4K_ z(5$};*B#d4Lkv5ye2F@^sdSMgW{I$@bpU;9GK5dMDnZlS=fh{6+pKi=e%|3UhJS&5 zyg@mN1a$PGP5fkfWQ;ZVRNFJp7zO%j%N@u}sTUuz3QMlbS3-4U(ud_=40)aF<^XSLvOIY8k0Hg}LyEM&E52&E4K*~!Rf(BOplxrfk- zXJK?y$3%WFzY4Rz&lECUCj8q?b)Mflp8trD=iPf+aLdCXJUvTbYI{_oRp4S_Hef}6 zOgqXeGgA1*t zy^Ud*P@Ii>a-(Tycql$74a2fq2%QtV@L)s-?zAa{Dq;7$?)Wu2X6<|Uyds|rT{)BD zztI?Rc?FG7-o^<1%bc zji4$@c{r_e82xzDk_NpTN2hxahXi>8+VVe&&O4mT?~UVRi?TC{5Q#L9Z}B-6}F*~a3QZX4$9Z3=F&bD>1&%m?frMW5ajShm~p@SLpyJv-+(PL%h-nVyeX z)ZP(XV(}xCd?ibNS{sPQ#n!;Rhv(qNkT&>n>k$qPenpIjJ%t;+vjlISCRLTW1AA1b zk&)T^Me`$fz_SS{c)xZt6ztxLky>(KZPEw_Ea$-BtMXzuRY{6BKfu7xxiH55Gw?s< z0@JAz)E~b@$-$wJQuh`51{m7K^hJYQ+%a~%<}FM+RtFhB=Ro+6-xW?Hf5Lt(+lsbw zE2cDOJgmAFf-;9%u{r!UTdNa-uD-^65-P!mb9>d=^gA3Ke*x&DDi(iJ z2Un^_VL{ARfhSS{S_UJbA<-MO-xPxL`a^7$jXzttuLLefw_#h~CGdJbglgG8$BuP} zAuxX-wyM8@$_NX%Xj{s59Lxl3n|ydztAo$emL7|;=OS>}Fr+@1DXVH!@&*4n^`#$Tc1s2aD$DGW+mqeA%bL%v}!R zp@{9IaQ1W(`?MaPrys(Pj)|i0V-JG;I0>$mEoAxTEP>R4wvcl5J&71Efz?@~!Tm^U zg~MSj@V>^eGp-+6gs#SE`710u=K|a3r-*}{jOoO(SXekd8|F;D3wE;&_=WfTV0ra> z>@qolU%uQxg-d_2V`K+RXRWxzbRm5CsKZy#Y_uDApFEW~4*^AMaA&a;&&nzTkG_vk z_UBiHN~PdM+)xPq^MAsfh62cznI!6xwV=f(LSgL!Nn$Q=(_RYS(kN7=+d~&nvzig; z(p)35J@5;c2ADzB`RVjXo-x1kF9+PRAHmMwUqN~7CJ3KBm|j~tl~+u@4nBtwp16t`VGp(@;aSO7NfU=10!0< zy`9K$aRNT_Y!>I~J|gZfzxJjgL`mR4 z%T}YAXd$1`;*3Xn_G7{cOZsE_Qb?6iCWoX&Ol)!pwiT93KJ+J7<@+Q;BF=|mFX zBgbFNPh$za-{JaTA|B?mj~Lup4O-T_MV?kF^k!##g?{r0-te^$6rST6#<}~;^aTxf#m_kh(t4Z`D2MD|!Np`zM5vFq!${ZxfW^EyNc3;RoX88&%GY_qFLD^9}~II=b! z-A_)#PtIy==;IU2j=KO;*d(~o=7aiM2fVC3k^WjSmRvu73NvoRpr%bQEdG*JD_#JBX__guHe4Q4F8@3TsWH;LG?V6xk)Q7>f>Z zO7kF?x>Sj_Iy6Cp!AYi2uEHz2iI5+^NV?SYaDu?(J1Ct52MWHENgFm{vXBK=eF%8( z<14Ymm9^}G=Qdn&^`y9Fi!7Y8zJbe7Q5)$XVC#`M$p#8mpOusIR(l0cq zZA?7m7bW23us~3?F#wTTGzjbfd|7l8r~9{)o8q(R`}>Qi+eDbZTJOb~$s0+Lu?adX z--ErvEXcBYB#d%tL-n{EccMQv5in?{A^R^1NC>QQZ)&jw7_+C6GZHSw$JYQx{@jN2(F!kxgc|78%R15 zh@7m3xo5gbk!u;IzWoSu(>IX?0qU5gHU=NO%7R^X*_eMa5K3!Lz}aV!Ao;Wv432q; z8#Ci!L#L2K`}BzAowdOcUjoGI&o4ofx^raQ!VRE*s6p&^Y&iA`4vy=tr_fEw3l)TY zSCFy-Nx!&X^!~tAbnpIy(HmmKeP^Udv%y+;t{@F3!`dm*Ptjr4eQ+zr&dU&-g|EbHog1FXTS?B=HDa8> z0y^e^8UHmfjki_UP;T)LE;W>*#XvFWd3^yTFFAVj)@6|JoDcqYYvIeVX*5Drme_~Q zqD3cwMJtW1!gBnKrA)@`cZ*(SRORx-jxN`a+fp6ipl&(MH%#Cl`(!aas@FSjTT*i@e z$A8b!aN=B;vG;}eh@w4jkGzVZDbI-6FDc$PN1aN12qGOnUHF5JO%N#&#J2^#$E*Vz zcxBXjzUO5|#liGFe4p1@dOcrP*zH7t)tiaff2S7~axa!1BxLNizl8Cz$4J4FC|rAN zH@n>Qojf>jpIsN68;;BgPVA~c>$7?={o68lCfUp?*+4L>egY$p&c?T~ed3%@St@y} z7w2?af@sWj7+4;Tlj83J+hqnXcD{l*ku=O49tYoR3WR6>G|mza!z~vCM;b{IyH&4e zn@v}c4^w8s>Qe;}cQ784w-11t!QJe3aRA=8vqz_wb9qgNkbj%_ovbr`ie5r4YxZRm zaiiu1D48IzjFO+Q4&mFOkmyAgjUUD?F1>^SJ!(8^Yzy`*xlPU|Y{0UsmxVpO5lR(Q z9X407@U% z=MH(dNpaV9bd@>`XYYQgSb8*pJUQb8gL6e-{7Vrv>{#+;bxB}RQ<}qSJN^;D@BvezhZ$#9W$V;u^At$Rig9kH0&@9C7#;Wpua#t(51P+ zl%avpZqxt?3s%FO772*guO!aabIJHiCzzyh3EZmAg1(?PJlY(}&rdQym#}fvI&Y1TNBT_T;+`>Qw4@L2 z34WQ?QT*E5Ua`dYd331sNd7lEl&T*eLF?C?rp9;H_jy_PC{@sF0X<)GN)5dyIK5x!)_cBn8$Z^=A-H9 zqx`(r0J=0Yk6Jq0k|kXtm@~qbRz7)2B4$Lu{JnSJ_>>zY;~Jr?)to!EKO{pFx6>w} zyYqHUD~za9rYZ-v(Pk-g`a5ks`dW{pO=eMCs??Fb_?d_~N5{e{V_DwOdW*@tk|rmj z?~z`C6aN0s2VDIs7FNGF0-=R(#j}#C#XjGg;mB=wp&!%*8ad5u%znXd7kY$@RDFpf z=6uIl7Lk0jzak2aG`dRDkuF=2%x?^Gr++HdxkJ)z^fVoW3f-2NwP-Rv{v`!z+e*mk z-fRqADovGiEO4EH8ld%bzVyN%o>(}NKiUZVjrMZxeUg#2b}OmE0SU^sx{_^4!L-cX zoiD?+7~>EM4f0npq)gzHm+atg-lc)up${-@c?CI+BPh7UQD?&#@Vz>f4V^9r3QioX zde!mxxgYGc!%P^ywH=0EOCwUB67czgiLkI)=x_Kr;Vj=)cxe_UyD0i$0*pSrof(uI2N~OY4gkjexVZD&*s667f#%J zN+qh~zh@JJ)hfmrsnUW|lvQl|z&?kRi>J*q;btmvL}{EQ%r-fUj^nGuR*B1S^3Zkc z-yVT&=`>xG_f?5XG;aVUojP>+c8{2yGNN@82hn=XZO|Db%i~W}kc)fE@!d~9Ce>L6 zD^pL~yM=4RBIRQk_t&2k7*Uw?BaXP03p2%rX}H~1;6R_@_~c46aW4uZovL=EUP8E& zTr(uyGY+trln8V*kp>@sEtC+tKOV+exaLR>43I8l>s=O+)V^!5#aHZ&cU5`d z#XqpiWFWoqcnyt`-iFcHYx&zt#dzTJBOgv9wYTo3;<;w&qu%peLEg_`Suk z&y(S0r4dcdJ_GmHk0+4}5{RmQA1v@mfiiCexN>SSxK-U3Mf(rLRR#ljxBFxEM(Y6R z?-zU*GX=Jg<08B>dpX%tHkuS({EEt&0=vISk+xsWXCe1rvE=>p!Rcf*lRa}9v33Hi z54wu`F3hixv>k?*F4i)e(Ys*CLmlF@v;;FB>CtUAb6`Q)Pu%jyACA->0V|Cyu<(#E zuMs@uPBX;l?C$_0^rUFd9S>4(Zv_uLi{X8tG5i{(L!AE&!2U7|_;6=DPMy79q|^En znj*FFuUZ0|5>WUkt!vlAIWtaR zsE)vfYtCdZ8ivqs&r@)gTN?N{l;S1o04e(IOd@zIb34(<^oLY}l#4cr*ZGBYj~8KC zi8k;5eHxwT8^K?dGx!k)(xIW|bk>g$;y0}Uq8F&~BbgJ)pItB5(K%_X&!5I2fugRX1#;t z_{h)c(5y8L*@m@rtlKdA$YGJ-t*0Q)mmkT`Wc0&}#p>WGA$VnEOYoIasxT`_5J|0h zNq9pz++M!F*^0H=<|=8Fp6%b z)$5;vr1f{q4NYQ2u2=AbYa`Un`vH%4e}%)l6}eqkKQ3&)2^-75;%_50XqtZ;mnyZw zW5ZGGz>*}kJj@?-0{yUkdju*qm9WfH-gIN|JO~V&hG$P4z^L9%7U)we_J6hxRv>5%k%YMbT}FPwuEBg+X$%Z-2*nyg|lf|G?V*J$Zq&$uz05ts3~_y z6s@7fzs=l2#m{v3eSxRg{Vj^Eh)kgQ7sJ>e-}6{lmBDk!u$l#63yXU|h;XG9lBk z;-ujEbALrv{mO^0BU=%4l=$&b146p>`F)EeY}D96U}cvGn0A;reH+ZTmtA5h@66y^ z_CGL@drrm-K93}4adg8h@okCaG5P^Td<$~K374EO>}U_JxxBiZUDU5 zp~8<`euz3!sr35OZ%D(9Q9tx2I_nyv;QW*E7=)1&MyM><($MO@!xS| z^H=OI$|5coSA%!n9n22thqX@gdD>*bXXf(}8nTkXQ@ z6J_5lW-;X$SSz;RC|5%vH}MIpCw;+ukK>S+s>=78rm;hU??!Tf9&h+u2*X3mK>EyP z?pV^=^MYr>!F-xxD&fl-lcK;=)7Z`B=j@`ktQ&P}jvNCDOXvMsDU3h!WJ$sPqdM`8pGW`(&WJ`Yda{XvSA8m7tU6jD&NedtrWq9qrta z4il*c|F7Z|$Xxs&cyn?=@3J$u8{~t9)`v;%=1w+2a~_-)X36F8f*Y%%J^Ba?~BmEZrp*>(vGs%vdOS5;w7GZauOyB%;%f#nWCDc z&p7Y2CN1b&4T{aOOu|c+8LC&4p$`&-3`jS6&M<=aW|dezPYPEbKF@3q%ka87Etrs& z&eSG8Wi+S{Z(PnMSql%7MMfj>uauI|b(i7mH>%OViU~NRZ782T`yf;;-GbijKEk`d z1!r9w3a`!oi9feqCt3m9cucwk^*OEr*R$R6#LLl`H9ehl@3#_yaAj=pk0s0@It=d5 zJc$-Qn)Hyi2~Bf;Pwwb+k@tpz!(#h43?CswT^>9_zYG1~PZjXbPG8K^@TM;ottb08 zOVV)-d%5cOB&>b=99u_A(7Wp^aNeQMWa!Rw(2F+EwYnLLoa))N5gizDT91ccOCi6P zY0!jm3Gi2Vm+N#oF<+TmFkHM8S|S`V%eap`xwDg%kr@13*MK*FEaqE3I*3MJKSVTF zUIm4UnXK1ZV2G%jla%>281}*#_ViptU)OVFUZM;)u{sCrNhtGHY-a~ADAV^PBhhAP zEDWt!$<28QF2Apbayu13XVwtD)~1WR3OWa?`%GZ%sg2+?_kvi@`v_jhljKG>P3WKY z*P^8ZZ=k`{!w}~&1#PTPvD=PqP_fjE$2fcQhxZ0U<;P%D2p8NJGp}ID-XfTr^9@U; zB#FWu@4>syCB#LKF{yzTd{bbbDCU^p)^f=}32ia@K06@H8H;f2rbaN(HKrNWA+SKd zSDZC*IcsZpi>nL6V2i&V{TO=@!-qJCAB`zyU)A>ucl|*$^W|~e`sFJ$$jRVs>*+Xu zX(qmF(+5wR2-q-VBc`2Q2X{{Avejn8=(Bzcm@&FV{6R*8&M{lf2mDb%8;xXCN=v0i z4ZE4z>y!9)@@+Ejp%Ijh))R8fuYn27I|*!JmOVm$BmWQBUh@^7KUrh9c#pwLlt7mNPpqJY;^m6B*q94C6!ZMV8)Z+i)V`m@S=b-AN2-(L@;Z32 z%#gm^mJIWqz2MH}3*{q^C8DH}sd!Y}e>|wE6~B84cOVH9oGUs3^tdL(xqq*i+ZF@Q zMoz%J*JtDY$k$AEWji`F3M?m)B9!1&a)0G}a_F!hH5Ey~*n@?jw|E2}bZj8Gb#p!z zrH|s?SvSzd#DpCCFca=yP^1qkk7D(#V2t{Z1}@nev?o#MFn!%2iX4;!jU``U%XmNL zE9_AQWuHO3(Zn|HAI_&<*-Ja+Ls|6nXf)K$#b(#@AkiqvuQ!*#>Mg?F^V>5rY1mgd zxKqpCOVZ3{hsUE`&W@xdDxTCqi140)N{exO;}_z`pHSq+NJs*X-Ir z0+3w1;keQHN=twVTd zJof7FCwb0j&lS1*Z;qiihQP6p1!A+mb^2pt0BN&EF!UYd6gbwua! z*+FCaA^Qy*`9<);cYR{pLrowBv>?Kxjs&RBBYov(K&qmKsO(k;E!}yrwjmy@Z(afg zE=9X8+T)DFEl?G_QJ6b_hF^Rd*DjL7TZJhQofu8~zJ)NS;cY~7#Yy|gPLGMr)ZzTy zU0d9sS_oz5d$4Xc$5}IG@}?^Sd1;3!x=vd{V@8|ty304&rb}m7Lqa^vc$Nrv<%&qw zjN8Z(WO&pOU6`?3kAB&A7So5EWSu4HM6S*slE%r=Mcp2xe~lS77JtBGeMeYq(2nhH z5_t8%VUn6tgV^t)`^A4z6wwZsjdq9(PfOA<-X~yc!V&29aOO1XHV$@7 zL7&+0+3xGY_j9FqYJNURK9dR6DREHh+DlNsMBF5>JQ_BfgwAK> z==x?FEB`ov&36og50S3)#C>I|9#KR>mp;e#;TJ1*DgI&BpI>0fRDGW3AO#+e4?=2f zn|+y;0o-W0fEn-P=)0(mu<)Ka{We&5*G&`{fV&;}tdlzQ{qMo_e#8m*wzU;Y9l}_U zdU3_Q-=;K1B;4;`&I6V9c-*PJpIlg)4b^)XiiUeo(PRhu*&&pEx?%vo2JGRf4hLcJ z<4Cxe^ai)Ne1_|dUFDDUze7uCHU!EYCSN6-c&JD?mzaA|&q8BX>^FwZFR3A4gEv#N zwQ3jQJ1=T=QE-RcpRD7aWSDdh|9Q%J&!*AIQFwmh!`4`V+oRgPPdE67Yp9Jv)UNh>PL2^hEn5GX7q4LqexM6Fm>pj z!b>|2vhKTeNF5JjkgqVi)tJWLzCHuG2L-m`h~sdf@Duy8Rtd+tJRwVe4x(S3n%J7^ zMA+b#$R36EWBjT_tQq?lO-9IKc*;zaC=Vf2wG>p;*OAqi0{HkWNmwM)2u`wN_*XN5 zQ|otvjNbE;9Gu;Xi|SqQQ|nlM-G3Qt$Q}=KBCT0y!%Yl5>dW3rhYC!?Y*34Hr?cYX zVPQ`s2Gp&BD{|WWdto_R!4hb97I@N*EBUy+j&Sj28%W%m0d;ShacRa~3^+TGFH(HV zT(S+Rc~UpL|2~(G{2B(cKD@x`Maoq9f*<6bk7UcE?bzDL`*m}0dL*84mp zS-$)6%cNS^RA5824X)z!nQg>hMF&2Q@F23Ad+@s123j-e5e~=?#~DinE`aI<{34uB zm)5UB!|yHlR7r&^3><17JnPudjc<5dc1CRJF zIvXCLzO6q-7tKRep*Q@bEgbwe%L;>z4z@XCD1?l^AecR*#VPV&EfLqBjP-#+aP+PEBM3Bf$gv&r`O~`+RkHm@PIb%N$Em`vr9nd z)N#0~x19`~tI6Z0sqy)<3&j(c%^~htc_diKU9DQ@;mXAig)NgDSxdF9n!$i5V2l33L{m|A-iNk1j z7r)xn_|Gbx(==|FcW<0vIh^1PcZh(t)>5G54-qxN`UmnH?Cz z=WDNo%r#T!7u(hR`(I_Qacvmi`C&BeG4ukbSEFH<(EFG+=!uYfO{06=TTyw)AR50Q z1wDh5XvoVGXtH<>f7SRL_9+aZ(>+H)MtKSe6NlsC2uYr&TuG#}*Yg)!K7dr?DEM%f zQ+L-?$X+ev!k=AbGI{?XK)o4ARVVzglBL~);<q;?bRUXT!(Wkf0on%HWy71@hZDKKf0-S8t z!1Baap)>Ub>l8Sehu?(akKJ}nLXRk8qZAb9OospV{=}wl7xBq8V>C02h79V=-t8@6 zUpAQ`crold8bjVl_CfM0Pq=@)5Mm?^;N|ow|QjpfuZncVp_g--8Hhx5afM7MSH*$XH`$yrvSy>_c;wp}32v@J#1 zqGG;}3DIbwTe~)_&9(OyLVTe4ga9d9eoAtS& zjtRw@RC@ZX7d`ar9Zc92#FLker&7JYp!MWDymn{^<;G)S1ccDw>`u{K&HwnOs_|UB zP+;?YxB{i(jpb{5iAk^d;JWEbOmi-fLqoepoBO^XQ z*rQwQ4yR>%(%Eh6*WeVEh1b>+zWL2_=;oSep*V$)-WGw=_Gwpc&Fsg(|3tj>#!_&5 z9>o`|t45{abLc(?f1a$T!lgbvAPPr(;pM6*vg1Efd{(v+Zdl6jh@>~*IlzElKa#XFF;e*76|aI66YV9PSb_F@hr=FuoKQr_Y97UUvAk72j3^N#_M?? zi3DrTv`Syhfcivrw*awqEnl8Jb;@(Aa zt!{h+3^;;Q^wUAttqP~E+=qenuGpF}0vEWdf>MAqy?P~%N&e^-c>WK`J(Vty zIsQl7DKiT%RQ-qZKDc6OnKGT{CCz?i8p3~rOJGKsGA3^h!%LDDT=}*Pm-kpet2Tec z!7aDgdkSDC_W`Y*tU+lnckUy1QP_PFZoOm%-6FY@s7$EG=DCgFykQOZmz|4jeZEB9ES+DycH`_$zJLmj0c1&fhM+ zuu6|6<;c)2x|?zD!g3Zk$QKvy|AqVamBVR;Q7mYD5DW-OgZIOR(MwBCqt!hXo-w8y zgU30-`ojIB>(NyhGPsm2a#i5P^&ub<%fO-k7Lz?W)@09r0&nP56NVPli;^zMk)KL) zMIN^`iR%Ovy7u6D_!3O`RPzovcS4R%nZAzNU;YRMh4T>aZGug$igZ$aD!(&f06dak z2PgV|!?7#-MQ&x^Wx>V=fK;c%bsVe&O& z;Fz)SP5I>vHYPQquqTa}6mnlA?$Dsm_D;f22N$q^hsRKvIhMTgn>?LxI*N_c+(Gi& z(}?NBq5PS>9G`5Y!daF*1nDn^N5||caGwKfaaCZs;=}AlgC$jN8v%Pxrix6GFY|Ug zO*}LA8d}#p0K)^PK;2XWj0!!3{oWax&Xno6qi*op*h=t2&f%RiROrx^s_<(20yx`v6f^*#$khP#i0-LfpWC_7)+U=8B!o{RXdEeU)@xd%ix z3GcuG^Pxt6HVm>|PF_rZ01oBJ)Vs`xR=7@Q1NsD3&9~Ex>FS`BbDX$tNf>hr!S7M<-PnOT{A~g{V`;6laJwrTQXEy`#XFZ(E+35G=yE8I&64!Ry->B z9*!>7gsyK_@$8LESmgT<+BV!`PLHf0Eo~eBsA2#cgJ$Anxvf~Uu?IB^t0C1>gPZ&5 z!mgvkpmB=@*PnZq>24Yc#YSqFBRIzNgzw4Vg{xrv$=(W+fDAAnQv};Cjv+l8H8=|_ zfn(b(c#NYZKa=gv7F!I!cA5Q7?LR z3(-~l3bsT3K<2A3SmL!0)_obyzxN+Od7E@J+xi@zg*=7J-GqlryV1a*9#rvK29N{kqh!sjM5i+*J7Z)^N3gyA zj)IT*C~*&%OMk1o@XfB<;g0lY(U-eLWS&V72CL14U7C-@qgtY%z8axXxHB7=pC*3` zN3elri9}IlE}cLhW6Y@%Jkk*k<5m~q@RCNz>DS^O|D2&*aE`&O5Ik?co8;U8G*!8d z%fGJxr`hXK*6AeN+hB|JFkM>py*=O?6mk>#hFHK>eIBCoFy=Fx4)K1g(-^u3iZ zAzhlb#g#*acosgl&cIDf;HRfGq8HaAF}Ztrg?)r*kzycQ^I;mVF1MnWk7`u(*QMaW zGnT0Pu!ViBw#7RK(m+?}wX=6`ndS8!Jn$?5bjK`XGg4YG;7}#3N1+yBDYzTGhF z-DWtI5k)6Y31?2f?t<#+1H`=b926-Xgq^C2a5~u)?;BnRmx*CGCHFV3s~s-zDuQtR zNnhZ>HEfn}zb~rWz?1g4(-~62eR6-az)LxROZ!Yks}tq0YTqYZkva|96$ayM@dZ{f z*N7jrD1sj67m(DPO2=)v3YBjx(d@j?QNN{5#1E2j@z8a+#}p55h9bXqIlbf-F(7;e=Fj*Y>lua+~1T zf7ij#Z7Z^PV`BAQi(Qpy6;Fwe2A>;mNSx$xme|#ent$`iqIbJl&LP3QG1Zeog$MSo zd50;Fro(}A<6u^j@OR#f!t1w+MG@-n*r)1W}fZ#;@O9)d&r9nkE6 z1OGDTDY3p;iyzPJ0=JX{a82-snXX(&RuM@$za$f?8Ux_3$uV5E8BwC6UVNhVHC%5h zCDE#WuwwQn6rUr!H+!3CbWb^M@B1lQynnLzPf#Z4YBWLT^Jo;yTqU}%9;0enBCj$l zKv|O)kiR{Jm0C7pzgLhr-8&i+&)*`7qbGvYyZ~+@WVnaCyM{{+3RyYHGVwpXICL#j zpuz6hWapG_7}(}OQ*_rrZ9z9&O-&cLrB+nkVFdg3{*oyFr4D(suam4yXokn_U$HAD zlnk)+;(0^I*k6u#i}hklnYEj+x@meQqSM+B#d3Zqx!BzWm1QTc1!jMsN&IQo=#?zSRD4 z2^-&2M!sK>r(X4=sp6b8s<9!KdiU^O2##^ItfxTc5^FjfOug8mC-Q;LZb!;AgKPKPMN?xBUx) zQ!i)pQC7JiwNLPlcy{BZV`fw_VkI4JJ(AzPtj>Kdbb{7PQ?g{&5cK)ofJ)!8QERX) zcXrkg4ZW2>e4cv4`M2t#Gh3bbA4v%w_9GBheh&w}VK998Qh`|wiR|?pBYN-BJ%LLk z&%Y15PGa`og;ME9kh<(Yc1UJ1?6o5_BK;gWFJyr0qpyKW({dg>QWH{S(y%Kw4rfk1 z4~6%O(K5&nDuwJjKjSDo<9TSI8-f-YW;AWO8+^DFBR1aF3`v zPr~!3O+^`_sjuHP9I@j%d^jq?OAUjB8FUcs3O)^Ko0j9)&q~m{=mnCipJ4lm)wsQP z61-1wr8l&6=)}?}TytK%hR(YiRgGdA=?;U35ZCg{MpE zVq3W+$j7@dEwleBa-ImY`~zEQ|G7!@uE5l=2|R}HP78f|gMIjM?-izV`wNZ}_n_&W zftcwL3zO~+z6NYUk+dUWW6^9$)3=iNBh&y@Q;C_&@hhE!is z6TaV&XL*6o#C=b~1YWl>YD`=vWX{K8?i5=-=7<```hG>@88hMf?Vseo_s=1E{5x_j z^BKE$r(2w6UxMO+2kG9Yv*E7yLy=&G#efHq^m)cm@!CLpC_Z@tqsVD=tUHbl_y4eG z%M1i}lRC7QDS)hW2>hG4kPdChVhLUzY^c|DR&%uq&xKt<^GE}FGB6q6$H`Ii_C83v zzXjfSO`?k@Z@~MT60vDRHFm}<6Q2_Jcdg&6ajMdK^6b_%kx^j+YOnqbA9@xNgAg6= zIK+{vZcgJqDaVLw$OtMbvk=Y_>>4+Z?iSl)Rd*_TGvf@7 zui48|{Fj3JGfA8_aWlv#{S*y*sl+!Qa3#sB=kcc%2y3@Y##)^|w)y4*P%HIDo7+pk zR#FoWU^Y%~)8$D|H2B#`?sUwbbLjEy8mn1sK{~BG;LzrI_+*JH&gvfuE6pN>uAd?v zo8l;1R@ng6&J25WzM@sN1FjsJiK0d2%xu$KeE(aHXdYE%PQJ@U8`G{3TYYPRGjB$V zRpj90_%Ko#b_4y(^N3H{0=g*kpSXU$B@7hyg{A9raiB^%+}tb7_|hi}dHt0rUw8uh z4<(Vz7;X0cR4Y--_NiEuR)&#!qo|MIC=9L-1Z&}5_{>p~Zy#RFtV?%7RFw^`I%o^l z$Dgw5KSQwWp^)*|;lcg;WguDVH(S_u4l5NG!LA8aFh3v%g2(*=6W?*z*u9XAUibK5lWw}4rs{!E4V zw~+6slbV8cx;EV1WFqWq9DwzFf(IE^^u^bUsM9tJykGqT*$L`sGmPOOjXTid z9tXdUe!>3FO{imkP8>MMmiy?9B`@v0Fi_8g-%Z#FT>-76NN~W-80#c_i~pj$t_ll( zK>4?jCNTzfF{RCu_`tXgI6l{u&YUK&DYBo!3+*V3bI->mlilfr*F-=!NJHwOT=sbD zNu2o|aDQBlm{e|uHy%81B$DO_e^>CnEC@U&OX9H1CQdX<{}vJLYs#>$AfD79$0o% zV9_L-bM>lNw0R{3L(2BT`SXImXVGp98oC5NmFn}@HJ?#E---){Nuquxk%U?*Q}d&; z;Ip>YzADU;teJNJ+%`s5S|eA$%&CRUEcY&Gx824m1t*~@RGw`yS`2;-8gyRiOSUvv2KB18;asiDNYZ5a zq_3wT)6Hqj2DlXz(4og&y)f1nFnwsHgD0 zuTkHN;eQ@N0FLI>;g3M{%@2L93+E}#6qrj5`OkneLO!?{RUZ$bfmwTSUzQTpFA1f3 zYJSjs#fP%vrkqDs5tBjk==n!rmN#4TVCgGxp>#GL5z=cNbpz?|VJ{(x})G~?d6 z9WZ}SlgRqK6pwv@h-P2$rZ1h9O#}#Vfz37?O)W3uh@b_$)nN@B z?`{S&aU>)kdkD6h_rk-wdzd))2vlwvNZV_Q@Ic8C^emdFRmqJh~4W-d1zR|n04XZ6z{UHck2V5dNnyCm)R=^633)F$k^TZr+VDx~?V z7qQn0=ZLqD{4IL2>nI$^ zpUSHgd+@xwB>WdU5QYnLn|O08I^e~7^y+qoi=zhcuJ2#)%j&0aFMI}fe; zq+)@Yd;%tXKY~A=9md!_(%jH!0y>V9g!F1RD9!nTGbheR{W2RqL(`J0gx!TzOAm{` zNePb231z4noI^woiddk);N22a_HalAsSk<;c@H5QXcEcHUPQ1G%MGweZxL)6IS6N} zJ^@YTU!b*h7y7JXVxMhu_<-_8+*frBJg%LB-)YZrY+x7*crlCI4bh~#I`-I~c%X!P z`%6W;{%gSVpUqciHKA+4x zUm5e|vpbolsuJI{{2{+8-V0Bc{$WnP-DyVma46k36SG2Ws7=Waobv1|dTbhmi`_f$ zZ-oM#n_$W%ZQ@wMixYTplOz-^*5s#$Tw$Aw+nK6UA2BLu!ik0=2$&KFJ2StMm42gz ze|sn~{U1YT;!jlio4fN zlM>QoNK{CZs5Gd)^qoJzkKc9fIcKl+zR$BDP{_H2LY?J!R;a6l^6k>>UW*}nP!I*C zekUiO!Pg;A%p6&cL5h41 z!z~;_T!1Wyeh>7c5^>$`L#`*T!(D1(*rer#lj=K&!T4@EbW{_@{&-9#Zb1CBf@czp zl;b8gj^d2d#xXCSrOch&hJ|naVd45Y+zLLgsr*%u+t2q3{npQ6qa?#H?)qHbfkv$t zTua3J?|GkQ&r9;}*Cq0-xtV+yGh^0Md_X%ujVp6g;iM>^!R+&>R!)8qD%|NVh)-H4O+%Xe3i-tm=+!Zu_`~qcM7m*8Wt3Wx%k_+3b zP8Iv}sFuPn%;ficDWg(hfsHk;6I`cToUO6OUIy%T`JnvEP?UO4uzj945$POD%x7F8 zI|J0<(qsi(H{6DrH6d8JzXejV{ZK8?i^eXP!`%1yz~i<^oVP~;SNuN4PX9ShH+N-$ zhqg2B``H1zF8_q1)=f0aJ)LvlJ?MY$brA=(GJ0@D0Q{Td3kCl#6T?cc%V6uF$IfobB!(?bLb-88Bw)D^ zmj8AX99)oG{pv(2+*R;{Lpk>JTlEd%Gd~8ErX*w0<7h}f!{@S8bMec9kxXYu7Gg9- zdDndgZ7+X=;e|Q87b6qM*fbpNScA$ks2}b*BarNPNS6LmM$bc8;GbxUX%A&MY4?7-EiwQK zZ=E48i_eL0$wY@!MEgV?ZsUt1OerG*=R*H07W~oVgdJi7Y`Wwl96e(px1?_)m%OPBEBU?O;TBJjP%0Jp z1nkBf%XzFZjGwU`O{VdSfL{6S2i@jlNNcSt7pU?ZKF<6NqSwZ9_FFfzW+NBw<ii0y6{ z^z5+a%z6HU=*(fPd%OY*qj<;ha~1q0V@0k0&17GA)=-L1D&;1B!N;Q<+0r&CuAbhc zgKaJtG_Zv%oAbwJ^P>Z>@USBsojn1hRMMfrEdsw?>%tQ^N8yK=`*3=C9hv+>iraNL zho&m5gIJ~gJO{{uJ9PYO3;VVT%RPF6qX2lWAbid#_j9K7#(snnd^D zL#op0iTjS;hvPPiAYD}i&HHS*rk#tpy=mo`=&eYr6_3IFuVwIX)?_A19>FdQqgyIn zXl(urF2XE=n-$yz2b%fzOver|>ea_FGP)4tJQ_zxy~bv~Yp%m*aF_13#E4hr@O7yw ztaVd@Dt$>j;Q1Q2NwksH*c$RN)tu>mm zg%_dW-wC{)y#wO4)}vTn4%$e|2%qoFhZk0-s?#5Sq8?saxbg+>Lid>s>zuz+qI4eP z;t>a0ta-4|Ae#(G{yEgY%?fFx4oB#E>3Av;GH=uxb)! z#(gK67Bb*AwF!p*F2@ZkCV+|JOSF@XrI8Dt&^_TD)ag=-z|}56py->5>&3O8tU!dT zR@7vl&2NMKwNP9lEyX!*lILzJg@DuTbTDb3&u%@OL)H15sl@SdZ19R5D&E~srY7Y< z;Wbrm$E{PeMSCBP8l5Z{J!?o<6nP1`L)tui*TF}eFQ>Ajo|MEBO!NNKS+4}2NNG2fvmd*j3Y?qz0$_1 zGNN3W+Em7ERA(QnyQuPxL@vO_i@W-?NO1etRXFo}EA|R@amO|;$0ger0`ghbH?uEu z9VI-gqV+QV+Ry`*?pmBkk0ML^xe5NfoePBxTe#xQC#mh*W`4%hkCRhTn12bm;etjC zty+b8rw)N|f+uIW{1=y<_Zo8Ee}Ij-S=fEg8V}tw#1@r))KDMC=2}>=YFy0Kwz%Ql zsfXZ?sTemYVk1+qaOUP}hT(2&YgWVO#vb3h&sLNyWMd~S65M|OkmzR{Vc6cCOgd1G zGcp>-Ms*fof9q+?dcx1pH_Sjg>6z?(R3~g6ZXxdbq|pCl9Yh?u0Sg9dNv-`#IO%tq zI8P}i*}WR58{&zRjx%lyV92q%{ zB`ve(M%U@Fq1o}65!i{*Dzn*pvkO9_rn`{)z>=LCC?xAe2g!Xc3pSXq!c9=qBiBV9 z;}Yffv4~FyYA&?gP47Pu|3`w;vPpSpfY3^If zbgzN2vIhD*@Z!=w+{e%_B9Nu21{vn(U|4A^dzk+Rr8#TNe_V;PzKF1YJ2=>SXakhZ zDgdjrAFzF12DN&r07C8nSNf%qj&46j#fM%AZ)mJVUE@?@J`$>x>|~i@EL{Tk=i;Rw^cAvi~rc?7j-;{fc=FnoI~+`af6o3A?3|ISR{Ty<(;(S+GB-NZ)d{6&&Pw5LGC`E9Vs ztR7;|>T;(f4q^81yLkWnJ95hH8f1}4g04~t7U}10Z6rFD)kP=6zC$i_p58&GGP((F zzl((HoqvU~W;!h4NGpcl|Ay;czkryyQ$po^(L(E>ICQLRA~A4*PMfd7wmn-7+4s(n zgXdd`_l?z15i<{_=N1sXsOzNlo;;o4W{&@!I$^;41f1Zc!!6sMYhzlx2|r{S!1NO< zpfFj3-FLf*hPFXCH&}sGZJSJjvwdlDf;L3nk;gv$4^*&2he>SCp_%i_XtU`|$Yup_ zOMC#+Cy23o;SV8f*dE?ZE+^+6xqkNb%a1A?`y1{{Y=9FEpqPw!gNUzx%IPd=) z;+;;BUkQ#N*YSq@dlbX}j@NYI%ns}yy_+1$OhCWBMNpaZ9?zuD13Qg-7%^By&#rq& z4bF~-#Lqdj!DJm6I_bfViTh#F#>GtDs7H8a$OpH7-A?8nzCrE;Iit$e1vFv45$rQ{ zho3Xe;7yJ%#`cAS;4Y zcvqX_g1gU96Pc@2KluT@p2N>LC+~!(%69N=j3^X-O(G}l90XBn<9Jr6EUnr2k-qsJ zf&R*0scXO`x|Fl!Zj4<8Qit~A6v=ns>L^DQx2y0@vx(d_VG``>D24bB`tbJDAG|yB zG_CsiAMYX5V7nHm31()h(`gEO1Q&-}`QG7Pdf2iDQjbJJVB=G=YP}v+(O8P6N!5Z4 zw~yoK^4Zn#G399Dw*_}8nZvZoxA4IDD5UNSV=23jf`hm|&l*pGO*(tY`(z3BVc$`H z2jGss_pC#E{6a4t_)G5cPVP+~e`4-~*|a{v0$||?E>64@)=qT8>bg?8xPXAnKp1tu zas=;7oq;I1k)S#N%(pET*PYR!y>)`H64`d{3h~^hMZ3o+ zQYRTzoR@1wVoM{)=pv5m4!%MhHjiPhaw=TuUMoapdzLmc61J#+BA<5tCetE+SLv;C z!p~g;=o3B?>MHqNwJGmIX!XISJ}v6FV<|nDaEyO;R#3SSEp$Yf9A`Wzg$I5;;QdKv zxb(qP4BVqA;9Z(RBM(*P)~JadPsEs;6o9081unM~MIG-gSWv7CYiCVmXQl?*NO`5; zn}GQ^dv7*QpFRPLf9AsXu&<=Pzy=hHCsY5mjpSHn8m3Ff2+Y=VSQfyMEjguR(j#fi zJ`e{{T{%!3uZ_>&g=53TOZa4K4a6CJMB}9fTzOIi23)A8mTF(XfpZrgmZK=IS;jN- z9HCcV9u3Q~@L$foj!b7@di4UsWV$NRE9 zluv$w!_*@NtDoPfdXr`T>HF@3Ktdg(4h(S|hYleMim;7r+gTy|`o17Vl_i$dIk4?=UE%-Svj)-${xQ@qZr95gBw){5Wq&gybmK*O(+4=$9M;9Sa&>_n^ zRk$lUvdoZ+fFZ6JLR&4_d5L9^dLoq!jy%fem1dAJLAM2V-n&70*>9UwvWYmdH3Xe^ z-NbtJ5zJKKKk75-2zESAh1#cW@J*p#u*F}8t>EX(6aD{EpD9J?;uAwB-=2o*rdKd) z^B^ud(~RE|Ct+dX2EIqR9U0Ft82RHW2wgPrv=XC^vE%WV%4uTw(i(3EUWByw3t>aM zC;c%c4h2^?@;-9D8!~X3=05pC>=fn`y2THxBX1Meo85w?d107yED=}l(m{z{QG9Xy zA@$j=h9{(&Nbc!w`eAuHDLea~&c2Rd?>hm%*geB-gE=T^`vC&QRPg8)e&5Y^d@uC$ z3Z&P}gN|SG;q00kYBkNAbgPWOJtl8#dd+U30hvo$&fFo%Gq&KH{^w-(wP&RMdIHQ_ zW(~vI^N6SRD_Fiah+2$m6DCJbB#Cm;@M~HI2-2r8m8^~E({oVh8Fv8(vNFM=;tg0o zapkrym!qE@yusq!Fn%sGz#861W1nLLD&F5n+TU5YSVe;0A9UeFw=X!wvjdXrLm6@B zXW0+#;LM4-_+#NJ2rS(J(^e*-e2Nu3zt>KDSKoj~i#)5U<@UjUe!MGY-#C1EOcHxW zZ4m6RWVDKR!7VI1#ErAzGYWe$ky|tZUcFw4CmIE`u{#Cz)=gwJTDh1u(~Nv5+KL@z zDxfc1#Vm3Z$hwO|aP#m^8XRB;vr7(P`5OLwI?0$ezOKeeIsOQl&ms4g1FJCFf>U

8jr?!> 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 [batch, seq_len, input_size] - rnn_out, _ = self.rnn(self.net(inputs)) # [batch, seq_len, num_directions * hidden_size] + inputs = inputs.permute( + 0, 2, 1 + ) # [batch, input_size, seq_len] -> [batch, seq_len, input_size] + rnn_out, _ = self.rnn( + self.net(inputs) + ) # [batch, seq_len, num_directions * hidden_size] attention_score = self.att_net(rnn_out) # [batch, seq_len, 1] out_att = torch.mul(rnn_out, attention_score) out_att = torch.sum(out_att, dim=1) diff --git a/qlib/contrib/model/pytorch_gats.py b/qlib/contrib/model/pytorch_gats.py old mode 100755 new mode 100644 index 77a02a9b2..226204fe7 --- a/qlib/contrib/model/pytorch_gats.py +++ b/qlib/contrib/model/pytorch_gats.py @@ -19,10 +19,12 @@ import torch.optim as optim from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP +from ...contrib.model.pytorch_lstm import LSTMModel +from ...contrib.model.pytorch_gru import GRUModel -class GAT(Model): - """GAT Model +class GATs(Model): + """GATs Model Parameters ---------- @@ -57,8 +59,8 @@ class GAT(Model): **kwargs ): # Set logger. - self.logger = get_module_logger("GAT") - self.logger.info("GAT pytorch version...") + self.logger = get_module_logger("GATs") + self.logger.info("GATs pytorch version...") # set hyper-parameters. self.d_feat = d_feat @@ -78,7 +80,7 @@ class GAT(Model): self.seed = seed self.logger.info( - "GAT parameters setting:" + "GATs parameters setting:" "\nd_feat : {}" "\nhidden_size : {}" "\nnum_layers : {}" @@ -124,7 +126,9 @@ class GAT(Model): elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.GAT_model.parameters(), lr=self.lr) else: - raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) + raise NotImplementedError( + "optimizer {} is not supported!".format(optimizer) + ) self._fitted = False if self.use_gpu: @@ -149,18 +153,18 @@ class GAT(Model): mask = torch.isfinite(label) - if self.metric == "" or self.metric == "loss": # use loss + if self.metric == "" or self.metric == "loss": return -self.loss_fn(pred[mask], label[mask]) raise ValueError("unknown metric `%s`" % self.metric) def get_daily_inter(self, df, shuffle=False): - # organize the train data into daily inter as daily batches + # organize the train data into daily batches daily_count = df.groupby(level=0).size().values daily_index = np.roll(np.cumsum(daily_count), 1) daily_index[0] = 0 if shuffle: - # shuffle the daily inter data + # shuffle data daily_shuffle = list(zip(daily_index, daily_count)) np.random.shuffle(daily_shuffle) daily_index, daily_count = zip(*daily_shuffle) @@ -172,7 +176,7 @@ class GAT(Model): y_train_values = np.squeeze(y_train.values) self.GAT_model.train() - # organize the train data into daily inter as daily batches + # organize the train data into daily batches daily_index, daily_count = self.get_daily_inter(x_train, shuffle=True) for idx, count in zip(daily_index, daily_count): @@ -203,7 +207,7 @@ class GAT(Model): scores = [] losses = [] - # organize the test data into daily inter as daily batches + # organize the test data into daily batches daily_index, daily_count = self.get_daily_inter(data_x, shuffle=False) for idx, count in zip(daily_index, daily_count): @@ -233,7 +237,9 @@ class GAT(Model): ): df_train, df_valid, df_test = dataset.prepare( - ["train", "valid", "test"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L + ["train", "valid", "test"], + col_set=["feature", "label"], + data_key=DataHandlerLP.DK_L, ) x_train, y_train = df_train["feature"], df_train["label"] @@ -251,17 +257,23 @@ class GAT(Model): if self.with_pretrain: self.logger.info("Loading pretrained model...") if self.base_model == "LSTM": - from ...contrib.model.pytorch_lstm import LSTMModel - pretrained_model = LSTMModel() - pretrained_model.load_state_dict(torch.load("benchmarks/LSTM/model_lstm_csi300.pkl")) - elif self.base_model == "GRU": - from ...contrib.model.pytorch_gru import GRUModel + pretrained_model.load_state_dict( + torch.load("benchmarks/LSTM/model_lstm_csi300.pkl") + ) + elif self.base_model == "GRU": pretrained_model = GRUModel() - pretrained_model.load_state_dict(torch.load("benchmarks/GRU/model_gru_csi300.pkl")) + pretrained_model.load_state_dict( + torch.load("benchmarks/GRU/model_gru_csi300.pkl") + ) + model_dict = self.GAT_model.state_dict() - pretrained_dict = {k: v for k, v in pretrained_model.state_dict().items() if k in model_dict} + pretrained_dict = { + k: v + for k, v in pretrained_model.state_dict().items() + if k in model_dict + } model_dict.update(pretrained_dict) self.GAT_model.load_state_dict(model_dict) self.logger.info("Loading pretrained model Done...") @@ -269,7 +281,6 @@ class GAT(Model): # train self.logger.info("training...") self._fitted = True - # return for step in range(self.n_epochs): self.logger.info("Epoch%d:", step) @@ -310,7 +321,7 @@ class GAT(Model): x_values = x_test.values preds = [] - # organize the data into daily inter as daily batches + # organize the data into daily batches daily_index, daily_count = self.get_daily_inter(x_test, shuffle=False) for idx, count in zip(daily_index, daily_count): @@ -332,7 +343,9 @@ class GAT(Model): class GATModel(nn.Module): - def __init__(self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, base_model="GRU"): + def __init__( + self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, base_model="GRU" + ): super().__init__() if base_model == "GRU": @@ -355,22 +368,29 @@ class GATModel(nn.Module): raise ValueError("unknown base model name `%s`" % base_model) self.hidden_size = hidden_size - self.bn1 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) - self.fc = nn.Linear(hidden_size, hidden_size) - self.bn2 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) + self.d_feat = d_feat + self.transformation = nn.Linear(self.hidden_size, self.hidden_size) + self.a = nn.Parameter(torch.randn(self.hidden_size * 2, 1)) + self.a.requires_grad = True + self.fc = nn.Linear(self.hidden_size, self.hidden_size) self.fc_out = nn.Linear(hidden_size, 1) self.leaky_relu = nn.LeakyReLU() self.softmax = nn.Softmax(dim=1) - self.d_feat = d_feat - def cal_convariance(self, x, y): # the 2nd dimension of x and y are the same - e_x = torch.mean(x, dim=1).reshape(-1, 1) - e_y = torch.mean(y, dim=1).reshape(-1, 1) - e_x_e_y = e_x.mm(torch.t(e_y)) - x_extend = x.reshape(x.shape[0], 1, x.shape[1]).repeat(1, y.shape[0], 1) - y_extend = y.reshape(1, y.shape[0], y.shape[1]).repeat(x.shape[0], 1, 1) - e_xy = torch.mean(x_extend * y_extend, dim=2) - return e_xy - e_x_e_y + def cal_attention(self, x, y): + x = self.transformation(x) + y = self.transformation(y) + + sample_num = x.shape[0] + dim = x.shape[1] + e_x = x.expand(sample_num, sample_num, dim) + e_y = torch.transpose(e_x, 0, 1) + attention_in = torch.cat((e_x, e_y), 2).view(-1, dim * 2) + self.a_t = torch.t(self.a) + attention_out = self.a_t.mm(torch.t(attention_in)).view(sample_num, sample_num) + attention_out = self.leaky_relu(attention_out) + att_weight = self.softmax(attention_out) + return att_weight def forward(self, x): # x: [N, F*T] @@ -378,10 +398,8 @@ class GATModel(nn.Module): x = x.permute(0, 2, 1) # [N, T, F] out, _ = self.rnn(x) hidden = out[:, -1, :] - hidden = self.bn1(hidden) - gamma = self.cal_convariance(hidden, hidden) - output = gamma.mm(hidden) - output = self.fc(output) - output = self.bn2(output) - output = self.leaky_relu(output) - return self.fc_out(output).squeeze() + att_weight = self.cal_attention(hidden, hidden) + hidden = att_weight.mm(hidden) + hidden + hidden = self.fc(hidden) + hidden = self.leaky_relu(hidden) + return self.fc_out(hidden).squeeze() diff --git a/qlib/contrib/model/pytorch_gru.py b/qlib/contrib/model/pytorch_gru.py index 02664b6ac..935716bcc 100755 --- a/qlib/contrib/model/pytorch_gru.py +++ b/qlib/contrib/model/pytorch_gru.py @@ -11,7 +11,12 @@ import pandas as pd import copy from sklearn.metrics import roc_auc_score, mean_squared_error import logging -from ...utils import unpack_archive_with_buffer, save_multiple_parts_file, create_save_path, drop_nan_by_y_index +from ...utils import ( + unpack_archive_with_buffer, + save_multiple_parts_file, + create_save_path, + drop_nan_by_y_index, +) from ...log import get_module_logger, TimeInspector import torch @@ -109,14 +114,19 @@ class GRU(Model): ) self.gru_model = GRUModel( - d_feat=self.d_feat, hidden_size=self.hidden_size, num_layers=self.num_layers, dropout=self.dropout + d_feat=self.d_feat, + hidden_size=self.hidden_size, + num_layers=self.num_layers, + dropout=self.dropout, ) if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.gru_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.gru_model.parameters(), lr=self.lr) else: - raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) + raise NotImplementedError( + "optimizer {} is not supported!".format(optimizer) + ) self._fitted = False if self.use_gpu: @@ -141,7 +151,7 @@ class GRU(Model): mask = torch.isfinite(label) - if self.metric == "" or self.metric == "loss": # use loss + if self.metric == "" or self.metric == "loss": return -self.loss_fn(pred[mask], label[mask]) raise ValueError("unknown metric `%s`" % self.metric) @@ -161,8 +171,12 @@ class GRU(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]).float() - label = torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]).float() + feature = torch.from_numpy( + x_train_values[indices[i : i + self.batch_size]] + ).float() + label = torch.from_numpy( + y_train_values[indices[i : i + self.batch_size]] + ).float() if self.use_gpu: feature = feature.cuda() @@ -194,7 +208,9 @@ class GRU(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy(x_values[indices[i : i + self.batch_size]]).float() + feature = torch.from_numpy( + x_values[indices[i : i + self.batch_size]] + ).float() label = torch.from_numpy(y_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: @@ -219,7 +235,9 @@ class GRU(Model): ): df_train, df_valid, df_test = dataset.prepare( - ["train", "valid", "test"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L + ["train", "valid", "test"], + col_set=["feature", "label"], + data_key=DataHandlerLP.DK_L, ) x_train, y_train = df_train["feature"], df_train["label"] diff --git a/qlib/contrib/model/pytorch_hats.py b/qlib/contrib/model/pytorch_hats.py deleted file mode 100644 index 7affea73c..000000000 --- a/qlib/contrib/model/pytorch_hats.py +++ /dev/null @@ -1,491 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from __future__ import division -from __future__ import print_function - -import os -import numpy as np -import pandas as pd -import copy -from ...utils import create_save_path -from ...log import get_module_logger - -import torch -import torch.nn as nn -import torch.optim as optim - -from ...model.base import Model -from ...data.dataset import DatasetH -from ...data.dataset.handler import DataHandlerLP - - -class HATS(Model): - """HATS Model - - Parameters - ---------- - d_feat : int - input dimension for each time step - metric: str - the evaluate metric used in early stop - optimizer : str - optimizer name - GPU : str - the GPU ID(s) used for training - """ - - def __init__( - self, - d_feat=6, - hidden_size=64, - num_layers=2, - dropout=0.5, - n_epochs=200, - lr=0.01, - metric="", - early_stop=20, - loss="mse", - base_model="GRU", - with_pretrain=True, - optimizer="adam", - GPU="0", - seed=0, - **kwargs - ): - # Set logger. - self.logger = get_module_logger("HATS") - self.logger.info("HATS pytorch version...") - - # set hyper-parameters. - self.d_feat = d_feat - self.hidden_size = hidden_size - self.num_layers = num_layers - self.dropout = dropout - self.n_epochs = n_epochs - self.lr = lr - self.metric = metric - self.early_stop = early_stop - self.optimizer = optimizer.lower() - self.loss = loss - self.base_model = base_model - self.with_pretrain = with_pretrain - self.visible_GPU = GPU - self.use_gpu = torch.cuda.is_available() - self.seed = seed - - self.logger.info( - "HATS parameters setting:" - "\nd_feat : {}" - "\nhidden_size : {}" - "\nnum_layers : {}" - "\ndropout : {}" - "\nn_epochs : {}" - "\nlr : {}" - "\nmetric : {}" - "\nearly_stop : {}" - "\noptimizer : {}" - "\nloss_type : {}" - "\nbase_model : {}" - "\nwith_pretrain : {}" - "\nvisible_GPU : {}" - "\nuse_GPU : {}" - "\nseed : {}".format( - d_feat, - hidden_size, - num_layers, - dropout, - n_epochs, - lr, - metric, - early_stop, - optimizer.lower(), - loss, - base_model, - with_pretrain, - GPU, - self.use_gpu, - seed, - ) - ) - - self.HATS_model = HATSModel( - d_feat=self.d_feat, - hidden_size=self.hidden_size, - num_layers=self.num_layers, - dropout=self.dropout, - base_model=self.base_model, - ) - if optimizer.lower() == "adam": - self.train_optimizer = optim.Adam(self.HATS_model.parameters(), lr=self.lr) - elif optimizer.lower() == "gd": - self.train_optimizer = optim.SGD(self.HATS_model.parameters(), lr=self.lr) - else: - raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) - - self._fitted = False - if self.use_gpu: - self.HATS_model.cuda() - # set the visible GPU - if self.visible_GPU: - os.environ["CUDA_VISIBLE_DEVICES"] = self.visible_GPU - - def mse(self, pred, label): - loss = (pred - label) ** 2 - return torch.mean(loss) - - def loss_fn(self, pred, label): - mask = ~torch.isnan(label) - - if self.loss == "mse": - return self.mse(pred[mask], label[mask]) - - raise ValueError("unknown loss `%s`" % self.loss) - - def metric_fn(self, pred, label): - mask = torch.isfinite(label) - - if self.metric == "" or self.metric == "loss": # use loss - return -self.loss_fn(pred[mask], label[mask]) - - raise ValueError("unknown metric `%s`" % self.metric) - - def get_daily_inter(self, df, shuffle=False): - # organize the train data into daily inter as daily batches - daily_count = df.groupby(level=0).size().values - daily_index = np.roll(np.cumsum(daily_count), 1) - daily_index[0] = 0 - if shuffle: - # shuffle the daily inter data - daily_shuffle = list(zip(daily_index, daily_count)) - np.random.shuffle(daily_shuffle) - daily_index, daily_count = zip(*daily_shuffle) - return daily_index, daily_count - - def train_epoch(self, x_train, y_train): - - x_train_values = x_train.values - y_train_values = np.squeeze(y_train.values) - - self.HATS_model.train() - - # organize the train data into daily inter as daily batches - daily_index, daily_count = self.get_daily_inter(x_train, shuffle=True) - - for idx, count in zip(daily_index, daily_count): - batch = slice(idx, idx + count) - feature = torch.from_numpy(x_train_values[batch]).float() - label = torch.from_numpy(y_train_values[batch]).float() - - if self.use_gpu: - feature = feature.cuda() - label = label.cuda() - - pred = self.HATS_model(feature) - loss = self.loss_fn(pred, label) - - self.train_optimizer.zero_grad() - loss.backward() - torch.nn.utils.clip_grad_value_(self.HATS_model.parameters(), 3.0) - self.train_optimizer.step() - - def test_epoch(self, data_x, data_y): - - # prepare testing data - x_values = data_x.values - y_values = np.squeeze(data_y.values) - - self.HATS_model.eval() - - scores = [] - losses = [] - - # organize the test data into daily inter as daily batches - daily_index, daily_count = self.get_daily_inter(data_x, shuffle=False) - - for idx, count in zip(daily_index, daily_count): - batch = slice(idx, idx + count) - feature = torch.from_numpy(x_values[batch]).float() - label = torch.from_numpy(y_values[batch]).float() - - if self.use_gpu: - feature = feature.cuda() - label = label.cuda() - - pred = self.HATS_model(feature) - loss = self.loss_fn(pred, label) - losses.append(loss.item()) - - score = self.metric_fn(pred, label) - scores.append(score.item()) - - return np.mean(losses), np.mean(scores) - - def fit( - self, - dataset: DatasetH, - evals_result=dict(), - verbose=True, - save_path=None, - ): - - df_train, df_valid, df_test = dataset.prepare( - ["train", "valid", "test"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L - ) - - x_train, y_train = df_train["feature"], df_train["label"] - x_valid, y_valid = df_valid["feature"], df_valid["label"] - - if save_path == None: - save_path = create_save_path(save_path) - stop_steps = 0 - best_score = -np.inf - best_epoch = 0 - evals_result["train"] = [] - evals_result["valid"] = [] - - # load pretrained base_model - if self.with_pretrain: - self.logger.info("Loading pretrained model...") - if self.base_model == "LSTM": - from ...contrib.model.pytorch_lstm import LSTMModel - - pretrained_model = LSTMModel() - pretrained_model.load_state_dict(torch.load("benchmarks/LSTM/model_lstm_csi300.pkl")) - elif self.base_model == "GRU": - from ...contrib.model.pytorch_gru import GRUModel - - pretrained_model = GRUModel() - pretrained_model.load_state_dict(torch.load("benchmarks/GRU/model_gru_csi300.pkl")) - model_dict = self.HATS_model.state_dict() - pretrained_dict = {k: v for k, v in pretrained_model.state_dict().items() if k in model_dict} - model_dict.update(pretrained_dict) - self.HATS_model.load_state_dict(model_dict) - self.logger.info("Loading pretrained model Done...") - - # train - self.logger.info("training...") - self._fitted = True - - for step in range(self.n_epochs): - self.logger.info("Epoch%d:", step) - self.logger.info("training...") - self.train_epoch(x_train, y_train) - self.logger.info("evaluating...") - train_loss, train_score = self.test_epoch(x_train, y_train) - val_loss, val_score = self.test_epoch(x_valid, y_valid) - self.logger.info("train %.6f, valid %.6f" % (train_score, val_score)) - evals_result["train"].append(train_score) - evals_result["valid"].append(val_score) - - if val_score > best_score: - best_score = val_score - stop_steps = 0 - best_epoch = step - best_param = copy.deepcopy(self.HATS_model.state_dict()) - else: - stop_steps += 1 - if stop_steps >= self.early_stop: - self.logger.info("early stop") - break - - self.logger.info("best score: %.6lf @ %d" % (best_score, best_epoch)) - self.HATS_model.load_state_dict(best_param) - torch.save(best_param, save_path) - - if self.use_gpu: - torch.cuda.empty_cache() - - def predict(self, dataset): - if not self._fitted: - raise ValueError("model is not fitted yet!") - - x_test = dataset.prepare("test", col_set="feature") - index = x_test.index - self.HATS_model.eval() - x_values = x_test.values - sample_num = x_values.shape[0] - preds = [] - - # organize the data into daily inter as daily batches - daily_index, daily_count = self.get_daily_inter(x_test, shuffle=False) - - for idx, count in zip(daily_index, daily_count): - batch = slice(idx, idx + count) - x_batch = torch.from_numpy(x_values[batch]).float() - - if self.use_gpu: - x_batch = x_batch.cuda() - - with torch.no_grad(): - if self.use_gpu: - pred = self.HATS_model(x_batch).detach().cpu().numpy() - else: - pred = self.HATS_model(x_batch).detach().numpy() - - preds.append(pred) - - return pd.Series(np.concatenate(preds), index=index) - - -class HATSModel(nn.Module): - def __init__(self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, base_model="GRU"): - super().__init__() - - if base_model == "GRU": - self.model = nn.GRU( - input_size=d_feat, - hidden_size=hidden_size, - num_layers=num_layers, - batch_first=True, - dropout=dropout, - ) - elif base_model == "LSTM": - self.model = nn.LSTM( - input_size=d_feat, - hidden_size=hidden_size, - num_layers=num_layers, - batch_first=True, - dropout=dropout, - ) - else: - raise ValueError("unknown base model name `%s`" % base_model) - - self.hidden_size = hidden_size - self.bn1 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) - self.fc = nn.Linear(hidden_size, hidden_size) - self.bn2 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) - self.fc_out = nn.Linear(hidden_size, 1) - self.leaky_relu = nn.LeakyReLU() - self.softmax = nn.Softmax(dim=1) - self.d_feat = d_feat - - num_head_att = [1] * num_layers - hidden_dim = [hidden_size] * num_layers - dims = [d_feat] + [d * nh for (d, nh) in zip(hidden_dim, num_head_att[:-1])] + [num_head_att[-1]] - in_dims = dims[:-1] - out_dims = [d // nh for (d, nh) in zip(dims[1:], num_head_att)] - self.attn = nn.ModuleList( - [GraphAttention(i, o, nh, dropout) for (i, o, nh) in zip(in_dims, out_dims, num_head_att)] - ) - self.bns = nn.ModuleList([nn.BatchNorm1d(dim) for dim in dims[1:-1]]) - self.dropout = nn.Dropout(dropout) - self.elu = nn.ELU() - - def forward(self, x): - x = x.reshape(len(x), self.d_feat, -1) # [N, F, T] - x = x.permute(0, 2, 1) # [N, T, F] - out, _ = self.model(x) - hidden = out[:, -1, :] - hidden = self.bn1(hidden) - attention = GraphAttention.cal_attention(hidden, hidden) - output = attention.mm(hidden) - output = self.fc(output) - output = self.bn2(output) - output = self.leaky_relu(output) - return self.fc_out(output).squeeze() - - -class GraphAttention(nn.Module): - def __init__(self, input_dim, output_dim, num_heads, dropout=0.5): - - super().__init__() - - """ - Parameters - ---------- - input_dim : int - Dimension of input node features. - output_dim : int - Dimension of output node features. - num_heads : list of ints - Number of attention heads in each hidden layer and output layer. Must be non empty. Note that len(num_heads) = len(hidden_dims)+1. - dropout : float - Dropout rate. Default: 0.5. - """ - - self.input_dim = input_dim - self.output_dim = output_dim - self.num_heads = num_heads - - self.fcs = nn.ModuleList([nn.Linear(input_dim, output_dim) for _ in range(num_heads)]) - self.a = nn.ModuleList([nn.Linear(2 * output_dim, 1) for _ in range(num_heads)]) - - self.dropout = nn.Dropout(dropout) - self.softmax = nn.Softmax(dim=0) - self.leakyrelu = nn.LeakyReLU() - - def forward(self, features, nodes, mappings, rows): - - """ - Parameters - ---------- - features : torch.Tensor - An (n' x input_dim) tensor of input node features. - nodes : list of numpy array - nodes[i] is an array of the nodes in the ith layer of the - computation graph. - mappings : list of dictionary - mappings[i] is a dictionary mappings node v (labelled 0 to |V|-1) - in nodes[i] to its position in nodes[i]. For example, - if nodes[i] = [2,5], then mappings[i][2] = 0 and - mappings[i][5] = 1. - rows : numpy array - rows[i] is an array of neighbors of node i. - Returns - ------- - out : torch.Tensor - An (len(node_layers[-1]) x output_dim) tensor of output node features. - """ - - nprime = features.shape[0] - rows = [np.array([mappings[v] for v in row], dtype=np.int64) for row in rows] - sum_degs = np.hstack(([0], np.cumsum([len(row) for row in rows]))) - mapped_nodes = [mappings[v] for v in nodes] - indices = torch.LongTensor([[v, c] for (v, row) in zip(mapped_nodes, rows) for c in row]).t() - - out = [] - for k in range(self.num_heads): - h = self.fcs[k](features) - - nbr_h = torch.cat(tuple([h[row] for row in rows]), dim=0) - self_h = torch.cat( - tuple([h[mappings[nodes[i]]].repeat(len(row), 1) for (i, row) in enumerate(rows)]), dim=0 - ) - cat_h = torch.cat((self_h, nbr_h), dim=1) - - e = self.leakyrelu(self.a[k](cat_h)) - - alpha = [self.softmax(e[lo:hi]) for (lo, hi) in zip(sum_degs, sum_degs[1:])] - alpha = torch.cat(tuple(alpha), dim=0) - alpha = alpha.squeeze(1) - alpha = self.dropout(alpha) - - adj = torch.sparse.FloatTensor(indices, alpha, torch.Size([nprime, nprime])) - out.append(torch.sparse.mm(adj, h)[mapped_nodes]) - - return out - - @staticmethod - def cal_attention(x, y): - att_x = torch.mean(x, dim=1).reshape(-1, 1) - att_y = torch.mean(y, dim=1).reshape(-1, 1) - att = att_x.mm(torch.t(att_y)) - return ( - torch.mean( - x.reshape(x.shape[0], 1, x.shape[1]).repeat(1, y.shape[0], 1) - * y.reshape(1, y.shape[0], y.shape[1]).repeat(x.shape[0], 1, 1), - dim=2, - ) - - att - ) diff --git a/qlib/contrib/model/pytorch_lstm.py b/qlib/contrib/model/pytorch_lstm.py index f8951509a..1d1c0c986 100755 --- a/qlib/contrib/model/pytorch_lstm.py +++ b/qlib/contrib/model/pytorch_lstm.py @@ -11,7 +11,12 @@ import pandas as pd import copy from sklearn.metrics import roc_auc_score, mean_squared_error import logging -from ...utils import unpack_archive_with_buffer, save_multiple_parts_file, create_save_path, drop_nan_by_y_index +from ...utils import ( + unpack_archive_with_buffer, + save_multiple_parts_file, + create_save_path, + drop_nan_by_y_index, +) from ...log import get_module_logger, TimeInspector import torch @@ -109,14 +114,19 @@ class LSTM(Model): ) self.lstm_model = LSTMModel( - d_feat=self.d_feat, hidden_size=self.hidden_size, num_layers=self.num_layers, dropout=self.dropout + d_feat=self.d_feat, + hidden_size=self.hidden_size, + num_layers=self.num_layers, + dropout=self.dropout, ) if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.lstm_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.lstm_model.parameters(), lr=self.lr) else: - raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) + raise NotImplementedError( + "optimizer {} is not supported!".format(optimizer) + ) self._fitted = False if self.use_gpu: @@ -141,7 +151,7 @@ class LSTM(Model): mask = torch.isfinite(label) - if self.metric == "" or self.metric == "loss": # use loss + if self.metric == "" or self.metric == "loss": return -self.loss_fn(pred[mask], label[mask]) raise ValueError("unknown metric `%s`" % self.metric) @@ -161,8 +171,12 @@ class LSTM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]).float() - label = torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]).float() + feature = torch.from_numpy( + x_train_values[indices[i : i + self.batch_size]] + ).float() + label = torch.from_numpy( + y_train_values[indices[i : i + self.batch_size]] + ).float() if self.use_gpu: feature = feature.cuda() @@ -194,7 +208,9 @@ class LSTM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy(x_values[indices[i : i + self.batch_size]]).float() + feature = torch.from_numpy( + x_values[indices[i : i + self.batch_size]] + ).float() label = torch.from_numpy(y_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: @@ -219,7 +235,9 @@ class LSTM(Model): ): df_train, df_valid, df_test = dataset.prepare( - ["train", "valid", "test"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L + ["train", "valid", "test"], + col_set=["feature", "label"], + data_key=DataHandlerLP.DK_L, ) x_train, y_train = df_train["feature"], df_train["label"] diff --git a/qlib/contrib/model/pytorch_sfm.py b/qlib/contrib/model/pytorch_sfm.py index 1d27f3927..bebc408a8 100644 --- a/qlib/contrib/model/pytorch_sfm.py +++ b/qlib/contrib/model/pytorch_sfm.py @@ -19,7 +19,12 @@ import pandas as pd import copy from sklearn.metrics import roc_auc_score, mean_squared_error import logging -from ...utils import unpack_archive_with_buffer, save_multiple_parts_file, create_save_path, drop_nan_by_y_index +from ...utils import ( + unpack_archive_with_buffer, + save_multiple_parts_file, + create_save_path, + drop_nan_by_y_index, +) from ...log import get_module_logger, TimeInspector import torch @@ -33,7 +38,16 @@ from ...data.dataset.handler import DataHandlerLP class SFM_Model(nn.Module): - def __init__(self, d_feat=6, output_dim=1, freq_dim=10, hidden_size=64, dropout_W=0.0, dropout_U=0.0, device="cpu"): + def __init__( + self, + d_feat=6, + output_dim=1, + freq_dim=10, + hidden_size=64, + dropout_W=0.0, + dropout_U=0.0, + device="cpu", + ): super().__init__() self.input_dim = d_feat @@ -42,30 +56,52 @@ class SFM_Model(nn.Module): self.hidden_dim = hidden_size self.device = device - self.W_i = nn.Parameter(init.xavier_uniform_(torch.empty((self.input_dim, self.hidden_dim)))) - self.U_i = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) + self.W_i = nn.Parameter( + init.xavier_uniform_(torch.empty((self.input_dim, self.hidden_dim))) + ) + self.U_i = nn.Parameter( + init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) + ) self.b_i = nn.Parameter(torch.zeros(self.hidden_dim)) - self.W_ste = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) - self.U_ste = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) + self.W_ste = nn.Parameter( + init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim)) + ) + self.U_ste = nn.Parameter( + init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) + ) self.b_ste = nn.Parameter(torch.ones(self.hidden_dim)) - self.W_fre = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.freq_dim))) - self.U_fre = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.freq_dim))) + self.W_fre = nn.Parameter( + init.xavier_uniform_(torch.empty(self.input_dim, self.freq_dim)) + ) + self.U_fre = nn.Parameter( + init.orthogonal_(torch.empty(self.hidden_dim, self.freq_dim)) + ) self.b_fre = nn.Parameter(torch.ones(self.freq_dim)) - self.W_c = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) - self.U_c = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) + self.W_c = nn.Parameter( + init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim)) + ) + self.U_c = nn.Parameter( + init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) + ) self.b_c = nn.Parameter(torch.zeros(self.hidden_dim)) - self.W_o = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) - self.U_o = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) + self.W_o = nn.Parameter( + init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim)) + ) + self.U_o = nn.Parameter( + init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) + ) self.b_o = nn.Parameter(torch.zeros(self.hidden_dim)) self.U_a = nn.Parameter(init.orthogonal_(torch.empty(self.freq_dim, 1))) self.b_a = nn.Parameter(torch.zeros(self.hidden_dim)) - self.W_p = nn.Parameter(init.xavier_uniform_(torch.empty(self.hidden_dim, self.output_dim))) + self.W_p = nn.Parameter( + init.xavier_uniform_(torch.empty(self.hidden_dim, self.output_dim)) + ) self.b_p = nn.Parameter(torch.zeros(self.output_dim)) self.activation = nn.Tanh() @@ -101,8 +137,12 @@ class SFM_Model(nn.Module): x_o = torch.matmul(x * B_W[0], self.W_o) + self.b_o i = self.inner_activation(x_i + torch.matmul(h_tm1 * B_U[0], self.U_i)) - ste = self.inner_activation(x_ste + torch.matmul(h_tm1 * B_U[0], self.U_ste)) - fre = self.inner_activation(x_fre + torch.matmul(h_tm1 * B_U[0], self.U_fre)) + ste = self.inner_activation( + x_ste + torch.matmul(h_tm1 * B_U[0], self.U_ste) + ) + fre = self.inner_activation( + x_fre + torch.matmul(h_tm1 * B_U[0], self.U_fre) + ) ste = torch.reshape(ste, (-1, self.hidden_dim, 1)) fre = torch.reshape(fre, (-1, 1, self.freq_dim)) @@ -157,7 +197,16 @@ class SFM_Model(nn.Module): init_state_time = torch.tensor(0).to(self.device) - self.states = [init_state_p, init_state_h, init_state_S_re, init_state_S_im, init_state_time, None, None, None] + self.states = [ + init_state_p, + init_state_h, + init_state_S_re, + init_state_S_im, + init_state_time, + None, + None, + None, + ] def get_constants(self, x): constants = [] @@ -282,7 +331,9 @@ class SFM(Model): elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.sfm_model.parameters(), lr=self.lr) else: - raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) + raise NotImplementedError( + "optimizer {} is not supported!".format(optimizer) + ) self._fitted = False self.sfm_model.to(self.device) @@ -305,8 +356,16 @@ class SFM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy(x_values[indices[i : i + self.batch_size]]).float().to(self.device) - label = torch.from_numpy(y_values[indices[i : i + self.batch_size]]).float().to(self.device) + feature = ( + torch.from_numpy(x_values[indices[i : i + self.batch_size]]) + .float() + .to(self.device) + ) + label = ( + torch.from_numpy(y_values[indices[i : i + self.batch_size]]) + .float() + .to(self.device) + ) pred = self.sfm_model(feature) loss = self.loss_fn(pred, label) @@ -332,8 +391,16 @@ class SFM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]).float().to(self.device) - label = torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]).float().to(self.device) + feature = ( + torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]) + .float() + .to(self.device) + ) + label = ( + torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]) + .float() + .to(self.device) + ) pred = self.sfm_model(feature) loss = self.loss_fn(pred, label) @@ -352,7 +419,9 @@ class SFM(Model): ): df_train, df_valid = dataset.prepare( - ["train", "valid"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L + ["train", "valid"], + col_set=["feature", "label"], + data_key=DataHandlerLP.DK_L, ) x_train, y_train = df_train["feature"], df_train["label"] x_valid, y_valid = df_valid["feature"], df_valid["label"] @@ -409,7 +478,7 @@ class SFM(Model): mask = torch.isfinite(label) - if self.metric == "" or self.metric == "loss": # use loss + if self.metric == "" or self.metric == "loss": return -self.loss_fn(pred[mask], label[mask]) raise ValueError("unknown metric `%s`" % self.metric) diff --git a/qlib/contrib/model/tabnet.py b/qlib/contrib/model/tabnet.py deleted file mode 100644 index bc13d1f62..000000000 --- a/qlib/contrib/model/tabnet.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import numpy as np -import pandas as pd -from pytorch_tabnet.tab_model import TabNetRegressor - -from ...model.base import Model -from ...data.dataset import DatasetH -from ...data.dataset.handler import DataHandlerLP - - -class TabNetModel(Model): - """TabNetModel Model""" - - def __init__( - self, - n_d, - n_a, - n_steps, - gamma, - n_independent, - n_shared, - seed, - momentum, - lambda_sparse, - optimizer_params, - **kwargs - ): - self.model = None - - self.n_d = n_d - self.n_a = n_a - self.n_steps = n_steps - self.gamma = gamma - self.n_independent = n_independent - self.n_shared = n_shared - self.seed = seed - self.momentum = momentum - self.lambda_sparse = lambda_sparse - self.optimizer_params = optimizer_params - - def fit( - self, - dataset: DatasetH, - n_d=8, - n_a=8, - n_steps=3, - gamma=1.3, - n_independent=2, - n_shared=2, - seed=0, - momentum=0.02, - lambda_sparse=1e-3, - optimizer_params={"lr": 2e-3}, - **kwargs - ): - - df_train, df_valid = dataset.prepare( - ["train", "valid"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L - ) - x_train, y_train = df_train["feature"].values, df_train["label"].values * 100 - x_valid, y_valid = df_valid["feature"].values, df_valid["label"].values * 100 - - self.model = TabNetRegressor( - n_d=self.n_d, - n_a=self.n_a, - n_steps=self.n_steps, - gamma=self.gamma, - n_independent=self.n_independent, - n_shared=self.n_shared, - seed=self.seed, - momentum=self.momentum, - lambda_sparse=self.lambda_sparse, - optimizer_params=self.optimizer_params, - **kwargs - ) - self.model.fit(x_train, y_train, eval_set=[(x_valid, y_valid)]) - - def predict(self, dataset): - if self.model is None: - raise ValueError("model is not fitted yet!") - x_test = dataset.prepare("test", col_set="feature") - test_pred = self.model.predict(x_test.values) - return pd.Series(test_pred.reshape([-1]), index=x_test.index) diff --git a/qlib/contrib/model/xgboost.py b/qlib/contrib/model/xgboost.py index 039fd2c80..32d631189 100755 --- a/qlib/contrib/model/xgboost.py +++ b/qlib/contrib/model/xgboost.py @@ -38,14 +38,18 @@ class XGBModel(Model): ): df_train, df_valid = dataset.prepare( - ["train", "valid"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L + ["train", "valid"], + col_set=["feature", "label"], + data_key=DataHandlerLP.DK_L, ) x_train, y_train = df_train["feature"], df_train["label"] x_valid, y_valid = df_valid["feature"], df_valid["label"] # Lightgbm need 1D array as its label if y_train.values.ndim == 2 and y_train.values.shape[1] == 1: - y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze(y_valid.values) + y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze( + y_valid.values + ) else: raise ValueError("XGBoost doesn't support multi-label training") @@ -68,4 +72,6 @@ class XGBModel(Model): if self.model is None: raise ValueError("model is not fitted yet!") x_test = dataset.prepare("test", col_set="feature") - return pd.Series(self.model.predict(xgb.DMatrix(x_test.values)), index=x_test.index) + return pd.Series( + self.model.predict(xgb.DMatrix(x_test.values)), index=x_test.index + ) From b89c191e6f37443142f05a32a243ea61cd6c3de5 Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Fri, 27 Nov 2020 22:31:50 +0800 Subject: [PATCH 16/30] Delete workflow code for testing baseline. --- examples/workflow_by_code_alstm.py | 138 ------------------------ examples/workflow_by_code_gats.py | 140 ------------------------ examples/workflow_by_code_gru.py | 144 ------------------------- examples/workflow_by_code_hats.py | 136 ------------------------ examples/workflow_by_code_lstm.py | 144 ------------------------- examples/workflow_by_code_sfm.py | 158 ---------------------------- examples/workflow_by_code_tabnet.py | 142 ------------------------- 7 files changed, 1002 deletions(-) delete mode 100644 examples/workflow_by_code_alstm.py delete mode 100644 examples/workflow_by_code_gats.py delete mode 100644 examples/workflow_by_code_gru.py delete mode 100644 examples/workflow_by_code_hats.py delete mode 100644 examples/workflow_by_code_lstm.py delete mode 100644 examples/workflow_by_code_sfm.py delete mode 100644 examples/workflow_by_code_tabnet.py diff --git a/examples/workflow_by_code_alstm.py b/examples/workflow_by_code_alstm.py deleted file mode 100644 index 8fd9e3565..000000000 --- a/examples/workflow_by_code_alstm.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys -from pathlib import Path - -import qlib -import pandas as pd -from qlib.config import REG_CN -from qlib.contrib.strategy.strategy import TopkDropoutStrategy -from qlib.contrib.evaluate import ( - backtest as normal_backtest, - risk_analysis, -) -from qlib.utils import exists_qlib_data -from qlib.utils import init_instance_by_config - -if __name__ == "__main__": - - # use default data - provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir - if not exists_qlib_data(provider_uri): - print(f"Qlib data is not found in {provider_uri}") - sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) - from get_data import GetData - - GetData().qlib_data(target_dir=provider_uri, region=REG_CN) - - qlib.init(provider_uri=provider_uri, region=REG_CN) - - MARKET = "csi300" - BENCHMARK = "SH000300" - - ################################### - # train model - ################################### - DATA_HANDLER_CONFIG = { - "start_time": "2008-01-01", - "end_time": "2020-08-01", - "fit_start_time": "2008-01-01", - "fit_end_time": "2014-12-31", - "instruments": MARKET, - } - - TRAINER_CONFIG = { - "train_start_time": "2008-01-01", - "train_end_time": "2014-12-31", - "validate_start_time": "2015-01-01", - "validate_end_time": "2016-12-31", - "test_start_time": "2017-01-01", - "test_end_time": "2020-08-01", - } - - task = { - "model": { - "class": "ALSTM", - "module_path": "qlib.contrib.model.pytorch_alstm", - "kwargs": { - "d_feat": 6, - "hidden_size": 64, - "num_layers": 2, - "dropout": 0.0, - "n_epochs": 200, - "lr": 1e-3, - "early_stop": 20, - "batch_size": 800, - "metric": "IC", - "loss": "mse", - "seed": 0, - "GPU": "0", - "rnn_type": "GRU", - }, - }, - "dataset": { - "class": "DatasetH", - "module_path": "qlib.data.dataset", - "kwargs": { - "handler": { - "class": "ALPHA360_Denoise", - "module_path": "qlib.contrib.data.handler", - "kwargs": DATA_HANDLER_CONFIG, - }, - "segments": { - "train": ("2008-01-01", "2014-12-31"), - "valid": ("2015-01-01", "2016-12-31"), - "test": ("2017-01-01", "2020-08-01"), - }, - }, - } - # You shoud record the data in specific sequence - # "record": ['SignalRecord', 'SigAnaRecord', 'PortAnaRecord'], - } - - model = init_instance_by_config(task["model"]) - dataset = init_instance_by_config(task["dataset"]) - model.fit(dataset) - - pred_score = model.predict(dataset) - - # save pred_score to file - pred_score_path = Path("~/tmp/qlib/pred_score.pkl").expanduser() - pred_score_path.parent.mkdir(exist_ok=True, parents=True) - pred_score.to_pickle(pred_score_path) - - ################################### - # backtest - ################################### - STRATEGY_CONFIG = { - "topk": 50, - "n_drop": 5, - } - BACKTEST_CONFIG = { - "verbose": False, - "limit_threshold": 0.095, - "account": 100000000, - "benchmark": BENCHMARK, - "deal_price": "close", - "open_cost": 0.0005, - "close_cost": 0.0015, - "min_cost": 5, - } - - # use default strategy - # custom Strategy, refer to: TODO: Strategy API url - strategy = TopkDropoutStrategy(**STRATEGY_CONFIG) - report_normal, positions_normal = normal_backtest(pred_score, strategy=strategy, **BACKTEST_CONFIG) - - ################################### - # analyze - # If need a more detailed analysis, refer to: examples/train_and_bakctest.ipynb - ################################### - analysis = dict() - analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"]) - analysis["excess_return_with_cost"] = risk_analysis( - report_normal["return"] - report_normal["bench"] - report_normal["cost"] - ) - analysis_df = pd.concat(analysis) # type: pd.DataFrame - print(analysis_df) diff --git a/examples/workflow_by_code_gats.py b/examples/workflow_by_code_gats.py deleted file mode 100644 index 20f3ae552..000000000 --- a/examples/workflow_by_code_gats.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys -from pathlib import Path - -import qlib -import pandas as pd -from qlib.config import REG_CN - -from qlib.contrib.strategy.strategy import TopkDropoutStrategy -from qlib.contrib.evaluate import ( - backtest as normal_backtest, - risk_analysis, -) -from qlib.utils import exists_qlib_data -from qlib.utils import init_instance_by_config - - -if __name__ == "__main__": - - # use default data - provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir - if not exists_qlib_data(provider_uri): - print(f"Qlib data is not found in {provider_uri}") - sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) - from get_data import GetData - - GetData().qlib_data(target_dir=provider_uri, region=REG_CN) - - qlib.init(provider_uri=provider_uri, region=REG_CN) - - MARKET = "csi300" - BENCHMARK = "SH000300" - - ################################### - # train model - ################################### - DATA_HANDLER_CONFIG = { - "start_time": "2008-01-01", - "end_time": "2020-08-01", - "fit_start_time": "2008-01-01", - "fit_end_time": "2014-12-31", - "instruments": MARKET, - } - - TRAINER_CONFIG = { - "train_start_time": "2008-01-01", - "train_end_time": "2014-12-31", - "validate_start_time": "2015-01-01", - "validate_end_time": "2016-12-31", - "test_start_time": "2017-01-01", - "test_end_time": "2020-08-01", - } - - task = { - "model": { - "class": "GAT", - "module_path": "qlib.contrib.model.pytorch_gats", - "kwargs": { - "d_feat": 6, - "hidden_size": 64, - "num_layers": 2, - "dropout": 0.7, - "n_epochs": 200, - "lr": 1e-4, - "early_stop": 20, - "metric": "loss", - "loss": "mse", - "base_model": "LSTM", - "with_pretrain": True, - "seed": 0, - "GPU": "0", - }, - }, - "dataset": { - "class": "DatasetH", - "module_path": "qlib.data.dataset", - "kwargs": { - "handler": { - "class": "ALPHA360_Denoise", - "module_path": "qlib.contrib.data.handler", - "kwargs": DATA_HANDLER_CONFIG, - }, - "segments": { - "train": ("2008-01-01", "2014-12-31"), - "valid": ("2015-01-01", "2016-12-31"), - "test": ("2017-01-01", "2020-08-01"), - }, - }, - } - # You shoud record the data in specific sequence - # "record": ['SignalRecord', 'SigAnaRecord', 'PortAnaRecord'], - } - - model = init_instance_by_config(task["model"]) - dataset = init_instance_by_config(task["dataset"]) - model.fit(dataset) - - pred_score = model.predict(dataset) - - # save pred_score to file - pred_score_path = Path("~/tmp/qlib/pred_score.pkl").expanduser() - pred_score_path.parent.mkdir(exist_ok=True, parents=True) - pred_score.to_pickle(pred_score_path) - - ################################### - # backtest - ################################### - STRATEGY_CONFIG = { - "topk": 50, - "n_drop": 5, - } - BACKTEST_CONFIG = { - "verbose": False, - "limit_threshold": 0.095, - "account": 100000000, - "benchmark": BENCHMARK, - "deal_price": "close", - "open_cost": 0.0005, - "close_cost": 0.0015, - "min_cost": 5, - } - - # use default strategy - # custom Strategy, refer to: TODO: Strategy API url - strategy = TopkDropoutStrategy(**STRATEGY_CONFIG) - report_normal, positions_normal = normal_backtest(pred_score, strategy=strategy, **BACKTEST_CONFIG) - - ################################### - # analyze - # If need a more detailed analysis, refer to: examples/train_and_bakctest.ipynb - ################################### - analysis = dict() - analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"]) - analysis["excess_return_with_cost"] = risk_analysis( - report_normal["return"] - report_normal["bench"] - report_normal["cost"] - ) - analysis_df = pd.concat(analysis) # type: pd.DataFrame - print(analysis_df) diff --git a/examples/workflow_by_code_gru.py b/examples/workflow_by_code_gru.py deleted file mode 100644 index dece520d1..000000000 --- a/examples/workflow_by_code_gru.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys -from pathlib import Path - -import qlib -import pandas as pd -from qlib.config import REG_CN -from qlib.contrib.model.pytorch_gru import GRU -from qlib.contrib.data.handler import ALPHA360_Denoise -from qlib.contrib.strategy.strategy import TopkDropoutStrategy -from qlib.contrib.evaluate import ( - backtest as normal_backtest, - risk_analysis, -) -from qlib.utils import exists_qlib_data - -# from qlib.model.learner import train_model -from qlib.utils import init_instance_by_config - -import pickle - -if __name__ == "__main__": - - # use default data - provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir - if not exists_qlib_data(provider_uri): - print(f"Qlib data is not found in {provider_uri}") - sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) - from get_data import GetData - - GetData().qlib_data(target_dir=provider_uri, region=REG_CN) - - qlib.init(provider_uri=provider_uri, region=REG_CN) - - MARKET = "csi300" - BENCHMARK = "SH000300" - - ################################### - # train model - ################################### - DATA_HANDLER_CONFIG = { - "start_time": "2008-01-01", - "end_time": "2020-08-01", - "fit_start_time": "2008-01-01", - "fit_end_time": "2014-12-31", - "instruments": MARKET, - } - - TRAINER_CONFIG = { - "train_start_time": "2008-01-01", - "train_end_time": "2014-12-31", - "validate_start_time": "2015-01-01", - "validate_end_time": "2016-12-31", - "test_start_time": "2017-01-01", - "test_end_time": "2020-08-01", - } - - task = { - "model": { - "class": "GRU", - "module_path": "qlib.contrib.model.pytorch_gru", - "kwargs": { - "d_feat": 6, - "hidden_size": 64, - "num_layers": 2, - "dropout": 0.0, - "n_epochs": 200, - "lr": 1e-3, - "early_stop": 20, - "batch_size": 800, - "metric": "loss", - "loss": "mse", - "seed": 0, - "GPU": 0, - }, - }, - "dataset": { - "class": "DatasetH", - "module_path": "qlib.data.dataset", - "kwargs": { - "handler": { - "class": "ALPHA360_Denoise", - "module_path": "qlib.contrib.data.handler", - "kwargs": DATA_HANDLER_CONFIG, - }, - "segments": { - "train": ("2008-01-01", "2014-12-31"), - "valid": ("2015-01-01", "2016-12-31"), - "test": ("2017-01-01", "2020-08-01"), - }, - }, - } - # You shoud record the data in specific sequence - # "record": ['SignalRecord', 'SigAnaRecord', 'PortAnaRecord'], - } - - # model = train_model(task) - model = init_instance_by_config(task["model"]) - dataset = init_instance_by_config(task["dataset"]) - model.fit(dataset) - - pred_score = model.predict(dataset) - - # save pred_score to file - pred_score_path = Path("~/tmp/qlib/pred_score.pkl").expanduser() - pred_score_path.parent.mkdir(exist_ok=True, parents=True) - pred_score.to_pickle(pred_score_path) - - ################################### - # backtest - ################################### - STRATEGY_CONFIG = { - "topk": 50, - "n_drop": 5, - } - BACKTEST_CONFIG = { - "verbose": False, - "limit_threshold": 0.095, - "account": 100000000, - "benchmark": BENCHMARK, - "deal_price": "close", - "open_cost": 0.0005, - "close_cost": 0.0015, - "min_cost": 5, - } - - # use default strategy - # custom Strategy, refer to: TODO: Strategy API url - strategy = TopkDropoutStrategy(**STRATEGY_CONFIG) - report_normal, positions_normal = normal_backtest(pred_score, strategy=strategy, **BACKTEST_CONFIG) - - ################################### - # analyze - # If need a more detailed analysis, refer to: examples/train_and_bakctest.ipynb - ################################### - analysis = dict() - analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"]) - analysis["excess_return_with_cost"] = risk_analysis( - report_normal["return"] - report_normal["bench"] - report_normal["cost"] - ) - analysis_df = pd.concat(analysis) # type: pd.DataFrame - print(analysis_df) diff --git a/examples/workflow_by_code_hats.py b/examples/workflow_by_code_hats.py deleted file mode 100644 index 64bc860b4..000000000 --- a/examples/workflow_by_code_hats.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys -from pathlib import Path -import qlib -import pandas as pd -from qlib.config import REG_CN -from qlib.contrib.strategy.strategy import TopkDropoutStrategy -from qlib.contrib.evaluate import ( - backtest as normal_backtest, - risk_analysis, -) -from qlib.utils import exists_qlib_data -from qlib.utils import init_instance_by_config - -if __name__ == "__main__": - - # use default data - provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir - if not exists_qlib_data(provider_uri): - print(f"Qlib data is not found in {provider_uri}") - sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) - from get_data import GetData - - GetData().qlib_data(target_dir=provider_uri, region=REG_CN) - - qlib.init(provider_uri=provider_uri, region=REG_CN) - - MARKET = "csi300" - BENCHMARK = "SH000300" - - ################################### - # train model - ################################### - DATA_HANDLER_CONFIG = { - "start_time": "2008-01-01", - "end_time": "2020-08-01", - "fit_start_time": "2008-01-01", - "fit_end_time": "2014-12-31", - "instruments": MARKET, - } - - TRAINER_CONFIG = { - "train_start_time": "2008-01-01", - "train_end_time": "2014-12-31", - "validate_start_time": "2015-01-01", - "validate_end_time": "2016-12-31", - "test_start_time": "2017-01-01", - "test_end_time": "2020-08-01", - } - - task = { - "model": { - "class": "HATS", - "module_path": "qlib.contrib.model.pytorch_hats", - "kwargs": { - "d_feat": 6, - "hidden_size": 64, - "num_layers": 2, - "dropout": 0.7, - "n_epochs": 200, - "lr": 1e-4, - "early_stop": 20, - "metric": "loss", - "loss": "mse", - "base_model": "LSTM", - "seed": 0, - "GPU": "2", - }, - }, - "dataset": { - "class": "DatasetH", - "module_path": "qlib.data.dataset", - "kwargs": { - "handler": { - "class": "ALPHA360_Denoise", - "module_path": "qlib.contrib.data.handler", - "kwargs": DATA_HANDLER_CONFIG, - }, - "segments": { - "train": ("2008-01-01", "2014-12-31"), - "valid": ("2015-01-01", "2016-12-31"), - "test": ("2017-01-01", "2020-08-01"), - }, - }, - } - # You shoud record the data in specific sequence - # "record": ['SignalRecord', 'SigAnaRecord', 'PortAnaRecord'], - } - - model = init_instance_by_config(task["model"]) - dataset = init_instance_by_config(task["dataset"]) - model.fit(dataset, save_path="benchmarks/HATS/model_hat.pkl") - - pred_score = model.predict(dataset) - - # save pred_score to file - pred_score_path = Path("~/tmp/qlib/pred_score.pkl").expanduser() - pred_score_path.parent.mkdir(exist_ok=True, parents=True) - pred_score.to_pickle(pred_score_path) - - ################################### - # backtest - ################################### - STRATEGY_CONFIG = { - "topk": 50, - "n_drop": 5, - } - BACKTEST_CONFIG = { - "verbose": False, - "limit_threshold": 0.095, - "account": 100000000, - "benchmark": BENCHMARK, - "deal_price": "close", - "open_cost": 0.0005, - "close_cost": 0.0015, - "min_cost": 5, - } - - # use default strategy - # custom Strategy, refer to: TODO: Strategy API url - strategy = TopkDropoutStrategy(**STRATEGY_CONFIG) - report_normal, positions_normal = normal_backtest(pred_score, strategy=strategy, **BACKTEST_CONFIG) - - ################################### - # analyze - # If need a more detailed analysis, refer to: examples/train_and_bakctest.ipynb - ################################### - analysis = dict() - analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"]) - analysis["excess_return_with_cost"] = risk_analysis( - report_normal["return"] - report_normal["bench"] - report_normal["cost"] - ) - analysis_df = pd.concat(analysis) # type: pd.DataFrame - print(analysis_df) diff --git a/examples/workflow_by_code_lstm.py b/examples/workflow_by_code_lstm.py deleted file mode 100644 index ee50c9aff..000000000 --- a/examples/workflow_by_code_lstm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys -from pathlib import Path - -import qlib -import pandas as pd -from qlib.config import REG_CN -from qlib.contrib.model.pytorch_lstm import LSTM -from qlib.contrib.data.handler import ALPHA360_Denoise -from qlib.contrib.strategy.strategy import TopkDropoutStrategy -from qlib.contrib.evaluate import ( - backtest as normal_backtest, - risk_analysis, -) -from qlib.utils import exists_qlib_data - -# from qlib.model.learner import train_model -from qlib.utils import init_instance_by_config - -import pickle - -if __name__ == "__main__": - - # use default data - provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir - if not exists_qlib_data(provider_uri): - print(f"Qlib data is not found in {provider_uri}") - sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) - from get_data import GetData - - GetData().qlib_data(target_dir=provider_uri, region=REG_CN) - - qlib.init(provider_uri=provider_uri, region=REG_CN) - - MARKET = "csi300" - BENCHMARK = "SH000300" - - ################################### - # train model - ################################### - DATA_HANDLER_CONFIG = { - "start_time": "2008-01-01", - "end_time": "2020-08-01", - "fit_start_time": "2008-01-01", - "fit_end_time": "2014-12-31", - "instruments": MARKET, - } - - TRAINER_CONFIG = { - "train_start_time": "2008-01-01", - "train_end_time": "2014-12-31", - "validate_start_time": "2015-01-01", - "validate_end_time": "2016-12-31", - "test_start_time": "2017-01-01", - "test_end_time": "2020-08-01", - } - - task = { - "model": { - "class": "LSTM", - "module_path": "qlib.contrib.model.pytorch_lstm", - "kwargs": { - "d_feat": 6, - "hidden_size": 64, - "num_layers": 2, - "dropout": 0.0, - "n_epochs": 200, - "lr": 1e-3, - "early_stop": 20, - "batch_size": 800, - "metric": "IC", - "loss": "mse", - "seed": 0, - "GPU": 0, - }, - }, - "dataset": { - "class": "DatasetH", - "module_path": "qlib.data.dataset", - "kwargs": { - "handler": { - "class": "ALPHA360_Denoise", - "module_path": "qlib.contrib.data.handler", - "kwargs": DATA_HANDLER_CONFIG, - }, - "segments": { - "train": ("2008-01-01", "2014-12-31"), - "valid": ("2015-01-01", "2016-12-31"), - "test": ("2017-01-01", "2020-08-01"), - }, - }, - } - # You shoud record the data in specific sequence - # "record": ['SignalRecord', 'SigAnaRecord', 'PortAnaRecord'], - } - - # model = train_model(task) - model = init_instance_by_config(task["model"]) - dataset = init_instance_by_config(task["dataset"]) - model.fit(dataset) - - pred_score = model.predict(dataset) - - # save pred_score to file - pred_score_path = Path("~/tmp/qlib/pred_score.pkl").expanduser() - pred_score_path.parent.mkdir(exist_ok=True, parents=True) - pred_score.to_pickle(pred_score_path) - - ################################### - # backtest - ################################### - STRATEGY_CONFIG = { - "topk": 50, - "n_drop": 5, - } - BACKTEST_CONFIG = { - "verbose": False, - "limit_threshold": 0.095, - "account": 100000000, - "benchmark": BENCHMARK, - "deal_price": "close", - "open_cost": 0.0005, - "close_cost": 0.0015, - "min_cost": 5, - } - - # use default strategy - # custom Strategy, refer to: TODO: Strategy API url - strategy = TopkDropoutStrategy(**STRATEGY_CONFIG) - report_normal, positions_normal = normal_backtest(pred_score, strategy=strategy, **BACKTEST_CONFIG) - - ################################### - # analyze - # If need a more detailed analysis, refer to: examples/train_and_bakctest.ipynb - ################################### - analysis = dict() - analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"]) - analysis["excess_return_with_cost"] = risk_analysis( - report_normal["return"] - report_normal["bench"] - report_normal["cost"] - ) - analysis_df = pd.concat(analysis) # type: pd.DataFrame - print(analysis_df) diff --git a/examples/workflow_by_code_sfm.py b/examples/workflow_by_code_sfm.py deleted file mode 100644 index 5bd91ded8..000000000 --- a/examples/workflow_by_code_sfm.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -from pathlib import Path - -import qlib -import pandas as pd -from qlib.config import REG_CN -from qlib.contrib.model.pytorch_gru import GRU -from qlib.contrib.data.handler import ALPHA360_Denoise -from qlib.contrib.strategy.strategy import TopkDropoutStrategy -from qlib.contrib.evaluate import ( - backtest as normal_backtest, - risk_analysis, -) -from qlib.utils import exists_qlib_data -from qlib.utils import init_instance_by_config - -import pickle - -if __name__ == "__main__": - - # use default data - provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir - if not exists_qlib_data(provider_uri): - print(f"Qlib data is not found in {provider_uri}") - sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) - from get_data import GetData - - GetData().qlib_data(target_dir=provider_uri, region=REG_CN) - - qlib.init(provider_uri=provider_uri, region=REG_CN) - - MARKET = "csi300" - BENCHMARK = "SH000300" - - ################################### - # train model - ################################### - DATA_HANDLER_CONFIG = { - "start_time": "2008-01-01", - "end_time": "2020-08-01", - "fit_start_time": "2008-01-01", - "fit_end_time": "2014-12-31", - "instruments": MARKET, - } - - TRAINER_CONFIG = { - "train_start_time": "2008-01-01", - "train_end_time": "2014-12-31", - "validate_start_time": "2015-01-01", - "validate_end_time": "2016-12-31", - "test_start_time": "2017-01-01", - "test_end_time": "2020-08-01", - } - - task = { - "model": { - "class": "SFM", - "module_path": "qlib.contrib.model.pytorch_sfm", - "kwargs": { - "d_feat": 6, - "hidden_size": 64, - "output_dim": 32, - "freq_dim": 25, - "dropout_W": 0.5, - "dropout_U": 0.5, - "n_epochs": 15, - "lr": 1e-3, - "metric": "", - "batch_size": 1600, - "early_stop": 20, - "eval_steps": 5, - "loss": "mse", - "lr_decay": 0.96, - "lr_decay_steps": 100, - "optimizer": "adam", - "GPU": 3, - "seed": 710, - }, - }, - "dataset": { - "class": "DatasetH", - "module_path": "qlib.data.dataset", - "kwargs": { - "handler": { - "class": "ALPHA360_Denoise", - "module_path": "qlib.contrib.data.handler", - "kwargs": DATA_HANDLER_CONFIG, - }, - "segments": { - "train": ("2008-01-01", "2014-12-31"), - "valid": ("2015-01-01", "2016-12-31"), - "test": ("2017-01-01", "2020-08-01"), - }, - }, - } - # You shoud record the data in specific sequence - # "record": ['SignalRecord', 'SigAnaRecord', 'PortAnaRecord'], - } - - # model = train_model(task) - model = init_instance_by_config(task["model"]) - dataset = init_instance_by_config(task["dataset"]) - model.fit(dataset) - - pred_score = model.predict(dataset) - - # save pred_score to file - pred_score_path = Path("~/tmp/qlib/pred_score.pkl").expanduser() - pred_score_path.parent.mkdir(exist_ok=True, parents=True) - pred_score.to_pickle(pred_score_path) - - ################################### - # backtest - ################################### - STRATEGY_CONFIG = { - "topk": 50, - "n_drop": 5, - } - BACKTEST_CONFIG = { - "verbose": False, - "limit_threshold": 0.095, - "account": 100000000, - "benchmark": BENCHMARK, - "deal_price": "close", - "open_cost": 0.0005, - "close_cost": 0.0015, - "min_cost": 5, - } - - # use default strategy - # custom Strategy, refer to: TODO: Strategy API url - strategy = TopkDropoutStrategy(**STRATEGY_CONFIG) - report_normal, positions_normal = normal_backtest(pred_score, strategy=strategy, **BACKTEST_CONFIG) - - ################################### - # analyze - # If need a more detailed analysis, refer to: examples/train_and_bakctest.ipynb - ################################### - analysis = dict() - analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"]) - analysis["excess_return_with_cost"] = risk_analysis( - report_normal["return"] - report_normal["bench"] - report_normal["cost"] - ) - analysis_df = pd.concat(analysis) # type: pd.DataFrame - print(analysis_df) diff --git a/examples/workflow_by_code_tabnet.py b/examples/workflow_by_code_tabnet.py deleted file mode 100644 index 3778b9d59..000000000 --- a/examples/workflow_by_code_tabnet.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys -from pathlib import Path - -import qlib -import pandas as pd -from qlib.config import REG_CN -from qlib.contrib.model.tabnet import TabNetModel -from qlib.contrib.data.handler import ALPHA360_Denoise -from qlib.contrib.strategy.strategy import TopkDropoutStrategy -from qlib.contrib.evaluate import ( - backtest as normal_backtest, - risk_analysis, -) -from qlib.utils import exists_qlib_data - -# from qlib.model.learner import train_model -from qlib.utils import init_instance_by_config - -import pickle - -if __name__ == "__main__": - - # use default data - provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir - if not exists_qlib_data(provider_uri): - print(f"Qlib data is not found in {provider_uri}") - sys.path.append(str(Path(__file__).resolve().parent.parent.joinpath("scripts"))) - from get_data import GetData - - GetData().qlib_data(target_dir=provider_uri, region=REG_CN) - - qlib.init(provider_uri=provider_uri, region=REG_CN) - - MARKET = "csi300" - BENCHMARK = "SH000300" - - ################################### - # train model - ################################### - DATA_HANDLER_CONFIG = { - "start_time": "2008-01-01", - "end_time": "2020-08-01", - "fit_start_time": "2008-01-01", - "fit_end_time": "2014-12-31", - "instruments": MARKET, - } - - TRAINER_CONFIG = { - "train_start_time": "2008-01-01", - "train_end_time": "2014-12-31", - "validate_start_time": "2015-01-01", - "validate_end_time": "2016-12-31", - "test_start_time": "2017-01-01", - "test_end_time": "2020-08-01", - } - - task = { - "model": { - "class": "TabNetModel", - "module_path": "qlib.contrib.model.tabnet", - "kwargs": { - "n_d": 8, - "n_a": 8, - "n_steps": 3, - "gamma": 1.3, - "n_independent": 2, - "n_shared": 2, - "seed": 0, - "momentum": 0.02, - "lambda_sparse": 1e-3, - "optimizer_params": {"lr": 2e-3}, - }, - }, - "dataset": { - "class": "DatasetH", - "module_path": "qlib.data.dataset", - "kwargs": { - "handler": { - "class": "ALPHA360_Denoise", - "module_path": "qlib.contrib.data.handler", - "kwargs": DATA_HANDLER_CONFIG, - }, - "segments": { - "train": ("2008-01-01", "2014-12-31"), - "valid": ("2015-01-01", "2016-12-31"), - "test": ("2017-01-01", "2020-08-01"), - }, - }, - } - # You shoud record the data in specific sequence - # "record": ['SignalRecord', 'SigAnaRecord', 'PortAnaRecord'], - } - - # model = train_model(task) - model = init_instance_by_config(task["model"]) - dataset = init_instance_by_config(task["dataset"]) - model.fit(dataset) - - pred_score = model.predict(dataset) - - # save pred_score to file - pred_score_path = Path("~/tmp/qlib/pred_score.pkl").expanduser() - pred_score_path.parent.mkdir(exist_ok=True, parents=True) - pred_score.to_pickle(pred_score_path) - - ################################### - # backtest - ################################### - STRATEGY_CONFIG = { - "topk": 50, - "n_drop": 5, - } - BACKTEST_CONFIG = { - "verbose": False, - "limit_threshold": 0.095, - "account": 100000000, - "benchmark": BENCHMARK, - "deal_price": "close", - "open_cost": 0.0005, - "close_cost": 0.0015, - "min_cost": 5, - } - - # use default strategy - # custom Strategy, refer to: TODO: Strategy API url - strategy = TopkDropoutStrategy(**STRATEGY_CONFIG) - report_normal, positions_normal = normal_backtest(pred_score, strategy=strategy, **BACKTEST_CONFIG) - - ################################### - # analyze - # If need a more detailed analysis, refer to: examples/train_and_bakctest.ipynb - ################################### - analysis = dict() - analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"]) - analysis["excess_return_with_cost"] = risk_analysis( - report_normal["return"] - report_normal["bench"] - report_normal["cost"] - ) - analysis_df = pd.concat(analysis) # type: pd.DataFrame - print(analysis_df) From c5a3b74a96ce319c1f2e1cd17f9009b7dd4825dd Mon Sep 17 00:00:00 2001 From: zhupr Date: Fri, 27 Nov 2020 22:33:22 +0800 Subject: [PATCH 17/30] Fix the target repository of get_data.py in google.colab --- examples/workflow_by_code.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/workflow_by_code.ipynb b/examples/workflow_by_code.ipynb index f8370789b..81dbf3e31 100644 --- a/examples/workflow_by_code.ipynb +++ b/examples/workflow_by_code.ipynb @@ -35,7 +35,7 @@ " scripts_dir = Path(\"~/tmp/qlib_code/scripts\").expanduser().resolve()\n", " scripts_dir.mkdir(parents=True, exist_ok=True)\n", " import requests\n", - " with requests.get(\"https://raw.githubusercontent.com/you-n-g/qlib/main/scripts/get_data.py\") as resp:\n", + " with requests.get(\"https://raw.githubusercontent.com/microsoft/qlib/main/scripts/get_data.py\") as resp:\n", " with open(scripts_dir.joinpath(\"get_data.py\"), \"wb\") as fp:\n", " fp.write(resp.content)" ] From 1353e81b5b8a277945959e3418d41ab736e19198 Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Fri, 27 Nov 2020 22:44:28 +0800 Subject: [PATCH 18/30] Fix code with block. --- qlib/contrib/model/catboost_model.py | 4 +- qlib/contrib/model/pytorch_alstm.py | 32 +++-------- qlib/contrib/model/pytorch_gats.py | 22 ++------ qlib/contrib/model/pytorch_gru.py | 16 ++---- qlib/contrib/model/pytorch_lstm.py | 16 ++---- qlib/contrib/model/pytorch_sfm.py | 80 +++++++--------------------- qlib/contrib/model/xgboost.py | 8 +-- 7 files changed, 42 insertions(+), 136 deletions(-) diff --git a/qlib/contrib/model/catboost_model.py b/qlib/contrib/model/catboost_model.py index 43a141418..01830d1b5 100644 --- a/qlib/contrib/model/catboost_model.py +++ b/qlib/contrib/model/catboost_model.py @@ -50,9 +50,7 @@ class CatBoostModel(Model): # CatBoost needs 1D array as its label if y_train.values.ndim == 2 and y_train.values.shape[1] == 1: - y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze( - y_valid.values - ) + y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze(y_valid.values) else: raise ValueError("CatBoost doesn't support multi-label training") diff --git a/qlib/contrib/model/pytorch_alstm.py b/qlib/contrib/model/pytorch_alstm.py index 227772499..40c2f8226 100644 --- a/qlib/contrib/model/pytorch_alstm.py +++ b/qlib/contrib/model/pytorch_alstm.py @@ -124,9 +124,7 @@ class ALSTM(Model): elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.ALSTM_model.parameters(), lr=self.lr) else: - raise NotImplementedError( - "optimizer {} is not supported!".format(optimizer) - ) + raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) self._fitted = False if self.use_gpu: @@ -171,12 +169,8 @@ class ALSTM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy( - x_train_values[indices[i : i + self.batch_size]] - ).float() - label = torch.from_numpy( - y_train_values[indices[i : i + self.batch_size]] - ).float() + feature = torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]).float() + label = torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: feature = feature.cuda() @@ -208,9 +202,7 @@ class ALSTM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy( - x_values[indices[i : i + self.batch_size]] - ).float() + feature = torch.from_numpy(x_values[indices[i : i + self.batch_size]]).float() label = torch.from_numpy(y_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: @@ -320,9 +312,7 @@ class ALSTM(Model): class ALSTMModel(nn.Module): - def __init__( - self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, rnn_type="GRU" - ): + def __init__(self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, rnn_type="GRU"): super().__init__() self.hid_size = hidden_size self.input_size = d_feat @@ -337,9 +327,7 @@ class ALSTMModel(nn.Module): except: raise ValueError("unknown rnn_type `%s`" % self.rnn_type) self.net = nn.Sequential() - self.net.add_module( - "fc_in", nn.Linear(in_features=self.input_size, out_features=self.hid_size) - ) + self.net.add_module("fc_in", nn.Linear(in_features=self.input_size, out_features=self.hid_size)) self.net.add_module("act", nn.Tanh()) self.rnn = klass( input_size=self.hid_size, @@ -365,12 +353,8 @@ class ALSTMModel(nn.Module): def forward(self, inputs): # inputs: [batch_size, input_size*input_day] inputs = inputs.view(len(inputs), self.input_size, -1) - inputs = inputs.permute( - 0, 2, 1 - ) # [batch, input_size, seq_len] -> [batch, seq_len, input_size] - rnn_out, _ = self.rnn( - self.net(inputs) - ) # [batch, seq_len, num_directions * hidden_size] + inputs = inputs.permute(0, 2, 1) # [batch, input_size, seq_len] -> [batch, seq_len, input_size] + rnn_out, _ = self.rnn(self.net(inputs)) # [batch, seq_len, num_directions * hidden_size] attention_score = self.att_net(rnn_out) # [batch, seq_len, 1] out_att = torch.mul(rnn_out, attention_score) out_att = torch.sum(out_att, dim=1) diff --git a/qlib/contrib/model/pytorch_gats.py b/qlib/contrib/model/pytorch_gats.py index 226204fe7..61a0ef714 100644 --- a/qlib/contrib/model/pytorch_gats.py +++ b/qlib/contrib/model/pytorch_gats.py @@ -126,9 +126,7 @@ class GATs(Model): elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.GAT_model.parameters(), lr=self.lr) else: - raise NotImplementedError( - "optimizer {} is not supported!".format(optimizer) - ) + raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) self._fitted = False if self.use_gpu: @@ -258,22 +256,14 @@ class GATs(Model): self.logger.info("Loading pretrained model...") if self.base_model == "LSTM": pretrained_model = LSTMModel() - pretrained_model.load_state_dict( - torch.load("benchmarks/LSTM/model_lstm_csi300.pkl") - ) + pretrained_model.load_state_dict(torch.load("benchmarks/LSTM/model_lstm_csi300.pkl")) elif self.base_model == "GRU": pretrained_model = GRUModel() - pretrained_model.load_state_dict( - torch.load("benchmarks/GRU/model_gru_csi300.pkl") - ) + pretrained_model.load_state_dict(torch.load("benchmarks/GRU/model_gru_csi300.pkl")) model_dict = self.GAT_model.state_dict() - pretrained_dict = { - k: v - for k, v in pretrained_model.state_dict().items() - if k in model_dict - } + pretrained_dict = {k: v for k, v in pretrained_model.state_dict().items() if k in model_dict} model_dict.update(pretrained_dict) self.GAT_model.load_state_dict(model_dict) self.logger.info("Loading pretrained model Done...") @@ -343,9 +333,7 @@ class GATs(Model): class GATModel(nn.Module): - def __init__( - self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, base_model="GRU" - ): + def __init__(self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, base_model="GRU"): super().__init__() if base_model == "GRU": diff --git a/qlib/contrib/model/pytorch_gru.py b/qlib/contrib/model/pytorch_gru.py index 935716bcc..5daf4707e 100755 --- a/qlib/contrib/model/pytorch_gru.py +++ b/qlib/contrib/model/pytorch_gru.py @@ -124,9 +124,7 @@ class GRU(Model): elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.gru_model.parameters(), lr=self.lr) else: - raise NotImplementedError( - "optimizer {} is not supported!".format(optimizer) - ) + raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) self._fitted = False if self.use_gpu: @@ -171,12 +169,8 @@ class GRU(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy( - x_train_values[indices[i : i + self.batch_size]] - ).float() - label = torch.from_numpy( - y_train_values[indices[i : i + self.batch_size]] - ).float() + feature = torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]).float() + label = torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: feature = feature.cuda() @@ -208,9 +202,7 @@ class GRU(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy( - x_values[indices[i : i + self.batch_size]] - ).float() + feature = torch.from_numpy(x_values[indices[i : i + self.batch_size]]).float() label = torch.from_numpy(y_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: diff --git a/qlib/contrib/model/pytorch_lstm.py b/qlib/contrib/model/pytorch_lstm.py index 1d1c0c986..eef1680ec 100755 --- a/qlib/contrib/model/pytorch_lstm.py +++ b/qlib/contrib/model/pytorch_lstm.py @@ -124,9 +124,7 @@ class LSTM(Model): elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.lstm_model.parameters(), lr=self.lr) else: - raise NotImplementedError( - "optimizer {} is not supported!".format(optimizer) - ) + raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) self._fitted = False if self.use_gpu: @@ -171,12 +169,8 @@ class LSTM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy( - x_train_values[indices[i : i + self.batch_size]] - ).float() - label = torch.from_numpy( - y_train_values[indices[i : i + self.batch_size]] - ).float() + feature = torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]).float() + label = torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: feature = feature.cuda() @@ -208,9 +202,7 @@ class LSTM(Model): if len(indices) - i < self.batch_size: break - feature = torch.from_numpy( - x_values[indices[i : i + self.batch_size]] - ).float() + feature = torch.from_numpy(x_values[indices[i : i + self.batch_size]]).float() label = torch.from_numpy(y_values[indices[i : i + self.batch_size]]).float() if self.use_gpu: diff --git a/qlib/contrib/model/pytorch_sfm.py b/qlib/contrib/model/pytorch_sfm.py index bebc408a8..8fddd1612 100644 --- a/qlib/contrib/model/pytorch_sfm.py +++ b/qlib/contrib/model/pytorch_sfm.py @@ -56,52 +56,30 @@ class SFM_Model(nn.Module): self.hidden_dim = hidden_size self.device = device - self.W_i = nn.Parameter( - init.xavier_uniform_(torch.empty((self.input_dim, self.hidden_dim))) - ) - self.U_i = nn.Parameter( - init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) - ) + self.W_i = nn.Parameter(init.xavier_uniform_(torch.empty((self.input_dim, self.hidden_dim)))) + self.U_i = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) self.b_i = nn.Parameter(torch.zeros(self.hidden_dim)) - self.W_ste = nn.Parameter( - init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim)) - ) - self.U_ste = nn.Parameter( - init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) - ) + self.W_ste = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) + self.U_ste = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) self.b_ste = nn.Parameter(torch.ones(self.hidden_dim)) - self.W_fre = nn.Parameter( - init.xavier_uniform_(torch.empty(self.input_dim, self.freq_dim)) - ) - self.U_fre = nn.Parameter( - init.orthogonal_(torch.empty(self.hidden_dim, self.freq_dim)) - ) + self.W_fre = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.freq_dim))) + self.U_fre = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.freq_dim))) self.b_fre = nn.Parameter(torch.ones(self.freq_dim)) - self.W_c = nn.Parameter( - init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim)) - ) - self.U_c = nn.Parameter( - init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) - ) + self.W_c = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) + self.U_c = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) self.b_c = nn.Parameter(torch.zeros(self.hidden_dim)) - self.W_o = nn.Parameter( - init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim)) - ) - self.U_o = nn.Parameter( - init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim)) - ) + self.W_o = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) + self.U_o = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) self.b_o = nn.Parameter(torch.zeros(self.hidden_dim)) self.U_a = nn.Parameter(init.orthogonal_(torch.empty(self.freq_dim, 1))) self.b_a = nn.Parameter(torch.zeros(self.hidden_dim)) - self.W_p = nn.Parameter( - init.xavier_uniform_(torch.empty(self.hidden_dim, self.output_dim)) - ) + self.W_p = nn.Parameter(init.xavier_uniform_(torch.empty(self.hidden_dim, self.output_dim))) self.b_p = nn.Parameter(torch.zeros(self.output_dim)) self.activation = nn.Tanh() @@ -137,12 +115,8 @@ class SFM_Model(nn.Module): x_o = torch.matmul(x * B_W[0], self.W_o) + self.b_o i = self.inner_activation(x_i + torch.matmul(h_tm1 * B_U[0], self.U_i)) - ste = self.inner_activation( - x_ste + torch.matmul(h_tm1 * B_U[0], self.U_ste) - ) - fre = self.inner_activation( - x_fre + torch.matmul(h_tm1 * B_U[0], self.U_fre) - ) + ste = self.inner_activation(x_ste + torch.matmul(h_tm1 * B_U[0], self.U_ste)) + fre = self.inner_activation(x_fre + torch.matmul(h_tm1 * B_U[0], self.U_fre)) ste = torch.reshape(ste, (-1, self.hidden_dim, 1)) fre = torch.reshape(fre, (-1, 1, self.freq_dim)) @@ -331,9 +305,7 @@ class SFM(Model): elif optimizer.lower() == "gd": self.train_optimizer = optim.SGD(self.sfm_model.parameters(), lr=self.lr) else: - raise NotImplementedError( - "optimizer {} is not supported!".format(optimizer) - ) + raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) self._fitted = False self.sfm_model.to(self.device) @@ -356,16 +328,8 @@ class SFM(Model): if len(indices) - i < self.batch_size: break - feature = ( - torch.from_numpy(x_values[indices[i : i + self.batch_size]]) - .float() - .to(self.device) - ) - label = ( - torch.from_numpy(y_values[indices[i : i + self.batch_size]]) - .float() - .to(self.device) - ) + feature = torch.from_numpy(x_values[indices[i : i + self.batch_size]]).float().to(self.device) + label = torch.from_numpy(y_values[indices[i : i + self.batch_size]]).float().to(self.device) pred = self.sfm_model(feature) loss = self.loss_fn(pred, label) @@ -391,16 +355,8 @@ class SFM(Model): if len(indices) - i < self.batch_size: break - feature = ( - torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]) - .float() - .to(self.device) - ) - label = ( - torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]) - .float() - .to(self.device) - ) + feature = torch.from_numpy(x_train_values[indices[i : i + self.batch_size]]).float().to(self.device) + label = torch.from_numpy(y_train_values[indices[i : i + self.batch_size]]).float().to(self.device) pred = self.sfm_model(feature) loss = self.loss_fn(pred, label) diff --git a/qlib/contrib/model/xgboost.py b/qlib/contrib/model/xgboost.py index 32d631189..c9e45d4ac 100755 --- a/qlib/contrib/model/xgboost.py +++ b/qlib/contrib/model/xgboost.py @@ -47,9 +47,7 @@ class XGBModel(Model): # Lightgbm need 1D array as its label if y_train.values.ndim == 2 and y_train.values.shape[1] == 1: - y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze( - y_valid.values - ) + y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze(y_valid.values) else: raise ValueError("XGBoost doesn't support multi-label training") @@ -72,6 +70,4 @@ class XGBModel(Model): if self.model is None: raise ValueError("model is not fitted yet!") x_test = dataset.prepare("test", col_set="feature") - return pd.Series( - self.model.predict(xgb.DMatrix(x_test.values)), index=x_test.index - ) + return pd.Series(self.model.predict(xgb.DMatrix(x_test.values)), index=x_test.index) From c52b84b3fe7423125fb8373ea78d4f9bf7f8734c Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Fri, 27 Nov 2020 22:50:07 +0800 Subject: [PATCH 19/30] Fix sfm readme. --- examples/benchmarks/SFM/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/benchmarks/SFM/README.md b/examples/benchmarks/SFM/README.md index 06ca50485..eb1c8b157 100644 --- a/examples/benchmarks/SFM/README.md +++ b/examples/benchmarks/SFM/README.md @@ -1,4 +1,4 @@ # State-Frequency-Memory -- State Frequency Memory (SFM) is a novel recurrent network that uses Discrete Fourier Transform (DFT) to decompose the hidden states of memory cells and capture the multi-frequency trading patterns from past market data to make stock price predictions. -- The code used in Qlib is a pyTorch implementation of SFM (Zhang, L., Aggarwal, C., & Qi, G. J. (2017,)). -- Paper: Stock Price Prediction via Discovering Multi-Frequency Trading Patterns. https://www.cs.ucf.edu/~gqi/publications/kdd2017_stock.pdf. \ No newline at end of file +- State Frequency Memory (SFM) is a novel recurrent network that uses Discrete Fourier Transform to decompose the hidden states of memory cells and capture the multi-frequency trading patterns from past market data to make stock price predictions. +- The code used in Qlib is a pyTorch implementation of SFM. +- Paper: Stock Price Prediction via Discovering Multi-Frequency Trading Patterns. [https://www.cs.ucf.edu/~gqi/publications/kdd2017_stock.pdf.](https://www.cs.ucf.edu/~gqi/publications/kdd2017_stock.pdf.) \ No newline at end of file From d7fc90ddcd623640546268deaad4495405036a20 Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Fri, 27 Nov 2020 23:36:29 +0800 Subject: [PATCH 20/30] Update readme. --- examples/benchmarks/ALSTM/README.md | 4 +--- examples/benchmarks/SFM/README.md | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/benchmarks/ALSTM/README.md b/examples/benchmarks/ALSTM/README.md index cd9dd3493..1b749bd80 100644 --- a/examples/benchmarks/ALSTM/README.md +++ b/examples/benchmarks/ALSTM/README.md @@ -2,9 +2,7 @@ - ALSTM contains a temporal attentive aggregation layer based on normal LSTM. -- The code used in Qlib is a pyTorch implementation of Code: https://github.com/fulifeng/Adv-ALSTM - - Paper: A dual-stage attention-based recurrent neural network for time series prediction. - https://www.ijcai.org/Proceedings/2017/0366.pdf + [https://www.ijcai.org/Proceedings/2017/0366.pdf](https://www.ijcai.org/Proceedings/2017/0366.pdf) diff --git a/examples/benchmarks/SFM/README.md b/examples/benchmarks/SFM/README.md index eb1c8b157..5f74c15d2 100644 --- a/examples/benchmarks/SFM/README.md +++ b/examples/benchmarks/SFM/README.md @@ -1,4 +1,3 @@ # State-Frequency-Memory - State Frequency Memory (SFM) is a novel recurrent network that uses Discrete Fourier Transform to decompose the hidden states of memory cells and capture the multi-frequency trading patterns from past market data to make stock price predictions. -- The code used in Qlib is a pyTorch implementation of SFM. - Paper: Stock Price Prediction via Discovering Multi-Frequency Trading Patterns. [https://www.cs.ucf.edu/~gqi/publications/kdd2017_stock.pdf.](https://www.cs.ucf.edu/~gqi/publications/kdd2017_stock.pdf.) \ No newline at end of file From 47cbfdc50cf4ea8505f988c32829004fe2088078 Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Sat, 28 Nov 2020 00:10:09 +0800 Subject: [PATCH 21/30] Update --- examples/benchmarks/GATs/workflow_config_gats.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/benchmarks/GATs/workflow_config_gats.yaml b/examples/benchmarks/GATs/workflow_config_gats.yaml index 7212e0ee2..c38b4b312 100644 --- a/examples/benchmarks/GATs/workflow_config_gats.yaml +++ b/examples/benchmarks/GATs/workflow_config_gats.yaml @@ -40,19 +40,19 @@ port_analysis_config: &port_analysis_config min_cost: 5 task: model: - class: GAT_Classic - module_path: qlib.contrib.model.pytorch_gats_classic + class: GATs + module_path: qlib.contrib.model.pytorch_gats kwargs: d_feat: 6 hidden_size: 64 num_layers: 2 - dropout: 0.0 + dropout: 0.7 n_epochs: 200 - lr: 1e-3 + lr: 1e-4 early_stop: 20 metric: loss loss: mse - base_model: GRU + base_model: LSTM seed: 0 GPU: 0 dataset: From 6fc4ff0a62b32e2526cad69eab7e5556d4f24928 Mon Sep 17 00:00:00 2001 From: zhupr Date: Sat, 28 Nov 2020 00:36:23 +0800 Subject: [PATCH 22/30] Fix yahoo collector --- scripts/data_collector/yahoo/collector.py | 69 +++++++++++++++++------ 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/scripts/data_collector/yahoo/collector.py b/scripts/data_collector/yahoo/collector.py index 69c7f8f15..0d41251f1 100644 --- a/scripts/data_collector/yahoo/collector.py +++ b/scripts/data_collector/yahoo/collector.py @@ -44,6 +44,7 @@ class YahooCollector: delay=0, check_data_length: bool = False, limit_nums: int = None, + show_1m_logging: bool = False, ): """ @@ -67,10 +68,13 @@ class YahooCollector: check data length, by default False limit_nums: int using for debug, by default None + show_1m_logging: bool + show 1m logging, by default False; if True, there may be many warning logs """ self.save_dir = Path(save_dir).expanduser().resolve() self.save_dir.mkdir(parents=True, exist_ok=True) self._delay = delay + self._show_1m_logging = show_1m_logging self.stock_list = sorted(set(self.get_stock_list())) if limit_nums is not None: try: @@ -83,7 +87,7 @@ class YahooCollector: self._interval = interval self._check_small_data = check_data_length self._start_datetime = pd.Timestamp(str(start)) if start else self.START_DATETIME - self._end_datetime = pd.Timestamp(str(end)) if end else self.END_DATETIME + self._end_datetime = min(pd.Timestamp(str(end)) if end else self.END_DATETIME, self.END_DATETIME) if self._interval == "1m": self._start_datetime = max(self._start_datetime, self.HIGH_FREQ_START_DATETIME) elif self._interval == "1d": @@ -91,8 +95,12 @@ class YahooCollector: else: raise ValueError(f"interval error: {self._interval}") + # using for 1m + self._next_datetime = self.convert_datetime(self._start_datetime.date() + pd.Timedelta(days=1)) + self._latest_datetime = self.convert_datetime(self._end_datetime.date()) + self._start_datetime = self.convert_datetime(self._start_datetime) - self._end_datetime = self.convert_datetime(min(self._end_datetime, self.END_DATETIME)) + self._end_datetime = self.convert_datetime(self._end_datetime) @property @abc.abstractmethod @@ -100,20 +108,24 @@ class YahooCollector: # daily, one year: 252 / 4 # us 1min, a week: 6.5 * 60 * 5 # cn 1min, a week: 4 * 60 * 5 - raise NotImplementedError("rewirte min_numbers_trading") + raise NotImplementedError("rewrite min_numbers_trading") @abc.abstractmethod def get_stock_list(self): - raise NotImplementedError("rewirte get_stock_list") + raise NotImplementedError("rewrite get_stock_list") @property - @abc.abstractclassmethod + @abc.abstractmethod def _timezone(self): raise NotImplementedError("rewrite get_timezone") - def convert_datetime(self, dt: pd.Timestamp): - dt = pd.Timestamp(dt, tz=self._timezone).timestamp() - return pd.Timestamp(dt, tz=tzlocal(), unit="s") + def convert_datetime(self, dt: [pd.Timestamp, datetime.date, str]): + try: + dt = pd.Timestamp(dt, tz=self._timezone).timestamp() + dt = pd.Timestamp(dt, tz=tzlocal(), unit="s") + except ValueError as e: + pass + return dt def _sleep(self): time.sleep(self._delay) @@ -136,7 +148,7 @@ class YahooCollector: df["symbol"] = symbol if stock_path.exists(): with stock_path.open("a") as fp: - df.to_csv(fp, index=False, header=None) + df.to_csv(fp, index=False, header=False) else: with stock_path.open("w") as fp: df.to_csv(fp, index=False) @@ -155,34 +167,47 @@ class YahooCollector: def _get_from_remote(self, symbol): def _get_simple(start_, end_): self._sleep() + error_msg = f"{symbol}-{self._interval}-{start_}-{end_}" + + def _show_logging_func(): + if self._interval == "1m" and self._show_1m_logging: + logger.warning(f"{error_msg}:{_resp}") + try: _resp = Ticker(symbol, asynchronous=False).history(interval=self._interval, start=start_, end=end_) if isinstance(_resp, pd.DataFrame): return _resp.reset_index() + elif isinstance(_resp, dict): + _temp_data = _resp.get(symbol, {}) + if isinstance(_temp_data, str) or ( + isinstance(_resp, dict) and _temp_data.get("indicators", {}).get("quote", None) is None + ): + _show_logging_func() else: - logger.warning(f"{symbol}-{self._interval}-{start_}-{end_}:{_resp}") + _show_logging_func() except Exception as e: - logger.warning(f"{symbol}-{self._interval}-{start_}-{end_}:{e}") + logger.warning(f"{error_msg}:{e}") _result = None if self._interval == "1d": _result = _get_simple(self._start_datetime, self._end_datetime) elif self._interval == "1m": - _start_date = self._start_datetime.date() + pd.Timedelta(days=1) - _end_date = self._end_datetime.date() - if _start_date >= _end_date: + if self._next_datetime >= self._latest_datetime: _result = _get_simple(self._start_datetime, self._end_datetime) else: _res = [] def _get_multi(start_, end_): _resp = _get_simple(start_, end_) - if _resp is not None: + if _resp is not None and not _resp.empty: _res.append(_resp) - for _s, _e in ((self._start_datetime, _start_date), (_end_date, self._end_datetime)): + for _s, _e in ( + (self._start_datetime, self._next_datetime), + (self._latest_datetime, self._end_datetime), + ): _get_multi(_s, _e) - for _start in pd.date_range(_start_date, _end_date, closed="left"): + for _start in pd.date_range(self._next_datetime, self._latest_datetime, closed="left"): _end = _start + pd.Timedelta(days=1) self._sleep() _get_multi(_start, _end) @@ -472,6 +497,7 @@ class Run: interval="1d", check_data_length=False, limit_nums=None, + show_1m_logging=False, ): """download data from Internet @@ -491,6 +517,9 @@ class Run: check data length, by default False limit_nums: int using for debug, by default None + show_1m_logging: bool + show 1m logging, by default False; if True, there may be many warning logs + Examples --------- # get daily data @@ -510,6 +539,7 @@ class Run: interval=interval, check_data_length=check_data_length, limit_nums=limit_nums, + show_1m_logging=show_1m_logging, ).collector_data() def normalize_data(self): @@ -531,6 +561,7 @@ class Run: interval="1d", check_data_length=False, limit_nums=None, + show_1m_logging=False, ): """download -> normalize @@ -550,6 +581,9 @@ class Run: check data length, by default False limit_nums: int using for debug, by default None + show_1m_logging: bool + show 1m logging, by default False; if True, there may be many warning logs + Examples ------- python collector.py collector_data --source_dir ~/.qlib/stock_data/source --normalize_dir ~/.qlib/stock_data/normalize --region CN --start 2020-11-01 --end 2020-11-10 --delay 0.1 --interval 1d @@ -562,6 +596,7 @@ class Run: interval=interval, check_data_length=check_data_length, limit_nums=limit_nums, + show_1m_logging=show_1m_logging, ) self.normalize_data() From aa2b28386aca03b8f953bc5005ee8cdf0b1ae744 Mon Sep 17 00:00:00 2001 From: Hong Zhang Date: Sat, 28 Nov 2020 02:04:35 +0800 Subject: [PATCH 23/30] revised HATS --- examples/benchmarks/HATS/README.md | 12 + examples/benchmarks/HATS/requirements.txt | 4 + .../benchmarks/HATS/worflow_config_hats.yaml | 77 +++ examples/run_all_model_records/0/meta.yaml | 4 + qlib/contrib/model/pytorch_gats.py | 1 + qlib/contrib/model/pytorch_hats.py | 491 ++++++++++++++++++ 6 files changed, 589 insertions(+) create mode 100644 examples/benchmarks/HATS/README.md create mode 100644 examples/benchmarks/HATS/requirements.txt create mode 100644 examples/benchmarks/HATS/worflow_config_hats.yaml create mode 100644 examples/run_all_model_records/0/meta.yaml create mode 100644 qlib/contrib/model/pytorch_hats.py diff --git a/examples/benchmarks/HATS/README.md b/examples/benchmarks/HATS/README.md new file mode 100644 index 000000000..1a0ac7bb3 --- /dev/null +++ b/examples/benchmarks/HATS/README.md @@ -0,0 +1,12 @@ +## Requirement + +* pandas==1.1.2 +* numpy==1.17.4 +* scikit_learn==0.23.2 +* torch==1.7.0 + +## HATS + +* HATS is a a hierarchical attention network for stock prediction which uses attention layers to broadcast weight for stock market prediction. HATS selectively aggregates information on different relation types and adds the information to the representations of each company. HATS is used as a module with initialized node representations.Furthermore, HATS can predict not only individual stock prices but also market index movements, which is similar to the graph classification task. +* HATS uses pretrained model of GRU and LSTM. The code of GRU and LSTM used in Qlib is a pyTorch implemention of GRU and LSTM. +* Paper address:HATS: A Hierarchical Graph Attention Network for Stock Movement Prediction https://arxiv.org/pdf/1908.07999.pdf \ No newline at end of file diff --git a/examples/benchmarks/HATS/requirements.txt b/examples/benchmarks/HATS/requirements.txt new file mode 100644 index 000000000..16de0a438 --- /dev/null +++ b/examples/benchmarks/HATS/requirements.txt @@ -0,0 +1,4 @@ +pandas==1.1.2 +numpy==1.17.4 +scikit_learn==0.23.2 +torch==1.7.0 diff --git a/examples/benchmarks/HATS/worflow_config_hats.yaml b/examples/benchmarks/HATS/worflow_config_hats.yaml new file mode 100644 index 000000000..ea9f21e76 --- /dev/null +++ b/examples/benchmarks/HATS/worflow_config_hats.yaml @@ -0,0 +1,77 @@ +provider_uri: "~/.qlib/qlib_data/cn_data" +region: cn +market: &market csi300 +benchmark: &benchmark SH000300 +data_handler_config: &data_handler_config + start_time: 2008-01-01 + end_time: 2020-08-01 + fit_start_time: 2008-01-01 + fit_end_time: 2014-12-31 + instruments: *market + infer_processors: + - class: RobustZScoreNorm + kwargs: + fields_group: feature + clip_outlier: true + - class: Fillna + kwargs: + fields_group: feature + learn_processors: + - class: DropnaLabel + - class: CSRankNorm + kwargs: + fields_group: label + label: ["Ref($close, -2) / Ref($close, -1) - 1"] +port_analysis_config: &port_analysis_config + strategy: + class: TopkDropoutStrategy + module_path: qlib.contrib.strategy.strategy + kwargs: + topk: 50 + n_drop: 5 + backtest: + verbose: False + limit_threshold: 0.095 + account: 100000000 + benchmark: *benchmark + deal_price: close + open_cost: 0.0005 + close_cost: 0.0015 + min_cost: 5 +task: + model: + class: HATS + module_path: qlib.contrib.model.pytorch_hats + kwargs: + d_feat: 6 + hidden_size: 64 + num_layers: 2 + dropout: 0.6 + n_epochs: 200 + lr: 1e-3 + early_stop: 20 + metric: loss + loss: mse + base_model: LSTM + seed: 0 + GPU: 0 + dataset: + class: DatasetH + module_path: qlib.data.dataset + kwargs: + handler: + class: ALPHA360 + module_path: qlib.contrib.data.handler + kwargs: *data_handler_config + segments: + train: [2008-01-01, 2014-12-31] + valid: [2015-01-01, 2016-12-31] + test: [2017-01-01, 2020-08-01] + record: + - class: SignalRecord + module_path: qlib.workflow.record_temp + kwargs: {} + - class: PortAnaRecord + module_path: qlib.workflow.record_temp + kwargs: + config: *port_analysis_config \ No newline at end of file diff --git a/examples/run_all_model_records/0/meta.yaml b/examples/run_all_model_records/0/meta.yaml new file mode 100644 index 000000000..df706fda3 --- /dev/null +++ b/examples/run_all_model_records/0/meta.yaml @@ -0,0 +1,4 @@ +artifact_location: file:///home/v-hozhan/qlib/examples/run_all_model_records/0 +experiment_id: '0' +lifecycle_stage: active +name: Default diff --git a/qlib/contrib/model/pytorch_gats.py b/qlib/contrib/model/pytorch_gats.py index 61a0ef714..e9cbcf9cb 100644 --- a/qlib/contrib/model/pytorch_gats.py +++ b/qlib/contrib/model/pytorch_gats.py @@ -12,6 +12,7 @@ import copy from ...utils import create_save_path from ...log import get_module_logger + import torch import torch.nn as nn import torch.optim as optim diff --git a/qlib/contrib/model/pytorch_hats.py b/qlib/contrib/model/pytorch_hats.py new file mode 100644 index 000000000..7affea73c --- /dev/null +++ b/qlib/contrib/model/pytorch_hats.py @@ -0,0 +1,491 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import division +from __future__ import print_function + +import os +import numpy as np +import pandas as pd +import copy +from ...utils import create_save_path +from ...log import get_module_logger + +import torch +import torch.nn as nn +import torch.optim as optim + +from ...model.base import Model +from ...data.dataset import DatasetH +from ...data.dataset.handler import DataHandlerLP + + +class HATS(Model): + """HATS Model + + Parameters + ---------- + d_feat : int + input dimension for each time step + metric: str + the evaluate metric used in early stop + optimizer : str + optimizer name + GPU : str + the GPU ID(s) used for training + """ + + def __init__( + self, + d_feat=6, + hidden_size=64, + num_layers=2, + dropout=0.5, + n_epochs=200, + lr=0.01, + metric="", + early_stop=20, + loss="mse", + base_model="GRU", + with_pretrain=True, + optimizer="adam", + GPU="0", + seed=0, + **kwargs + ): + # Set logger. + self.logger = get_module_logger("HATS") + self.logger.info("HATS pytorch version...") + + # set hyper-parameters. + self.d_feat = d_feat + self.hidden_size = hidden_size + self.num_layers = num_layers + self.dropout = dropout + self.n_epochs = n_epochs + self.lr = lr + self.metric = metric + self.early_stop = early_stop + self.optimizer = optimizer.lower() + self.loss = loss + self.base_model = base_model + self.with_pretrain = with_pretrain + self.visible_GPU = GPU + self.use_gpu = torch.cuda.is_available() + self.seed = seed + + self.logger.info( + "HATS parameters setting:" + "\nd_feat : {}" + "\nhidden_size : {}" + "\nnum_layers : {}" + "\ndropout : {}" + "\nn_epochs : {}" + "\nlr : {}" + "\nmetric : {}" + "\nearly_stop : {}" + "\noptimizer : {}" + "\nloss_type : {}" + "\nbase_model : {}" + "\nwith_pretrain : {}" + "\nvisible_GPU : {}" + "\nuse_GPU : {}" + "\nseed : {}".format( + d_feat, + hidden_size, + num_layers, + dropout, + n_epochs, + lr, + metric, + early_stop, + optimizer.lower(), + loss, + base_model, + with_pretrain, + GPU, + self.use_gpu, + seed, + ) + ) + + self.HATS_model = HATSModel( + d_feat=self.d_feat, + hidden_size=self.hidden_size, + num_layers=self.num_layers, + dropout=self.dropout, + base_model=self.base_model, + ) + if optimizer.lower() == "adam": + self.train_optimizer = optim.Adam(self.HATS_model.parameters(), lr=self.lr) + elif optimizer.lower() == "gd": + self.train_optimizer = optim.SGD(self.HATS_model.parameters(), lr=self.lr) + else: + raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) + + self._fitted = False + if self.use_gpu: + self.HATS_model.cuda() + # set the visible GPU + if self.visible_GPU: + os.environ["CUDA_VISIBLE_DEVICES"] = self.visible_GPU + + def mse(self, pred, label): + loss = (pred - label) ** 2 + return torch.mean(loss) + + def loss_fn(self, pred, label): + mask = ~torch.isnan(label) + + if self.loss == "mse": + return self.mse(pred[mask], label[mask]) + + raise ValueError("unknown loss `%s`" % self.loss) + + def metric_fn(self, pred, label): + mask = torch.isfinite(label) + + if self.metric == "" or self.metric == "loss": # use loss + return -self.loss_fn(pred[mask], label[mask]) + + raise ValueError("unknown metric `%s`" % self.metric) + + def get_daily_inter(self, df, shuffle=False): + # organize the train data into daily inter as daily batches + daily_count = df.groupby(level=0).size().values + daily_index = np.roll(np.cumsum(daily_count), 1) + daily_index[0] = 0 + if shuffle: + # shuffle the daily inter data + daily_shuffle = list(zip(daily_index, daily_count)) + np.random.shuffle(daily_shuffle) + daily_index, daily_count = zip(*daily_shuffle) + return daily_index, daily_count + + def train_epoch(self, x_train, y_train): + + x_train_values = x_train.values + y_train_values = np.squeeze(y_train.values) + + self.HATS_model.train() + + # organize the train data into daily inter as daily batches + daily_index, daily_count = self.get_daily_inter(x_train, shuffle=True) + + for idx, count in zip(daily_index, daily_count): + batch = slice(idx, idx + count) + feature = torch.from_numpy(x_train_values[batch]).float() + label = torch.from_numpy(y_train_values[batch]).float() + + if self.use_gpu: + feature = feature.cuda() + label = label.cuda() + + pred = self.HATS_model(feature) + loss = self.loss_fn(pred, label) + + self.train_optimizer.zero_grad() + loss.backward() + torch.nn.utils.clip_grad_value_(self.HATS_model.parameters(), 3.0) + self.train_optimizer.step() + + def test_epoch(self, data_x, data_y): + + # prepare testing data + x_values = data_x.values + y_values = np.squeeze(data_y.values) + + self.HATS_model.eval() + + scores = [] + losses = [] + + # organize the test data into daily inter as daily batches + daily_index, daily_count = self.get_daily_inter(data_x, shuffle=False) + + for idx, count in zip(daily_index, daily_count): + batch = slice(idx, idx + count) + feature = torch.from_numpy(x_values[batch]).float() + label = torch.from_numpy(y_values[batch]).float() + + if self.use_gpu: + feature = feature.cuda() + label = label.cuda() + + pred = self.HATS_model(feature) + loss = self.loss_fn(pred, label) + losses.append(loss.item()) + + score = self.metric_fn(pred, label) + scores.append(score.item()) + + return np.mean(losses), np.mean(scores) + + def fit( + self, + dataset: DatasetH, + evals_result=dict(), + verbose=True, + save_path=None, + ): + + df_train, df_valid, df_test = dataset.prepare( + ["train", "valid", "test"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L + ) + + x_train, y_train = df_train["feature"], df_train["label"] + x_valid, y_valid = df_valid["feature"], df_valid["label"] + + if save_path == None: + save_path = create_save_path(save_path) + stop_steps = 0 + best_score = -np.inf + best_epoch = 0 + evals_result["train"] = [] + evals_result["valid"] = [] + + # load pretrained base_model + if self.with_pretrain: + self.logger.info("Loading pretrained model...") + if self.base_model == "LSTM": + from ...contrib.model.pytorch_lstm import LSTMModel + + pretrained_model = LSTMModel() + pretrained_model.load_state_dict(torch.load("benchmarks/LSTM/model_lstm_csi300.pkl")) + elif self.base_model == "GRU": + from ...contrib.model.pytorch_gru import GRUModel + + pretrained_model = GRUModel() + pretrained_model.load_state_dict(torch.load("benchmarks/GRU/model_gru_csi300.pkl")) + model_dict = self.HATS_model.state_dict() + pretrained_dict = {k: v for k, v in pretrained_model.state_dict().items() if k in model_dict} + model_dict.update(pretrained_dict) + self.HATS_model.load_state_dict(model_dict) + self.logger.info("Loading pretrained model Done...") + + # train + self.logger.info("training...") + self._fitted = True + + for step in range(self.n_epochs): + self.logger.info("Epoch%d:", step) + self.logger.info("training...") + self.train_epoch(x_train, y_train) + self.logger.info("evaluating...") + train_loss, train_score = self.test_epoch(x_train, y_train) + val_loss, val_score = self.test_epoch(x_valid, y_valid) + self.logger.info("train %.6f, valid %.6f" % (train_score, val_score)) + evals_result["train"].append(train_score) + evals_result["valid"].append(val_score) + + if val_score > best_score: + best_score = val_score + stop_steps = 0 + best_epoch = step + best_param = copy.deepcopy(self.HATS_model.state_dict()) + else: + stop_steps += 1 + if stop_steps >= self.early_stop: + self.logger.info("early stop") + break + + self.logger.info("best score: %.6lf @ %d" % (best_score, best_epoch)) + self.HATS_model.load_state_dict(best_param) + torch.save(best_param, save_path) + + if self.use_gpu: + torch.cuda.empty_cache() + + def predict(self, dataset): + if not self._fitted: + raise ValueError("model is not fitted yet!") + + x_test = dataset.prepare("test", col_set="feature") + index = x_test.index + self.HATS_model.eval() + x_values = x_test.values + sample_num = x_values.shape[0] + preds = [] + + # organize the data into daily inter as daily batches + daily_index, daily_count = self.get_daily_inter(x_test, shuffle=False) + + for idx, count in zip(daily_index, daily_count): + batch = slice(idx, idx + count) + x_batch = torch.from_numpy(x_values[batch]).float() + + if self.use_gpu: + x_batch = x_batch.cuda() + + with torch.no_grad(): + if self.use_gpu: + pred = self.HATS_model(x_batch).detach().cpu().numpy() + else: + pred = self.HATS_model(x_batch).detach().numpy() + + preds.append(pred) + + return pd.Series(np.concatenate(preds), index=index) + + +class HATSModel(nn.Module): + def __init__(self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, base_model="GRU"): + super().__init__() + + if base_model == "GRU": + self.model = nn.GRU( + input_size=d_feat, + hidden_size=hidden_size, + num_layers=num_layers, + batch_first=True, + dropout=dropout, + ) + elif base_model == "LSTM": + self.model = nn.LSTM( + input_size=d_feat, + hidden_size=hidden_size, + num_layers=num_layers, + batch_first=True, + dropout=dropout, + ) + else: + raise ValueError("unknown base model name `%s`" % base_model) + + self.hidden_size = hidden_size + self.bn1 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) + self.fc = nn.Linear(hidden_size, hidden_size) + self.bn2 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) + self.fc_out = nn.Linear(hidden_size, 1) + self.leaky_relu = nn.LeakyReLU() + self.softmax = nn.Softmax(dim=1) + self.d_feat = d_feat + + num_head_att = [1] * num_layers + hidden_dim = [hidden_size] * num_layers + dims = [d_feat] + [d * nh for (d, nh) in zip(hidden_dim, num_head_att[:-1])] + [num_head_att[-1]] + in_dims = dims[:-1] + out_dims = [d // nh for (d, nh) in zip(dims[1:], num_head_att)] + self.attn = nn.ModuleList( + [GraphAttention(i, o, nh, dropout) for (i, o, nh) in zip(in_dims, out_dims, num_head_att)] + ) + self.bns = nn.ModuleList([nn.BatchNorm1d(dim) for dim in dims[1:-1]]) + self.dropout = nn.Dropout(dropout) + self.elu = nn.ELU() + + def forward(self, x): + x = x.reshape(len(x), self.d_feat, -1) # [N, F, T] + x = x.permute(0, 2, 1) # [N, T, F] + out, _ = self.model(x) + hidden = out[:, -1, :] + hidden = self.bn1(hidden) + attention = GraphAttention.cal_attention(hidden, hidden) + output = attention.mm(hidden) + output = self.fc(output) + output = self.bn2(output) + output = self.leaky_relu(output) + return self.fc_out(output).squeeze() + + +class GraphAttention(nn.Module): + def __init__(self, input_dim, output_dim, num_heads, dropout=0.5): + + super().__init__() + + """ + Parameters + ---------- + input_dim : int + Dimension of input node features. + output_dim : int + Dimension of output node features. + num_heads : list of ints + Number of attention heads in each hidden layer and output layer. Must be non empty. Note that len(num_heads) = len(hidden_dims)+1. + dropout : float + Dropout rate. Default: 0.5. + """ + + self.input_dim = input_dim + self.output_dim = output_dim + self.num_heads = num_heads + + self.fcs = nn.ModuleList([nn.Linear(input_dim, output_dim) for _ in range(num_heads)]) + self.a = nn.ModuleList([nn.Linear(2 * output_dim, 1) for _ in range(num_heads)]) + + self.dropout = nn.Dropout(dropout) + self.softmax = nn.Softmax(dim=0) + self.leakyrelu = nn.LeakyReLU() + + def forward(self, features, nodes, mappings, rows): + + """ + Parameters + ---------- + features : torch.Tensor + An (n' x input_dim) tensor of input node features. + nodes : list of numpy array + nodes[i] is an array of the nodes in the ith layer of the + computation graph. + mappings : list of dictionary + mappings[i] is a dictionary mappings node v (labelled 0 to |V|-1) + in nodes[i] to its position in nodes[i]. For example, + if nodes[i] = [2,5], then mappings[i][2] = 0 and + mappings[i][5] = 1. + rows : numpy array + rows[i] is an array of neighbors of node i. + Returns + ------- + out : torch.Tensor + An (len(node_layers[-1]) x output_dim) tensor of output node features. + """ + + nprime = features.shape[0] + rows = [np.array([mappings[v] for v in row], dtype=np.int64) for row in rows] + sum_degs = np.hstack(([0], np.cumsum([len(row) for row in rows]))) + mapped_nodes = [mappings[v] for v in nodes] + indices = torch.LongTensor([[v, c] for (v, row) in zip(mapped_nodes, rows) for c in row]).t() + + out = [] + for k in range(self.num_heads): + h = self.fcs[k](features) + + nbr_h = torch.cat(tuple([h[row] for row in rows]), dim=0) + self_h = torch.cat( + tuple([h[mappings[nodes[i]]].repeat(len(row), 1) for (i, row) in enumerate(rows)]), dim=0 + ) + cat_h = torch.cat((self_h, nbr_h), dim=1) + + e = self.leakyrelu(self.a[k](cat_h)) + + alpha = [self.softmax(e[lo:hi]) for (lo, hi) in zip(sum_degs, sum_degs[1:])] + alpha = torch.cat(tuple(alpha), dim=0) + alpha = alpha.squeeze(1) + alpha = self.dropout(alpha) + + adj = torch.sparse.FloatTensor(indices, alpha, torch.Size([nprime, nprime])) + out.append(torch.sparse.mm(adj, h)[mapped_nodes]) + + return out + + @staticmethod + def cal_attention(x, y): + att_x = torch.mean(x, dim=1).reshape(-1, 1) + att_y = torch.mean(y, dim=1).reshape(-1, 1) + att = att_x.mm(torch.t(att_y)) + return ( + torch.mean( + x.reshape(x.shape[0], 1, x.shape[1]).repeat(1, y.shape[0], 1) + * y.reshape(1, y.shape[0], y.shape[1]).repeat(x.shape[0], 1, 1), + dim=2, + ) + - att + ) From 4f69ab6ed84b3bbbb34180735e5e9acdd37ae031 Mon Sep 17 00:00:00 2001 From: Dong Zhou Date: Sat, 28 Nov 2020 08:25:38 +0800 Subject: [PATCH 24/30] clean up --- examples/portfolio_optimization_example.ipynb | 437 ------------------ examples/run_all_model_records/0/meta.yaml | 4 - 2 files changed, 441 deletions(-) delete mode 100644 examples/portfolio_optimization_example.ipynb delete mode 100644 examples/run_all_model_records/0/meta.yaml diff --git a/examples/portfolio_optimization_example.ipynb b/examples/portfolio_optimization_example.ipynb deleted file mode 100644 index 7ef593efa..000000000 --- a/examples/portfolio_optimization_example.ipynb +++ /dev/null @@ -1,437 +0,0 @@ -{ - "metadata": { - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.9-final" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - } - }, - "nbformat": 4, - "nbformat_minor": 2, - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import sys\n", - "import copy\n", - "from pathlib import Path\n", - "\n", - "import qlib\n", - "import numpy as np\n", - "import pandas as pd\n", - "from qlib.config import REG_CN\n", - "from qlib.contrib.model.gbdt import LGBModel\n", - "from qlib.contrib.data.handler import Alpha158\n", - "from qlib.contrib.strategy.strategy import TopkDropoutStrategy\n", - "from qlib.contrib.evaluate import (\n", - " backtest as normal_backtest,\n", - " risk_analysis,\n", - ")\n", - "from qlib.utils import exists_qlib_data, init_instance_by_config\n", - "from qlib.workflow import R\n", - "from qlib.workflow.record_temp import SignalRecord, PortAnaRecord\n", - "from qlib.utils import flatten_dict" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[36502:MainThread](2020-11-27 16:26:57,240) INFO - qlib.Initialization - [__init__.py:41] - default_conf: client.\n", - "[36502:MainThread](2020-11-27 16:26:57,242) WARNING - qlib.Initialization - [__init__.py:57] - redis connection failed(host=127.0.0.1 port=6379), cache will not be used!\n", - "[36502:MainThread](2020-11-27 16:26:57,243) INFO - qlib.Initialization - [__init__.py:76] - qlib successfully initialized based on client settings.\n", - "[36502:MainThread](2020-11-27 16:26:57,244) INFO - qlib.Initialization - [__init__.py:79] - data_path=/home/dongzho/.qlib/qlib_data/cn_data\n" - ] - } - ], - "source": [ - "# use default data\n", - "# NOTE: need to download data from remote: python scripts/get_data.py qlib_data_cn --target_dir ~/.qlib/qlib_data/cn_data\n", - "provider_uri = \"~/.qlib/qlib_data/cn_data\" # target_dir\n", - "if not exists_qlib_data(provider_uri):\n", - " print(f\"Qlib data is not found in {provider_uri}\")\n", - " sys.path.append(str(Path.cwd().parent.joinpath(\"scripts\")))\n", - " from get_data import GetData\n", - " GetData().qlib_data(target_dir=provider_uri, region=REG_CN)\n", - "qlib.init(provider_uri=provider_uri, region=REG_CN)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "market = \"csi300\"\n", - "benchmark = \"SH000300\"" - ] - }, - { - "source": [ - "## Model Training" - ], - "cell_type": "markdown", - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[36502:MainThread](2020-11-27 16:27:17,338) INFO - qlib.timer - [log.py:81] - Time cost: 19.994s | Loading data Done\n", - "[36502:MainThread](2020-11-27 16:27:18,164) INFO - qlib.timer - [log.py:81] - Time cost: 0.245s | DropnaLabel Done\n", - "[36502:MainThread](2020-11-27 16:27:26,086) INFO - qlib.timer - [log.py:81] - Time cost: 7.921s | CSZScoreNorm Done\n", - "[36502:MainThread](2020-11-27 16:27:26,087) INFO - qlib.timer - [log.py:81] - Time cost: 8.747s | fit & process data Done\n", - "[36502:MainThread](2020-11-27 16:27:26,088) INFO - qlib.timer - [log.py:81] - Time cost: 28.744s | Init data Done\n", - "[36502:MainThread](2020-11-27 16:27:26,097) INFO - qlib.workflow - [exp.py:180] - Experiment 2 starts running ...\n", - "[36502:MainThread](2020-11-27 16:27:26,221) INFO - qlib.workflow - [recorder.py:234] - Recorder 3fa4def1f6694119a3d336a7a06c88cb starts running under Experiment 2 ...\n", - "[36502:MainThread](2020-11-27 16:27:26,223) INFO - qlib.workflow - [expm.py:251] - No tracking URI is provided. The default tracking URI is set as `mlruns` under the working directory.\n", - "Training until validation scores don't improve for 50 rounds\n", - "[20]\ttrain's l2: 0.990559\tvalid's l2: 0.994332\n", - "[40]\ttrain's l2: 0.98687\tvalid's l2: 0.993702\n", - "[60]\ttrain's l2: 0.984308\tvalid's l2: 0.993503\n", - "[80]\ttrain's l2: 0.982202\tvalid's l2: 0.993446\n", - "[100]\ttrain's l2: 0.980318\tvalid's l2: 0.993423\n", - "[120]\ttrain's l2: 0.97854\tvalid's l2: 0.993409\n", - "[140]\ttrain's l2: 0.97679\tvalid's l2: 0.993413\n", - "[160]\ttrain's l2: 0.975116\tvalid's l2: 0.993473\n", - "Early stopping, best iteration is:\n", - "[127]\ttrain's l2: 0.977957\tvalid's l2: 0.993381\n" - ] - } - ], - "source": [ - "###################################\n", - "# train model\n", - "###################################\n", - "data_handler_config = {\n", - " \"start_time\": \"2008-01-01\",\n", - " \"end_time\": \"2020-08-01\",\n", - " \"fit_start_time\": \"2008-01-01\",\n", - " \"fit_end_time\": \"2014-12-31\",\n", - " \"instruments\": market,\n", - "}\n", - "\n", - "task = {\n", - " \"model\": {\n", - " \"class\": \"LGBModel\",\n", - " \"module_path\": \"qlib.contrib.model.gbdt\",\n", - " \"kwargs\": {\n", - " \"loss\": \"mse\",\n", - " \"colsample_bytree\": 0.8879,\n", - " \"learning_rate\": 0.0421,\n", - " \"subsample\": 0.8789,\n", - " \"lambda_l1\": 205.6999,\n", - " \"lambda_l2\": 580.9768,\n", - " \"max_depth\": 8,\n", - " \"num_leaves\": 210,\n", - " \"num_threads\": 20,\n", - " },\n", - " },\n", - " \"dataset\": {\n", - " \"class\": \"DatasetH\",\n", - " \"module_path\": \"qlib.data.dataset\",\n", - " \"kwargs\": {\n", - " \"handler\": {\n", - " \"class\": \"Alpha158\",\n", - " \"module_path\": \"qlib.contrib.data.handler\",\n", - " \"kwargs\": data_handler_config,\n", - " },\n", - " \"segments\": {\n", - " \"train\": (\"2008-01-01\", \"2014-12-31\"),\n", - " \"valid\": (\"2015-01-01\", \"2016-12-31\"),\n", - " \"test\": (\"2017-01-01\", \"2017-12-31\"), # NOTE: use a shorter time range\n", - " },\n", - " },\n", - " },\n", - "}\n", - "\n", - "# model initiaiton\n", - "model = init_instance_by_config(task[\"model\"])\n", - "dataset = init_instance_by_config(task[\"dataset\"])\n", - "\n", - "# start exp to train model\n", - "with R.start(experiment_name=\"train_model\"):\n", - " R.log_params(**flatten_dict(task))\n", - " model.fit(dataset)\n", - " R.save_objects(trained_model=model)\n", - " rid = R.get_recorder().id\n" - ] - }, - { - "source": [ - "## Optimization Based Strategy" - ], - "cell_type": "markdown", - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "from qlib.contrib.strategy.strategy import BaseStrategy\n", - "\n", - "\n", - "class OptBasedStrategy(BaseStrategy):\n", - " \"\"\"Optimization Based Strategy\"\"\"\n", - "\n", - " def __init__(self, data_handler, cov_estimator, optimizer):\n", - " self.data_handler = data_handler\n", - " self.cov_estimator = cov_estimator\n", - " self.optimizer = optimizer\n", - "\n", - " def generate_order_list(self, score_series, current, trade_exchange, pred_date, trade_date):\n", - " \"\"\"\n", - " Parameters\n", - " -----------\n", - " score_series : pd.Seires\n", - " stock_id , score.\n", - " current : Position()\n", - " current of account.\n", - " trade_exchange : Exchange()\n", - " exchange.\n", - " trade_date : pd.Timestamp\n", - " date.\n", - " \"\"\"\n", - " score_series = score_series.dropna()\n", - "\n", - " # check stock holdings, if\n", - " # 1. doesn't have score: target amount = 0 (force sell)\n", - " # 2. stock not tradable: target amount = current amount\n", - " current_position = current.get_stock_amount_dict()\n", - " target_position = {}\n", - " for stock_id in current_position:\n", - " if not trade_exchange.is_stock_tradable(stock_id=stock_id, trade_date=trade_date):\n", - " target_position[stock_id] = current_position[stock_id]\n", - " elif stock_id not in score_series.index:\n", - " target_position[stock_id] = 0\n", - " else:\n", - " # need to be solved by optimizer\n", - " pass\n", - "\n", - " # filter scores, if\n", - " # 1. kept in `amount_dict` by previous rules\n", - " # 2. not tradable\n", - " skipped = []\n", - " for stock_id in score_series.index:\n", - " if stock_id in target_position:\n", - " skipped.append(stock_id)\n", - " elif not trade_exchange.is_stock_tradable(stock_id=stock_id, trade_date=trade_date):\n", - " skipped.append(stock_id)\n", - " score_series = score_series[~score_series.index.isin(skipped)]\n", - "\n", - " # calc remaining value\n", - " current_value = pd.Series({\n", - " stock_id: current.get_stock_price(stock_id) * amount\n", - " for stock_id, amount in current_position.items()\n", - " })\n", - " risk_total_value = self.get_risk_degree(trade_date) * current.calculate_value()\n", - " traded_value = risk_total_value - current_value.loc[list(target_position)].sum()\n", - "\n", - " # portfolio init weight\n", - " init_weight = current_value.reindex(score_series.index, fill_value=0)\n", - " init_weight_sum = init_weight.sum()\n", - " if init_weight_sum > 0:\n", - " init_weight /= init_weight_sum\n", - "\n", - " # covariance estimation\n", - " selector = (self.data_handler.get_range_selector(pred_date, 252), score_series.index)\n", - " price = self.data_handler.fetch(selector, level=None, squeeze=True)\n", - " cov = self.cov_estimator(price)\n", - " cov = cov.reindex(\n", - " index=score_series.index, \n", - " columns=score_series.index, \n", - " #fill_value=cov.max().max()\n", - " )\n", - "\n", - " # optimize target portfolio\n", - " try:\n", - " if init_weight.sum() > 0:\n", - " target_weight = self.optimizer(cov, score_series, init_weight)\n", - " else:\n", - " target_weight = self.optimizer(cov, score_series)\n", - " target_weight = target_weight[target_weight > 1e-6]\n", - " for stock_id, weight in target_weight.items():\n", - " target_position[stock_id] = int(traded_value * weight / trade_exchange.get_close(stock_id, pred_date))\n", - " except Exception as e:\n", - " print('Unknown exception:', trade_date, e)\n", - " for stock_id in score_series.index:\n", - " if stock_id in current_position:\n", - " target_position[stock_id] = current_position[stock_id]\n", - "\n", - " # generate order list\n", - " order_list = trade_exchange.generate_order_for_target_amount_position(\n", - " target_position=target_position,\n", - " current_position=current_position,\n", - " trade_date=trade_date,\n", - " )\n", - "\n", - " return order_list" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from qlib.data.dataset.loader import QlibDataLoader\n", - "from qlib.data.dataset.handler import DataHandler\n", - "from qlib.model.riskmodel import ShrinkCovEstimator\n", - "from qlib.portfolio.optimizer import PortfolioOptimizer" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[36502:MainThread](2020-11-27 16:27:43,722) INFO - qlib.timer - [log.py:81] - Time cost: 6.369s | Loading data Done\n", - "[36502:MainThread](2020-11-27 16:27:43,724) INFO - qlib.timer - [log.py:81] - Time cost: 6.371s | Init data Done\n" - ] - } - ], - "source": [ - "data_loader = QlibDataLoader([\"$close\"])\n", - "data_handler = DataHandler(\"all\", \"2015-01-01\", \"2020-08-01\", data_loader)\n", - "cov_estimator = ShrinkCovEstimator(nan_option=\"mask\")\n", - "optimizer = PortfolioOptimizer(\"mvo\", lamb=2, delta=0.2, tol=1e-5)\n", - "strategy = OptBasedStrategy(data_handler, cov_estimator, optimizer)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[36502:MainThread](2020-11-27 16:27:43,761) INFO - qlib.workflow - [exp.py:180] - Experiment 3 starts running ...\n", - "[36502:MainThread](2020-11-27 16:27:43,779) INFO - qlib.workflow - [recorder.py:234] - Recorder 67d105113f424259889fc0b6b0b94973 starts running under Experiment 3 ...\n", - "[36502:MainThread](2020-11-27 16:27:43,780) INFO - qlib.workflow - [expm.py:251] - No tracking URI is provided. The default tracking URI is set as `mlruns` under the working directory.\n", - "[36502:MainThread](2020-11-27 16:27:43,991) INFO - qlib.workflow - [record_temp.py:127] - Signal record 'pred.pkl' has been saved as the artifact of the Experiment 3\n", - "[36502:MainThread](2020-11-27 16:27:44,050) INFO - qlib.Evaluate - [evaluate.py:161] - Create new exchange\n", - "'The following are prediction results of the LGBModel model.'\n", - " score\n", - "datetime instrument \n", - "2017-01-03 SH600000 -0.053414\n", - " SH600008 0.001820\n", - " SH600009 0.023472\n", - " SH600010 -0.005625\n", - " SH600015 -0.137476\n", - "/home/dongzho/miniconda3/lib/python3.7/site-packages/ipykernel_launcher.py:55: DeprecationWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.\n", - "/home/dongzho/qlib/qlib/portfolio/optimizer.py:256: UserWarning: optimization not success (9)\n", - " warnings.warn(f\"optimization not success ({sol.status})\")\n", - "Unknown exception: 2017-01-16 00:00:00 ('SZ300104', Timestamp('2017-01-13 00:00:00'))\n", - "Unknown exception: 2017-01-23 00:00:00 ('SZ000671', Timestamp('2017-01-20 00:00:00'))\n", - "Unknown exception: 2017-03-03 00:00:00 ('SZ002465', Timestamp('2017-03-02 00:00:00'))\n", - "Unknown exception: 2017-03-07 00:00:00 ('SH601127', Timestamp('2017-03-06 00:00:00'))\n", - "/home/dongzho/qlib/qlib/portfolio/optimizer.py:256: UserWarning: optimization not success (4)\n", - " warnings.warn(f\"optimization not success ({sol.status})\")\n", - "Unknown exception: 2017-05-08 00:00:00 ('SH601727', Timestamp('2017-05-05 00:00:00'))\n", - "Unknown exception: 2017-06-20 00:00:00 ('SH600036', Timestamp('2017-06-19 00:00:00'))\n", - "Unknown exception: 2017-06-21 00:00:00 ('SH600739', Timestamp('2017-06-20 00:00:00'))\n", - "Unknown exception: 2017-06-29 00:00:00 ('SZ300168', Timestamp('2017-06-28 00:00:00'))\n", - "Unknown exception: 2017-09-01 00:00:00 ('SH601088', Timestamp('2017-08-31 00:00:00'))\n", - "Unknown exception: 2017-09-12 00:00:00 ('SH601872', Timestamp('2017-09-11 00:00:00'))\n", - "Unknown exception: 2017-09-21 00:00:00 ('SH600100', Timestamp('2017-09-20 00:00:00'))\n", - "Unknown exception: 2017-09-22 00:00:00 ('SH600021', Timestamp('2017-09-21 00:00:00'))\n", - "Unknown exception: 2017-10-11 00:00:00 ('SH600959', Timestamp('2017-10-10 00:00:00'))\n", - "Unknown exception: 2017-10-25 00:00:00 ('SZ000792', Timestamp('2017-10-24 00:00:00'))\n", - "Unknown exception: 2017-12-26 00:00:00 ('SH600682', Timestamp('2017-12-25 00:00:00'))\n", - "[36502:MainThread](2020-11-27 17:28:14,269) INFO - qlib.workflow - [record_temp.py:249] - Portfolio analysis record 'port_analysis.pkl' has been saved as the artifact of the Experiment 3\n", - "'The following are analysis results of the excess return without cost.'\n", - " risk\n", - "mean 0.001247\n", - "std 0.005437\n", - "annualized_return 0.314237\n", - "information_ratio 3.640637\n", - "max_drawdown -0.033416\n", - "'The following are analysis results of the excess return with cost.'\n", - " risk\n", - "mean 0.001028\n", - "std 0.005432\n", - "annualized_return 0.259041\n", - "information_ratio 3.003970\n", - "max_drawdown -0.041455\n" - ] - } - ], - "source": [ - "###################################\n", - "# prediction, backtest & analysis\n", - "###################################\n", - "port_analysis_config = {\n", - " \"strategy\": strategy,\n", - " \"backtest\": {\n", - " \"verbose\": False,\n", - " \"limit_threshold\": 0.095,\n", - " \"account\": 100000000,\n", - " \"benchmark\": benchmark,\n", - " \"deal_price\": \"close\",\n", - " \"open_cost\": 0.0005,\n", - " \"close_cost\": 0.0015,\n", - " \"min_cost\": 5,\n", - " },\n", - "}\n", - "\n", - "\n", - "# backtest and analysis\n", - "with R.start(experiment_name=\"backtest_analysis\"):\n", - " recorder = R.get_recorder(rid, experiment_name=\"train_model\")\n", - " model = recorder.load_object(\"trained_model\")\n", - "\n", - " # prediction\n", - " recorder = R.get_recorder()\n", - " ba_rid = recorder.id\n", - " sr = SignalRecord(model, dataset, recorder)\n", - " sr.generate()\n", - "\n", - " # backtest & analysis\n", - " par = PortAnaRecord(recorder, port_analysis_config)\n", - " par.generate()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ] -} \ No newline at end of file diff --git a/examples/run_all_model_records/0/meta.yaml b/examples/run_all_model_records/0/meta.yaml deleted file mode 100644 index df706fda3..000000000 --- a/examples/run_all_model_records/0/meta.yaml +++ /dev/null @@ -1,4 +0,0 @@ -artifact_location: file:///home/v-hozhan/qlib/examples/run_all_model_records/0 -experiment_id: '0' -lifecycle_stage: active -name: Default From dc8bf6f077cfc2a40a993429621b1287e7621dbe Mon Sep 17 00:00:00 2001 From: Dong Zhou Date: Sat, 28 Nov 2020 08:26:18 +0800 Subject: [PATCH 25/30] add colab button --- examples/workflow_by_code.ipynb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/workflow_by_code.ipynb b/examples/workflow_by_code.ipynb index 81dbf3e31..20fce92fe 100644 --- a/examples/workflow_by_code.ipynb +++ b/examples/workflow_by_code.ipynb @@ -1,5 +1,12 @@ { "cells": [ + { + "source": [ + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/microsoft/qlib/blob/main/examples/workflow_by_code.ipynb)" + ], + "cell_type": "markdown", + "metadata": {} + }, { "cell_type": "code", "execution_count": null, @@ -369,4 +376,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file From cde5c59b7e8bcd6aade991434f975ecf408e6c3c Mon Sep 17 00:00:00 2001 From: Dong Zhou Date: Sat, 28 Nov 2020 08:38:42 +0800 Subject: [PATCH 26/30] fix colab link --- examples/workflow_by_code.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/workflow_by_code.ipynb b/examples/workflow_by_code.ipynb index 20fce92fe..d5711e0b5 100644 --- a/examples/workflow_by_code.ipynb +++ b/examples/workflow_by_code.ipynb @@ -2,7 +2,7 @@ "cells": [ { "source": [ - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/microsoft/qlib/blob/main/examples/workflow_by_code.ipynb)" + "\"Open" ], "cell_type": "markdown", "metadata": {} From d820d5b8f6abca3e0a3840f0fab62ca26f56c438 Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Sat, 28 Nov 2020 10:40:31 +0800 Subject: [PATCH 27/30] Delete Hats --- examples/benchmarks/HATS/README.md | 12 - examples/benchmarks/HATS/requirements.txt | 4 - .../benchmarks/HATS/worflow_config_hats.yaml | 77 --- qlib/contrib/model/pytorch_hats.py | 491 ------------------ 4 files changed, 584 deletions(-) delete mode 100644 examples/benchmarks/HATS/README.md delete mode 100644 examples/benchmarks/HATS/requirements.txt delete mode 100644 examples/benchmarks/HATS/worflow_config_hats.yaml delete mode 100644 qlib/contrib/model/pytorch_hats.py diff --git a/examples/benchmarks/HATS/README.md b/examples/benchmarks/HATS/README.md deleted file mode 100644 index 1a0ac7bb3..000000000 --- a/examples/benchmarks/HATS/README.md +++ /dev/null @@ -1,12 +0,0 @@ -## Requirement - -* pandas==1.1.2 -* numpy==1.17.4 -* scikit_learn==0.23.2 -* torch==1.7.0 - -## HATS - -* HATS is a a hierarchical attention network for stock prediction which uses attention layers to broadcast weight for stock market prediction. HATS selectively aggregates information on different relation types and adds the information to the representations of each company. HATS is used as a module with initialized node representations.Furthermore, HATS can predict not only individual stock prices but also market index movements, which is similar to the graph classification task. -* HATS uses pretrained model of GRU and LSTM. The code of GRU and LSTM used in Qlib is a pyTorch implemention of GRU and LSTM. -* Paper address:HATS: A Hierarchical Graph Attention Network for Stock Movement Prediction https://arxiv.org/pdf/1908.07999.pdf \ No newline at end of file diff --git a/examples/benchmarks/HATS/requirements.txt b/examples/benchmarks/HATS/requirements.txt deleted file mode 100644 index 16de0a438..000000000 --- a/examples/benchmarks/HATS/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pandas==1.1.2 -numpy==1.17.4 -scikit_learn==0.23.2 -torch==1.7.0 diff --git a/examples/benchmarks/HATS/worflow_config_hats.yaml b/examples/benchmarks/HATS/worflow_config_hats.yaml deleted file mode 100644 index ea9f21e76..000000000 --- a/examples/benchmarks/HATS/worflow_config_hats.yaml +++ /dev/null @@ -1,77 +0,0 @@ -provider_uri: "~/.qlib/qlib_data/cn_data" -region: cn -market: &market csi300 -benchmark: &benchmark SH000300 -data_handler_config: &data_handler_config - start_time: 2008-01-01 - end_time: 2020-08-01 - fit_start_time: 2008-01-01 - fit_end_time: 2014-12-31 - instruments: *market - infer_processors: - - class: RobustZScoreNorm - kwargs: - fields_group: feature - clip_outlier: true - - class: Fillna - kwargs: - fields_group: feature - learn_processors: - - class: DropnaLabel - - class: CSRankNorm - kwargs: - fields_group: label - label: ["Ref($close, -2) / Ref($close, -1) - 1"] -port_analysis_config: &port_analysis_config - strategy: - class: TopkDropoutStrategy - module_path: qlib.contrib.strategy.strategy - kwargs: - topk: 50 - n_drop: 5 - backtest: - verbose: False - limit_threshold: 0.095 - account: 100000000 - benchmark: *benchmark - deal_price: close - open_cost: 0.0005 - close_cost: 0.0015 - min_cost: 5 -task: - model: - class: HATS - module_path: qlib.contrib.model.pytorch_hats - kwargs: - d_feat: 6 - hidden_size: 64 - num_layers: 2 - dropout: 0.6 - n_epochs: 200 - lr: 1e-3 - early_stop: 20 - metric: loss - loss: mse - base_model: LSTM - seed: 0 - GPU: 0 - dataset: - class: DatasetH - module_path: qlib.data.dataset - kwargs: - handler: - class: ALPHA360 - module_path: qlib.contrib.data.handler - kwargs: *data_handler_config - segments: - train: [2008-01-01, 2014-12-31] - valid: [2015-01-01, 2016-12-31] - test: [2017-01-01, 2020-08-01] - record: - - class: SignalRecord - module_path: qlib.workflow.record_temp - kwargs: {} - - class: PortAnaRecord - module_path: qlib.workflow.record_temp - kwargs: - config: *port_analysis_config \ No newline at end of file diff --git a/qlib/contrib/model/pytorch_hats.py b/qlib/contrib/model/pytorch_hats.py deleted file mode 100644 index 7affea73c..000000000 --- a/qlib/contrib/model/pytorch_hats.py +++ /dev/null @@ -1,491 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from __future__ import division -from __future__ import print_function - -import os -import numpy as np -import pandas as pd -import copy -from ...utils import create_save_path -from ...log import get_module_logger - -import torch -import torch.nn as nn -import torch.optim as optim - -from ...model.base import Model -from ...data.dataset import DatasetH -from ...data.dataset.handler import DataHandlerLP - - -class HATS(Model): - """HATS Model - - Parameters - ---------- - d_feat : int - input dimension for each time step - metric: str - the evaluate metric used in early stop - optimizer : str - optimizer name - GPU : str - the GPU ID(s) used for training - """ - - def __init__( - self, - d_feat=6, - hidden_size=64, - num_layers=2, - dropout=0.5, - n_epochs=200, - lr=0.01, - metric="", - early_stop=20, - loss="mse", - base_model="GRU", - with_pretrain=True, - optimizer="adam", - GPU="0", - seed=0, - **kwargs - ): - # Set logger. - self.logger = get_module_logger("HATS") - self.logger.info("HATS pytorch version...") - - # set hyper-parameters. - self.d_feat = d_feat - self.hidden_size = hidden_size - self.num_layers = num_layers - self.dropout = dropout - self.n_epochs = n_epochs - self.lr = lr - self.metric = metric - self.early_stop = early_stop - self.optimizer = optimizer.lower() - self.loss = loss - self.base_model = base_model - self.with_pretrain = with_pretrain - self.visible_GPU = GPU - self.use_gpu = torch.cuda.is_available() - self.seed = seed - - self.logger.info( - "HATS parameters setting:" - "\nd_feat : {}" - "\nhidden_size : {}" - "\nnum_layers : {}" - "\ndropout : {}" - "\nn_epochs : {}" - "\nlr : {}" - "\nmetric : {}" - "\nearly_stop : {}" - "\noptimizer : {}" - "\nloss_type : {}" - "\nbase_model : {}" - "\nwith_pretrain : {}" - "\nvisible_GPU : {}" - "\nuse_GPU : {}" - "\nseed : {}".format( - d_feat, - hidden_size, - num_layers, - dropout, - n_epochs, - lr, - metric, - early_stop, - optimizer.lower(), - loss, - base_model, - with_pretrain, - GPU, - self.use_gpu, - seed, - ) - ) - - self.HATS_model = HATSModel( - d_feat=self.d_feat, - hidden_size=self.hidden_size, - num_layers=self.num_layers, - dropout=self.dropout, - base_model=self.base_model, - ) - if optimizer.lower() == "adam": - self.train_optimizer = optim.Adam(self.HATS_model.parameters(), lr=self.lr) - elif optimizer.lower() == "gd": - self.train_optimizer = optim.SGD(self.HATS_model.parameters(), lr=self.lr) - else: - raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) - - self._fitted = False - if self.use_gpu: - self.HATS_model.cuda() - # set the visible GPU - if self.visible_GPU: - os.environ["CUDA_VISIBLE_DEVICES"] = self.visible_GPU - - def mse(self, pred, label): - loss = (pred - label) ** 2 - return torch.mean(loss) - - def loss_fn(self, pred, label): - mask = ~torch.isnan(label) - - if self.loss == "mse": - return self.mse(pred[mask], label[mask]) - - raise ValueError("unknown loss `%s`" % self.loss) - - def metric_fn(self, pred, label): - mask = torch.isfinite(label) - - if self.metric == "" or self.metric == "loss": # use loss - return -self.loss_fn(pred[mask], label[mask]) - - raise ValueError("unknown metric `%s`" % self.metric) - - def get_daily_inter(self, df, shuffle=False): - # organize the train data into daily inter as daily batches - daily_count = df.groupby(level=0).size().values - daily_index = np.roll(np.cumsum(daily_count), 1) - daily_index[0] = 0 - if shuffle: - # shuffle the daily inter data - daily_shuffle = list(zip(daily_index, daily_count)) - np.random.shuffle(daily_shuffle) - daily_index, daily_count = zip(*daily_shuffle) - return daily_index, daily_count - - def train_epoch(self, x_train, y_train): - - x_train_values = x_train.values - y_train_values = np.squeeze(y_train.values) - - self.HATS_model.train() - - # organize the train data into daily inter as daily batches - daily_index, daily_count = self.get_daily_inter(x_train, shuffle=True) - - for idx, count in zip(daily_index, daily_count): - batch = slice(idx, idx + count) - feature = torch.from_numpy(x_train_values[batch]).float() - label = torch.from_numpy(y_train_values[batch]).float() - - if self.use_gpu: - feature = feature.cuda() - label = label.cuda() - - pred = self.HATS_model(feature) - loss = self.loss_fn(pred, label) - - self.train_optimizer.zero_grad() - loss.backward() - torch.nn.utils.clip_grad_value_(self.HATS_model.parameters(), 3.0) - self.train_optimizer.step() - - def test_epoch(self, data_x, data_y): - - # prepare testing data - x_values = data_x.values - y_values = np.squeeze(data_y.values) - - self.HATS_model.eval() - - scores = [] - losses = [] - - # organize the test data into daily inter as daily batches - daily_index, daily_count = self.get_daily_inter(data_x, shuffle=False) - - for idx, count in zip(daily_index, daily_count): - batch = slice(idx, idx + count) - feature = torch.from_numpy(x_values[batch]).float() - label = torch.from_numpy(y_values[batch]).float() - - if self.use_gpu: - feature = feature.cuda() - label = label.cuda() - - pred = self.HATS_model(feature) - loss = self.loss_fn(pred, label) - losses.append(loss.item()) - - score = self.metric_fn(pred, label) - scores.append(score.item()) - - return np.mean(losses), np.mean(scores) - - def fit( - self, - dataset: DatasetH, - evals_result=dict(), - verbose=True, - save_path=None, - ): - - df_train, df_valid, df_test = dataset.prepare( - ["train", "valid", "test"], col_set=["feature", "label"], data_key=DataHandlerLP.DK_L - ) - - x_train, y_train = df_train["feature"], df_train["label"] - x_valid, y_valid = df_valid["feature"], df_valid["label"] - - if save_path == None: - save_path = create_save_path(save_path) - stop_steps = 0 - best_score = -np.inf - best_epoch = 0 - evals_result["train"] = [] - evals_result["valid"] = [] - - # load pretrained base_model - if self.with_pretrain: - self.logger.info("Loading pretrained model...") - if self.base_model == "LSTM": - from ...contrib.model.pytorch_lstm import LSTMModel - - pretrained_model = LSTMModel() - pretrained_model.load_state_dict(torch.load("benchmarks/LSTM/model_lstm_csi300.pkl")) - elif self.base_model == "GRU": - from ...contrib.model.pytorch_gru import GRUModel - - pretrained_model = GRUModel() - pretrained_model.load_state_dict(torch.load("benchmarks/GRU/model_gru_csi300.pkl")) - model_dict = self.HATS_model.state_dict() - pretrained_dict = {k: v for k, v in pretrained_model.state_dict().items() if k in model_dict} - model_dict.update(pretrained_dict) - self.HATS_model.load_state_dict(model_dict) - self.logger.info("Loading pretrained model Done...") - - # train - self.logger.info("training...") - self._fitted = True - - for step in range(self.n_epochs): - self.logger.info("Epoch%d:", step) - self.logger.info("training...") - self.train_epoch(x_train, y_train) - self.logger.info("evaluating...") - train_loss, train_score = self.test_epoch(x_train, y_train) - val_loss, val_score = self.test_epoch(x_valid, y_valid) - self.logger.info("train %.6f, valid %.6f" % (train_score, val_score)) - evals_result["train"].append(train_score) - evals_result["valid"].append(val_score) - - if val_score > best_score: - best_score = val_score - stop_steps = 0 - best_epoch = step - best_param = copy.deepcopy(self.HATS_model.state_dict()) - else: - stop_steps += 1 - if stop_steps >= self.early_stop: - self.logger.info("early stop") - break - - self.logger.info("best score: %.6lf @ %d" % (best_score, best_epoch)) - self.HATS_model.load_state_dict(best_param) - torch.save(best_param, save_path) - - if self.use_gpu: - torch.cuda.empty_cache() - - def predict(self, dataset): - if not self._fitted: - raise ValueError("model is not fitted yet!") - - x_test = dataset.prepare("test", col_set="feature") - index = x_test.index - self.HATS_model.eval() - x_values = x_test.values - sample_num = x_values.shape[0] - preds = [] - - # organize the data into daily inter as daily batches - daily_index, daily_count = self.get_daily_inter(x_test, shuffle=False) - - for idx, count in zip(daily_index, daily_count): - batch = slice(idx, idx + count) - x_batch = torch.from_numpy(x_values[batch]).float() - - if self.use_gpu: - x_batch = x_batch.cuda() - - with torch.no_grad(): - if self.use_gpu: - pred = self.HATS_model(x_batch).detach().cpu().numpy() - else: - pred = self.HATS_model(x_batch).detach().numpy() - - preds.append(pred) - - return pd.Series(np.concatenate(preds), index=index) - - -class HATSModel(nn.Module): - def __init__(self, d_feat=6, hidden_size=64, num_layers=2, dropout=0.0, base_model="GRU"): - super().__init__() - - if base_model == "GRU": - self.model = nn.GRU( - input_size=d_feat, - hidden_size=hidden_size, - num_layers=num_layers, - batch_first=True, - dropout=dropout, - ) - elif base_model == "LSTM": - self.model = nn.LSTM( - input_size=d_feat, - hidden_size=hidden_size, - num_layers=num_layers, - batch_first=True, - dropout=dropout, - ) - else: - raise ValueError("unknown base model name `%s`" % base_model) - - self.hidden_size = hidden_size - self.bn1 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) - self.fc = nn.Linear(hidden_size, hidden_size) - self.bn2 = nn.BatchNorm1d(num_features=hidden_size, track_running_stats=False) - self.fc_out = nn.Linear(hidden_size, 1) - self.leaky_relu = nn.LeakyReLU() - self.softmax = nn.Softmax(dim=1) - self.d_feat = d_feat - - num_head_att = [1] * num_layers - hidden_dim = [hidden_size] * num_layers - dims = [d_feat] + [d * nh for (d, nh) in zip(hidden_dim, num_head_att[:-1])] + [num_head_att[-1]] - in_dims = dims[:-1] - out_dims = [d // nh for (d, nh) in zip(dims[1:], num_head_att)] - self.attn = nn.ModuleList( - [GraphAttention(i, o, nh, dropout) for (i, o, nh) in zip(in_dims, out_dims, num_head_att)] - ) - self.bns = nn.ModuleList([nn.BatchNorm1d(dim) for dim in dims[1:-1]]) - self.dropout = nn.Dropout(dropout) - self.elu = nn.ELU() - - def forward(self, x): - x = x.reshape(len(x), self.d_feat, -1) # [N, F, T] - x = x.permute(0, 2, 1) # [N, T, F] - out, _ = self.model(x) - hidden = out[:, -1, :] - hidden = self.bn1(hidden) - attention = GraphAttention.cal_attention(hidden, hidden) - output = attention.mm(hidden) - output = self.fc(output) - output = self.bn2(output) - output = self.leaky_relu(output) - return self.fc_out(output).squeeze() - - -class GraphAttention(nn.Module): - def __init__(self, input_dim, output_dim, num_heads, dropout=0.5): - - super().__init__() - - """ - Parameters - ---------- - input_dim : int - Dimension of input node features. - output_dim : int - Dimension of output node features. - num_heads : list of ints - Number of attention heads in each hidden layer and output layer. Must be non empty. Note that len(num_heads) = len(hidden_dims)+1. - dropout : float - Dropout rate. Default: 0.5. - """ - - self.input_dim = input_dim - self.output_dim = output_dim - self.num_heads = num_heads - - self.fcs = nn.ModuleList([nn.Linear(input_dim, output_dim) for _ in range(num_heads)]) - self.a = nn.ModuleList([nn.Linear(2 * output_dim, 1) for _ in range(num_heads)]) - - self.dropout = nn.Dropout(dropout) - self.softmax = nn.Softmax(dim=0) - self.leakyrelu = nn.LeakyReLU() - - def forward(self, features, nodes, mappings, rows): - - """ - Parameters - ---------- - features : torch.Tensor - An (n' x input_dim) tensor of input node features. - nodes : list of numpy array - nodes[i] is an array of the nodes in the ith layer of the - computation graph. - mappings : list of dictionary - mappings[i] is a dictionary mappings node v (labelled 0 to |V|-1) - in nodes[i] to its position in nodes[i]. For example, - if nodes[i] = [2,5], then mappings[i][2] = 0 and - mappings[i][5] = 1. - rows : numpy array - rows[i] is an array of neighbors of node i. - Returns - ------- - out : torch.Tensor - An (len(node_layers[-1]) x output_dim) tensor of output node features. - """ - - nprime = features.shape[0] - rows = [np.array([mappings[v] for v in row], dtype=np.int64) for row in rows] - sum_degs = np.hstack(([0], np.cumsum([len(row) for row in rows]))) - mapped_nodes = [mappings[v] for v in nodes] - indices = torch.LongTensor([[v, c] for (v, row) in zip(mapped_nodes, rows) for c in row]).t() - - out = [] - for k in range(self.num_heads): - h = self.fcs[k](features) - - nbr_h = torch.cat(tuple([h[row] for row in rows]), dim=0) - self_h = torch.cat( - tuple([h[mappings[nodes[i]]].repeat(len(row), 1) for (i, row) in enumerate(rows)]), dim=0 - ) - cat_h = torch.cat((self_h, nbr_h), dim=1) - - e = self.leakyrelu(self.a[k](cat_h)) - - alpha = [self.softmax(e[lo:hi]) for (lo, hi) in zip(sum_degs, sum_degs[1:])] - alpha = torch.cat(tuple(alpha), dim=0) - alpha = alpha.squeeze(1) - alpha = self.dropout(alpha) - - adj = torch.sparse.FloatTensor(indices, alpha, torch.Size([nprime, nprime])) - out.append(torch.sparse.mm(adj, h)[mapped_nodes]) - - return out - - @staticmethod - def cal_attention(x, y): - att_x = torch.mean(x, dim=1).reshape(-1, 1) - att_y = torch.mean(y, dim=1).reshape(-1, 1) - att = att_x.mm(torch.t(att_y)) - return ( - torch.mean( - x.reshape(x.shape[0], 1, x.shape[1]).repeat(1, y.shape[0], 1) - * y.reshape(1, y.shape[0], y.shape[1]).repeat(x.shape[0], 1, 1), - dim=2, - ) - - att - ) From eaa9f1a80dd764370f603064cd3f58f037b050bf Mon Sep 17 00:00:00 2001 From: Jactus Date: Sat, 28 Nov 2020 10:50:39 +0800 Subject: [PATCH 28/30] Fix tft --- README.md | 2 -- examples/benchmarks/TFT/tft.py | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 884bbb5c0..123134319 100644 --- a/README.md +++ b/README.md @@ -201,9 +201,7 @@ Here is a list of models built on `Qlib`. - [LSTM based on pytorcn](qlib/contrib/model/pytorch_lstm.py) - [ALSTM based on pytorcn](qlib/contrib/model/pytorch_alstm.py) - [GATs based on pytorch](qlib/contrib/model/pytorch_gats.py) -- [TabNet based on pytorch](qlib/contrib/model/tabnet.py) - [SFM based on pytorch](qlib/contrib/model/pytorch_sfm.py) -- [HATs based on pytorch](qlib/contrib/model/pytorch_hats.py) - [TFT based on tensorflow](examples/benchmarks/TFT/tft.py) Your PR of new Quant models is highly welcomed. diff --git a/examples/benchmarks/TFT/tft.py b/examples/benchmarks/TFT/tft.py index a3b4fc919..3387a5947 100644 --- a/examples/benchmarks/TFT/tft.py +++ b/examples/benchmarks/TFT/tft.py @@ -233,9 +233,8 @@ class TFTModel(ModelFT): tf.keras.backend.set_session(default_keras_session) predict = format_score(p90_forecast, "pred", 0) # self.label_shift - label = format_score(targets, "label", 0) # ===========================Predicting Process=========================== - return predict, label + return predict def finetune(self, dataset: DatasetH): """ From ac96dde4c992777961f5993898123ad2946fd320 Mon Sep 17 00:00:00 2001 From: Jactus Date: Sat, 28 Nov 2020 10:53:24 +0800 Subject: [PATCH 29/30] Rename DNN --- examples/benchmarks/{DNN => MLP}/requirements.txt | 0 .../workflow_config_dnn.yaml => MLP/workflow_config_mlp.yaml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename examples/benchmarks/{DNN => MLP}/requirements.txt (100%) rename examples/benchmarks/{DNN/workflow_config_dnn.yaml => MLP/workflow_config_mlp.yaml} (100%) diff --git a/examples/benchmarks/DNN/requirements.txt b/examples/benchmarks/MLP/requirements.txt similarity index 100% rename from examples/benchmarks/DNN/requirements.txt rename to examples/benchmarks/MLP/requirements.txt diff --git a/examples/benchmarks/DNN/workflow_config_dnn.yaml b/examples/benchmarks/MLP/workflow_config_mlp.yaml similarity index 100% rename from examples/benchmarks/DNN/workflow_config_dnn.yaml rename to examples/benchmarks/MLP/workflow_config_mlp.yaml From 627fc628e082094044970e652a2a97ac5ae81450 Mon Sep 17 00:00:00 2001 From: zhupr Date: Sat, 28 Nov 2020 12:57:00 +0800 Subject: [PATCH 30/30] Fix get_data.py detection --- examples/workflow_by_code.ipynb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/workflow_by_code.ipynb b/examples/workflow_by_code.ipynb index d5711e0b5..5a992e339 100644 --- a/examples/workflow_by_code.ipynb +++ b/examples/workflow_by_code.ipynb @@ -1,11 +1,11 @@ { "cells": [ { + "cell_type": "markdown", + "metadata": {}, "source": [ "\"Open" - ], - "cell_type": "markdown", - "metadata": {} + ] }, { "cell_type": "code", @@ -28,16 +28,17 @@ "import sys, site\n", "from pathlib import Path\n", "\n", - "TEMP_CODE_DIR = str(Path(\"~/tmp/qlib_code\").expanduser().resolve())\n", "\n", "try:\n", " import qlib\n", - " scripts_dir = Path.cwd().parent.joinpath(\"scripts\")\n", "except ImportError:\n", " # install qlib\n", " ! pip install pyqlib\n", " # reload\n", " site.main()\n", + "\n", + "scripts_dir = Path.cwd().parent.joinpath(\"scripts\")\n", + "if not scripts_dir.joinpath(\"get_data.py\").exists():\n", " # download get_data.py script\n", " scripts_dir = Path(\"~/tmp/qlib_code/scripts\").expanduser().resolve()\n", " scripts_dir.mkdir(parents=True, exist_ok=True)\n", @@ -376,4 +377,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +}