Skip to content

Instantly share code, notes, and snippets.

@elston
Last active May 19, 2025 13:10
Show Gist options
  • Select an option

  • Save elston/b54899209f4a74fedfb550ebdfb39205 to your computer and use it in GitHub Desktop.

Select an option

Save elston/b54899209f4a74fedfb550ebdfb39205 to your computer and use it in GitHub Desktop.
HuBERT-ECG demo with ptb-xl
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "602779eb-e2ac-43e8-b13f-cf9654c2aa6a",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import torch\n",
"import transformers\n",
"from transformers import HubertConfig\n",
"from HuBERT_ECG.code.hubert_ecg import HuBERTECGConfig, HuBERTECG as HuBERT\n",
"from HuBERT_ECG.code.hubert_ecg_classification import HuBERTForECGClassification as HuBERTClassification\n",
"from HuBERT_ECG.code.dataset import ECGDataset\n",
"from torch.utils.data import DataLoader\n",
"from tqdm import tqdm\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c5cc643e-f152-48f4-8f4d-c9f4caa3b85f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('2.5.1+cu124', '4.51.3')"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"torch.__version__, transformers.__version__"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "056eb1a2-f71c-4edf-abe5-b3ac60df328d",
"metadata": {},
"outputs": [],
"source": [
"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b1c0a76f-6eac-4796-9a90-a5d0187b63c3",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"device(type='cuda')"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"device"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "19d9831b-2531-420a-9ce2-d9f36bba0b0f",
"metadata": {},
"outputs": [],
"source": [
"cardio_learning_labels = pd.read_pickle('/libs/HuBERT_ECG/reproducibility/cardio-learning-labels.pkl')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "26a51f78-590f-44d6-a256-49f4cf43eb6f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array(['filename', 'age', 'sex', 'PAC|SVPB', 'AVCR', 'RAH', 'NORM',\n",
" 'AIVR', 'SAB', 'OLDMI', 'PACE', 'ISCI', 'ANMI', 'SAAWR', 'PSVT',\n",
" 'AP', 'LMI', 'ILMI', 'STE_', 'IVCD', 'IRBBB', 'AVBHG', 'AFL',\n",
" 'SARRH', 'STTVH', 'SVT', 'LNGQT', 'NDT', 'LQRSV', 'ABQRS', 'AB',\n",
" 'SBRAD', 'MI', 'VFL', 'HVOLT', 'NST_', 'VESR', 'PVT', 'VTRIG',\n",
" 'PWC', 'LVHV', 'RAD', 'LAA', 'LAFB/LPFB', 'PRC(S)', 'TRIGU', 'TPW',\n",
" 'AVB', 'BPAC', 'SARR', 'AVJR', 'BRU', 'CHD', 'AF', 'ASMI', 'INJLA',\n",
" 'INJAS', 'ATACH', 'RAHV', 'JEC', 'STACH', 'VESB', 'TUF', 'ISCIL',\n",
" 'PPW', 'ISCLA', 'INJIL', 'HYP', 'JE', 'SPRI', 'INJIN', 'IMI',\n",
" 'VTACH', '-ROT', 'HF', 'VPP', 'SQT', 'WPW', 'HVD', 'ANMIS',\n",
" 'AVNRT', 'CRBBB|RBBB', 'FB', 'ISCAS', 'RAF', 'EAMI', '+ROT',\n",
" '2AVB', 'AAR', 'CIAHB', 'EL', 'AVD', 'STC', 'BBB', 'QAB', 'ARH',\n",
" 'ANEUR', 'LOWT', '1AVB', 'STD_', 'FQRS', 'LAD', 'CMIS', 'PR',\n",
" 'UAB', 'STTC', 'SEHYP', 'ERE', 'AJR', 'LPR', 'ISC_', 'BIGU',\n",
" 'IPLMI', 'JPC', 'LAH', 'PRWP', 'VEB', 'TAB', 'CVCL/CCVCL', 'AMI',\n",
" 'ILBBB', 'LIS', 'LPFB', 'IPMI', 'PAC_NC', 'INJAL', 'AVBVC', 'NT_',\n",
" 'AFAFL', 'AVRT', 'VBIG', 'CD', 'LAFB', 'ALR', 'SR', 'PMI', 'ISCAL',\n",
" 'NSIVCB', 'STIAB', 'LVH', 'WAP', 'IIS', '2AVB2', 'CLBBB|LBBB',\n",
" 'NSSTTA', 'LVOLT', 'AED', 'VF', 'VCLVH', 'ISCA', 'VH', 'JTACH',\n",
" 'SVARR', 'SAMITROP-DEATH', 'BRADY', 'MIS', 'ALMI', 'RVH', 'TINV',\n",
" 'AH', 'DIG', 'RAAB', 'VPEX', 'RAB', 'VPC|VPB', '2AVB1', '3AVB'],\n",
" dtype=object)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cardio_learning_labels"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "a382612b-6b89-4c6e-ac24-297b4ddf494e",
"metadata": {},
"outputs": [],
"source": [
"num_labels = cardio_learning_labels[3:].shape[0]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9b64a032-6855-478e-b2e2-08b3acfea039",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"164"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"num_labels"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "654f0f36-3a31-4cc4-b68e-83964f43031b",
"metadata": {},
"outputs": [],
"source": [
"args = {\n",
" 'model_path': '/models/Edoardo-BS/HuBERT-ECG-SSL-Pretrained/hubert_ecg_large.pt',\n",
" 'path_to_dataset_csv': '/libs/HuBERT_ECG/reproducibility/ptbxl/ptbxl_all_test.csv',\n",
" 'ecg_dir_path': '/shared/ptb-xl',\n",
" 'num_labels': num_labels,\n",
" 'label_start_index': 4,\n",
" 'tta': False,\n",
" 'tta_aggregation': 'mean',\n",
" 'downsampling_factor': 5,\n",
" 'batch_size': 64,\n",
" 'task': 'multi_label',\n",
" # 'task': 'multi_class',\n",
" 'return_full_length': True,\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "b2e7a862-8431-42eb-afc1-b7ae5297735f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'model_path': '/models/Edoardo-BS/HuBERT-ECG-SSL-Pretrained/hubert_ecg_large.pt',\n",
" 'path_to_dataset_csv': '/libs/HuBERT_ECG/reproducibility/ptbxl/ptbxl_all_test.csv',\n",
" 'ecg_dir_path': '/shared/ptb-xl',\n",
" 'num_labels': 164,\n",
" 'label_start_index': 4,\n",
" 'tta': False,\n",
" 'tta_aggregation': 'mean',\n",
" 'downsampling_factor': 5,\n",
" 'batch_size': 64,\n",
" 'task': 'multi_label',\n",
" 'return_full_length': True}"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"args"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "60a4aabf-e7bf-4e65-91fb-ed38e7d85ecb",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_835086/3449556894.py:1: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
" checkpoint = torch.load(args['model_path'], map_location=device)\n"
]
}
],
"source": [
"checkpoint = torch.load(args['model_path'], map_location=device)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "b5a58917-70f5-4215-b2f4-8ff82fa18480",
"metadata": {},
"outputs": [],
"source": [
"# config = checkpoint[\"model_config\"]\n",
"config = HuBERTECGConfig(**checkpoint['model_config'].to_dict())"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "6d72cfeb-37c9-43ce-876d-755eea51c9da",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"HuBERTECGConfig {\n",
" \"activation_dropout\": 0.1,\n",
" \"apply_spec_augment\": true,\n",
" \"attention_dropout\": 0.1,\n",
" \"bos_token_id\": 1,\n",
" \"classifier_proj_size\": 512,\n",
" \"conv_bias\": false,\n",
" \"conv_dim\": [\n",
" 512,\n",
" 512,\n",
" 512,\n",
" 512,\n",
" 512\n",
" ],\n",
" \"conv_kernel\": [\n",
" 10,\n",
" 3,\n",
" 3,\n",
" 2,\n",
" 2\n",
" ],\n",
" \"conv_pos_batch_norm\": false,\n",
" \"conv_stride\": [\n",
" 4,\n",
" 2,\n",
" 2,\n",
" 2,\n",
" 2\n",
" ],\n",
" \"ctc_loss_reduction\": \"sum\",\n",
" \"ctc_zero_infinity\": false,\n",
" \"do_stable_layer_norm\": false,\n",
" \"ensemble_length\": 1,\n",
" \"eos_token_id\": 2,\n",
" \"feat_extract_activation\": \"gelu\",\n",
" \"feat_extract_norm\": \"group\",\n",
" \"feat_proj_dropout\": 0.0,\n",
" \"feat_proj_layer_norm\": true,\n",
" \"final_dropout\": 0.1,\n",
" \"hidden_act\": \"gelu\",\n",
" \"hidden_dropout\": 0.1,\n",
" \"hidden_size\": 960,\n",
" \"initializer_range\": 0.02,\n",
" \"intermediate_size\": 3840,\n",
" \"layer_norm_eps\": 1e-05,\n",
" \"layerdrop\": 0.0,\n",
" \"mask_feature_length\": 10,\n",
" \"mask_feature_min_masks\": 0,\n",
" \"mask_feature_prob\": 0.0,\n",
" \"mask_time_length\": 1,\n",
" \"mask_time_min_masks\": 2,\n",
" \"mask_time_prob\": 0.33,\n",
" \"model_type\": \"hubert_ecg\",\n",
" \"num_attention_heads\": 12,\n",
" \"num_conv_pos_embedding_groups\": 16,\n",
" \"num_conv_pos_embeddings\": 128,\n",
" \"num_feat_extract_layers\": 5,\n",
" \"num_hidden_layers\": 16,\n",
" \"pad_token_id\": 0,\n",
" \"transformers_version\": \"4.51.3\",\n",
" \"use_weighted_layer_sum\": false,\n",
" \"vocab_size\": 32,\n",
" \"vocab_sizes\": [\n",
" 1000\n",
" ]\n",
"}"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"config"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "43cacee6-03a6-4340-968c-b10825a6877a",
"metadata": {},
"outputs": [],
"source": [
"pretrained_hubert = HuBERT(config)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "44a36a1b-35ff-4b1f-99f6-63e38c4c6125",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"dict_keys(['global_step', 'patience_count', 'model_config', 'model_state_dict', 'optimizer_state_dict', 'best_val_loss', 'lr_scheduler_state_dict', 'best_val_accuracy', 'pretraining_vocab_sizes'])"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"checkpoint.keys()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "070bb3d9-7f95-4509-91d9-6bab151d2303",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1000]"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"checkpoint['pretraining_vocab_sizes']"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "f8c2ced8-7171-4170-aa78-04ae45bb4168",
"metadata": {},
"outputs": [],
"source": [
"keys = list(checkpoint['model_state_dict'].keys())"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "826e13a1-1394-4605-9c34-2781b31cf37f",
"metadata": {},
"outputs": [],
"source": [
"# num_labels = 100\n",
"classifier_hidden_size = checkpoint['model_state_dict'][keys[-2]].size(-1)\n",
"use_label_embedding = False"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "beba5c25-c704-46d8-82f6-2c40d0e92afe",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"512"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"classifier_hidden_size"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "f0689e2c-8db6-4c0d-ab32-5fe97db49baa",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'num_labels': 164, 'classifier_hidden_size': 512, 'use_label_embedding': False}\n"
]
}
],
"source": [
"print({\n",
" 'num_labels': args['num_labels'],\n",
" 'classifier_hidden_size': classifier_hidden_size,\n",
" 'use_label_embedding': use_label_embedding,\n",
"})"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "6e2cc36d-83f3-4783-bc5a-b45d4916847e",
"metadata": {},
"outputs": [],
"source": [
"hubert = HuBERTClassification(\n",
" pretrained_hubert,\n",
" num_labels=args['num_labels'],\n",
" classifier_hidden_size=classifier_hidden_size,\n",
" use_label_embedding=use_label_embedding\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "71fd7f85-031e-4ba3-9738-88192e605045",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"HuBERTForECGClassification(\n",
" (hubert_ecg): HuBERTECG(\n",
" (feature_extractor): HubertFeatureEncoder(\n",
" (conv_layers): ModuleList(\n",
" (0): HubertGroupNormConvLayer(\n",
" (conv): Conv1d(1, 512, kernel_size=(10,), stride=(4,), bias=False)\n",
" (activation): GELUActivation()\n",
" (layer_norm): GroupNorm(512, 512, eps=1e-05, affine=True)\n",
" )\n",
" (1-2): 2 x HubertNoLayerNormConvLayer(\n",
" (conv): Conv1d(512, 512, kernel_size=(3,), stride=(2,), bias=False)\n",
" (activation): GELUActivation()\n",
" )\n",
" (3-4): 2 x HubertNoLayerNormConvLayer(\n",
" (conv): Conv1d(512, 512, kernel_size=(2,), stride=(2,), bias=False)\n",
" (activation): GELUActivation()\n",
" )\n",
" )\n",
" )\n",
" (feature_projection): HubertFeatureProjection(\n",
" (layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n",
" (projection): Linear(in_features=512, out_features=960, bias=True)\n",
" (dropout): Dropout(p=0.0, inplace=False)\n",
" )\n",
" (encoder): HubertEncoder(\n",
" (pos_conv_embed): HubertPositionalConvEmbedding(\n",
" (conv): ParametrizedConv1d(\n",
" 960, 960, kernel_size=(128,), stride=(1,), padding=(64,), groups=16\n",
" (parametrizations): ModuleDict(\n",
" (weight): ParametrizationList(\n",
" (0): _WeightNorm()\n",
" )\n",
" )\n",
" )\n",
" (padding): HubertSamePadLayer()\n",
" (activation): GELUActivation()\n",
" )\n",
" (layer_norm): LayerNorm((960,), eps=1e-05, elementwise_affine=True)\n",
" (dropout): Dropout(p=0.1, inplace=False)\n",
" (layers): ModuleList(\n",
" (0-15): 16 x HubertEncoderLayer(\n",
" (attention): HubertSdpaAttention(\n",
" (k_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" (v_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" (q_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" (out_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" )\n",
" (dropout): Dropout(p=0.1, inplace=False)\n",
" (layer_norm): LayerNorm((960,), eps=1e-05, elementwise_affine=True)\n",
" (feed_forward): HubertFeedForward(\n",
" (intermediate_dropout): Dropout(p=0.1, inplace=False)\n",
" (intermediate_dense): Linear(in_features=960, out_features=3840, bias=True)\n",
" (intermediate_act_fn): GELUActivation()\n",
" (output_dense): Linear(in_features=3840, out_features=960, bias=True)\n",
" (output_dropout): Dropout(p=0.1, inplace=False)\n",
" )\n",
" (final_layer_norm): LayerNorm((960,), eps=1e-05, elementwise_affine=True)\n",
" )\n",
" )\n",
" )\n",
" )\n",
" (activation): ActivationFunction(\n",
" (act): Tanh()\n",
" )\n",
" (classifier_dropout): Dropout(p=0.1, inplace=False)\n",
" (classifier): Sequential(\n",
" (0): Linear(in_features=960, out_features=512, bias=True)\n",
" (1): ActivationFunction(\n",
" (act): Tanh()\n",
" )\n",
" (2): Linear(in_features=512, out_features=164, bias=True)\n",
" )\n",
")"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hubert"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "96953204-cb20-419e-9795-cde46e8ecbe2",
"metadata": {},
"outputs": [],
"source": [
"model_state_dict = checkpoint['model_state_dict']"
]
},
{
"cell_type": "raw",
"id": "2c0de0df-4d3f-4a6d-a007-92df17e87771",
"metadata": {},
"source": [
"In some transformers versions keys:\n",
" - \"hubert_ecg.encoder.pos_conv_embed.conv.parametrizations.weight.original0\", \n",
" - \"hubert_ecg.encoder.pos_conv_embed.conv.parametrizations.weight.original1\"\n",
"have been moved to keys:\n",
" - \"hubert_ecg.encoder.pos_conv_embed.conv.weight_g\", \n",
" - \"hubert_ecg.encoder.pos_conv_embed.conv.weight_v\". \n",
"The following snippet ensures the model state is loaded properly in case of version mismatch\n",
"Remove:\n",
" - \"final_proj.0.weight\",\n",
" - \"final_proj.0.bias\",\n",
" - \"label_embedding.0.weight\""
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "c60e80ee-0d27-4c34-87df-47562a7e6c86",
"metadata": {},
"outputs": [],
"source": [
"new_model_state_dict = {\n",
" k:v for k, v in hubert.state_dict().items() \n",
" if k in [\"classifier.0.weight\", \"classifier.0.bias\", \"classifier.2.weight\", \"classifier.2.bias\"]}"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "b349961d-3cfb-4efc-9cf3-2fdee64a15fa",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"dict_keys(['classifier.0.weight', 'classifier.0.bias', 'classifier.2.weight', 'classifier.2.bias'])"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"new_model_state_dict.keys()"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "4cae081b-8b59-4072-87bf-013a5134a56e",
"metadata": {},
"outputs": [],
"source": [
"for k, v in model_state_dict.items():\n",
" if k in [\"final_proj.0.weight\", \"final_proj.0.bias\", \"label_embedding.0.weight\"]:\n",
" continue\n",
" new_key = k\n",
" # with this changes - load_state_dict fails \n",
" # if k.endswith(\"parametrizations.weight.original0\"):\n",
" # new_key = k.replace(\"parametrizations.weight.original0\", \"weight_g\")\n",
" # elif k.endswith(\"parametrizations.weight.original1\"):\n",
" # new_key = k.replace(\"parametrizations.weight.original1\", \"weight_v\")\n",
"\n",
" new_model_state_dict[f\"hubert_ecg.{new_key}\"] = v"
]
},
{
"cell_type": "raw",
"id": "322d7a1f-1881-4af1-a3ee-4060d64e0fc5",
"metadata": {},
"source": [
"strict false prevents missing mask_spec_embed, something not important at test time"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "25dd1608-1ba8-473a-aef6-ef75c4e01d53",
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"<All keys matched successfully>"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hubert.load_state_dict(new_model_state_dict, strict=True) "
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "370ab451-6c97-44e2-874c-e7b0a16392a1",
"metadata": {},
"outputs": [],
"source": [
"#fixing seed\n",
"torch.manual_seed(42)\n",
"np.random.seed(42)\n",
"torch.cuda.manual_seed(42)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "b4c1353d-3262-4559-bc4d-d93a534b7994",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'model_path': '/models/Edoardo-BS/HuBERT-ECG-SSL-Pretrained/hubert_ecg_large.pt',\n",
" 'path_to_dataset_csv': '/libs/HuBERT_ECG/reproducibility/ptbxl/ptbxl_all_test.csv',\n",
" 'ecg_dir_path': '/shared/ptb-xl',\n",
" 'num_labels': 164,\n",
" 'label_start_index': 4,\n",
" 'tta': False,\n",
" 'tta_aggregation': 'mean',\n",
" 'downsampling_factor': 5,\n",
" 'batch_size': 64,\n",
" 'task': 'multi_label',\n",
" 'return_full_length': True}"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"args"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "767010e9-ef7e-4ef3-9d7c-1b0be5e0ab6c",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32m2025-05-19 11:46:53.840\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mHuBERT_ECG.code.dataset\u001b[0m:\u001b[36m__init__\u001b[0m:\u001b[36m53\u001b[0m - \u001b[1mLoading dataset from /libs/HuBERT_ECG/reproducibility/ptbxl/ptbxl_all_test.csv...\u001b[0m\n",
"\u001b[32m2025-05-19 11:46:53.845\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mHuBERT_ECG.code.dataset\u001b[0m:\u001b[36mcompute_weights\u001b[0m:\u001b[36m81\u001b[0m - \u001b[1mComputing weights...\u001b[0m\n",
"\u001b[32m2025-05-19 11:46:53.846\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mHuBERT_ECG.code.dataset\u001b[0m:\u001b[36mcompute_weights\u001b[0m:\u001b[36m92\u001b[0m - \u001b[1mDone with the weights.\u001b[0m\n"
]
}
],
"source": [
"testset = ECGDataset(\n",
" path_to_dataset_csv = args['path_to_dataset_csv'],\n",
" ecg_dir_path = args['ecg_dir_path'],\n",
" pretrain = False,\n",
" downsampling_factor=args['downsampling_factor'],\n",
" label_start_index=args['label_start_index'],\n",
" return_full_length=args['return_full_length'],\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "f64b5f05-4113-4593-8d5b-314d7d6419ac",
"metadata": {},
"outputs": [],
"source": [
"dataloader = DataLoader(\n",
" testset,\n",
" collate_fn=testset.collate,\n",
" num_workers=5,\n",
" batch_size = args['batch_size'],\n",
" shuffle=False, # don't touch if you wanne save probs\n",
" sampler=None,\n",
" pin_memory=True,\n",
" drop_last=False\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "08a89854-e8c4-4796-b943-2859d9174aea",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"HuBERTForECGClassification(\n",
" (hubert_ecg): HuBERTECG(\n",
" (feature_extractor): HubertFeatureEncoder(\n",
" (conv_layers): ModuleList(\n",
" (0): HubertGroupNormConvLayer(\n",
" (conv): Conv1d(1, 512, kernel_size=(10,), stride=(4,), bias=False)\n",
" (activation): GELUActivation()\n",
" (layer_norm): GroupNorm(512, 512, eps=1e-05, affine=True)\n",
" )\n",
" (1-2): 2 x HubertNoLayerNormConvLayer(\n",
" (conv): Conv1d(512, 512, kernel_size=(3,), stride=(2,), bias=False)\n",
" (activation): GELUActivation()\n",
" )\n",
" (3-4): 2 x HubertNoLayerNormConvLayer(\n",
" (conv): Conv1d(512, 512, kernel_size=(2,), stride=(2,), bias=False)\n",
" (activation): GELUActivation()\n",
" )\n",
" )\n",
" )\n",
" (feature_projection): HubertFeatureProjection(\n",
" (layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n",
" (projection): Linear(in_features=512, out_features=960, bias=True)\n",
" (dropout): Dropout(p=0.0, inplace=False)\n",
" )\n",
" (encoder): HubertEncoder(\n",
" (pos_conv_embed): HubertPositionalConvEmbedding(\n",
" (conv): ParametrizedConv1d(\n",
" 960, 960, kernel_size=(128,), stride=(1,), padding=(64,), groups=16\n",
" (parametrizations): ModuleDict(\n",
" (weight): ParametrizationList(\n",
" (0): _WeightNorm()\n",
" )\n",
" )\n",
" )\n",
" (padding): HubertSamePadLayer()\n",
" (activation): GELUActivation()\n",
" )\n",
" (layer_norm): LayerNorm((960,), eps=1e-05, elementwise_affine=True)\n",
" (dropout): Dropout(p=0.1, inplace=False)\n",
" (layers): ModuleList(\n",
" (0-15): 16 x HubertEncoderLayer(\n",
" (attention): HubertSdpaAttention(\n",
" (k_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" (v_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" (q_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" (out_proj): Linear(in_features=960, out_features=960, bias=True)\n",
" )\n",
" (dropout): Dropout(p=0.1, inplace=False)\n",
" (layer_norm): LayerNorm((960,), eps=1e-05, elementwise_affine=True)\n",
" (feed_forward): HubertFeedForward(\n",
" (intermediate_dropout): Dropout(p=0.1, inplace=False)\n",
" (intermediate_dense): Linear(in_features=960, out_features=3840, bias=True)\n",
" (intermediate_act_fn): GELUActivation()\n",
" (output_dense): Linear(in_features=3840, out_features=960, bias=True)\n",
" (output_dropout): Dropout(p=0.1, inplace=False)\n",
" )\n",
" (final_layer_norm): LayerNorm((960,), eps=1e-05, elementwise_affine=True)\n",
" )\n",
" )\n",
" )\n",
" )\n",
" (activation): ActivationFunction(\n",
" (act): Tanh()\n",
" )\n",
" (classifier_dropout): Dropout(p=0.1, inplace=False)\n",
" (classifier): Sequential(\n",
" (0): Linear(in_features=960, out_features=512, bias=True)\n",
" (1): ActivationFunction(\n",
" (act): Tanh()\n",
" )\n",
" (2): Linear(in_features=512, out_features=164, bias=True)\n",
" )\n",
")"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hubert.to(device)\n",
"hubert.eval()"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "20b94068-8e14-41ed-8efe-bcb12dc80271",
"metadata": {},
"outputs": [],
"source": [
"ecg_batch, _, labels_batch = next(iter(dataloader))"
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "05541ec8-bf3e-47d8-abf6-23e7383ff909",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(torch.Size([64, 12000]), torch.Size([64, 71]))"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ecg_batch.shape, labels_batch.shape"
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "7c07bdc5-2587-4ea3-bc2c-766d23885f3d",
"metadata": {},
"outputs": [],
"source": [
"ecg, labels = ecg_batch[0], labels_batch[0]"
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "5fcd35e7-3012-47d2-97f4-ad8129ca8da4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([-0.5492, -0.5440, -0.4032, ..., -0.4387, -0.4392, -0.4397]),\n",
" torch.Size([12000]))"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ecg, ecg.shape"
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "4b691e62-c0e5-4703-8515-066cd649d58d",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGdCAYAAADaPpOnAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAnUNJREFUeJztvXmcHGW1///pvWftyWQyM5nsgWyQhCVAFkBAIIAgXpcriL+AslwxRgQEr5HrJfpVo6hcRAUUBFRAuF6MFxUD4RLWEAJJhrCEkH2yzGQySaZn7Z5e6vdH9VP11NZd3V3VS/V5v17zSqa7uqf66arnOc85n3OOSxAEAQRBEARBEGWCu9gnQBAEQRAEkQ1kvBAEQRAEUVaQ8UIQBEEQRFlBxgtBEARBEGUFGS8EQRAEQZQVZLwQBEEQBFFWkPFCEARBEERZQcYLQRAEQRBlhbfYJ2A1yWQSBw8eRF1dHVwuV7FPhyAIgiAIEwiCgP7+frS1tcHtTu9bcZzxcvDgQUyYMKHYp0EQBEEQRA7s27cP48ePT3uM44yXuro6AOKHr6+vL/LZEARBEARhhr6+PkyYMEFax9PhOOOFhYrq6+vJeCEIgiCIMsOM5IMEuwRBEARBlBVkvBAEQRAEUVaQ8UIQBEEQRFlBxgtBEARBEGUFGS8EQRAEQZQVZLwQBEEQBFFWkPFCEARBEERZQcYLQRAEQRBlBRkvBEEQBEGUFWS8EARBEARRVpDxQhAEQRBEWUHGC0EQBEEQZQUZLwRBEIStvH8wjIde3YV4IlnsUyEcguO6ShMEQRClxaX3vgYA8HvduHrh5OKeDOEIyPNCEARBFIT3DoSLfQqEQyDjhSCIiiY8FMNXH9uI59/vKvapOB5BKPYZEE6BjBeCICqau9dswz/f68K//XFjsU/F8STJeCEsgowXgiAqmkN90WKfQsUgkOuFsAgyXgiCIIiCQKYLYRUFMV7uu+8+TJkyBcFgEPPmzcOrr76a9vhoNIo77rgDkyZNQiAQwHHHHYeHH364EKdKEARB2ESSPC+ERdieKv3UU0/h5ptvxn333YczzzwTv/nNb3DJJZfggw8+wMSJE3Vf8/nPfx6HDh3C7373Oxx//PHo7u5GPB63+1QJgiAIGyHbhbAK242Xu+++G9dddx2uv/56AMA999yD5557Dvfffz9WrlypOX716tV4+eWXsWvXLjQ2NgIAJk+ebPdpEgRBEDZDnhfCKmwNG42MjGDjxo1YvHix4vHFixdj3bp1uq955plncNppp+Guu+7CuHHjMH36dNx2220YHh7WPT4ajaKvr0/xQxAEYRaXq9hnUDmQ7UJYha2el56eHiQSCbS0tCgeb2lpQVeXfk2FXbt24bXXXkMwGMSqVavQ09ODpUuX4ujRo7q6l5UrV+J73/ueLedPEARBWAd5XgirKIhg16Xa2giCoHmMkUwm4XK58Pjjj+OMM87AJz7xCdx999149NFHdb0vy5cvRzgcln727dtny2cgCIIg8oNsF8IqbPW8NDU1wePxaLws3d3dGm8MY+zYsRg3bhxCoZD02KxZsyAIAvbv349p06Ypjg8EAggEAtafPEEQBGEpAiVLExZhq+fF7/dj3rx5WLNmjeLxNWvWYNGiRbqvOfPMM3Hw4EEMDAxIj3300Udwu90YP368nadLEARB2AhV2CWswvaw0a233oqHHnoIDz/8MLZu3YpbbrkFHR0duPHGGwGIYZ+rr75aOv6qq67C6NGj8eUvfxkffPABXnnlFdx+++249tprUVVVZffpEgRBEDZBYSPCKmxPlb7iiitw5MgRfP/730dnZydmz56NZ599FpMmTQIAdHZ2oqOjQzq+trYWa9aswde//nWcdtppGD16ND7/+c/jBz/4gd2nShAEQdgItQcgrMJ24wUAli5diqVLl+o+9+ijj2oemzlzpibURBAEQZQ3ZLoQVkG9jQiCIIiCQKnShFWQ8UIQREVDReoKBwl2Casg44UgCIIoCKR5IayCjBeCIAiiIJDtQlgFGS8EQVQ0LlDcqFBQkTrCKsh4IQiCIApCMlnsMyCcAhkvBEEQREEgzwthFWS8EARBEAWBso0IqyDjhSAIgigIlG1EWAUZLwRBEERBINuFsAoyXgiCIIiCQBV2Casg44UgiMqGMqULBpkuhFWQ8UIQBEEUBBLsElZBxgtBEARREEiwS1gFGS8EQRBEQSDbhbAKMl4IgiCIgkCCXcIqyHghCIIgCgLZLoRVkPFCEASRgjQZ9kKeF8IqyHghCIJIQdkwBFEekPFCEASRgjwD1sN7s2h8Casg44UgiIqGr1FHi6v18ENKni3CKsh4IYgyQRAE3P/STrzwwaFin4pjIdvFeniDkDRFhFV4i30CBEGY442dR/CT1R8CAPb8+NIin40zSZBrwHL4ESXbhbAK8rwQRJmwv3e42KfgeChsZD0Kz0sRz4NwFmS8EESZkCSvgO3QEFuPUvNCA0xYAxkvBFEmJGjitx3SZFgPP6Q0vIRVkPFCEGUCeV7sh4bYepKUKk3YABkvBFEmkJjUHvhRpcXVekiwS9gBGS8EUSbEyXixB9Jk2AqlShN2QMYLQZQJNO/bg3JxLeKJOBQhKf+f7G/CKgpivNx3332YMmUKgsEg5s2bh1dffdXU615//XV4vV6cfPLJ9p4gQZQBJNi1B9Jk2IsAQff/BJEPthsvTz31FG6++Wbccccd2Lx5M84++2xccskl6OjoSPu6cDiMq6++Gueff77dp0gQZQFpXuwhKej/n7AGGl/CDmw3Xu6++25cd911uP766zFr1izcc889mDBhAu6///60r/vKV76Cq666CgsXLrT7FAmiLCDjxR4UjQNpjC1HoLAcYQO2Gi8jIyPYuHEjFi9erHh88eLFWLduneHrHnnkEezcuRN33nlnxr8RjUbR19en+CEIJ0LGiz0kSbBrKzS+hB3Yarz09PQgkUigpaVF8XhLSwu6urp0X7N9+3Z8+9vfxuOPPw6vN3PrpZUrVyIUCkk/EyZMsOTcCaLUoInfHpSalyKeiEPhdS7xRDLNkQRhnoIIdl0ul+J3QRA0jwFAIpHAVVddhe9973uYPn26qfdevnw5wuGw9LNv3z5LzpkgSg3yvNgDeQbsRSDNC2EDtnaVbmpqgsfj0XhZuru7Nd4YAOjv78fbb7+NzZs3Y9myZQCAZDIJQRDg9Xrx/PPP4+Mf/7jiNYFAAIFAwL4PQeSFIAjYsPsoprfUYVSNv9inU9ZQtpE9CFSHxFZ4g5AMcMIqbPW8+P1+zJs3D2vWrFE8vmbNGixatEhzfH19Pd599120t7dLPzfeeCNmzJiB9vZ2zJ8/387TJWxgU0cvrvjteiz/y7vFPpWyJ5Ggid8OKGxkL7w9SAY4YRW2el4A4NZbb8WSJUtw2mmnYeHChfjtb3+Ljo4O3HjjjQDEsM+BAwfwhz/8AW63G7Nnz1a8vrm5GcFgUPM4UR5090XEf/sjRT6T8ocmfnugrsf2kqRsroJwuD+K+iovAl5PsU+lINhuvFxxxRU4cuQIvv/976OzsxOzZ8/Gs88+i0mTJgEAOjs7M9Z8IcoXNlfRnJU/NPHbg3JxLeKJOJRCeF6i8QT+Z+N+fGzaGExorLblb5Qye3oGce7PXsKUphqsve3cYp9OQbDdeAGApUuXYunSpbrPPfroo2lfu2LFCqxYscL6kyoij63fiwO9w/jWRTN0hctOgk1WtOzmD3le7KFQgl2jRAWnww+pIIhGuNtt7Tg88NIu/NcLH6E+6MWWFRdZ+t7lwHPvi7rS3T2DRT6TwkG9jYrAf/z1Pdz/0k68vfdYsU/Fdpi3gISQ+cOLHWk8raMQRdSWPbEJi//rFURiCXv+QAmjNgjtMMJf3NYNAOiLxC1/73KgEjc2ZLwUmBhX52BPBVjJbOIiLUH+8CENGk7rsNvzMjQSx9+3dGJ79wDePxi2/P1LHfWI2pFxlKjweF8lhpTJeCkwg1F5Z3B4IFrEMykMCcnzUuQTcQAJRVYMDahVKFJ5bRjX7YcGpP9X4temvlbtuHbjFZ6JV4G2Cxkvhaafc2vuPzZcxDMpDGyeqsSby2oUYaMinofT4K9NO8Jx2w71S//vj1ZeWEM9pPZ4Xir7jqjEzQwZLwVmgJu8KiFsJAl2K/Dmshql5qWIJ+IwFI0ZbRjXj7o446UCNRnqe9+OCE/FGy8V+PnJeCkwfNho75GhIp5JYSDNi3VQ2Mge7K5D8lG3HDYaqETjRfW7HaG5eIWL2SvQdiHjpdDwbuOD4WFEYgms2rwfHQ41ZJKkebGMStxdFQLeE2DHEO/kjJf+SMz6P1DiqA3tuA2uF97zMlyBGV2UbUTYDr/zEgTgW/+zBbc89Q5u/e/24p2UjchF6irv5rIaChvZA39tWr1rjyeS6OqTq0sPVKDmRW2r2BE2GuGyOAejlWe8VOLGhoyXAjOomryeeecgADi25gtlG1lHksJGtmBn1+ND/VGF0VmRmhdV4MgOL0HfsOzRUs+xlUAlan7IeCkwRjuvsaFggc+kMLBFtvJuLeuJU7aRLdhpFB5QZRRWpPGiGlKrvQSRWALROOd5Gam8Ma5A26Uw7QEIGaPJy2NxuexSgQS71kEVdu3BVuOlV6llG4hWnubFzlTp/20/oKlaXJFhowqcD8h4KTBGLs3hEWfecCwUXYk3l9UoF9kinojDUPfesRLmefF73RiJJyvS82JXe4CucATfeLJd83glho0qcX6lsFGBMQobDTnUeJHCRpV3b1mOYsdK42kZigq7FluFB3pF42Vmax2AyhTsqkfUqrBRn0HmViWGjUjzQtgOm7wmjVa2bR+OJRypGKdUaetQ9DYi68Uy7OxtdKBXzDSa0SIaL+R5UWq38sGoJUBlel6KfQaFh4yXAsOMlylNNZrneNGZU6BUaetIUNjIFuwMxx0bHAEATE7d75VovKj1WVZ5CYYMPCwVqXmpwAmBjJcCw+q8jA1VaZ4zuhnLmQSFjSyDBLv2oNS8WDuuvcOi8TJ+lHi/V2KROk22kUVjbBRqr0zPS+XNB2S8FBjmeTlujOx5CXjFr8GJuheBso0sgxoz2oOdnpfwkGisjB8lhomj8SRGHOhhTYd6TK0aY0PPiwPn0UxQhV3CdpiY7JSJo/CTz87BH687A9V+DwBnlrVmC24FejUtR9m/pYgn4jDsSpVOJAX0pTyt4xpkT6sTPazpUHuzrPJukedFphLnAzJeCkwkJu66gj43rjh9Is6eNgbVfjFj3Ynp0vJ6W4F3l8UkOMUuhY2swy7BLh8iGl3rh88j1nJyooc1HeqNi1UjrB5HZiBWYmiOso0I22EFlYI+j/RY0OfcsJFcpK7IJ+IA+OwKGk7r4A1BK23CcKpkfbXfA5/HjarUPe/E+zwdWs+LNe+r9mBNbhJDc+HhCjReKnAzQ8ZLgYlKnhfZeJE8LzHnuTvlsFHl3VxWQ2Eje7DL89Kb0rs0VPkAwNEe1nRoR9SesNHk0aKOsLcCjRfKNiJsJZEUpO6nQa889FV+5+7IJM9LBd5cVhPnOueSMWgddgl2mQegXjJe2H3uvE1KOtTXqnWCXeV8ycpPVKLnpRLnAzJeCkg0Lt9sSs9LSrDrROOFFakr8nk4AWrMaA+8YW2lkc0W0YZq0XiRNikOFOanQ72u2hY2SnleWIZXJVGJe0MyXgoIE+sCBsaLAyc1dlNV4MbAcuJU58UWBLvCRinjJaT2vFRYETX1mNqVbTSZ87xU2v1RiZ5tMl4KCBPr+jwuRRfpoIOFfAlJsFt5N5fV8GEjGk7rsCts1KcyXqpSmpdKCxuph9SybCOVEdjWEAQgGvlOnEvTQYJdwlakTCOvR/F4tYM1L2wHVIH3luWQYNce7BLsymEjPwCg2udcD2s6bMs2Uo1jlc8jpaNXmmi3Ah0vZLwUEhY2CvjUxos39bzzJjXKNrIOpeaFxtMq+GvTynBD75DYGkAKGwWcu0lJR1JVUNiysFGqGN35M5vx9FcXweVyIVQlGoqVpnuhsBFhK5E4q/GiHHa5/oPz3MmkebEOZbZREU/EYSg1L9a9L2vCWBcUNydO9rCmw7awUWoclyychHmTRgEAQlXiWLOeUpVCJW4OyXgpIHo1XgCHp0pL2UaVd3NZSTIpKBbWShMk2old7QFYj50aPzNeWJ0X521S0qEV7Frzviz8VhPwSo+xEF1fhYWNKrFpKxkvBSST5yUac17DNqqwaw1qQR4Np3XYJdhlPXbY4lq5FXZVv1t09bLxreI2gyxE11thYSNlZ/TinUchKYjxct9992HKlCkIBoOYN28eXn31VcNj//KXv+DCCy/EmDFjUF9fj4ULF+K5554rxGnaTjS1UwioBLusq3TUgd1mWUX7SnRrWgnfGgConN1VIbDLoyUbL+L97uR6TumwS7DLxpGNKyBXM660QnUJm7yHpYztxstTTz2Fm2++GXfccQc2b96Ms88+G5dccgk6Ojp0j3/llVdw4YUX4tlnn8XGjRtx3nnn4ZOf/CQ2b95s96naDt+UkccvGS/Om9SksFFl3E+2EVepHmk8rUG9sFobNhKNl2p/ZWte1N4sK8ZYEAQp24gPGzF9EdMbVQqJCixgabvxcvfdd+O6667D9ddfj1mzZuGee+7BhAkTcP/99+sef8899+Bb3/oWTj/9dEybNg0/+tGPMG3aNPztb3+z+1RtxyhVmnlinOh5sSuTo9LQeF6KdB5OQ31JWhk2YnVIalnYiNV5cWBWYTrUYSIrhjgaT0oLdhXneWEbwZGE8+bSdNjVXLSUsdV4GRkZwcaNG7F48WLF44sXL8a6detMvUcymUR/fz8aGxt1n49Go+jr61P8lCp6HaUB7oZzoPHC7whI95I7as1LpbiG7UY9jgkLL9KBKPO8qMNGleUV0AypBUPMh96qufmUbQSdOJemw65aRaWMrcZLT08PEokEWlpaFI+3tLSgq6vL1Hv8/Oc/x+DgID7/+c/rPr9y5UqEQiHpZ8KECXmft11E4qzOi3LYnax5KdRN5XSvjl0ZG5WOemG16jqKJ5LS/Sx7XiozbKTRvFhgvbCQnN/rhtcjz6d+B8+l6bDS6C4XCiLYdblcit8FQdA8psef/vQnrFixAk899RSam5t1j1m+fDnC4bD0s2/fPkvO2Q6MPC8ByfPivEktWQB35jf/+x1ccPfLjizyx7CruV2lY1fH40HeM8AEu75KFeym/z0X9MS6gLO92OkoxDxbangzH5I7TU1N8Hg8Gi9Ld3e3xhuj5qmnnsJ1112HP//5z7jgggsMjwsEAggEApacr91Igl2v/g3nxN2CXTU0GIIg4G/vHMRIIondPYOYNbbe8r9RCmgX2QqZoWxGq3mxqmmg6BnweVxSKKNa6m1UYcYLrDcQh1Q1dBh+j3OTH9KhMF4qRBFnq+fF7/dj3rx5WLNmjeLxNWvWYNGiRYav+9Of/oQvfelLeOKJJ3DppZfaeYoFRfa8qMNGzhXsJmzux9M7FJPEeTEHi/Qq0CtcEGzzvESVmUaAHDYarDTNiw3tAdgYVpHnBUBlagtt9bwAwK233oolS5bgtNNOw8KFC/Hb3/4WHR0duPHGGwGIYZ8DBw7gD3/4AwDRcLn66qvxi1/8AgsWLJC8NlVVVQiFQnafrq1E45Un2FUUT7JhR9DVF5H+72Tjxa5aGZWOVktkVQE15hmQ7/X6VOn6wWgciaSg6CzvZDRjbMF7Do9oxxeo3GyjSqy+bbvxcsUVV+DIkSP4/ve/j87OTsyePRvPPvssJk2aBADo7OxU1Hz5zW9+g3g8jq997Wv42te+Jj1+zTXX4NFHH7X7dG3FqM5LwMF1XuzeERzijBcneq4YdoU3Kh07apAAsmdAUbo+1TQwKYjl60fV+C35W6WOJtnIgiFmmiK15yXg4I1gOpRho8rAduMFAJYuXYqlS5fqPqc2SF566SX7T6hIGAp2ffINZ1bMXC7YXfnxkMLz4tzb1o7dK6FXpM6a92Wel2rOePF73agLeNEfjePo0EjlGC+a+z7/QR4e0YblADJegMrxylJvowJiWKTOI/6eFIC4wwKWdhdPOtQXlf4fc/CEZVdKL09/JIZ1O3qkqsiVgF2eFybYrQ0o73VmsPQOVU7XYzsKAQ5lyjaqtLAR93ErJWxExksBYWEjTZ0X7nenhT7s7nZaKZqXQnhevvFkO6566E08/uZeG969NLGrfs6AjmAXkI2Xo4OV03tHa3jn/56GxounMovU2Z0YUYqQ8VJA1L1OGH6uyJLTbjplkTrr37+bM16cvNvSCnatH8wXP+wGAPx67U7L37tUsavCrro1AGNUtdg48Nhg5XhetIZ3/mM8ZDSXVmjYKFGBmhcyXgrI0dSE1aiKdbvdLvg8os7FaaJdu3sb8WEjJ09YhSxS190fyXyQQ7BLCK1uDcBorBbv/WOVFDZS/26FYDeaPmzkNA92JpI2e7hLETJeCsgxA+MFcG5PDmWROuvfv6tiBLvK3+38pBUkebEtbMTqvNSoPS8sbFRJxouNqdLasFFlGi8Jm+fZUoSMlwIRiSWk9D4948WpO4aEjUKyeCKJngHe8+IsrxWPppiaxTOU+rs5WiFhDbsEu/0R0XipDyqNF3bvV1LYSOs1tCBsFGPGi1HYyLlzgR4KzUuFBI7IeCkQvUOiQM/jdmkmNMC5KX6CjbHYwwNRxcTobM+LvYLdvoiy6uuHnaXbnd1K1EagVcZLX0S83+urfIrHR0lho0oS7Frv3RoyCMsFKjTbSKHVcu40qICMlwLBdrKjqv26dVz8Di1UpyxSZ+1dxetdAGdPWHZrXtSegPW7j1r7B0oUO9J4ASA8LBonIY3xUnmCXc21a4lgl4rU8cST9m0SSxUyXgqELNb16T4vVdmNOeums1PzwheoAyosVdpi60UtIH19R4+l71+q2DWukuclqLzfG6orT/Nii+eFVTA2CBslBTGsXCnYuUksVch4KRBssmJuYzVSc0aH3XCKVGmLrRe18eLk3ZZ292otzHgZndJktO/rlRZgJ6PVElnzvn3DKc1LlXJxZWEOp21S0mGH1zBTkTrA2Z5YHkEQqM4LYR/MTTy6Vt948TvU82JV3Qw9KtvzYu37s6Jps8eF0BYKIpEU8FFXv7V/pASxS7Br5HlxqjA/HeowUb4jvPbDbmzvHgCgbL8AKGtmOW0uNUI9x1aI7ULGS6HgNS96OFVopgwbWXtbdYVFzQurkeNswa76d4vDRlwa/5j6IIDKyDiyo7dRMimgz0DzEqjAbBirr92frP5Q+v/ExmrFc16PG6xZt9PmUiMSNoeUSxUyXgoEc8vrpUkDvOfFWZNaUhGLtfa9medlXEMVAGfvZu2olcHDwpoN1T4pdFQJxosdRuHgSFx6X3W2USX23rGyL6MgCNh3dAgA8N9fWZi27ISTw8g8Gs9LZdguZLwUiiMD5jwvTluAFRl8Ft9V+46Jk9jUMbUAnB42Uv5uuWCXeV6q/dKCcKQijBfrU6VZ2rnf45buawYfNqqUHbKV7QH6InGpXtaccSHdYyT9oMPmUiPUzXwr5LIi46VQsEqwraGg7vNOrbBrV+XHeCKJA8eGAQBTm2oAON14sVvzkjKua/wV5nmxPmzEQkb1VV5NWQTWQV4QnNdB3ghtX67c3+tgr3jPN9b4NWnSjIrzvCSsMw7LCW21NMIWOlM33VgD48WpQj5FkToLV9zOcATxpAC/x40Jqbh3RRkvFk9QfFiT9eWpBOPFjuqvsvGiLYugyIaJJ+HzOH//aGWmHDNe2hr051FAFu1WSmiOPC+EbSSSAg71i+LSsaEq3WOcWlwpYVPxJBYyGjeqCkGfM8dOgd1F6lIVX0dxYaNKMF7s8GixAnXqTCNAa7xUAlbqig4w48VgHgWcO5caoda8VEqdF/K8FIDD/VEkkgI8bhfG1AV0j3FqhV1FnRcLbyom2pvQWC3tXkcqKtvI2vfns41YAbDKMF6Uv1vh0WKaFz3Pi8ftgsftQiIpVIxnQJMqnccQS8ZLg7Hx4tS51AhNtlGRzqPQkOelABwMizdcS10AHre2NQAAVPnE+C0rvuQUFNlGFs7V+46KYzphVJVkvMQcvNOys8JuMilIYaNR1b6K9rxYcY0apUkzpLCGg69XHis7oh/sVWYY6lHxmpcKsV7I81IAusLiDTc2zQ3XkqqtwY51CvziYKVOg+3Axo/iPS/OnazsbMzYF4lJC0xDtR+RVHGvI4PRNK9yBtoUdCs8LyxspD+9+r1uDMcSjtO3GWJhid0jqS7yRh5soBKNw8LUeREEQbcvX7Egz0sBOJhBrMs/15ny0jiFhEKwa937Hk5piJrrAlKM28mCXTuEpQzmYakLeOH3utGYqgIdiSWlEJJTsSMcF04j2AUqT5NhpeelP6LfdoEn4HP+ZobH7o7zAPDgK7sw/0f/h909gza8e26Q8VIAWDG11vp0xovolel0mueFmz+s1Lz0pHZgTXUB2fPi4MXAzlRpKWSUChfV+D3wpsKbvUPO7m+k7rdlTbZRaoHVEewClafJ0Ibmch/j/pRXq85gbAHZ81Ipni1tDSjr/8YPn92K7v4ofvTsVuvfPEfIeCkAbLdgFAMHgLGp1L/DA1FHLcJJmzwvkvFS6+faAzhn3NTY2ZiR9TVixovL5aoY74AdEz8LGxlqXipkbBkWFtiV5tI6g5Ac4NyyE0bYEfo0gum5SgEyXgrAcKrkv1FRJUDs5uv3uiEI2oaD5YyySJ01N1UiKUihjjG1AfgqoOS6HZVgGVKNl2p5sfVXQCgOsKnCLlekTo9Kq0NipddQNl6MN4LVfnHcIw5LfjBCE/q08bIqpWuWjJcCILdvN94tuFwuTvfiDONFEATFRGVVeu/RwREkBcDlElN7/VK2kXNl9na6ho/pNA31VYjr3Y6OvOnqvACVp3nR1CjK8W0isYS0eKbzvLBNotMyN42wu4Al79kppWuWjJcCMCwZL8aeF0DWxDhFtGtXPx6WBTOq2g+vx10RXgI7GzMy8fPoWtl4qYQxBexpD5ApTFxpYSOrsmFYOM7lAmrTbARrJOPF2WJzht2tQ4a5ZsGldM2S8VIA2E0U9KU3XpjnxSlhI7tU8D39oqegKbXYVoZgV/m7ldlGrO9WCycor5R0UzvSTNO1BwAqr7O0VZnSzCis9XvhNqiXBcge7sFKMV5svozCnM5lMFo6Y0rGSwEYMul5aUi57Z2S4aEpW21R3EgW64q1Hphg18mLgZ27q+4+cTz5pqGy58W5oThAO/HnO66JpID+KMs2Sq95cXpIjmFVFWMzYl1AnmcrNWxkdXsAfj3qGRgpmW7oZLwUAOZ2y2S8MDdzuIQU3fmgvsat0ryojZdKCHFoMzZs9rxI3gFnLwDa0ur5jStL5QXS1XkR54HKMV6sMbzNpEkDQHVANG6Gos6+dhl2h41442UkkZQKhBYbMl4KANsBpMs2ApxnvFi9MDAODyg1GmwnmxS03h6noNG8WPQxBUHQrUMkh+KcOZ4M9bjm64JnNV6q/R7DjtGVpnlRk+sVZdbzwjQvFRM2srGMAqBdj876yVr0DhW/dUhBjJf77rsPU6ZMQTAYxLx58/Dqq6+mPf7ll1/GvHnzEAwGMXXqVDzwwAOFOE3bGDaRbQQ40HixqVW7rHlhYSPnd+q1Q1gKiNca8wA018sl1yslnVf98fI1sOXWAGmKqFWY8WK158XIo8WotLCRdmNjrfkSHtYaKpv39Vr6N3LBduPlqaeews0334w77rgDmzdvxtlnn41LLrkEHR0dusfv3r0bn/jEJ3D22Wdj8+bN+M53voObbroJTz/9tN2naguCIEiCXbNho1IqBJQPml2tRTcVCxuNqSTjRaPNsGYsD/WxzC2fFM4AINfOceh4MtTewXyNwkw1XoDKM160BRbt1rykwkYVYrzY3XGebaanNddKj23r6rf2j+SA7cbL3Xffjeuuuw7XX389Zs2ahXvuuQcTJkzA/fffr3v8Aw88gIkTJ+Kee+7BrFmzcP311+Paa6/Fz372M7tP1Rai8aR0MWUMG1WT58UMcmsAlm3kAusX5lRPgV2ZW3p6F0D2vDhZRwToGIF5DmymGi8A79WqlMXVmnmgz2zYKFDZqdJWB46Y5uXM45vwzQunA6gA42VkZAQbN27E4sWLFY8vXrwY69at033NG2+8oTn+oosuwttvv41YTLuoR6NR9PX1KX5KiWHO+q/KkCrttLCRdkdgT7YRX87eqf1i7GrM2J0yXprVxos3lcHlcO+AJiMuz3FlmUbpFthKK1JnVZq/acEuS5Umwa4l9KbWo4ZqH2aOrQcAfOh046WnpweJRAItLS2Kx1taWtDV1aX7mq6uLt3j4/E4enp6NMevXLkSoVBI+pkwYYJ1H8AChlKZRj6Py1DAx+CNF6vSiouJHTdVMingyIBS8wI4P/1U7Wq3aoJihvKoauWCUCmeF6vFjkMp46UmQGEjmfyv3fBQDM+/fwgA0FIXSHssC88PV4jnxc6+ZwDQw4pY1vgxs7UOALCze6Doc0NBBLsul7KgkCAImscyHa/3OAAsX74c4XBY+tm3b58FZ2wdzPOSyesCyMZLUgAGHHDj2VG2ui8SQzy14vAVYQOp8Y3GnLkg2JVRwHantarFtlKa26k3Cfl6XgZT93tNGnG+0w1tNRq9Vg7v8dibe3GgdxiTR1fjc6el36BKmpdYwhGbwExY2bVbj/3HxNTocaOq0NZQBUAMzzMNUrFIHzzMk6amJng8Ho2Xpbu7W+NdYbS2tuoe7/V6MXr0aM3xgUAAgUB6S7yYmM00AsQKvH6vGyPxJMJDsbRx83JAW6Qu//dkIaP6oFchMA04vGqpXYWoBqKi50VtvPgqxvPCNkbiDjbfYWUVSKsDxpuVgK+yPC9WeA0/6BTlAF+cP0lzraphmhdBACLxhKm5t5yxO1V6/7EhAMD4UdXwuF3wuF1IJAXEnex58fv9mDdvHtasWaN4fM2aNVi0aJHuaxYuXKg5/vnnn8dpp50Gn6/8FnOzmUYMJ+letEXq8r+tDrM0aZXrWNK8xJwZ57arMeNAarE18rw4fYFl2UbeVLn5fLVELMMl3QIreV4cbhgyrNC+7To8CACYOqYm47FBblNTCRlHdmpe+iIxSSg9LuV1YfdKsTeKtoeNbr31Vjz00EN4+OGHsXXrVtxyyy3o6OjAjTfeCEAM+1x99dXS8TfeeCP27t2LW2+9FVu3bsXDDz+M3/3ud7jtttvsPlVbYJqXTJlGDCelS9vRsVct1mX4HV611K7GjFK/GJXAtGI0L6lr1JOakPP1uDNjMN1un12rTjcMGflqMpJJAbt7BgAAU8fUZjgacLtdcq2XChDtaucG66yXA6mQ0ahqn6TjkueG4obkbPenXXHFFThy5Ai+//3vo7OzE7Nnz8azzz6LSZMmAQA6OzsVNV+mTJmCZ599Frfccgt+/etfo62tDffeey8++9nP2n2qtmC2ozTDSZ4XO5reHZGMF7/i8YDDNRrqOLZV2UaDFe55YcPqc7sRQTLviZ95WmvShI0qZWwZ2nT07Ma4sy+CSCwJn8eFCaOqTL2m2u/F0EiiIqrsagotWmhTHOD0Lgyf1w1Ei7+xKUgwcOnSpVi6dKnuc48++qjmsXPOOQebNm2y+awKg9wawNxQs0VkoIS6d+aKHVVhBw3c8k5PP7U7bKRO7ZXaAzi8MSPzDnpSzT3z1WUxAXR6z4uzr1U1+dYo2tktel0mNlbDmyFjk1FJVXbtDBtJepeGaukxqRFuka9f6m1kMyxdr9pEthEABFNivogDJja1YW6F5kXWEKmMF5Zt5NQ6L+rfLZqhWNhIndpbKQtsUq15yfP9ZE9WGs9LGbdeGIzGsfwvW/D6Dm3ZCiO01252f7PjqLiATmnKrHdhyMZL+W8CM2Fn2Gh7ynCc0Mh5XkokpEzGi82YbcrICEopv+W/CNuxIxgyCMM5Pf1U22rBmvc1Euz6yniBzQZ2jXosEuwOmsguLGcv4b0vbsefNuzDFx960/Rr8hXsMoMwm+xL1v+oMxzJ6m+VI3Z5ZQVBwMsfHQYAzJ8iZ/qWiuaFjBebkcuFmwsbMaV8xAHGi9XVSwFjDRFLP3WC0aeHXe0BjMJGzPMSK8MFNhuYbeZ1i58330s0G81LOXoJP+zMvrIqMwhT9mHW124kVbuJ3eNmWDClEQDw4tbuLP9a+WFXGYWdhwew/9gw/B43Fh0vGy/keakQjg6Kqb2NNeZq0bCwkRM8CHnq9HQx0hA5v86L8ncrwkaCIHBhDuWuNlChnpe8i9Q5vMJuLpsqNqSydyu71zMjj6/rlIkLT2gFALyy/bAjNoLpsKvOy7qdRwAA86c2KjyJPm+FpEpXOpLxosqOMYKFjUr5hovEEqZ2jeqOvVbEYo3CRmxic26FXetDcNF4UnL9qj0FbIIq9u7KblgWl3Wal8wVdss5bJSLFi8peV7YGGc3yszzEjSpGwSA2ePq0VQbwNBIAls7S6vfndVY3VyUsbtHrK1zQqqfEUPyvJBg19kw42V0jTnjJSAZL6U5scUTSXzsrrU496cvZSxDrS1bnf/fNyr65/RUae38lP8MxWe0qRdbv8fZdXMYSZVXIB/PSyIpYDiWuTSCv4y9hHxY1mwZejakzHjJ9tKNpDZKwSzCRi6XS5pznZ5xZEcbFgDYlxJKj2+sVjzOjJd4kVsvkPFiM3LYyKznJZVtVKKel+7+KLr7o+gMR3BsaCTtsVb3jQG4sJHPyHgpzXHLF01GgQXzxkBEFuu63cq+YSwd0umel4QqbJTPvD/M3bNpw0ZlLC7nvUVm+6/lG5pjc2E2nhcACEoNGp05JzDUm0IrNokAsO+oWONFXVunVApYkvFiM0eyNF5Y+KNUU6X5CTej8WKDN9OoV1Q5u+LNYMdYGmUaAeWty8gGZhR6Pfl7XpjexeN2SdejHuU8tn0RuXim2cZ8ScnzIv6bteaFhY3SjKkeVamN4HCJbgStwg4xvyAI2Jeq8TJR43mhOi+OJ55IStlGTvG8DHKhhiMD6Y0XTXsAKzwvMf3Gd3Kdl/JbEMxgR0bBQNQ4M6ZUdld2IxWpY9lGebyX1JTR74HL5TI8jg8bWVWvpxDEE0lpMwaYb2GSSLkC2OfOPtsoN89LVRnoB61AmxhhQSXzwREMjSTgcimr6wKQCgVSqrSDOTYk3twuFzCq2qTxUuKp0grjZTCT58X62iSGqdIO17zYUctB7mukrZ9Rzt6BbFA3ZszHKGQhzXRiXQAIeOSux8VeALLh6OCI4roz63mJS6Lo3NLRZc1LlsaLv7TnUquwY55lepeWuqAmy6tUNjZkvNgI07s0VPnkmHoG5CJ1pblo8L1CsjVeLC1S59OvS1IpmhcrODoo9olqrK5c4yXfNF4eqSljmhovgDy2QHmJdrv7o4rf+yNmPS/K0Fzu2UbZLVdsLnV62Eid1WlF4Ohgr1jcb7xOL6lS0cOR8WIjR9jiYDJkBPB1XkrzhhvkurQezTJslG+oI8llc6grFjtf86LO3LLGNQzo1yCqtN5GbELO5xLtTXlaWXNVIxTGSxldr6yjOyNbzwu7prL2vKTu+UCOYaPhkfIZ41ywwysrVTXWuZZLpfo2GS82IqdJmytQB/B1XkrzhlOGjaJ470DYsH+I1bHYSDwhvadhnZcyWgyywY5SDsz4HK1Tg0j2vJSmEW0VciYMW1hzH1mzZRE8bpcUpion4+Vwjp6XpKQryq0Fg6R5yaJIHcAZLw73vGh7G+UPm9PVWZ1Aqqs0gFicNC+OhWleGnTc8kbIjRlL84bja4M88WYHLvvla7jm4Q26x2oEu3n+bb5eg2GqdIkafflix+4qXRp/qfQvsRt1kbp8HFosDGdms1KOYbnDKs9LX66elyz/bq5ho4rRvNjRhiVNYUDSvFQATI2fjfESKHHBLm9AsEnprT3HdI+1OtTBxLpBn1tTl6TSNC9WTFDp0vjLuZBaNqiL1OVT4OtIFtW05fEtn+tV7Xnpy1LzkmtoLpqjYFfSvDi9zosNG5t0xRZJ81IBsDTpTDFwnnIKG2XCahV8umwOp4eN7KjlwOr06IU52C45kRQ0HjQnoc02yv29sqmmXY6F6pjxwprMZp9tVLj2AEDlhI3smBuGWdhI13gpDa8sGS82EjYp4OMp9TovA1kZL+rf87vYh9LcUKzjbDm54bNBs6ha4XkZMPa88GG5Ur0WrSBfPQZPuvFU4y/D1H5mvBzfXAtAnt8yweq8eLMQ7N79/Db89LkPAfB1XijbSA876rwMp6mtQ12lK4DeYXEyy8XzEo2XZgGrbPqEWL1jN6rxAlRCnRfrazmkE5TzC4WTe8MkVZ6XfG65bKppl7PmZe74BgDA1q4+rH6vM2OoNq4KG2W6dgeicdz74g78eu1O7OgekF6ftWDXX9obQauwoyQFy9DSm2vZtUvGi4ORwkYmC9QBSku3FBfi7Dwv1uo0BllfI52wkfM1L6rf83QOD48kpN3VqBqtce1yuSqiQimbf5lXIJ9rNCvBrqcMjZeU5+XkCQ0AgF2HB3HjY5vw25d3pX1dQiXYzRTYOMbVj3pz9xHp/zmHjRxsfAN6rUOs8LykyTZi7QHIeHEu4WHxAsjG88L3RCnFRcNI8xLXuZCt9haw1Mw6nV48Tte8WN2YkdUg8nvcur2NADk852S3u6D2vOTxPkezEOyWW12iSCwhaVxOShkvjFWbD6R9bTyRXYXdo5zxsm6HbLyk6xelR6WEjezxvOg3wAVI81IR9OUg2PV53FL8vRQXYub9UBcMHtKZINT2TL43VVefWPWxud44zBGJJUoy3JYvWv1Qfu/Xw9V4MerDwyYuJ4eN1F2lc82I64/GpcncjGCXGdvF3r2ahRWo83vcmDy6WhFW1LsfedjiajbbiG/4+vrOHvHverUZhpmoBM8hYE/HeaNioADX24gaMzqX3iG5PUA2sO6ppXjTMc+LutPoUFR7rlaHjbrCovEyNhTUPNdUG4DH7UIsIWjKmDsBbUZBfmPZkepdMmFUteExkueljIyXv71zED9Z/SHiiSTueeEj/GNLZ9rjma2Sr+elJ3XNVfs9psIbepqXrZ19eOGDQzmegb2wkNGYuoDG2G2oSm+sSdlGUp2XDGEjznhhVYuz7SgN8HVeysNAzBWrEyMAuc6LnufFXyKp0uk7iBE5E0skJS9FNp4XQHR3Do4kCnLThYdjeP9gGAumjDa1s2HGy+jaAPYcGZIf16myq97F5usRYcZLa0iv34YbbQ1B7Ds6jL1HhtBSrzVwyhlttlF+77e3ZxAAMHF0GuOlDHeuX//TZgDAtq5+vPhhNwDg0rmXGh4vZxvlVrqesWH3UQDA9JY6U8erjRdBEHDJL14FALxw68dwfLO59ykUzHhpqhO9LPzclEkHl1ClSmdybh0d1GYxZat3ASonVdrqYqCAuVTpYnsNyfNiE3y7eL3+EOkIFnDRuObhDbjqwTfxP5v2mzqeGS+TVJ4XPS2M1cWTWNio1cAwYd6gvUcG8/tDJYjVRer2pjwv6u+Rp9zCRr3cjp0ZLkD6xVXTNDDHcf2/1N87f2azqeOlOi+pBWBXj3zNsqZ4pQTLNBpTKxovvE6KZVUawfRwZsNG/PfIyMV4qZQidZpr1uawEaVKOxyWaVQX9JruKM0IFLDWS/u+XgDAkxs6Mh6bTArSQrD0vONxwth66blBnbCRuttpvjqNzjRhIwCY2FgDQG7n7iSsFuUxA29SU43hMeUm2N3do2+0dvYOG74mqda85DCukVgCr+8QtRnnmTVepHYW4ti+lfLcAKU53nzYCAB+/cVTpefCw+nrvSSyDBsd1elWn61YF1Bev07UwTFsyTYaMQ4bkWDX4fTmINZlsF2NXijGLsyIg8PDMelGmTS6Gs9+42ycND4EALrNGa30FsQSSUk0aBQSmpQKgex1oPFi9eZq7xHznpdSXEz12HtE/3s/YMJ4ybX6KwC8ufsohkYSaKkP4MS2+swvgLb9woY9svGSyRgoBmrj5ZzpY/DiN88BIOtSjJDqvLBNXEbPi7VhI6A0kx+swo4aUMNpGjP6veL3qJdhWkjIeLEJFjaqD2ZvvDCDJ9OkYCVmbm4mpKsLeiXru9rPDC29bCPrYrHd/VEIguh6NsrmmCSFjZxnvKiFpfkYgsMjCUnUPHl0Zs9LpEzc7szzMrO1TpENly4Mo+5tlMvEvzYVMvr4zGbDzC01as3L1s5+6bm+MjBeAHme6o/E0y5kWs9LepjnhfdYG6Xzp4M3eJwcOrI6PC8Igqmw0Qh5XpwJ0wnkctM1pIraHSug8WImRMWMl1Fc0b2aQEoXYUrzkvvFzsS6LfVBQ2Hx2AZRyNvdV3qagXxhY+e2oBIsW4iCPjdCaZqGsomrXDQve1KhsE+dPA7P3/IxfGz6GADAwTSeF7WYNBcLm+lrzpthLmQEaIvUhTmdR0l6XlSaF0DpVU7XYTqu0hVlMrzZPDMt1YYAAGaOzV7A7HG7JCNRr5SDU9CkSufplx1JJKW5mzQvFQib8KsD2bs7R6UWlLCOcM1KeIPFjOeFZQGM4jwfzPOiJ4q0slW7lGmUJouI1Z4otgreDiRthsmdfTrYd1UbSO8VLLewEUv/njy6Gsc312Hh1NEA0hsvsuYltwq73X0RdBwdgtsFLDq+yfTr1EXqeIOlkB5Xs+h5Xrwet1QwUk9kCyjnAJ/JjC5mvMxolQ2Wk1ItCbKFeb7N9mEqR6zWw0VG5PkzveaFjBdHwjQger0hMsHqwtjteeEnTDPdolnZ7kZut16Tmrz0dudW3lSdYXEBajUQ6wLyTeXE+LaVYSOmparNYFiXW6o0W/RHp7wDbQ3itbI/K81LdmzZHwYgNivMxssa4DQvfFkFoPQ8L4IgSHqz5jplQTrmues1OOc4b7yYDBuxz8+HNOemtHXZ0ljDQvDWbQQjsQS+8eRm/G+7WFl4R/cAfvPyTlNzqB1Y6eEGgKFUawCv28W1dJDxV0KRumPHjmHJkiUIhUIIhUJYsmQJent7DY+PxWL493//d8yZMwc1NTVoa2vD1VdfjYMHD9p5mrbAsm+qdfrwZIKFjYwmBKvgJ8mhkUTGm+8oCxtxnpealHGmJy62MtvoUIY0aYC7qRzsebEibMQ8LzUZFls5bFScSTlbpPYRQfFzMWF3T5qihepU6WyNwi37ewEAc8Y1ZPU6XvOiNlZKzXgZ4mpOjVa1PmhgXmKDc+Z1b2bS0eOJpPS3mri/lU6blQ42lx610Hh56q19+N/2g/jGk+3Y2tmHC+5+GSv/+SH+2p6+TYJdaAtY5ofUGsBg4+3zst5GDta8XHXVVWhvb8fq1auxevVqtLe3Y8mSJYbHDw0NYdOmTfjud7+LTZs24S9/+Qs++ugjXH755Xaepi2wCb8mF89LtfW7BT3UE066yrThoRhe3X4YgFLzUs08L3oVdi0sUtcpFagzNl7KrV9MNrCRk6UZeXhezBovUtioPMaT6S6Y8TJK0o4Z30eaCrtZDuuWA6Ln5aQJ2XkG/FwX9FI3Xtj14nZpwwisuu6RAf0xjifla0eq85L2b8nzyKdPHY/zZzbju5edkHVrAEajDfpBPpX70df3SP/fd9TYw2cnmkzEPG0KSaxrkOHFelQVe5NoW4XdrVu3YvXq1Vi/fj3mz58PAHjwwQexcOFCbNu2DTNmzNC8JhQKYc2aNYrHfvnLX+KMM85AR0cHJk6caNfpWs5Qmg7ImZCNF5s9L6r37+6LYIpB3Y9b/rsdr6eapDXqeV5MCHbzCXUwz8tYneq6DJ/UGVjcwXl1XJ7liqDSZuQzQQ1KmhdznpdyyNSIxBKS0cqKQo7ivALJpKC7AKor7ALiWJvJGoolkti09xiA7DUZrLdRJJbQGCullm3EQlo1fq9mXCaNrsZrO4Cdhwd0X8t7XuRr1/jiHUht+vxesWHo7750el7nzjqmH9OpHZMr/KLNp7gfGShOWxJteD4/6yWT56VUPNy2ze5vvPEGQqGQZLgAwIIFCxAKhbBu3TrT7xMOh+FyudDQ0KD7fDQaRV9fn+KnFMjP85J5x2gF6rCU0d+LxBKKiqW85yXgNdaZaFKl89K8MM+LcRM4P1fIqtgFlKyGbWCZPZZPCI7tbs17Xko/bMQ6HrtcQG1qw8Duo6QA9EX0DQK15gUwf52+vecY+iJxNNb4MXtcdp4XFno9OjgiGS/sHErN8yLp93Q0UkxU+1FXv+Y5QK15yezdGmDesxyyNPUw433Lli4um5EvjHi4RIyXfOu8DKXpKA3IYSPHGi9dXV1obtamDjY3N6Orq8vUe0QiEXz729/GVVddhfp6/eJPK1eulDQ1oVAIEyZMyOu8rULSvOSSKl1VGIW8epI08vRs7uhV/D6KE+z6UztIPeNFW6Qul7MUd8fdfeLEoNfXSD4X+XJ2WuhIm22Uf9goo2C3jDwvTO9S6/dKHha/1y1tHoyubWbj8jVFzHgIY4kk7ntpBwAxRTrbKtpMz3F4ICrd56y9RXg4VlIVYYc4z4uaGaleTtsO6RsvCcmz5ZK8NmmNF5MhTbNIxouFnheW+aimp2jGi/L3fK+coZH0ntmm2gD+cdNZ+MdNZ+f5l/Ija+NlxYoVcLlcaX/efvttANB1vZp2ycZiuPLKK5FMJnHfffcZHrd8+XKEw2HpZ9++fdl+JFvIJ9uI3XD90bit1q3aeDGKC7PS5wzeSAioKoXyaAW7ud1WR4dGMJJIwuXSZjvw8LvnaKL0F9xskIqpmewPk44BySvoHM0L07uo+4hl8mIyI4GJSQFzRvYdq97Fq9vF++KS2a1Zny+rl9LTH5Xuwwkp4yWeFHSLPhYLZuzqhRFYI8r9x4Z1yyXEeeMl9Vg6vdaAyZCmWZiHy0rNi5HxcrhI3eytboCbaePt87hxYlvIdBNSu8j6Clm2bBmuvPLKtMdMnjwZW7ZswaFD2vbuhw8fRktLS9rXx2IxfP7zn8fu3bvx4osvGnpdACAQCCAQMF7QioVU5yUH46W+ygeXS1ygwsMxNNXa8/nUsXV1g7VXtx/Glx95S5qATmyrx+SmGqn4F8BnTeilSltznmyyaKoN6KbuMVwusSjVSDzpuLCRoPK85JUqnWW20XAZZBupM40Yo2p8ONA7bOx54RZXRiYx9J6eQfzPRrGR6b9fPBPnzzJfnI7B7um+SFzasY8NBeF1uxBPCuiPxCxbwPMlnedlVI0fzXUBdPdH8dGhfpw6cZTiebawet0usD2rmbCRZcZLNSs7YY3nRRAERdiIp2dgxFBbZSf5NmlVk4/koZBkfYU0NTWhqSlzMaaFCxciHA5jw4YNOOOMMwAAb775JsLhMBYtWmT4Oma4bN++HWvXrsXo0aOzPcWSYDDNDZ8Jj9uF+qAP4eEYjg2O2Ga8MJ1Ajd+DwZGEJkx1/0s7JcPl0jlj8YsrT9aIYNVlznmsKFI3Ek9KYkCjhow8AY9ovJRb2OhQXwRBr8ew4q2cbZS/54XtrDIKdsuoSF2/KtOIkUnzIFfY5QW7xn9HEAT8+J8fIikA580Yg6+ee1xO5xuq8kmGyq7Dom4iVO1DTcCL8HCsaDVD9GDnYlRwc+qYGnT3R9FxZEhjvCg9L9pso/cPhnHzk+245cLp+MScsXJIM2i158Ua46UvEjesOJ1ICjg2NCLVGSoUViZGAPLalUuZj0Jim+Zl1qxZuPjii3HDDTdg/fr1WL9+PW644QZcdtllikyjmTNnYtWqVQCAeDyOz33uc3j77bfx+OOPI5FIoKurC11dXRgZsVe8ajWsXH4unhdADo8c6rPPFcmKj7Gy+mx3Gk8kseKZ97Fup5hd9IsrT8a9XzhFN3tH3WCORysky/6m+vxv3sA3nmwHYNyQkcdXhunSO7oHcM5P1+Iz979ueIy6+3E+01O2dV7KQfNi1EsslKHgI7skzQp2n3nnIFa/3wWv24XbLtJmTJrF7XZJNVN2dA9I58oMyv405fYLTTrPCwCMHyWGu/S6uSdSSnOl50Ue4J89tw3buwew9PFNAMRQOWCH5sWasNGBY2I6tLrLNfPw9BikjNuJ1RV2h6TvoLQ9L7bmkj7++OOYM2cOFi9ejMWLF2Pu3Ln44x//qDhm27ZtCIfFWgn79+/HM888g/379+Pkk0/G2LFjpZ9sMpRKAbk9QG43ITMoDobtqx3A3IPMo8HCRn9tP4hH1+0BAJw9rQmfOnmcoSAxwKra6ugi1PZMtjeVIAho39cr/W7G81IqaXzZcOcz7yESS2Ln4UFDkba2gaAVYSNzFXad4HkxqpnEdFkeT3rBbjIp4I9v7MHt/7MFAHDT+dNwYltuVV8ZrNT+jsNa42VQp25SscgUAp/AjJdjWuNF9ry4Zc0LN7y8fm4gGjedxm8WVtZhIBq3JIvrg04xm/WkCQ2Kx9l3WQzdi9Ud5wdMZiMWG1vPrrGxEY899ljaY3grfPLkySWlss+HfOOGY1NeBiNxmBWwRUkyXobELIdH1+0GAExvqcVPP3dS2vfIzvOS2/kxzHhe/GlSt0uRSCwh1c8BxP48c6q1i6LUmFHavub+N80uEGyxiiUExBLJtHqjYtMnaV6UnpdMmgfdVGmd417b0YPv/u/7AMRS9bmGi3hYOJiFrlrrg5JBqSd+LRbSXGZwvUxoFDda+49pN1rxhDy+UsiTe57/vjbsPsIJdq3Z9YeqfJg6pga7Dg/i9R09+MScsWmP39MziFHVfoSqfUgmBXQcHcKk0dVSksl7qaKEs9tC2LBbrvEypi6Ajw4N4PBA4ZvCWuHh5ikXzUvpzkZlzmCGQj+ZGJvqy9Jpq/EiLvAs/fhQXwQX3/Mq3jvQB5cL+P21Z6StaAvIxbZ0NS/Sgiv+nq1hqp7AzXheWC2JcgkbqYWkertXoDhhoyBX56HU+xv1S9lGys+UqUN7Ukewqzf5v3dQXLSqfB788dr5lhhyai3bpNE1qE0t5qVkvMitTvTnsvFpPC8KQbTOPMB7Qzbt7TXdNDQbWLfvtVytKj2eeecgzvv5S7jmkQ0AgF/833ac+7OX8Mw7cnua91PXwexxyiQSlj1WDM+LtrdRfu9X8ZqXSiaekAWjuQh2AXmh7rQxbBQZUXpejg3FpHoN1581JW01W0Y6wa5aDJntTTWgivtnMqTE82HegsIaL919EY1A2QxqV7aebgCQi9S5LQkbmRPsBrxuSadQ6qEjI88L05UYLSoJQXmNAvrX6Y5DYmjna+cdZyiqzhY+7d/lEj0YzONQSoLdTGUfmOflYG8EcdV9p58qLcOHSXuHR6R73kq9BTNeXkm1N9GjLxLDTX/aDEEA2vf14nB/FL/4v+0AgJXPfghBEPCdVe/irT1iReU540Ko50KUzBAtBc1LvpDmpYIZ4iZ6I4V+Jpg35KVth/HQq7ssOS816rAR46vnHoc7Lj3B1HukzTbKU6ehjvuna8po5nzs4qVt3TjjR/+HO/76XtavVWsxOoyMFylVWvzdivYAmTwvLpdL1r2UuGi3b1hf8zJ+lHgfHdAJaQB85WJesKsd3O0pUe3xzdbVtpjDVeVtC1Uh4PVIm51S8LwkkmI36Uw78Za6IHweFxJJQeMpTihSpdk8ID/PG+/9EVnzov4e8+GUiQ0AxOSHu5/fJgmkefar+hLd+t/t0v8ba/x4YkMHnnizA4A4D00dU4vfLDkNtQEv7vrc3CJrXiyu8yIZq+R5qThYk0KP2yUJSLOljTMofvCPrZaclxpmvIxRFX773Lzxpt8jncZEqvHg0U5aZuiPKr0SpjwvLGxUQM/LT5/bBgD404aOrF+r8bwYLLJs6PINGyWTgjQ5mdlZsd12qXtejKqCspBGV5/WKwCYaw+QTArSgje9pdaycz59SqP0f9bPjKUIl4Lxcseqd3HaD17AGzvFYnxG14vb7cLoGnEOUV/PCT3Pi0HYaCAStzzbiL0X8w7d++IOfPZ+bfLH0UF1jSu5MOehvgjuW7sTAPCZU8fhb18/Cx63CwuPG40tdy7G50+bUFTjJam6rK1qD1AqdYaMIOPFBgY5N6uZasJ6qBdqO8IgEW5HxcRZnzq5DceNMT9BM+NsJJHUaQegXBiy7YTMh40e+fLppnYCzJgqZNgon4Wd9ZdiqZdGYSNBpXnJ1VXc2RdBUhC1QXyPKiOCZeJ5MerHMqY2AL/HjURSv7iYWkvEP8Y40DuM4VgCfo9bKuFvBbzmhS16crZR8Y2XJ98Sq5WzUEi6+6/aoEErb7y4dVYbI8+L1QvnDK4abHg4pjFkjwxqjQ7WpPbI4AgO9Iqbiv+87ATFZo+FcYtqvKSuVzNFAM0wmGeZj0JBxosNSG75PNxudUEfrlk4Sfrdjp0v3/r87itOxs0XTMPP/jV9dpGagI/rJ6SaEKQ01Bw1L8wIPHtakxS3zgQzpuzINjJKY47ksbCz+iQs9XLf0SFdw0sdgsvV9bIz5UGYNLrGlOi0XMJGRhkxbrcLbSnxu142TEIvbKQ6pju1ILWEApZ3Kv/XlJfz5gumA5AX7VLwvKhJ56lj4fEh1TwVZ3VePFyRutQAR+MJxbzWz6Uzq7VL+TJNVcr+o0PK0BHzvFw6Zyweuvo0vPqt87D2tnMVIfVQlU8SgKuRNS/FE+x6XLltEtWYbdxabMh4sQEWfw9V5XcDrrj8RGlSzWeB1COWSEpiuiqfBxed2IqbL5iedRYFHxZT60zYJOXz5OYtyKVUODt/qzUvD726Cyf/v+d19Uf5GJZssp7RUocqnwfxpKDrfZEzt/KboFi14uPG1Jg6vnzCRsbZfePS6F54jxbbuaqv0yGTvaBy4YefnoOnv7oQV54uNpRlC4ZarF4oWKhX7/5J63nxic8NqXRqCb7Oi2R3i4+pQ0z9kZi0+I+xuErt6Bql0bFuZw9e3X5Y8sAw46Wxxo8LTmiR+kzxXuhJo429bszzcnRopODJAppMxLyzjcjzUrGwzAd12ma2KASTFi8e/PsF/blfBumMF3XfmOw1L9nHv+0KG/1l0wEIgqg/YrUeGBGuQJ+eWC6dgI6lSjdU+yQ3NSsXz6MRP+f48WTjxVxoMFgmherSVYEd3yAuOrqeFy6d36iGTjrDKF/8XjfmTWqUwg9S2KgI/aQ+7OrDyd9/Hr9eu0PXg5DOeJM8L6rzjnOCXQa7dtW91Q71RaR7qakuc0gzGz59yjhJ9wKI9/GS322QinEe4YwXnlMnye0OJo02NvhHVfvhcbsgCFr9jN1ojZfcrZf3DoTltjHkeak8jEqV54Jdiwfz5LhdyFlUDIhueeZZUYdqNJqXrLONsve82JVtxJ85X/UXUH436jHYd3QIF/7XK4rsBR62+2TFtABgV482G0LdmDFnz0u3aBgd32zOeGELtlE/l1IhXTovW7SY4cbDDGy3SxaUqo3s4TyarGaLHDYq/Hg/8NJO9EXi+Olz26RQGU86463a4DpRCHZV1y679uUNhyC9l9WZLqNrA3j1Wx/HvV84RfH4EymR/dGUroel1jPOOl7u4zex0bh0hMftkrw7hda9aMNGuRGJJXDZL1+TfifjpQJhnpd8w0YAUJXyimTSHGw/1I/D/VH87Z2DWLejJ6OhwOtdchUVM/wGoZp83ZkDBiXfczmXfOHr7bDvF9DuNPndZDIp4JO/eg07ugfwl00HdN9XYbyk8bywsXPn4RqOxBJSeXOznhe2KJVykbpEUpB27HoGxrxJYlbPup1HNLV4pHF1uQxDcrIY2P7JXA4bydfRvqNDWPnPrThk0M3YKljBSQDYlTL0Zo+rx8UntuLUiQ1pwybM2FAbL7znxa0SlLJrn6WzM9QGhJVMUxntTaksqaMGnheWZg0gY0idCeCtaEOQDVL17TzDRmqPkVr8XmqUtmlVpjDNS70VxksGz4sgCLjtz1vw9Kb9isdvWzwdyz4+zfB9JePFgt1kwCd2pdYIdlMTF7vps9W8lErYaDAaV1TCZd8vAHSrGmf2ReJoThXf7BmMKl43Ek8qerkAcrZRQ7VfSinfc0QvbKT0YuWSDrlq8wGEh2NoCwVxQlt95hegPLKN+HtD71o5dVIDqv0e9AxE8WFXv+Kz61WAVY9tpiJtVsIMdb7G0eceWIdDfVHs7B7AQ9ecbtvfjnGxyP/bKlajba2vwgNL5mV8rex5UWcbie+p11Wa3Uet9UHs6RmUxl1dedhKWGiWweZolm2kNl58HjeuPXMKVm3ej8+fNiHtezNBc6Gbauab1clQG11G/exKBfK82EBYChvlbxsy48Vo57u9e0BjuADAh139ad+XLUZBC6xrY8+L+G+unpecwkYs28hC40Vd5Zj3vKjd6/3cc+rsJP45AHju/S68kwpBhap8kqdOrylfUuV5ycU5/Oe3xdTXa8+aYlqYza6/Ug4bsQXT5dJ2+wVEj8KCqaMBAK/tUFZZTXK7VqM2FoUMG9XopEqzzvLvqrRWVsOHO9Z8cAgA0FxvzpAw9LwktIJodukyo7Pa71Hc43YaL+r5jl07zOug97f/85MnYPN/LkZbQ/qK43rfXSHQVt/O7X14r/EFs1ryPS3bIePFBmTBroWalxH9xZhvDsaTaac8bFAXIxeMCtUlk2pvQZbZRjlU2/TZoHk50Kt01/M7lD09Si8Jv+tS99PpU+3I7lr9ofT/xhof/B7jPlEazUsOExRbBE+b3JjhSJlMxnMpwDJcavxewxDoqSn3v9qoT+oIdtVjO2ShlzITkuZlJK4JcWVaPPOFD0sxL2pznVnjRd/zwnsFXKrH2DUV8HkUqdF2Gi9qjg2JNV+YB1TteckGyWuWo9i6d2gEf357X9bGj1ZbmNOfl+anueNDeOia03J7kwJCxosNWCnYrcqQqvr2Hn3jJdMNZGXYSDZelOeo1rxkuyOQ+pxkId5jnhcrw0YHe1WeF854YRoSBm+8qEv/q7MrDqaMosUntOC4MbWmOnTnaggCSn2NWcohVdpMNtDUlMZHrSfixY7qxZVRSM8LWwAFQTRg+E1Im4leY/mgJ9KdmqU2ykjzwnte2OgynVLQ61FsUMbYqHkBgC/Onyj9v3doBEcHRyAIogFrpnCjEWyeyjVs9LUnNuH2/9mCn3CbGjPwui0g/7CRUS2bUoOMFxvoM+hwmwuZNC+sUZhaTJfJzc/ez86wUcpjzBX2ys3zkovmxUrPy5b9orueFaziPSha44VrNKcJG8mvi8TkAl0//dxJcLlcpvpESaK8LD9DPJGUxjMb4yXoL33Ni1yHxfhaZgLlnYcHFGEhpnlx8YJdQ82L/RLBoM8jhb76hmMK/ZOdxlMklpCuV9ZzqbU+iE/MbjX1ejY26pCnsjmrMuswIs1BboXxMtpmz8sdl87Cf1w6C4AYLmJG2+jaQF46j9pgfmGj13ccAQD8+W2tDCAd0sbGo7IOs6Qvh81NMSHjxQYkz4uFgl29InWCIOBgSo8xm2vyBugbL8cGR/Dxn7+EFc+8b2nYiFXZ1Rapy09kGk4J+hqy6OIre17yrNSU4lBfRNIUXZEqJNaf+n4FQcDWg6LxMrNVrOCp8LwMqzwvnGHDYuw+j0syctNVBxa48Ib4e3afgze4stFiSZqXsvC8GH+uSaOr4XKJ3w8rd8+HZZSCXaNso8JkX7B5Y1NHLy75xavS43b262J6l4DXjXu/cAq+cMZE/PdXFpquKFxjVOeF07yoi0NH4vIGis90mZgmq8kKqv1e6V6OxpNSUUizITIjrKqOXJulVjKpCinn2jrESq1mISDjxQYkzYsVdV7SuO1jCUFaxD45d6ziuSGdG+jJt/Zh1+FBPLpuj3SDWaJ58eiHO7RF6szfVIIgIJxa/LMyXiz2vPz3W/swEk9i3qRRuOhEcRfKvt/9x4bRH43D73FLKZV9aTwvfNiIFQEbXROQdBryuesZquK/UquFLD8Hm5hqA96sStxLqdJl7nkJ+jyYkGrSyNKA+etRoXlRvbaQYSNAXjz+/s5BxeN2dkrv7hdDmC31QUxpqsHKz8zJyogwEnbrdZWW2gOwsJHPjTNSTSqnNtXgY9PG5P5BTFIb8Eqbqm2HRB2UukFtLu8J5Ga88B7bbPs6abyyOWterNt0F4LyMLHKhA27j2Ln4QHL2gMA6cNGvMbk3BnN+MO1Z8DlApb8boPUxp5n3zG57Py7qVCIlZoXozovuQjJBkcSkvekocp8DDZdl+tcePa9LgCi14V9n33DcQiCgO3d4qQ3dUyNFCtPL9iVfz+iUxQrYELzwuyObAv+5aJ3Acqjwq7ZCrhTx9Sg4+gQdvUMYv7U0VJ1XUCc+OXGdvqel+oCFe1i4tWPDinFxXYaL0zMnav3gYV2jSrs6nWVlsJGXg++cf50zB4Xwr+cPK4gKboulwujavw43B/F9lSfo3xbEuTT2mE3J/zPNkSrLWCZG1auW4WAjBcL+fxv3lD8bolgN02dDX4y83vc+Nj0MZK4VO/49o5e6f9vprKUrNC8BAyzjcR/mbcgG88LE7v6vW4EfeY9BT4LBbt7egaxtbMPHrcLF85qkTOZEklE40mpWu1xY2qlLAXmUXn5o8P4U6p6J4OvDyN5XrgJ04zmJddso3COocxySJVmhnomYTfrCL0/ZcTzY+hJp3lhKb0FDhvtOSKe59zxIWzZH7Y1bMQyjVrqgxmO1Me4wi7XmFEj2JXDRq2hIL44f1JOfztXRlX7cLg/KnlezKaFG5FPawe++vPhgSiSSYEri5AeuQFufp6XsIWJJoWAwkYWoV4sXa7sY5d6VKWpcMqMBZ/HJV3obAIfSSQVi+DQSBwfdsniUtbi3QorO5PnhbUPyOamknr+VPmyqgBsZdiItQE4ZUIDRtX4UeP3SHH78HBMKuN/3JgajE1lgnSGxUXgmoc3SO/DxNS854XpLpo4z4tfKuYHqWEcQ/a8sLBRrp6X7K5JuX5H6XU5ZgybLCLHUo1ZllciyYeN0rUHKGyjOrXmgPXUsaNTOoOJVnMNnWSqsKvINkqNb4QLGxUDllXDvB75el5q8/C87D8qZzQmkoLUa8kM8iYxP82LVT35CgUZLxah7mcxZXSNJe7PdG57NpnxZb151znvfdl/bFhXMGu2u3A62N/XZhupdgRZLLjMeMk2ddFIf5OJdTt78B9/fVeRKcC8I2zRc7lc0q64bzgmeV6mjqnF2AZxx9rZO6wpTsd2/Lzm5UjqvZt0PC965y9rXsR/1Y0Zn3izA197YpNhafJcw0Z8qKxUYRkurDmgEeNS3yMz3BWaFzc0vXcYdjZm1EPtHZuUun7sDRtZ5XlR1XlJyiEN9fgywW6gSGXoR6W0dMyIHVOX22dn1OSheVHP70yDZAa+0GI+lFu2UXmYWGWAuu/IrLHmyq9nwkzYiK8q6ve64fO4EEsIGIrFEYJ4ITJjwO91KybBaS11eZ+jkcGgKVKXxdzLMnVCWYh1ATnzSV1zJhNXPfgmAHE3GKryYdXmA7pVN0NVPvQOxdAX4T0vtZLL+RDnhmZMYMYLtyNjO6vRXFEshfEST4K32+RsI/0F9jur3gUgNpj7078t0Hy+XCcmJpZWZ06VEnKl1vTTGTNCD6S6S/PXo9slewbU16ks2C3MdKl22zPhrK2C3ZTmpSXH0IncAyuJRFKQNiyy58XNaV6QOta6cg25oC5Il2/YiKV7D0Tj2NMziGq/B80mjUH1d9vdH8WJJv+utLEx0GyZxcr6ZIWAPC95wi4UtfHCUmfzRWrMmEawq+6Xo1dz4VhKQ3LC2HpFe3qz3YXTYVhhV5Uhk53mRQ4bZUPQK0+iufA/G/fjd6/tVqRuNtXJkxw7n12HB6XQz5QxNWiqDcDrdiGRFDTaJ5bl8uKH3fi/rWLZdbaz4jUvXs61nmurhTd2HcGRgShe+OCQIo6es+clZbxEYsmCVtlNJgWs3dZtqsndoMnMOeZ56eqLIJEUFIJdUfMi/t/I81KwsBHntve6XdJ526l5Yddjc47eB74WE+99kbKNPNpsI7lIXXHDRgyrBLuH+qK45Bev4vJfva4ITaZD/d2u33nE9N+VEyNyy0RkyPXJyHhxPOt29OD0H76A1e91oiusMl4s9rywhSMaT2Dj3mNIJAUubKT8Gmt0XLhhKQzjU7i/s03L08Oowq7cmDF7zYtc7TFLgWkORdXUZdjV8JPauFQHXCZ4bqr1ozbghcftQmtIOfF73C7ceM5xOHlCg/TYw6/vBgDs6BYNC75RnMvlMqz1kqlDdx33PT78+m5c/4e38aVHZN0N+/6zNV7qUp8NyK5b7hNvduDjP3tJ0z4hEz0DUdz4x42YveI5fPmRt/DT5zJXG+3lru10jKmTDcxDfRGFMe1yQW4cyI1tMilYWo3aDPzOt6U+KGlCrPK8qMOagJxtlKvnJeB1S9fWnh45q1Ev20jdHqBYnhf19WJVqjQgbja7+iKGFdDVsO/2xFTT0N++ugvbVR5cI7SVzM1NtM+934VzfroWG/cezbmIZTEh4yUPvvLYRvQMjODGxzahS9VdeHpL/h4NQKt5+dWLO/DZ+9fha49vknaEvOYFkCdZXjzXK9VM8eOUiaMsOTeGZCypqmtqFtws9gTHUp6PbDUvwRzCRpnEcU3cpDY+5UV5c7e4M+L7zagXl+WXzMS3L5mJeZNGYWrKSBmIxNE7NCItFurrxKhFgFTLwSBsxHvmfr12JwBg39FhyTOYq+fF5XJJr1HXrUnHd1a9i109g/jRs1tNvyaZFHDd79/G6ve7pGv3sfUdGV4lZmcAmSuzetwuSZt0sHdYMlrdLlZhVzyOn/v5cS2c50X+jo5vrk3b8ypbHnl9N076/vNYtVmu4hqJJaTrw2yYQ43L5cJJKSO9fd8x6XFlnRfla9jnKZbxwnteavyerCp566G3EXz8zQ5TYRw2Fp86uQ2nTGyAIGRursuQ9XDmN4nhoRi+8seN2HtkCA++sltR4iGbXnLFhIyXPOB37N2psNFJExpw12fnShkC+aLWvDz3vlh3ZPX7XXh2SycAbdhIr+YCqznSUO3Dzz43FxfMasafbtBqI3KB1aVQd03W9uMx/56sUVq2mhcWNsrG86L2mqnhPS/jU56XfansAL7fDG9wtIWC+MQcsXCg3+vG//uX2QBEV/m21KQ0rqFK0ZAO4Gq9ZKhWzE9QsURS2uGqYUJb1hk7l6Z3DZLxkr3uJRtvzc7DA1KXbYYZQfkRyXjJbOiObxCNz909g5pQHAtrsOs2kRQU4cOgt1CeF3nxWHxii3R/W5H+/72/fQAAuOWpd6TH+Oq6+VRXZR7GzanvsHdoBK9uF7t4B30eTSo63x6gGPAbo3y9LoD4OdRJGs+8cxA/NtGrKJr6bv0eN0bXiOdiVvirFuxmmmY/OtSP03/0gvR7XdArZRrV+D2mO84Xm/I4yxKFL1rVlTJevrRoEj6fKj1tBVWcEC6eSKLjqOySZf9Xh43YDpHXvMgaEj+a64N46JrTsfC40ZacI7PU1Q3J2OLg9eSjecnO8yKNVxa71K6+DMZLndZ4YfCel59+7iSMqQvg8evnY93y8xXPSd6wWFwqPqanizLqE6WeoPixTKdF6ewbRjyRlHZxM3LQYoUk0a45Q4TfaWaTcXdUxwNmJk2eec7MaBaYkP79g32S5oX9DXUdki89sgFn37UWgOixyjebwyy8J3XxCa229OviYfPIuIaqrMoSqDklZby8sfMI7lr9Ib72xCbsPDyI5roAPn3KOFmwK2UbFdfz0lgjbxysMF5cLpeiTQsrkWBGv8K+Wz/XpNJsyrU0z0obG/15NhpPIBpPYMPuo4prKRJP5lwHqpiUh3+oRKkNeKVdi15mihXwYaMdhwcUQlRmLQd8+oJd3vvAds3ZakjMUGtkvCSNvQV6JJMCNuw5ipMnNKDjqKiVyLZFPdsdJ5ICYomkqV1EJuOFPwcWNmKM44yZC09owYUntOi+h+xBS0rZSNP1jBfTYSOZdNVvO3sj8LpdiMaTqPZ7cvIIMs+LnlZCD3VlYbMw4+iUiQ247qwpWPbE5oyem1giKRm6Zhr6zRnPjJewIo0XkI0XZhi+ur1Hel1rjuGUXDh1UgMWTG3EnHEhjKkLSNdC1ALPy6TR1dibKn7HCqHt6mEp//l5i0+Z2ACfx4XOcAT3vbRTevzGc45DW0OV1GRS43kpkEdLDR82ylWorOa3S+bh+3//AHUBLz47bzz+9YE3THkfo5Lx4pbCT2pPthHSxiZNAcvBaBzn/ewlNNUG8KmT2xTP9Udikoe2XDKNADJe8oKPgbOFWx0GyBc+bMS6G6v/pt9j4Hnhwka9Q7kJYM3APnOfQdjIk2FHwPjDG3uw4m8f4LRJo/DRoQF43S4smNqY1bkE/fJYDMcSpoyXQxnCRvx7jGtQel7GNZib9KolIXEcPf2iIdkW0r7WaJedLmwUGTFe1DrDEfSn3M8zWutyqj3EJnmz6dJ8GC4bnUyYyzBjIYjwcAyCIBh6BNimwe0yl5k2u03cGb9/sE8KtbEh4Sd/tYg73zTabAh4PXjy3xZKv/PeuHRjYYaWuqBkvHQcHcLkphqp1xMvHs+Fhmo/rjx9Iv64fq/i8RNSIlQm2U0KAgRBcFzYCBAF1r++6lQAkAS3ZjyWI1zmqOTJNhk2UteA0ptmV7/Xhe7+KLr7ozhnhtg7akxdAIf7o+iPxHPWxBUTChvlAV+OnHV3tiJ7h4f3vLyrMV5SnhfVzqVGp9plr5S9k50nwwx8fQOeRJaal/96YTsA4O29ouBv4XGjsz5fv8ct7aDNpvYyzws/iZ4+WRQ1nzZJKW6uCXgVE12bypgxgu9RxYxKPYGgsfEi/uvWMQTTel7Cw5KO5IQcM+CkWi8mDRG+bAAT05qBF5Wz730knkyb9s4KCTbWBEyFdaaOqUWVz4OhkYSkPWJhTb73jrqLdq7F26yA17Tl2y2d996witu7Jc9L/kkGN50/DdNU5RdYqI4Py8USgjQfFKtIXajKJ52TVcaL+v0BsX5KpoxGKWzkcUuebPNhI3WqtPZvvcVlPfWkogVjU5un/kis7KrrAjYbL8eOHcOSJUsQCoUQCoWwZMkS9Pb2mn79V77yFbhcLtxzzz22nWM+8BcJW0usbifOp2eyC5BddCwvXxM2SlUa5Y0JKWxkg2Vdbxg2Ev81W9JefeMwwWs2uFwuudZLGo8ED/MUzGiVF/cT20LY8J3z8dj18zXHf/vimQDEFHBWPTcT7HtMCrK3QNd4yZQqrdJlAOmNtNXvdeGPb4g74QVTc9M4Md2RWc1LJ+d5OdwfxWfue91U9lcvl85d4/dkTNFOJgU8/qaYjdRkQqwLiF5ApltiRQbZhoMPyQ2qDPFcU4itIJCm8nK2DHPeWObJ3XVYNF7y9bwAohGw5tZzFNowtohLpqUgV9cFiud58bjlTDo7jBemH0kKwECG9hrsew143VLZg1wFu3p20vpdsu6GGfwsFNofiZddgTrAZuPlqquuQnt7O1avXo3Vq1ejvb0dS5YsMfXav/71r3jzzTfR1taW+eAiobfjtaKfEQ9fwImJLuelvAEsDVEdNmK6G75lQSHCRgPRuMIjIO0IPJkr7AqCgGODykUqF+MF4EW72XleZnBpy/VBL5rrg7piws/OG4+n/m0BHv7S6aY9Q3wBNfa96HnpMmlePKnv2iidl8F2lNu7BzCSSOKCWc24bG5u48muGbOaF7WGaFNHLz442GdwtAxf24dP0TYyXlZtPoAnUsaLmUwjBjMaWVXZmpSxL1fYFTSGeDE9L3zYMl/RLn+tbO7oRTSekBpV5qt54fnymVMAyB5MQJkNwwxul0s7fxUSpmfLtZt2OoI+j2SYZbp3+GrpbD7953tduO3P72j6nKlhT8sVdrXvzZp8ArI3VPa8xEmwy7N161asXr0a69evx/z54u71wQcfxMKFC7Ft2zbMmDHD8LUHDhzAsmXL8Nxzz+HSSy+16xTzRt2EzON2ZazymS1ejxt+j1tazOqCXkxvqQPQKR2j9rywiZa57weicWnSsjNslEgKGBpJSItDIqkOGxl7Xrr6IoqdxoyWupzjr8zgM5suzTQv07lWCZm0S/Oz9GLw3yPb+eiHjfRresieF+1Y6hkvnzllPLxusWHn3PEhMdsjR60E8zq07+s1JYI+rNOXJZYQJMPW6Dyk0Gbqe2+o8uHo4IhhivbqVNkA8W+aD08xY4W9hgnc5VRprefFKkFnLnjcLnhSxfXyNl44b+Q7+3txKBxFUhCN5nwrzPJce+ZkjGuoUhgvfFguKlXX9eSl4cmXZecdj7XbDufslcxEqMqHSCyK8HAM6XJQR3QEu4BY8fvSuWNx3oxmw9cKKs+L2sOtFv6y674lZbwMROOSyL6cjBfbTN433ngDoVBIMlwAYMGCBQiFQli3bp3h65LJJJYsWYLbb78dJ56YubtDNBpFX1+f4sdu2MUSUS2OtQGvLTci71ad3RbSuFnVmhe22DDj5a1UNdgJjVW2CLKqfLKLn9+xqutopGP7IdGF7/e48cX5E/HotafnfD7BNJ241QxG45IwbiYXNrKjUBP73ti41Oo0EmS70O2H+rHyn1uliUbTmFEh2NV+zjF1Afzkc3Ox8jNz8IUzJuaVjnrujGY01fpxoHcYf99yMOPxag8aINZiOfuutbjxsY2Gwm1JsJsysOszeF52c9V7v3DGxIznxWCaMLUHjG8PUEphI8A4hT5b+LDR0EgC61PFFhur/ZbOXS6XCxfPblVkgMnZXMWv8cL4zKnj8csvnGJburYUcjXpefF73Rrv/aa9x/ReIqGup6WOzqu9iOy65zPoWB0oq2UPdmLbldPV1YXmZq212NzcjK6uLp1XiPzkJz+B1+vFTTfdZOrvrFy5UtLUhEIhTJhgXY0VPfoiMZz1k7W4Y9W7mh2vXZUJed1LayiocbOqi9Sxi5JpOV7bIaZ8nnlcky3n53K5uFov8k2qLVJn7Hlh5fI/PrMZP/z0HIwNmRPC6iFpXkxM9CzEURfwStVXAeuzxgBtYz89zwvTN/zmlV34zcu7cNufxWJibDcl6Yf4Oi864TErr8Wgz4MrUrWL+PRhNYIg4L0DYalPzu0Xyd7V9v292H9sGM+9fwgvbO3Wfb26GadU2VfHeOmLxKTeTX//+lm4ZuFk059HChulzlMKG3HtAdSZHqxwWLGQw4m595cSBLnVAasxtPo9cS62I5ysRfYMSH2NiiTWLRSZQp8MPlVafe+mu+cA7SZRPc9qs0DFf0fV+KXran+qWamjs41WrFgBV6q9udHP22+/DUDfPZwu1W/jxo34xS9+gUcffdT0LmD58uUIh8PSz759+7L9SFnxt3cO4kDvMB5/s0NjvFidacTgQ1G1Aa9Gna8uUsdKfPdF4hgeSeB1Zrwcb4/xAsiLZZ/C88I0L0qPgx7bU8aLFY0is+lvxEJGLaGg4sat1vGKWHVejHTZRox1O8XvLl0IblhHmGz1tcjCJtE0mT9/3rgfl/3yNWzq6AUAzB4XwtnTxGuul/PGPPjKLt3Xq5txsgW1T2fif29/GIIgehNnjwtlVUCOGSusRUONFDYSnxdUYaPPzRuPCY25G9NWYNT8NBtGEknpHjx/lrixfPFD0ZDMtp5SLvDjywxupxsv9ZIBnr7MAJ9tVBdQGhBb9vemra6sbcOiRO15YdQGvJKnhXVaL6ewUdYz3LJly3DllVemPWby5MnYsmULDh06pHnu8OHDaGnRL+T16quvoru7GxMnyi7gRCKBb37zm7jnnnuwZ88ezWsCgQACgcLtivh4vzqF0y6lNn+D1wa9Gs+LOmxUH/SiyueRmoOxHSrfINBqxBtuWOF5US+46eq87OgWxcjTLOgJlU1/I5YZ01oftH3XodZD1fh1jBfVd8uK7bFFhxlAfLdaZkR73S6pdkm+fVrUBLyZx/Th13Yrfg9V+aRrl+8f9e6BMBJJQRNOVIeN+F3rXzbtx2s7evCjT89B0OfB/l5xsp3alP31wsadjRsbK749ADNeLpndip/960lZ/w2rYdeFUar0gd5htIWCaTd9fPbdBbNapB5YgLgLtxu+js6AyU7g5Y4kds/keUnInhe1h5ZpsIz0ilJI2aBInZ7xD4j3QV3Qh56BEeleKKdso6xnuKamJjQ1Zd7BL1y4EOFwGBs2bMAZZ5wBAHjzzTcRDoexaNEi3dcsWbIEF1xwgeKxiy66CEuWLMGXv/zlbE/VFtLdbFZnGkl/k9ux1wW9GoGuerfucokdjnf3DGL7oX5pwrMjHZA/L0Bp5ZttGCYIgqWel2z6G7GwUUt9UGGYjrZhMue/R14nxKP+Lnl9ACB7VBRF6lLPN9b40Z0mkykf1Dv/eCIJj9uFpCB/v2rPUkOVTyrOd3RQFtQOxxLYc2QQx3F1ReKJpBSqCXGCXfG1I/jlizsAAHPGhfDlM6dIoudcKlqrDTvmZeMlA+xcrDYCc4XveaX2Xv918wHc/FQ7bjh7Cu649ATD9xiKiZ/J53HhpPENCFX5pEU1U0duK+CvdhbSVndidxpmwkaCIHDZRh7de7c/Ymy8yJ4XVpJCiTpsxKgJeDQhKrMlB0oB2zQvs2bNwsUXX4wbbrgB69evx/r163HDDTfgsssuU2QazZw5E6tWrQIAjB49GrNnz1b8+Hw+tLa2ps1OKiTuNDsb2zQvnMFUF/BqPC3qsBEgp/590CkKmGsDXltdtHrGi+R58aTXvBwZHEHvUAwuFxQLWq5kI9g9MpDqi5Mar19fdSr+/eKZmDu+Ie/zUMNXZDZaFNXGCyB7CFwu2auU4DUvqed5caTV1yK75qLxJH7+/Dac8v01OP/nL+Pcn62VGoCquy6HqnzStavuW/S+KnWapW963S7Jlc1c2HyNig87RQ8dq1Kci0GuPs9aVdiI97zYFQrOFnZd/PvTW3DWT9YqUm/vfOZ9AMCDr+7WfS2DGfNBnwdut0vR0bzRhixENfz4dlaa8ZL6vn7z8k586tevKzLoeG+a3+vWnQMG09SJkY0X5e8Mo7BRTcCrmSeKmVWXLbZKvR9//HHMmTMHixcvxuLFizF37lz88Y9/VByzbds2hMNhg3coPdIJ5vSMCCtQGC9Bn+bi1vu7bFJg9TWyqYORC3qZIeodgZHmhYl1J4yqtsTAkjwvafQZDBYGYWN86dyx+Oq5x+V9DrrnpdAu6X9OvYmLufuruM68Ss1LynjhvEX2hY2S+OWLO9AfjWNXzyD2HR3GS9sOS+fHU28QNgKgqfuyO1UobWJjtaSRYhP/zsNyVtFTb+/Dxr3HJGMnl52i2iCpURWpgyA3Na2xQfuUC+y62N0ziAO9w3jq7Q7puXQVlnnYcex7msz1uSpE2IgXRHf2sq7s5bNY5gLzaB1LGSsr//kh3tnXi4df3yMdw9d0MlpD0lXblfqeGWQbGYWNagNeRajc73WXVYVdW8+0sbERjz32WNpjMvW70dO5FBO1YLE+6JVEqmYWy1wI+vlFz6u5wPUWPOZOZ00A7QiD8LD3P8btKNTZRkYVdllXW9aFNV+qUv2NzHheWBhEHYqzAzOeF73Ji190JOMlqX2eF11a7TFg4xPVGVNmsKqvQ4/bJYWS1Ls/JkRm7D6irfJq5Cb/3APrpFYHOXleNMYLyzYSSQpykbraQGloANRaqAGuY7zZ9Gl2P7DrcDI31qMK6HkRIEjh2tY8sgrLAeYNPTo4omgR0M0VcuS/P/Y9X3xiK17f2YMav1dTA0sNW0MlzYtqnu3TMXzcLnGu4RvNttQHilpzJ1uot1GWqNX+/CI0ZLKcc7YoPS9a40UdRgLkSZ01YTPTcTcf2Pv3cL1sNBkyBnMsU7rzJcXzQU6VzsJ4sclrxsN/j9mEjdjEFeR0MrxglwnHeePF6rBRujojbGenl4lUrfLGfGJOK3weF7bsD2PjXrnfCvO88AuqWkDNKoIKghx2ykXzovZ6qQW7fLaRkYes0Kivi+EM5eb1GBpRZvjwhmJBPC9cttHBCvG8sHvyyMAIejjdF2+MMO8vKyoJAPd98VS8dccF0oYunfHCpgK9pq2ArHnh57iaVE0yvr1JOYWMADJeskadbcEvSOqKu1ZRpc42MhE2Uk/qdnte+JuUId1UnvTZRvsl48UazwubnPWKt6lhngQ9A9BqqlQeND30SqWzXVqVXz9sxHbU/O5ZLZ7NF5aer9fploWE9CZY9XmMH1WNS2aLbQqW/G4DVr/Xibf2HMVDqUylKWmMl4euOQ0XzFLWjspN86IfNlJoXtI0zywG6nt+aCSBlf/civ9+y3xpCBZeZN8JHzYqiOZFqvOCitG8sLBmz0AUnb2yt2XfUblcP1+gjuF2uxD0yeLd9GEjdW8jfc0L3+KCvS9/DdjRIsFOSuPOLCPUu8ugz4Nrz5yCh1/fjW8unm7L3+QXgPqgT+P90TNe1JO63ZoXdpMeSe0ueBep10AFzzjQK97I40x2aM6E1NvIlOaltDwveh12D4blrtduHVEeCxuNbQjixnOOg8dtfcojGx+9MvxsIdIz3tUapiqfB7dfNAMfdvXho0MDuPGxTYrnpyrCRsrPMKYugJmt9Yoid7l5XlTGi1/bmFEOG5XGFKk2al/8sFvRAJORro7WsCZsJG8WCqHtYafVNxyT7rt8ilGWA6y4YV8kLoXHAbEZJvuu9IwXhtRh2sDzIgiC6VTp1vqgdA5s/uFD9bncS8WkNO7MMkJtOFT5PfjuZbPwjQum2VYnxMul1IqTqfJC1tNrqIWMdlcIZe9/NOV54RfXTBV291scNmIL7YvbunXrifAwT1qhNS9G4YhJOl2qmbixyufhehvJz/NZJN++ZKZVp6sgnXHXlSotrpcRoRbxVvk9mNBYjWdvOhvfenoL/rLpgPSc2wVM4/pLqe+n0TUBzBwrP+9xu3Lqkq7ONlJrXsRGdmIYy+5wq1nURqCe4QKIBruR103yvKTeq9rvxbLzjsfh/qglHaUzwYwXNofWBb2WewhLjVCVT+pL9d5BOTGlPxrH4YEomuuCaTdQkufFwHjh5wG3TpG6ZFLA1lTGaQvn5WJC4rHcY0aaxFKFwkZZohc24jvg2gGfFluro3nRy9AptOeFhY16BkcgCILinOWy1drXJZKCVPNhnEXGC/M6HO6P4pcvbk97bJSrr2A3fEzZSIx6Ylu95rFOyfMiN7HjNS9SqrKNqfB6u0L1+an7AQFaQ4H97vW48Z+XyTVJFp/Qgqe+slBx3aqva4/bhVlj5fFpqvVnVVmXkcnz8tr2HvQOxdBUG7C1sGM2mC3mpm7CxzMcU2peAOC2i2bgJ5+bWxChpgvKv6G+NpyI2+2S5sb3DiizapnWbySRxvOSulb17i1Af5PI7xG/+ed3JMFuC3dvndgWEl/DefTSVUAvRcjzkiVqz0shylvHuToAPo9bs9DqeVVG1wTgcskXsu2el5RxNBJPYiAaVxR8kzUv2tcd6osgnhTg87gsE4xddGIr7l7zEbr6Inhrz9G0x7IwYCHCRp8+dRxiySS2dfXj/1swSfcYvZ0+a5rGF7bjJy2mQ7GrzhCQ3rg71BdBMilgKCo323v4GrGxZlC1QPH3S0O1Hw9efRrWfNCF7152gql+UseNqcVti6fj1e09uPzktlw+ik62Uer31NrKmk9eOqfVVFPRQmC2gWFfJI5mrf0LQJsqXWjU9hELJzud0TV+HO6P4t39SuOFhWD51gBqMntetJtEXlu4ZX+v9P+TJzZI/589LiT9/8ZzjsPj6/fi386eaubjlAxkvGSJWvOyYGqj7X8zoTKJ1Ra6Xl8Sj9ulMBbmjA9pjrGSar8X1X4PhkYSODo4ooif6jUTZLACZMeNqbVsoQhV+/Dbq+fh8l+9jq2d/Wl1AFLYqADGi8/jxhfn6xst6WCeKVGwKz7Ga4qYmM+uCs9A+rBaLCGmFrOw0Su3nyf111IvlOrd9oUntODCE/TbhfDwE/uyj0/Dso9PM33uatQeKmb0sbEdTIVXzpgyOue/YTVqI9CIdJ6XwSJXDVbf3um8eU6CbeyYB8TvcWMkkZSqYcuaF+13XKtT/JNHkWbt1WoLmQ7tmWVnKgygudx68O1LZuL2i2aUjKFulsq4eiyELXYnT2jA379+Fq4vgLWqbsqlvukzTQJ1QW9BuoWOlpT1I6Y1L8++K3a1XXxiq6XnMq25Dm6XWF/h8IBWZMqQ67yUjgv76oVKA0cS7HrlbCM+LMcmJXVDNyvJZNyFh2OS25n3bGg0LzmOs5ULndvtksJT1545RfIGqcMapVSwy3zYyDgrhVU5LkQTRn2U4+vzlNdimSu817vK58FlJ4nZdszzEk0j2K3JEDZi2Z01fo90HQs6c0NNwKuwatSVzMvNcAHI85I17EL77KnjFK43O5moEnHyF1q6Se0LZ0zEnzZ04NdXnWrbufE01gSw7+gwjgxEkeRuDilspDo+Ekvgle1iddZPzLHWeKnyezC5qQa7Dg/iw85+w5BUIbONzPKdT8zCOdPHYMPuo/gN14G5yu/R6IcSSUHaXdnpedFzafPwNSx4z4ZakJmrQNPqhe6B/28euvsiuHi2fN2poxhmwliFwgrj5XCqpYLd+jcj1M5PX4Zryinw433WtCZMSJWEYJsqqa+RznjUZQgbMe/NmLqAIlsOEI0Yvs3FpCmN+JeT23DShIayNFbUkPGSJYUUeDKuWTQZneEILpilda+ncwGvuPwE3HT+8QVLR2SZH32RuMIzIBepU5ovB3uHMRJPojbgxQwuy8QqZrXWY9fhQWzr6sfHpo/RPUau81I6E2nQ58H5s1oUtSDY43KFXXEs+UnNznRXl8sFv9dtWM2VZZlV+z0KEW2+npcLZjXjha3dlns4500apXlM3bfMTg1Rtpg1+tKFjVgZg2KlxKqXy0oxXi46sRWrNh9A71AMV54+Qaou3N2XMl4SxhmPbEPy1p5jeOqtDlxx+kTF84c540WuEC3+G4nJ3ehrAl54PW7cc+UpVn60olI6d2eZUMjUWkbQ58GKy0/UfS7dBBvwegpaR4HpGYZH4iohGdO8KI9nN15znT1lqdsaRG+LmbBRKcbf1VVPq3weTZ0XZrz4vVoht9UE0hkvg8x4UV6P6kVX/XwmfnHlKWjf14v5U+zXlqmxulZOPphNDDDqIAzIIYZidQ5W3+OZvHlOYcHU0dj0HxdiKJZAbcCL598XQ+VsXmKtHvTGg9+c/vvT7+oYL6IhNKYuwFUwFucGvnSBnZmIxaIyrh4LKWR2ihlKpYgWIC9MgyMJyTPgcslCPbXmhbk8m2yq7Kju6KpGEISieNLMok6nrvK7uQq74vkzsW5dAa6DdGPEquyq69fk63mpCXhx5vFNipROuyhpz0uGcWOnni5sdCS1WNqdeWiEOlLhrRDNCyDqrNhczfRWPf1RrP2wG4+kqkvzZQAYmareMgNoTG1AE5ZjISO1N9QplMYKXEaU2mJXWsaLOCZDIwm506nLpYnFMniXpx2EdDpd8yi6uRbQk2aWUaoKs0GuSB0gerIGouJns1PvwkhnsB9NhSTUnhW1xyDoL71xZvCTv9/jLkgZBLPwxsvM1jqNAL8t5WE16iA8PJKQsqiKpnnRCHZL91qwEzbfHegdxpcffQu7esSCiJ+YM1Zz7PhR1bjp48cD0G/xInmv64PyPJuaaAeKnF1mN5V59eRBIVNrzVBKu0M+bMQ0Lx6XS9EzhoffNdhBfWqC7x0e0X2er9lTKt8nT0OVcrLiNS+AmHHE0i8LcR2kGyPmeVHrbjxuF07i0jKzDRsVEn5sSynTCFCG38bUBbD2tnNx3gxZx8UKPBoZ6qxhqt/rLtqGp1IFu2r4EA9j6pgazBqrr/v73LwJAOQ6PTzSBpCbQ9k8Kwn5yXghgNJLrS3EjtssfNgonvJqeNwuKdatruBot+eFhV3Cw3EMRuPYf0wpgOVr9pRi/L2hRrm75jUvgJhpNFDAHjzpdEFSyqbOeXzyJLmYXLEKpJmBX09KKdMIUHqwAl4PGmv8aOX0bKy1Rq+B8cKMy6Yaf0Gq6ZrB7y2N8yg0Aa8Hc7lM1YZqH+698hTD74V5K4djCU2tLD7byGXoeSndey4fSm/GLnFKRfPylY9NRX3Qi1svtKcZZC7InpcEBlMitJqAV451F9h4Ya71vuEYvvzIWzjrJ2ux8/CA9DwLGwW87pKZ0HnqAl5FXyu+wi7AwkbMeLF/sVUXS+Rhgt0aHc/KF+dPwolt9Th/ZnNJp2jy10ApeTQBpdHHQpx8wT/Wkd3I8yLpXYrYq4k8LzLnzpC7o//yC6ekLbtRJdVv0VZ4P6xIlRYfYz2KZM1LaV3LVlG5V0+OsLCR2XLddrH8E7Ow+T8XS5NWKVAlaV7kaqu1AS69Vx02KqDmZUOqTcDv1+2Rni/FNGkel8ul6KwsVthVho0GChg2isS1bmsGWxz1+tVU+T34+9fPwu++dLpt52YF/OJaSplGgHK+YddrjcJ4SYWNDMTpPZLxUqwCdVpBdKW0B9DjY9ObpP/PHd+Q9lje6xbhQkfh4ZjkeRnXUCVpitgeg6/x4kSc+alspJQEu6W2i2XuyaGRhEIsZqR56daJ11oJM174WigfHOyT/l9qIUA9Gqr96EmFZII+t2IBSAqC1NeoEBPU8Ig2Tbq5LoDu/iinedE/j1L0bKnhb6eS87xwhgpbzPjrdnxDes3LoVRNkRaL+oflgvoSqNSwEQCcOnEUbjp/GkbX+DNWP/d53PB5XIglBAzHEmgA8NPnPsSv1+4EIAp5R9X45fGVjBfZ++1EnPmpbKQUK7KWClU+8XIaGkko+qiwHQFvuhwdHJGKZrWG7JlQ63UWoA86+xBPJOH1uMviu1wwtRE7usVQ15jaoGKBTfKal0J4Xrhd39rbzsWO7gH839ZDePKtfdJYlnN8nc+GKTnjhTNUPDqGIPPA9g7HdHt5scJodt1rZqBsIxmXy5VVyD/o8yCWiGM4JcJlhgsAHNcsVjNXl6SQPS/le0+mo7Tu0BInnkhKcf9S8LyUGmzhGozGFS5LKRabqk3icrnw/PtdEATgxLZ62yp+ej1u1AW8kncCEA2rznAEExqrSz5sBAD/71Oz8amTxyEaS2Li6GqFYC+RFORU6QLsrnjjZUpTDaY01eD1HT2KY8o5vs5HMUpZsMs0DXzFahZ6TSQFDI4kNNcDa+45tpjGC2lecqbK50F/JK6bcXR8ynhxqUpSDKRC93o6NCfgzE9lE4rU2hKsC1JsJMFuLCFVjRQFu0qRqcsF/OPdTgD6tQ2spL7KpzBeANG1PgGlFQI0wuVy4fTJjYrfGUmFYNf+WzmuI9hVG37lHF/nPQOlpnnhx5l9DfzXEfS5pfYNvUMjmu+hM1wKnhclZLyYh4UNIzrGC+uVJDVtTV0YQ6k5uLqM78l00NWTBbzxUoqptcVGL2xUG/AodlwCRO/L5o5eAMDHZzbDTvTiyayQl6x5Ka/v0sN16Wa1HHJteJgvar2QnmC3XOCv01ILf7kUGwCtEelyudIWZewKDwNAQduFaFBrXiqowm6+sLDh8EgSgiAowseTRovGC1uShAoJG5XXrF1kDvaKE4DP43JkueV8kQS7XNioxu9VeQsEHBuKSR6DKU01tp6TnvESloyX0g8b6eHhsrdYDLxY9VPUWXflLA7kr9NSDn8lU3uoq+ZPRGONH1+cL/a7aTBohxGJJXAs9VhrffE8L5psI9oAmoaFDUWvdlzyun3tvONw8YliZ3R2/bICoU6vsOvMT2UDvUMjuOlPmwGIjbYILVKqdCwhhWoUdV4gLrgdqW7JLfUB20uwt9Rr9TSseZ1cs6e8diZsDUgkBSkGXgiPx39cOgs/+MdW/Mels6THgqqxK+eJkr9OS9mDxDQvY+oCeOuOCyRPnNrz8vTG/Ygnk9J8VeXzFLVyMIWNcqeKM17Y9+v3unH7RTOlY6RNTcq4LeTcUAzKd6YpML1DMcSTAsY1VOHuz59c7NMpSZgwTBCAowOsUZ9X5fKGZLxMbLS/Rs1JExrw1/aDisfC6rBRuXle3HIlzeECho2uP3sqLj+pDc3c7l1tfNaU8UTJL66l1NdIDa914cslsJpAvcMxHBmI4pt/fgcA8F9XnARAFOsWM2Vd/bd9FDYyjaR5GUmgN+VFU3uV1fW0mD6mlKta5wMZLyaZ3FSDv37tTBwdHLGtqFq5w98krG+R2vMiCMC+lPEyoQDGy7xJozSPhYdjeGlbN3b3iCnIpVznRQ9emDdU4LBRsyrsoA4blXK4JRNuRdiodK8JHckLACCU6oV1dHAEb+89Jj3+wtZuALAtq88salMlXbsJQgnveWGavQaN8SL+y4wX5nkpt/nNLOU70xSBxho/GnU6exIibrcLQZ8bkVgS3f1idkNNwKMprNZxpHCeF7028//Y0qmok1BVZoJdNkklBEHaXRXLaFB7KMo52wglHjaa0VKHbYf68amT23SfZ5uq13f04Hev7ZYef227mM5e7GaTlCqdO0GdsJHG8yIJ+cXfI6mwOHleCMIENX4vIrERqfS/ejFLCgL2HSuc8eLzuHHC2Hp80NmHy09qwzPvHMSeI8oGjcUUMeaCHDYqvOdFjcbzUsaZDbyRXYpho1VfW4R9R4cxo1W/+zAzXtbtPKJ4nC12xTYs1WEjLyU9mKaKNWccSUjNNw3DRinrhYWUS/FatgIyXghLqfJ7gEHZ6tfUeQGkzIdCNYl76isLcLA3gnf29+KZdw5qnm9rKGL6aA6w8Yxzgt1ipUprBLtlHTaS/1+Knpdqv9fQcAEy9wgrdgd6bXsA8ryYhW1OImk8L8yRxcJGLJvSqZ4XunoIS1EvXrVqzUuSa25ZoMmrLujDjNY6wx4iY8vNeEkNKPO6AKVR58XtKn7D0nzgi9SVo3ZH3SPsTzcsUPxeiM7j6aBso9zhNS+SYLda+X2qU6Vlz4szx9nWT3Xs2DEsWbIEoVAIoVAIS5YsQW9vb8bXbd26FZdffjlCoRDq6uqwYMECdHR02HmqhEWod3c1AW2dFylFucA7AiPjZVxDeYWNmDE4yFUOLoWwkbqmT7mR4JSw5bhb5T0vTbUBLJjaqMhGKna/Jm22kTMXVTsIsurlI2k0L1yqtCAIiKSyKZ0aNrL16rnqqqvQ3t6O1atXY/Xq1Whvb8eSJUvSvmbnzp0466yzMHPmTLz00kt455138N3vfhfBYHktMJWKeoIUBbvy7wKKl6JsVPK9qFVHc4DVc2DGi9/rLlqHcX5iLGe9C6AsvV4sT1Y+8MbLmLqAououUAKaF9XvlCptHr1sI03YyCVr4WIJQWoT4FTjxbareevWrVi9ejXWr1+P+fPnAwAefPBBLFy4ENu2bcOMGTN0X3fHHXfgE5/4BO666y7psalTp9p1moTFqCfI+qBP43kZKVJlW7WblVFuhdXYeLL+UcXUZ7SFqjCq2odjQzFMazbWY5QDvPFSjnoMvos6u7caqnw4OijXXCom6gq75HkxD695YZs/dfNQqXilICASl69lChtlyRtvvIFQKCQZLgCwYMEChEIhrFu3Tvc1yWQS//jHPzB9+nRcdNFFaG5uxvz58/HXv/7V8O9Eo1H09fUpfojiwXte/F63ZPWzGyspCNLNV+gFYkxtAGNDQbSFglIn1nKEeVkGUpWCixniqPJ78Pq3P45VSxfhvv/v1KKdhxUwkXm5wm8S2L3FG+zFDxspfyfjxTxS9fKRBPojosdV/X16uFTpSErv4nY5tw+fbZ+qq6sLzc3apnvNzc3o6urSfU13dzcGBgbw4x//GBdffDGef/55fPrTn8ZnPvMZvPzyy7qvWblypaSpCYVCmDBhgqWfg8gOfnfHuzXdkkuzeN2c/V431t52Lv7vm+fimxdOBwAsLMNWD2ySGixyU0ZGtd+LUyaOKrlOzNmi17G3XGH3Hl/IrNjZRmoobGQelggxGI2jP7VpUd9vfKo0M8SDPk9Z69DSkbXxsmLFCrhcrrQ/b7/9NgCtQAsQ43FGg5lMNWX41Kc+hVtuuQUnn3wyvv3tb+Oyyy7DAw88oPua5cuXIxwOSz/79u3L9iMRFsJnNCiNF/HfKLe7LUY356DPgyq/BxfPbsWqpYvw4DWnFfwc8oXdPqzxWjmKS0sR3tVernzr4hlorPHj25eIPW8aquWimnXFzjYiz0vOMMOzPxo39LzwFXaHHd4aAMhB87Js2TJceeWVaY+ZPHkytmzZgkOHDmmeO3z4MFpaWnRf19TUBK/XixNOOEHx+KxZs/Daa6/pviYQCCAQoHL9pQJ/Q/HGi2iwyjcVUNyeQi6XC6dM1LYOKAfUgt1SrElSjgyPlL/xsvTc4/HVc46TNoihEvK8uFSS3XLUFRUL5tEe5IwXjeeFDxvFnF2gDsjBeGlqakJTU1PG4xYuXIhwOIwNGzbgjDPOAAC8+eabCIfDWLRoke5r/H4/Tj/9dGzbtk3x+EcffYRJkyZle6pEEag1Ml5S/ypEkbTzygm3JNgVJzEnT1CFpNw1Lwzes11fStlG5HnJGfbdHRuKYUQS7Ko9L3LYaDjm7BovgI2al1mzZuHiiy/GDTfcgPXr12P9+vW44YYbcNlllykyjWbOnIlVq1ZJv99+++146qmn8OCDD2LHjh341a9+hb/97W9YunSpXadKWEgdN0E26GhepGZhXrdjY7F2w3ZY5HmxlqgDwkZqeO9msQW76mwjag9gHrYpZIYL/xiDeWSTXM8zJ29sbDXLHn/8ccyZMweLFy/G4sWLMXfuXPzxj39UHLNt2zaEw2Hp909/+tN44IEHcNddd2HOnDl46KGH8PTTT+Oss86y81QJi+BvqHodzQtvvBC5IRepK25TRqfhFM8LD1//p9j3HHWVzh2116zK59F4rhSp0hVgvNg66zU2NuKxxx5Le4yg09/92muvxbXXXmvXaRE2YpRtxLwsUYe3aS8EbEHqp7CRpQw7KNuI4eG8HcX2dFLYKHcCXje8bhfiqcJzel40pebF2R2lAeptRFgMXzhJabyI/5LnJX/cJNi1BVaR1Emc0FZf7FOQ0LYHoLCRWVwul6KYZr1OqxNPhWleyN9MWIpRtpGkeRkpTmsAJ8EiAcdKpHKqU5jQWIV9R4cdNZ5nHt+En/3rSZjeUnpFGcnzkh21Aa/U10jX88KlSlPYiCCyhJ/4+Zi21vPi3JvKbljY6EjKeJncVF3M03EMj3zpdPzXC9vx9Y8fX+xTsZTPzRtf7FPQhbINs4OfW9WtAQBl2GiYjBeCyA4+hMFnF7D/RyTNC01cuaJ2v09pKr1ddTlyfHMdfn1Vebc4KBdmttZJiy1hDj4ZQt/zIo8nq1lEmheCMAm/sI5tkDuByxV2SfOSLx618TK6pkhnQhC5cc6MMcU+hbJDoXlJEzYC5ExE0rwQRBY8ePVp2HtkEKcqKtgq67z4KWyUM25uPhpd4zfslk0Qpcon57YV+xTKjjqF8WIcNgKAoRHntw4h44WwnAtP0LZ/YPcVS+Ejz0vu8O7hKU3kdSHKhyeun4/+aByzx4WKfSplR01ANkT0ssj4eUGqvu3gTEQyXoiCoFdhl8gNvvDYxEYS6xLlw6LjM7eWIfThjZPTJzdqnufDySwrqdw7vaeDVhCiIFC2kXXwk1h1gMaRICqBjw71S/9va6jSPM9L4Y4NiZmIevVgnAIZL0RBcGsq7NKllyu88eL3kPFCEJXA+bPEcPyssfqFB3mP7LFB0fMScrDxQmEjoiBQhV3r4LMKfF5KNyWISuC6s6ZgXEMVPjZdP1OL39QwzwsZLwSRJ3KFXQob5Yui2R4V+iKIiiDo8+BfThln+Dy/qRlKzbNONl5o5iMKgouyjSyDT4mkzrwEQQBijS1180syXggiT6jCrnUoNC9kvBAEkcKtsl70KvE6BZr5iIJA2UbWwTfjpf4wBEEw+HTpGr/H0c0vnfvJiJKC3VIREuzmjdLzQkYgQRAivOPFySEjgIwXokDIYSPSvOQLaV4IgtCD39g4ucYLQMYLUSA0FXYd3HPDbvisAjJeCIJg8JmI5HkhCAtQq+BJq5E7/ATl91CdF4IgRChsRBAW41JZL5RtlDuUbUQQhB78xobCRgRhAW6Vg4A0L7lD7QEIgtCDnxtqA85NkwbIeCEKhDpsRKnSueMhwS5BEDooWoc4PKRMMx9RENTFk8jzkjsuEuwSBKEDP8963M6eG5z96YiSQa15CZLmJWc8irARjSNBECK88eJVx+odBs18REFQ30YUNsodqvNCEIQefEjZS2EjgsgfEuxaB7+7onEkCILBO7jJ80IQFqDVvJDnJVf4SBF5XgiCYJDmhSAsRpNtRJqXnBEE+f9ObrxGEER28GEjyjYiCAtQC3ZJaJo7iaRsvZDnhSAIBj/NeihsRBD5o+jH43ErRKdEdsR544WMQIIgUngo28gajh07hiVLliAUCiEUCmHJkiXo7e1N+5qBgQEsW7YM48ePR1VVFWbNmoX777/fztMkCgCJTK2D97w43TVMEIR5FKnSDt/Y2PrprrrqKrS3t2P16tVYvXo12tvbsWTJkrSvueWWW7B69Wo89thj2Lp1K2655RZ8/etfx//+7//aeaqEzVBhNeuIJ5PS/9XhOIIgKhcKG1nA1q1bsXr1ajz00ENYuHAhFi5ciAcffBB///vfsW3bNsPXvfHGG7jmmmtw7rnnYvLkyfi3f/s3nHTSSXj77bftOlWiAJDnxTp4zwtBEARDUeeFjJfceOONNxAKhTB//nzpsQULFiAUCmHdunWGrzvrrLPwzDPP4MCBAxAEAWvXrsVHH32Eiy66yK5TJQoA7yEI+ChNOh9iCTJeCILQUklhI9vaTnZ1daG5uVnzeHNzM7q6ugxfd++99+KGG27A+PHj4fV64Xa78dBDD+Gss87SPT4ajSIajUq/9/X15X/yhOXwewDyvOQHeV4IgtDDTZ4XY1asWAGXy5X2h4V49OLxgiCkjdPfe++9WL9+PZ555hls3LgRP//5z7F06VK88MILusevXLlSEgSHQiFMmDAh249EFAD+PiLjJT9iiWTmgwiCqDjcFaR5ydrzsmzZMlx55ZVpj5k8eTK2bNmCQ4cOaZ47fPgwWlpadF83PDyM73znO1i1ahUuvfRSAMDcuXPR3t6On/3sZ7jgggs0r1m+fDluvfVW6fe+vj4yYEoQpeaFwkb5QJ4XgiD04OdZp2ciZm28NDU1oampKeNxCxcuRDgcxoYNG3DGGWcAAN58802Ew2EsWrRI9zWxWAyxWAxuVVljj8eDZFJ/txkIBBAIBLL8FESh4Z1tVF03P+JkvBAEoYOH2gPkz6xZs3DxxRfjhhtuwPr167F+/XrccMMNuOyyyzBjxgzpuJkzZ2LVqlUAgPr6epxzzjm4/fbb8dJLL2H37t149NFH8Yc//AGf/vSn7TpVogC4KNvIMsjzQhCEHpXUmNE2wS4APP7447jpppuwePFiAMDll1+OX/3qV4pjtm3bhnA4LP3+5JNPYvny5fjiF7+Io0ePYtKkSfjhD3+IG2+80c5TJWxGqXmhsFE+kOeFIAg9KilV2lbjpbGxEY899ljaYwRBORG3trbikUcesfO0iCLgAnlerCJOgl2CIHRQpko723ihVYQoCHz4lTQv+XHbRWLY9UuLJhf3RAiCKCmUFXadPc/a6nkhCIaLso0s49SJo/DB9y9CtZ9uX4IgZCopbORs04woGahInbWQ4UIQhBoKGxGExfA3FTVmJAiCsB6F8eLwsJGzPx1RMlCFXYIgCHtxV1CqNK0iREHgNS9VFPIgCIKwHLeiSB0ZLwSRN7wKvsZPgl2CIAir4Q0Wn8O7Sjv70xElg1vheSHjhSAIwmqUqdLkeSGIvOHvI8qUIQiCsBfSvBCEBfAVdqvJ80IQBGE5Sa5iPaVKE4QF8Fl7ZLwQBEFYT5LrHEKp0gRhAXy2EYWNCIIgrEeA7HkhzQtBWAB/G5HnhSAIwnr4hvOkeSEIi6FsI4IgCOsROM2Lm4wXgsifaFwOxtZQ2IggCMJyeM+L0yHjhSgIw7GE9P+gjy47giAIq+GzjZwOrSJEQYiMyMYLL94lCIIgrIE8LwRhMZF4IvNBBEEQRM4I5HkhCGsZHiHjhSAIwk4obEQQFjMcS2Y+iCAIgsiZZAVNs2S8EAUhGiPPC0EQhJ2Q54UgLCZCxgtBEIStVJDtQsYLURiGyXghCIKwFfK8EITFkPFCEARhL2S8EITFVNA9RRAEURSozgtBWMzDXzodo6p9uP+Lpxb7VAiCIBxJBdkuoCYzREE48/gmbPruhVRdlyAIwiaSFeR6Ic8LUTDIcCEIgrAP0rwQBEEQBFFWVJDtQsYLQRAEQTgB8rxYxA9/+EMsWrQI1dXVaGhoMPUaQRCwYsUKtLW1oaqqCueeey7ef/99O0+TIAiCIMqeCrJd7DVeRkZG8K//+q/46le/avo1d911F+6++2786le/wltvvYXW1lZceOGF6O/vt/FMCYIgCKK8Ic+LRXzve9/DLbfcgjlz5pg6XhAE3HPPPbjjjjvwmc98BrNnz8bvf/97DA0N4YknnrDzVAmCIAiirDl72hgAQEO1r8hnYj8lpXnZvXs3urq6sHjxYumxQCCAc845B+vWrSvimREEQRBEaXP7RTPw//5lNv5x09nFPhXbKak6L11dXQCAlpYWxeMtLS3Yu3ev7mui0Sii0aj0e19fn30nSBAEQRAlSpXfgyULJhX7NApC1p6XFStWwOVypf15++238zopdT0QQRAMa4SsXLkSoVBI+pkwYUJef5sgCIIgiNIma8/LsmXLcOWVV6Y9ZvLkyTmdTGtrKwDRAzN27Fjp8e7ubo03hrF8+XLceuut0u99fX1kwBAEQRCEg8naeGlqakJTU5Md54IpU6agtbUVa9aswSmnnAJAzFh6+eWX8ZOf/ET3NYFAAIFAwJbzIQiCIAii9LBVsNvR0YH29nZ0dHQgkUigvb0d7e3tGBgYkI6ZOXMmVq1aBUAMF91888340Y9+hFWrVuG9997Dl770JVRXV+Oqq66y81QJgiAIgigTbBXs/ud//id+//vfS78zb8ratWtx7rnnAgC2bduGcDgsHfOtb30Lw8PDWLp0KY4dO4b58+fj+eefR11dnZ2nShAEQRBEmeASBGdVtenr60MoFEI4HEZ9fX2xT4cgCIIgCBNks36XVJ0XgiAIgiCITJDxQhAEQRBEWUHGC0EQBEEQZQUZLwRBEARBlBVkvBAEQRAEUVaQ8UIQBEEQRFlBxgtBEARBEGVFSXWVtgJWtoa6SxMEQRBE+cDWbTPl5xxnvPT39wMANWckCIIgiDKkv78foVAo7TGOq7CbTCZx8OBB1NXVweVyWfrerGP1vn37qHqvjdA4Fw4a68JA41wYaJwLhx1jLQgC+vv70dbWBrc7varFcZ4Xt9uN8ePH2/o36uvr6cYoADTOhYPGujDQOBcGGufCYfVYZ/K4MEiwSxAEQRBEWUHGC0EQBEEQZQUZL1kQCARw5513IhAIFPtUHA2Nc+GgsS4MNM6Fgca5cBR7rB0n2CUIgiAIwtmQ54UgCIIgiLKCjBeCIAiCIMoKMl4IgiAIgigryHghCIIgCKKsIOPFJPfddx+mTJmCYDCIefPm4dVXXy32KZUdr7zyCj75yU+ira0NLpcLf/3rXxXPC4KAFStWoK2tDVVVVTj33HPx/vvvK46JRqP4+te/jqamJtTU1ODyyy/H/v37C/gpSpuVK1fi9NNPR11dHZqbm/Ev//Iv2LZtm+IYGmdruP/++zF37lypSNfChQvxz3/+U3qextkeVq5cCZfLhZtvvll6jMbaGlasWAGXy6X4aW1tlZ4vqXEWiIw8+eSTgs/nEx588EHhgw8+EL7xjW8INTU1wt69e4t9amXFs88+K9xxxx3C008/LQAQVq1apXj+xz/+sVBXVyc8/fTTwrvvvitcccUVwtixY4W+vj7pmBtvvFEYN26csGbNGmHTpk3CeeedJ5x00klCPB4v8KcpTS666CLhkUceEd577z2hvb1duPTSS4WJEycKAwMD0jE0ztbwzDPPCP/4xz+Ebdu2Cdu2bRO+853vCD6fT3jvvfcEQaBxtoMNGzYIkydPFubOnSt84xvfkB6nsbaGO++8UzjxxBOFzs5O6ae7u1t6vpTGmYwXE5xxxhnCjTfeqHhs5syZwre//e0inVH5ozZeksmk0NraKvz4xz+WHotEIkIoFBIeeOABQRAEobe3V/D5fMKTTz4pHXPgwAHB7XYLq1evLti5lxPd3d0CAOHll18WBIHG2W5GjRolPPTQQzTONtDf3y9MmzZNWLNmjXDOOedIxguNtXXceeedwkknnaT7XKmNM4WNMjAyMoKNGzdi8eLFiscXL16MdevWFemsnMfu3bvR1dWlGOdAIIBzzjlHGueNGzciFospjmlra8Ps2bPpuzAgHA4DABobGwHQONtFIpHAk08+icHBQSxcuJDG2Qa+9rWv4dJLL8UFF1ygeJzG2lq2b9+OtrY2TJkyBVdeeSV27doFoPTG2XGNGa2mp6cHiUQCLS0tisdbWlrQ1dVVpLNyHmws9cZ579690jF+vx+jRo3SHEPfhRZBEHDrrbfirLPOwuzZswHQOFvNu+++i4ULFyISiaC2tharVq3CCSecIE3UNM7W8OSTT2LTpk146623NM/RNW0d8+fPxx/+8AdMnz4dhw4dwg9+8AMsWrQI77//fsmNMxkvJnG5XIrfBUHQPEbkTy7jTN+FPsuWLcOWLVvw2muvaZ6jcbaGGTNmoL29Hb29vXj66adxzTXX4OWXX5aep3HOn3379uEb3/gGnn/+eQSDQcPjaKzz55JLLpH+P2fOHCxcuBDHHXccfv/732PBggUASmecKWyUgaamJng8Ho3V2N3drbFAidxhivZ049za2oqRkREcO3bM8BhC5Otf/zqeeeYZrF27FuPHj5cep3G2Fr/fj+OPPx6nnXYaVq5ciZNOOgm/+MUvaJwtZOPGjeju7sa8efPg9Xrh9Xrx8ssv495774XX65XGisbaempqajBnzhxs37695K5pMl4y4Pf7MW/ePKxZs0bx+Jo1a7Bo0aIinZXzmDJlClpbWxXjPDIygpdfflka53nz5sHn8ymO6ezsxHvvvUffRQpBELBs2TL85S9/wYsvvogpU6YonqdxthdBEBCNRmmcLeT888/Hu+++i/b2dunntNNOwxe/+EW0t7dj6tSpNNY2EY1GsXXrVowdO7b0rmlL5b8OhaVK/+53vxM++OAD4eabbxZqamqEPXv2FPvUyor+/n5h8+bNwubNmwUAwt133y1s3rxZSjn/8Y9/LIRCIeEvf/mL8O677wpf+MIXdNPwxo8fL7zwwgvCpk2bhI9//OOU7sjx1a9+VQiFQsJLL72kSHccGhqSjqFxtobly5cLr7zyirB7925hy5Ytwne+8x3B7XYLzz//vCAINM52wmcbCQKNtVV885vfFF566SVh165dwvr164XLLrtMqKurk9a6UhpnMl5M8utf/1qYNGmS4Pf7hVNPPVVKPSXMs3btWgGA5ueaa64RBEFMxbvzzjuF1tZWIRAICB/72MeEd999V/Eew8PDwrJly4TGxkahqqpKuOyyy4SOjo4ifJrSRG98AQiPPPKIdAyNszVce+210pwwZswY4fzzz5cMF0GgcbYTtfFCY20NrG6Lz+cT2trahM985jPC+++/Lz1fSuPsEgRBsNaXQxAEQRAEYR+keSEIgiAIoqwg44UgCIIgiLKCjBeCIAiCIMoKMl4IgiAIgigryHghCIIgCKKsIOOFIAiCIIiygowXgiAIgiDKCjJeCIIgCIIoK8h4IQiCIAiirCDjhSAIgiCIsoKMF4IgCIIgygoyXgiCIAiCKCv+f9piKkN74z4yAAAAAElFTkSuQmCC",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.plot(ecg[:500])\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 38,
"id": "8349bd1d-2616-45d6-bbc8-804826a50e56",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"labels"
]
},
{
"cell_type": "code",
"execution_count": 39,
"id": "9faeb1cd-dd7f-4256-91f0-2f0498ee5d2b",
"metadata": {},
"outputs": [],
"source": [
"ecg_batch = ecg_batch.to(device) # BS x 12 * L\n",
"labels_batch = labels_batch.squeeze().to(device)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"id": "42444f09-6dca-4cfd-bb05-608aa636b97b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([[-0.5492, -0.5440, -0.4032, ..., -0.4387, -0.4392, -0.4397],\n",
" [ 0.9272, 0.9503, 0.4917, ..., 0.5619, 0.5549, 0.5576],\n",
" [-0.6697, -0.6657, -0.6151, ..., -0.7586, -0.7588, -0.7587],\n",
" ...,\n",
" [ 0.0311, 0.0310, 0.0356, ..., -0.0298, -0.0298, -0.0298],\n",
" [-0.5828, -0.5607, -0.5452, ..., -0.8266, -0.8265, -0.8266],\n",
" [-0.4400, -0.4378, -0.4420, ..., -0.9468, -0.9450, -0.9459]],\n",
" device='cuda:0'),\n",
" torch.Size([64, 12000]))"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ecg_batch, ecg_batch.shape"
]
},
{
"cell_type": "code",
"execution_count": 41,
"id": "2a617eac-0c2e-4686-95ba-8dc73ae3bbd4",
"metadata": {},
"outputs": [],
"source": [
"with torch.no_grad():\n",
" out = hubert(ecg_batch, attention_mask=None, output_attentions=False, output_hidden_states=False, return_dict=True)"
]
},
{
"cell_type": "code",
"execution_count": 42,
"id": "30c3479e-cf83-4371-8927-44224f384e60",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(hubert, HuBERT)"
]
},
{
"cell_type": "code",
"execution_count": 43,
"id": "4f2a70a1-f72c-4b87-bf1e-729c9e2012d6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(hubert, HuBERTClassification)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"id": "3faef67c-48dd-4dfa-a3a4-5aab92304434",
"metadata": {},
"outputs": [],
"source": [
"if isinstance(hubert, HuBERT):\n",
" logits = hubert.logits(out['last_hidden_state']).transpose(1, 2)\n",
"elif isinstance(hubert, HuBERTClassification):\n",
" logits = out[0]"
]
},
{
"cell_type": "code",
"execution_count": 45,
"id": "044c7fa5-7f53-4cd7-a907-bad8b7e9985b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([[-0.1433, 0.0417, -0.1806, ..., -0.1443, 0.1629, -0.0422],\n",
" [-0.0837, 0.0876, -0.1442, ..., -0.1580, 0.0015, 0.0679],\n",
" [-0.0043, 0.0627, -0.1646, ..., -0.1646, 0.4012, 0.0721],\n",
" ...,\n",
" [ 0.1006, -0.1355, 0.2410, ..., -0.0020, -0.0798, 0.1021],\n",
" [-0.1505, 0.1835, -0.0089, ..., -0.1749, 0.2812, -0.0333],\n",
" [-0.1209, 0.0659, 0.0195, ..., -0.3086, 0.2039, 0.0047]],\n",
" device='cuda:0'),\n",
" torch.Size([64, 164]))"
]
},
"execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"logits, logits.shape"
]
},
{
"cell_type": "code",
"execution_count": 46,
"id": "864bf09d-a718-4a86-9e6a-ec839422f06f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'model_path': '/models/Edoardo-BS/HuBERT-ECG-SSL-Pretrained/hubert_ecg_large.pt',\n",
" 'path_to_dataset_csv': '/libs/HuBERT_ECG/reproducibility/ptbxl/ptbxl_all_test.csv',\n",
" 'ecg_dir_path': '/shared/ptb-xl',\n",
" 'num_labels': 164,\n",
" 'label_start_index': 4,\n",
" 'tta': False,\n",
" 'tta_aggregation': 'mean',\n",
" 'downsampling_factor': 5,\n",
" 'batch_size': 64,\n",
" 'task': 'multi_label',\n",
" 'return_full_length': True}"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"args"
]
},
{
"cell_type": "code",
"execution_count": 47,
"id": "b33a7c81-024f-45bd-b71f-7653b0382137",
"metadata": {},
"outputs": [],
"source": [
"# [BS x num_labels] * N_AUGS\n",
"# probs = torch.sigmoid(logits) if args['task'] == 'multi_label' else torch.softmax(logits, dim=-1)"
]
},
{
"cell_type": "code",
"execution_count": 48,
"id": "d61e0667-71b9-4123-80fd-4b5bd703b3f9",
"metadata": {},
"outputs": [],
"source": [
"# average probs over augmented batches for tta\n",
"# probs = torch.stack(probs).mean(dim=0) if args['tta_aggregation'] == 'mean' else torch.stack(probs).max(dim=0).values"
]
},
{
"cell_type": "code",
"execution_count": 49,
"id": "78b866d1-bedb-4fb5-82bd-dbcc83d64bc3",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),\n",
" torch.Size([71]))"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"labels, labels.shape"
]
},
{
"cell_type": "code",
"execution_count": 50,
"id": "4518e2a2-76d6-4c80-93ce-0f0ffadf796d",
"metadata": {},
"outputs": [],
"source": [
"probs = torch.sigmoid(logits)"
]
},
{
"cell_type": "code",
"execution_count": 51,
"id": "f4e959a8-8a6f-4249-8d7d-27889987cbbd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([[0.4642, 0.5104, 0.4550, ..., 0.4640, 0.5406, 0.4895],\n",
" [0.4791, 0.5219, 0.4640, ..., 0.4606, 0.5004, 0.5170],\n",
" [0.4989, 0.5157, 0.4589, ..., 0.4590, 0.5990, 0.5180],\n",
" ...,\n",
" [0.5251, 0.4662, 0.5600, ..., 0.4995, 0.4800, 0.5255],\n",
" [0.4624, 0.5457, 0.4978, ..., 0.4564, 0.5698, 0.4917],\n",
" [0.4698, 0.5165, 0.5049, ..., 0.4235, 0.5508, 0.5012]],\n",
" device='cuda:0'),\n",
" torch.Size([64, 164]))"
]
},
"execution_count": 51,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"probs, probs.shape"
]
},
{
"cell_type": "code",
"execution_count": 52,
"id": "08dad8ea-85a3-4384-848a-38b1fcda5ed3",
"metadata": {},
"outputs": [],
"source": [
"threshold = 0.5"
]
},
{
"cell_type": "code",
"execution_count": 53,
"id": "a861584d-758b-484c-ba96-23c1526f4e28",
"metadata": {},
"outputs": [],
"source": [
"predicted_labels = (probs > threshold).float()"
]
},
{
"cell_type": "code",
"execution_count": 54,
"id": "326298ef-1cea-4f3f-a782-bbbce817ee33",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([[0., 1., 0., ..., 0., 1., 0.],\n",
" [0., 1., 0., ..., 0., 1., 1.],\n",
" [0., 1., 0., ..., 0., 1., 1.],\n",
" ...,\n",
" [1., 0., 1., ..., 0., 0., 1.],\n",
" [0., 1., 0., ..., 0., 1., 0.],\n",
" [0., 1., 1., ..., 0., 1., 1.]], device='cuda:0'),\n",
" torch.Size([64, 164]))"
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"predicted_labels, predicted_labels.shape"
]
},
{
"cell_type": "code",
"execution_count": 57,
"id": "3e30aeac-fae3-4c81-9582-4ce0124944c7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([0., 1., 0., 1., 0., 1., 1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 1.,\n",
" 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 0., 0.,\n",
" 0., 0., 0., 1., 1., 1., 0., 1., 1., 0., 0., 0., 1., 1., 1., 0., 0., 0.,\n",
" 0., 1., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 1., 1., 1., 1., 0., 1., 0.,\n",
" 1., 1., 0., 0., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 1., 0., 1., 1.,\n",
" 0., 1., 0., 0., 1., 0., 1., 0., 0., 1., 1., 0., 1., 0., 0., 1., 0., 1.,\n",
" 1., 0., 1., 1., 1., 0., 0., 1., 0., 1., 1., 0., 1., 1., 0., 0., 1., 1.,\n",
" 0., 1., 1., 1., 1., 0., 1., 0., 0., 0., 1., 1., 0., 0., 1., 1., 1., 0.,\n",
" 1., 0.], device='cuda:0'),\n",
" torch.Size([164]))"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"predicted_labels[0], predicted_labels[0].shape"
]
},
{
"cell_type": "code",
"execution_count": 58,
"id": "8d79bba3-d9a2-4d3f-a57e-132ac94fa254",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
" device='cuda:0'),\n",
" torch.Size([71]))"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"labels_batch[0], labels_batch[0].shape"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"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.12.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
import os
import numpy as np
# from sklearn.utils import resample
from scipy.signal import resample
from biosppy.signals.tools import filter_signal
from tqdm import tqdm
import wfdb
def apply_filter(
signal,
filter_bandwidth,
fs=500
):
order = int(0.3 * fs)
signal, _, _ = filter_signal(
signal=signal,
ftype='FIR',
band='bandpass',
order=order,
frequency=filter_bandwidth,
sampling_rate=fs
)
return signal
def scaling(
ecg_signal,
smooth=1e-8
):
return 2 * (
ecg_signal - np.min(ecg_signal, axis=1)[None].T
) / (
np.max(ecg_signal, axis=1) - np.min(ecg_signal, axis=1) + smooth
)[None].T - 1
def ecg_preprocessing(
ecg_signal,
original_frequency,
# target_frequency=100,
band_pass=[0.05, 47]
)-> np.ndarray:
assert ecg_signal.shape[0] == 12, "ecg_signal should have (12, signal_length) shape for pre-processing"
num_samples = int(ecg_signal.shape[-1] * (500 / original_frequency))
ecg_signal = resample(ecg_signal, num_samples, axis=1)
ecg_signal = apply_filter(ecg_signal, band_pass)
return scaling(ecg_signal)
def dataset_processing(
filenames: list[str],
path_wfdb: str,
path_norm: str,
skip_existing=True
):
os.makedirs(path_norm, exist_ok=True)
for filename in tqdm(filenames, desc='Processing ECG files'):
output_filename = f"HR{os.path.basename(filename).replace('_hr', '.hea.npy')}"
output_path = os.path.join(path_norm, output_filename)
if skip_existing and os.path.exists(output_path):
continue
try:
signal, meta = wfdb.rdsamp(os.path.join(path_wfdb, filename))
signal = signal.T
if np.isnan(signal).any():
signal = np.nan_to_num(signal, nan=0.0)
signal_norm = ecg_preprocessing(signal, meta['fs'])
np.save(output_path, signal_norm)
except Exception as e:
print(f"Error processing {filename}: {str(e)}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment