{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PSEUDo vs. DTW: Gas Sensor Dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this experiment we will compare the LSH algorithm of PSEUDo to DTW using a Synthetic dataset. The metrics we will be comparing these two algorithms with are **computing time**, **recall** and **precision**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We first load the EEG data and convert it to a numpy array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import os\n",
    "from generator import generate_dataset\n",
    "\n",
    "# data_path = 'data/processed-data.npy'\n",
    "\n",
    "# if not os.path.isfile(data_path):\n",
    "#     print(\"Generating dataset\")\n",
    "#     generate_dataset()\n",
    "# original_data = np.load(data_path, allow_pickle=True)\n",
    "# print(\"Dataset loaded\")\n",
    "\n",
    "# print(original_data.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "original_data = np.load('data/time_series.npy')\n",
    "query = np.load('data/query.npy')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, the data is cut into subwindows of size T. We use a stepsize of T/8. Because of memory issues, only 40 of the 70 channels are used for this experiment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(49749, 251, 3)\n"
     ]
    }
   ],
   "source": [
    "from sklearn import preprocessing\n",
    "\n",
    "N = 3\n",
    "T = 251\n",
    "data = np.array([preprocessing.minmax_scale(original_data[i:i+T, :]) for i in range(0, original_data.shape[0]-T, 1)], dtype='float32')\n",
    "print(data.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We sample a number of subwindows which will be used as query for the search algorithms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[48143, 39297, 36827, 23445, 43587, 20299, 49290, 29668, 30609, 4287]\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "targets = random.sample(list(range(data.shape[0])), 10)\n",
    "print(targets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the LSH algorithm some preprocessing is done to find the right LSH parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Preprocessing:\n",
      "r = 3\n",
      "r = 4.5\n",
      "r = 6.75\n",
      "r = 3.375\n",
      "r = 5.0625\n",
      "r = 7.59375\n",
      "r = 3.796875\n",
      "r = 5.6953125\n",
      "r = 8.54296875\n",
      "r = 4.271484375\n",
      "r = 6.4072265625\n",
      "Mean: 7.683647399212971\n",
      "Stdev: 1.6953473569825595\n",
      "Ratio mean: 0.9030339393346839\n",
      "Ratio stdev: 0.0517880456670425\n",
      "Theta: 3.309651218197967\n",
      "r: 0.3549249876302004\n",
      "Preprocessing time: 13.705193281173706\n",
      "Preprocessing done. Took 13.71 seconds (0.2 minutes).\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "from time import time\n",
    "\n",
    "sys.path.insert(0, '../Flaskserver')\n",
    "import importlib\n",
    "from pseudo import preprocess\n",
    "import _lsh\n",
    "\n",
    "topk_dtw = []\n",
    "\n",
    "print('Preprocessing:')\n",
    "t0 = time()\n",
    "r,a,sd = preprocess(data, data.shape[2])\n",
    "print('Preprocessing done. Took {:.2f} seconds ({:.1f} minutes).'.format(time() - t0, (time() - t0) / 60))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we run the LSH algorithm for all targets and calculate the most similar subwindows"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "doing lsh\n",
      "Target #0 done! Took 3.22 seconds (0.1 minutes).\n",
      "doing lsh\n",
      "Target #1 done! Took 3.66 seconds (0.1 minutes).\n",
      "doing lsh\n",
      "Target #2 done! Took 1.80 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #3 done! Took 3.60 seconds (0.1 minutes).\n",
      "doing lsh\n",
      "Target #4 done! Took 0.60 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #5 done! Took 3.07 seconds (0.1 minutes).\n",
      "doing lsh\n",
      "Target #6 done! Took 2.57 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #7 done! Took 2.88 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #8 done! Took 3.60 seconds (0.1 minutes).\n",
      "doing lsh\n",
      "Target #9 done! Took 3.33 seconds (0.1 minutes).\n",
      "Done! Took 28.34 seconds (0.5 minutes).\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "t0 = time()\n",
    "total_lsh_times = []\n",
    "all_lsh_candidates = []\n",
    "for i, target in enumerate(targets):\n",
    "    t1 = time()\n",
    "    query = data[target]\n",
    "    print('doing lsh')\n",
    "    lsh_candidates, lsh_distances, _ = _lsh.lsh(data, query, r, a, sd, 0)\n",
    "#     topk_dtw.append(candidates)\n",
    "    dict = defaultdict(int)\n",
    "    for l in range(len(lsh_candidates)):\n",
    "        for k in range(len(lsh_candidates[0])):\n",
    "            for a in range(len(lsh_candidates[0][0])):\n",
    "                dict[lsh_candidates[l][k][a]] += lsh_distances[l][k][a]\n",
    "    sorted_dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])}\n",
    "    candidates = list(sorted_dict.keys())\n",
    "    total_lsh_times.append(time()-t1)\n",
    "    print('Target #{} done! Took {:.2f} seconds ({:.1f} minutes).'.format(i, time() - t1, (time() - t1) / 60))\n",
    "    all_lsh_candidates.append(candidates)\n",
    "    \n",
    "# print(candidates[0:10])\n",
    "print('Done! Took {:.2f} seconds ({:.1f} minutes).'.format(time() - t0, (time() - t0) / 60))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "doing lsh\n",
      "Target #0 done! Took 1.00 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #1 done! Took 1.01 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #2 done! Took 0.75 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #3 done! Took 1.12 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #4 done! Took 0.87 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #5 done! Took 1.03 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #6 done! Took 0.84 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #7 done! Took 1.10 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #8 done! Took 1.23 seconds (0.0 minutes).\n",
      "doing lsh\n",
      "Target #9 done! Took 2.67 seconds (0.0 minutes).\n",
      "Done! Took 11.63 seconds (0.2 minutes).\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "t0 = time()\n",
    "total_lsh_times_ed = []\n",
    "all_lsh_candidates_ed = []\n",
    "for i, target in enumerate(targets):\n",
    "    t1 = time()\n",
    "    query = data[target]\n",
    "    print('doing lsh')\n",
    "    lsh_candidates, lsh_distances, _ = _lsh.lsh(data, query, r, a, sd, 1)\n",
    "#     topk_dtw.append(candidates)\n",
    "    dict = defaultdict(int)\n",
    "    for l in range(len(lsh_candidates)):\n",
    "        for k in range(len(lsh_candidates[0])):\n",
    "            for a in range(len(lsh_candidates[0][0])):\n",
    "                dict[lsh_candidates[l][k][a]] += lsh_distances[l][k][a]\n",
    "    sorted_dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])}\n",
    "    candidates = list(sorted_dict.keys())\n",
    "    total_lsh_times_ed.append(time()-t1)\n",
    "    print('Target #{} done! Took {:.2f} seconds ({:.1f} minutes).'.format(i, time() - t1, (time() - t1) / 60))\n",
    "    all_lsh_candidates_ed.append(candidates)\n",
    "    \n",
    "# print(candidates[0:10])\n",
    "print('Done! Took {:.2f} seconds ({:.1f} minutes).'.format(time() - t0, (time() - t0) / 60))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We do the same for DTW"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Target #0 done! Took 21.22 seconds (0.4 minutes).\n",
      "Target #1 done! Took 20.44 seconds (0.3 minutes).\n",
      "Target #2 done! Took 20.21 seconds (0.3 minutes).\n",
      "Target #3 done! Took 20.57 seconds (0.3 minutes).\n",
      "Target #4 done! Took 20.43 seconds (0.3 minutes).\n",
      "Target #5 done! Took 20.57 seconds (0.3 minutes).\n",
      "Target #6 done! Took 20.63 seconds (0.3 minutes).\n",
      "Target #7 done! Took 20.11 seconds (0.3 minutes).\n",
      "Target #8 done! Took 20.33 seconds (0.3 minutes).\n",
      "Target #9 done! Took 20.18 seconds (0.3 minutes).\n",
      "Done! Took 204.70 seconds (3.4 minutes).\n"
     ]
    }
   ],
   "source": [
    "from scipy.spatial.distance import cdist\n",
    "from tslearn.metrics import dtw_path_from_metric\n",
    "from tslearn.metrics import dtw\n",
    "from time import time\n",
    "\n",
    "t0 = time()\n",
    "total_dtw_times = []\n",
    "all_dtw_candidates = []\n",
    "for i, target in enumerate(targets):\n",
    "    t1 = time()\n",
    "    query = data[target]\n",
    "    dtw_distances = [dtw(window, query, global_constraint='sakoe_chiba', sakoe_chiba_radius=int(0.05 * T)) for window in data]\n",
    "    dtw_candidates = sorted(range(len(dtw_distances)), key=lambda k: dtw_distances[k])\n",
    "    print('Target #{} done! Took {:.2f} seconds ({:.1f} minutes).'.format(i, time() - t1, (time() - t1) / 60))\n",
    "    total_dtw_times.append(time()-t1)\n",
    "    all_dtw_candidates.append(dtw_candidates)\n",
    "print('Done! Took {:.2f} seconds ({:.1f} minutes).'.format(time() - t0, (time() - t0) / 60))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Target #0 done! Took 0.31 seconds (0.0 minutes).\n",
      "Target #1 done! Took 0.29 seconds (0.0 minutes).\n",
      "Target #2 done! Took 0.32 seconds (0.0 minutes).\n",
      "Target #3 done! Took 0.31 seconds (0.0 minutes).\n",
      "Target #4 done! Took 0.31 seconds (0.0 minutes).\n",
      "Target #5 done! Took 0.31 seconds (0.0 minutes).\n",
      "Target #6 done! Took 0.31 seconds (0.0 minutes).\n",
      "Target #7 done! Took 0.31 seconds (0.0 minutes).\n",
      "Target #8 done! Took 0.31 seconds (0.0 minutes).\n",
      "Target #9 done! Took 0.31 seconds (0.0 minutes).\n",
      "Done! Took 3.32 seconds (0.1 minutes).\n"
     ]
    }
   ],
   "source": [
    "t0 = time()\n",
    "all_ed_candidates = []\n",
    "total_ed_times = []\n",
    "for i, target in enumerate(targets):\n",
    "    t1 = time()\n",
    "    query = data[target]\n",
    "    ed_distances = [np.linalg.norm(query-window) for window in data]\n",
    "    print('Target #{} done! Took {:.2f} seconds ({:.1f} minutes).'.format(i, time() - t1, (time() - t1) / 60))\n",
    "    ed_candidates = sorted(range(len(ed_distances)), key=lambda k: ed_distances[k])\n",
    "    total_ed_times.append(time()-t1)\n",
    "    all_ed_candidates.append(ed_candidates)\n",
    "print('Done! Took {:.2f} seconds ({:.1f} minutes).'.format(time() - t0, (time() - t0) / 60))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# from tslearn.piecewise import SymbolicAggregateApproximation\n",
    "\n",
    "# t0 = time()\n",
    "# sax = SymbolicAggregateApproximation(n_segments=128, alphabet_size_avg=10)\n",
    "# sax_data = sax.fit_transform(data)\n",
    "# print('Done! Took {:.2f} seconds ({:.1f} minutes).'.format(time() - t0, (time() - t0) / 60))\n",
    "\n",
    "# t0 = time()\n",
    "# all_sax_candidates = []\n",
    "# for i, target in enumerate(targets):\n",
    "#     t1 = time()\n",
    "#     query = sax_data[target]\n",
    "#     sax_distances = [np.linalg.norm(query - window) for window in sax_data]\n",
    "#     print('Target #{} done! Took {:.2f} seconds ({:.1f} minutes).'.format(i, time() - t1, (time() - t1) / 60))\n",
    "#     sax_candidates = sorted(range(len(sax_distances)), key=lambda k: sax_distances[k])\n",
    "#     all_sax_candidates.append(sax_candidates)\n",
    "# sax_time = time() - t0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We compare the LSH candidates to the DTW candidates and test on recall, precision and number of pruned candidates"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=================================================\n",
      "Total pruned: 57.5%\n",
      "Total recall: 83.79999999999998%\n",
      "Total precision: 51.09999999999999%\n",
      "Total precision 2: 90.10000000000001%\n"
     ]
    }
   ],
   "source": [
    "k = 100\n",
    "total_recall_pseudo = []\n",
    "total_precision_pseudo = []\n",
    "total_precision2_pseudo = []\n",
    "total_pruned_pseudo = []\n",
    "for i in range(len(targets)):\n",
    "    top_10_percent = int(len(all_lsh_candidates[i]) * 0.1)\n",
    "    pruned = int(100*(1-len(all_lsh_candidates[i])/len(all_dtw_candidates[i])))\n",
    "#     print(\"Pruned: \" + str(pruned) + \"%\")\n",
    "    recall = 0\n",
    "    for index in all_dtw_candidates[i][0:k]:\n",
    "        if index in all_lsh_candidates[i]:\n",
    "            recall += 1\n",
    "#     print(\"Recall: \" + str(100*recall/k) + \"%\")\n",
    "\n",
    "    precision = 0\n",
    "    for index in all_dtw_candidates[i][0:k]:\n",
    "        if index in all_lsh_candidates[i][0:k]:\n",
    "            precision += 1\n",
    "#     print(\"Precision: \" + str(100*precision/k) + \"%\")\n",
    "    \n",
    "    precision2 = 0\n",
    "    for index in all_lsh_candidates[i][0:k]:\n",
    "        if index in all_dtw_candidates[i][0:top_10_percent]:\n",
    "            precision2 += 1\n",
    "#     print(\"Precision 10th percentile: \" + str(100*precision2/k) + \"%\")\n",
    "    total_pruned_pseudo.append(pruned)\n",
    "    total_recall_pseudo.append(recall/k)\n",
    "    total_precision_pseudo.append(precision/k)\n",
    "    total_precision2_pseudo.append(precision2/k)\n",
    "    \n",
    "print(\"=================================================\")\n",
    "print(\"Total pruned: \" + str(np.mean(total_pruned_pseudo)) + \"%\")\n",
    "print(\"Total recall: \" + str(100 * np.mean(total_recall_pseudo)) + \"%\")\n",
    "print(\"Total precision: \" + str(100 * np.mean(total_precision_pseudo)) + \"%\")\n",
    "print(\"Total precision 2: \" + str(100 *np.mean(total_precision2_pseudo)) + \"%\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=================================================\n",
      "Total pruned: 63.0%\n",
      "Total recall: 79.9%\n",
      "Total precision: 28.300000000000004%\n",
      "Total precision 2: 79.60000000000001%\n"
     ]
    }
   ],
   "source": [
    "total_recall_pseudo_ed = []\n",
    "total_precision_pseudo_ed = []\n",
    "total_precision2_pseudo_ed = []\n",
    "total_pruned_pseudo_ed = []\n",
    "for i in range(len(targets)):\n",
    "    top_10_percent = int(len(all_lsh_candidates_ed[i]) * 0.1)\n",
    "    pruned = int(100*(1-len(all_lsh_candidates_ed[i])/len(all_dtw_candidates[i])))\n",
    "#     print(\"Pruned: \" + str(pruned) + \"%\")\n",
    "    recall = 0\n",
    "    for index in all_dtw_candidates[i][0:k]:\n",
    "        if index in all_lsh_candidates_ed[i]:\n",
    "            recall += 1\n",
    "#     print(\"Recall: \" + str(100*recall/k) + \"%\")\n",
    "\n",
    "    precision = 0\n",
    "    for index in all_dtw_candidates[i][0:k]:\n",
    "        if index in all_lsh_candidates_ed[i][0:k]:\n",
    "            precision += 1\n",
    "#     print(\"Precision: \" + str(100*precision/k) + \"%\")\n",
    "    \n",
    "    precision2 = 0\n",
    "    for index in all_lsh_candidates_ed[i][0:k]:\n",
    "        if index in all_dtw_candidates[i][0:top_10_percent]:\n",
    "            precision2 += 1\n",
    "#     print(\"Precision 10th percentile: \" + str(100*precision2/k) + \"%\")\n",
    "    total_pruned_pseudo_ed.append(pruned)\n",
    "    total_recall_pseudo_ed.append(recall/k)\n",
    "    total_precision_pseudo_ed.append(precision/k)\n",
    "    total_precision2_pseudo_ed.append(precision2/k)\n",
    "    \n",
    "print(\"=================================================\")\n",
    "print(\"Total pruned: \" + str(np.mean(total_pruned_pseudo_ed)) + \"%\")\n",
    "print(\"Total recall: \" + str(100 * np.mean(total_recall_pseudo_ed)) + \"%\")\n",
    "print(\"Total precision: \" + str(100 * np.mean(total_precision_pseudo_ed)) + \"%\")\n",
    "print(\"Total precision 2: \" + str(100 *np.mean(total_precision2_pseudo_ed)) + \"%\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=================================================\n",
      "Total pruned: 0.0%\n",
      "Total recall: 100.0%\n",
      "Total precision: 18.6%\n",
      "Total precision 2: 99.8%\n"
     ]
    }
   ],
   "source": [
    "total_recall_ed = []\n",
    "total_precision_ed = []\n",
    "total_precision2_ed = []\n",
    "total_pruned_ed = []\n",
    "for i in range(len(targets)):\n",
    "    top_10_percent = int(len(all_ed_candidates[i]) * 0.1)\n",
    "    pruned = int(100*(1-len(all_ed_candidates[i])/len(all_dtw_candidates[i])))\n",
    "#     print(\"Pruned: \" + str(pruned) + \"%\")\n",
    "    recall = 0\n",
    "    for index in all_dtw_candidates[i][0:k]:\n",
    "        if index in all_ed_candidates[i]:\n",
    "            recall += 1\n",
    "#     print(\"Recall: \" + str(100*recall/k) + \"%\")\n",
    "\n",
    "    precision = 0\n",
    "    for index in all_dtw_candidates[i][0:k]:\n",
    "        if index in all_ed_candidates[i][0:k]:\n",
    "            precision += 1\n",
    "#     print(\"Precision: \" + str(100*precision/k) + \"%\")\n",
    "    \n",
    "    precision2 = 0\n",
    "    for index in all_ed_candidates[i][0:k]:\n",
    "        if index in all_dtw_candidates[i][0:top_10_percent]:\n",
    "            precision2 += 1\n",
    "#     print(\"Precision 10th percentile: \" + str(100*precision2/k) + \"%\")\n",
    "    total_pruned_ed.append(pruned)\n",
    "    total_recall_ed.append(recall/k)\n",
    "    total_precision_ed.append(precision/k)\n",
    "    total_precision2_ed.append(precision2/k)\n",
    "    \n",
    "print(\"=================================================\")\n",
    "print(\"Total pruned: \" + str(np.mean(total_pruned_ed)) + \"%\")\n",
    "print(\"Total recall: \" + str(100 * np.mean(total_recall_ed)) + \"%\")\n",
    "print(\"Total precision: \" + str(100 * np.mean(total_precision_ed)) + \"%\")\n",
    "print(\"Total precision 2: \" + str(100 *np.mean(total_precision2_ed)) + \"%\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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JCkldR/ZSI4cOcLWrVs5fPhwbYcicWrUqBHt2rUjNTW1tkMRERGRJFLnEtmtW7eSlpZGx44dMbPaDkfK4Zxj9+7dbN26lU6dOtV2OCIiIpJE6lzXgsOHD3PKKacoiU0SZsYpp5yiFnQRERGpsDqXyAJKYpOMni8RERGpjDqZyIqIiIhI3Vfn+siG63jbX6u1vM0zLq7W8kRERESkctQimwD16tUjJyeHrKwsrrzySgoKCgC4++676dmzJ9nZ2eTk5PDmm28CkJubS/fu3cnJySEnJ4crrrgCgDFjxvCnP/2pTNlNmzYFYPPmzTRu3Jg+ffrQo0cPzjjjDObMmVPhWNesWcONN94IwOzZs2nZsiV9+vSha9euDBkyhJUrVwIwfvx4cnJy+M53vkPjxo1LY506dSo5OTml5c2bN48mTZpw5MgRANavX092djYAI0aMID8/v8IxioiIiERS51tka0Pjxo1Zu3YtACNHjuSxxx5j4MCBvPDCC7zzzjs0bNiQL774gqKiotJt5s6dS79+/Sq0ny5durBmzRoAPvnkEy677DK+/vprrr/++rjLmD59OpMnTy6dvvrqq5k5cyYAS5cu5bLLLmPp0qU8+uijgE+gL7nkktLj+/rrr3nooYc4cOAAaWlprFy5ktNOO401a9ZwxhlnsHLlSs466ywAbr75Zu655x5++9vfVug4RURERCJRi2yCfe973+Ojjz5ix44dpKen07BhQwDS09PJyMiotv107tyZBx54gEceeQSAPXv2MHz4cLKzsxkwYADr1q07ZpsDBw6wbt06evfuHbHM8847j5tuuolZs2ZF3W9KSgr9+/cvbV1evXo148ePL23JXblyJWeeeSbgz8Urr7zC0aNHq3SsIiIiIqBENqGOHj3KSy+9RK9evbjwwgvZsmUL3bp1Y9y4cbz66qtl1h05cmTp1/U///nPK7W/vn378v777wNwxx130KdPH9atW8f06dMZNWrUMeuvWrWKrKysuMuM5swzz2TlypUcOnSIlJQUcnNzyySyJS2yKSkpZGZm8u6771bm8ERERETKUCKbAIWFheTk5NCvXz86dOjA2LFjadq0KatXr2bWrFm0bNmSq6++mtmzZ5duM3fuXNauXcvatWu59957gcjDUsUaqso5V/r/ihUruO666wA4//zz2b17N/v37y+z/o4dO2jZsmXMYwktM5qzzjqLlStX8tZbb9G/f3+6dOnCRx99xOeff87Bgwfp3Llz6bqtWrVi+/bt5ZYpIiIiUh71kU2A0D6yoerVq0dubi65ubn06tWLOXPmMGbMmKjlnHLKKezdu7d0es+ePaSnp0ddf82aNfTo0QOInICGJ8GNGzcu94cIQsuMZsCAAbz99tusWLGCgQMHAtCuXTvmz59f2q2gxOHDh2ncuHHM8kRERETiUecT2eNluKwPPviAlJQUunbtCsDatWs59dRTY26Tm5vLQw89xOjRo2nQoAGzZ8/mvPPOi7ju5s2bufXWW5kwYQIA55xzDnPnzmXKlCksW7aM9PR0mjVrVmabHj16cP/990fd/6uvvsqsWbNYunRpzDjT0tJo3749s2fPZtmyZQAMHDiQhx56iHHjxpVZ98MPP6Rnz54xyxMRERGJR8ISWTP7PXAJsMs5lxXMuxf4AVAEfAxc75zbFyy7HRgLFAO3OOf+lqjYasPBgweZMGEC+/bto379+mRmZpa5iWrkyJGlLZXp6em88sorXHLJJaxevZrTTz+devXq0aVLFx577LHSbT7++GP69OnD4cOHSUtLY8KECaUjFuTl5XH99deTnZ1NkyZNIg7Nddppp7F///7SEQcAnn76aVasWEFBQQGdOnXiueeeK7dFFnz3goULF9K+fXvAJ7K/+MUvyrTI7ty5k8aNG9OmTZtKnEERERGRsiyePpCVKtjsHOAg8ERIInsh8H/OuaNm9isA59wkM/sOMA84A8gAXgG6OeeKY+2jX79+btWqVWXmvffee3ElXuI9+OCDpKWllY4lm+h9NWvWjLFjxx6zTM+biIiIRGJmq51zEccoTdjNXs655cCesHmLnXMlYy+9AbQL/h8GzHfOfeWc2wR8hE9qJcFuvvnm0iHBEq1FixaMHj26RvYlIiIidV9tjlpwA/BS8H9bYEvIsq3BvGOY2U1mtsrMVn3++ecJDrHua9SoUenoBol2/fXXU79+ne+WLSIiIjWkVhJZM/tP4Cgwt2RWhNUi9nlwzs1yzvVzzvUrb+goERERqXl5eXmYWbU98vLyavuQ5DhV481jZjYafxPYIPdNB92tQPuQ1doBGmxUREQkCeXl5ZWbfObm5gKUjnYjUhk12iJrZt8HJgFDnXMFIYv+Aowws4Zm1gnoCrxVk7GJiIiISHJJ5PBb84BcIN3MtgJ3ALcDDYGXg8H533DO/dg59w8zewbYiO9yML68EQvilte8Wor5prz95a8jIiIiIgmXyFELrnHOtXHOpTrn2jnnfuecy3TOtXfO5QSPH4esf7dzrotzrrtz7qVYZR/v6tWrR05ODllZWVx55ZUUFPjG57vvvpuePXuSnZ1NTk4Ob775JuC/XunevTs5OTnk5ORwxRVXADBmzBj+9Kc/lSm7adOmgP8BhMaNG9OnTx969OjBGWecEXGs2PKsWbOmdOit2bNn07Jly9I4cnJy2LhxY7n7euGFF7jjjjsqfqJEREREqkC3kCdA6E/Ujhw5kscee4yBAwfywgsv8M4779CwYUO++OILioqKSreZO3cu/fpFHCItqi5durBmzRoAPvnkEy677DK+/vrr0h9FiMf06dOZPHly6fTVV1/NzJkzy6yzefPmmPu6+OKLmTJlCpMmTaJJkyYVOgYRERGRyqrN4bdOCN/73vf46KOP2LFjB+np6aVjtqanp5ORkVFt++ncuTMPPPAAjzzyCAB79uxh+PDhZGdnM2DAANatW3fMNgcOHGDdunX07t27SvsyM3Jzc3nhhReqfiAiIiIicVIim0BHjx7lpZdeolevXlx44YVs2bKFbt26MW7cOF599dUy644cObL06/yf//znldpf3759ef/99wG444476NOnD+vWrWP69OmMGjXqmPVXrVpFVlZWmXlPP/10ma4FhYWF5e4LoF+/frz22muViltERESkMtS1IAEKCwvJyckBfIvs2LFjadCgAatXr+a1115j6dKlXH311cyYMYMxY8YAkbsWBDfElTuvROjPDa9YsYLnnnsOgPPPP5/du3ezf/9+mjf/5ua3HTt2ED4Wb6SuBeXtC6BVq1Zs364R00RERKTmKJFNgNA+sqHq1atHbm4uubm59OrVizlz5pQmspGccsop7N27t3R6z549pKenR11/zZo19OjRAzg20YRjk+DGjRtz+PDh8g6n3H0BHD58mMaNG1eqLBEREZHKqPuJ7HEyXNYHH3xASkoKXbt2BWDt2rWceuqpMbfJzc3loYceYvTo0TRo0IDZs2dz3nnnRVx38+bN3HrrrUyYMAGAc845h7lz5zJlyhSWLVtGeno6zZo1K7NNjx49uP/++yt8LOH7Avjwww+P6aYgIiIikkh1P5E9Thw8eJAJEyawb98+6tevT2ZmJrNmzSpdPnLkyNIWzfT0dF555RUuueQSVq9ezemnn069evXo0qULjz32WOk2H3/8MX369OHw4cOkpaUxYcKE0hEL8vLyuP7668nOzqZJkyYRh+Y67bTT2L9/PwcOHCAtLQ3wfWRXrFhRus6vf/1rMjIyYu4LYOnSpfzyl7+s3pMmIiIiEoNF+go6WfTr18+tWrWqzLz33nuvzFfeEtuDDz5IWlpa6ViylbFz506uvfZalixZUuky9LyJiJxY9BO1Ei8zW+2cizhGqUYtOMHdfPPNpUOCVdann35aqS4KIiIiIlWhrgUnuEaxKKUAACAASURBVEaNGnHddddVqYz+/ftXUzQiIiIi8VOLrIiIiIgkJSWyIiIiIpKUlMiKiIiISFKq831ke83pVa3lrR+9vtx16tWrR69e3+x3xIgR3HbbbeTm5rJjxw4aNmxIUVERF1xwAXfddRctWrSo1hhFRERETgR1PpGtDdF+2Qu++SnaoqIibr/9doYNG8arr75awxGKiIiIJD91LaglDRo04J577uHTTz/l3Xffre1wJInk5eVhZtX2yMvLU7wiIpKUlMgmQGFhITk5OaWPp59+OuJ69erVo3fv3rz//vs1HKEks7y8PJxzMR/nnnsu5557brnrOedqJJFNpnhFRCR5qGtBAsTqWhAumX9ZTURERKQ2qUW2FhUXF7N+/Xr9NKuIiIhIJSiRrSVHjhzh9ttvp3379mRnZ9d2OCIiIiJJp853LYhnuKzqVtJHtsT3v/99ZsyYAcDIkSNp2LAhX331FRdccAELFy6s8fhERERE6oI6n8jWhuLi4ojzly1bVrOBiIiISLny8vKYOnVqtZV3xx136MbUGqJEVkRERE5oeXl55Saeubm5gBqljjfqIysiIiIiSalOJrIa0iq56PkSERGRyqhziWyjRo3YvXu3kqMk4Zxj9+7dNGrUqLZDERERkSRT5/rItmvXjq1bt/L555/XdigSp0aNGtGuXbvaDkNERESSTJ1LZFNTU+nUqVNthyEiIiIiCVbnuhaIiIiIyIlBiayIiIiIJCUlsiIiIiKSlJTIioiIiEhSUiIrIiIiIklJiayIiIiIJCUlsiIiIiKSlJTIioiIiEhSUiIrIiIiIklJiayIiIiIJCUlsiIiIiKSlJTIioiIiEhSUiIrIiIiIklJiayIiIiIJCUlsiIiIiKSlJTIioiIiEhSUiIrIiIiIklJiayIiIiIJCUlsiIiIiKSlJTIioiIiEhSUiIrIiIiIklJiayIiIiIJCUlsiIiIiKSlJTIioiIiEhSUiIrIiIiIklJiayInDBuuOEGWrVqRVZWVum8PXv2MHjwYLp27crgwYPZu3dv6bJf/vKXZGZm0r17d/72t79FLDPa9q+//jrZ2dn079+fjz76CIB9+/YxZMgQnHMJPEoRSWaqpypGiayInDDGjBnDokWLysybMWMGgwYNIj8/n0GDBjFjxgwANm7cyPz58/nHP/7BokWLGDduHMXFxceUGW37+++/n+eee47p06fzm9/8BoA777yTX/ziF5hZgo9URJKV6qmKUSIrIieMc845h5NPPrnMvIULFzJ69GgARo8ezYIFC0rnjxgxgoYNG9KpUycyMzN56623jikz2vapqakUFhZSUFBAamoqH3/8Mdu2bePcc89N5CGKSJJTPVUx9Ws7ABGR2rRz507atGkDQJs2bdi1axcA27ZtY8CAAaXrtWvXjm3btsW9/e23385NN91E48aNefLJJ7n11lu58847E304IlIHqZ6KTomsiEgEkfqHVeSrtpycHN544w0Ali9fTkZGBs45rr76alJTU7n//vtp3bp1tcUrIice1VPqWiAiJ7jWrVuzY8cOAHbs2EGrVq0A37KxZcuW0vW2bt1KRkZG3NuXcM5x1113MWXKFKZOncrUqVP54Q9/yCOPPJKoQxKROkb1VHRKZEXkhDZ06FDmzJkDwJw5cxg2bFjp/Pnz5/PVV1+xadMm8vPzOeOMM+LevsScOXO4+OKLOemkkygoKCAlJYWUlBQKCgoSfGQiUleonorBOZe0j9NPP92JyLHOPfdcd+6559Z2GHGrqXhHjBjhvv3tb7v69eu7tm3buscff9x98cUX7vzzz3eZmZnu/PPPd7t37y5d/6677nKdO3d23bp1cy+++GLp/LFjx7q3337bOedibn/o0CGXm5vrioqKnHPOLV++3GVlZbm+ffu6Dz74IOHHK3I8Uz0VmeqpYwGrXJRc0FySjBMWSb9+/dyqVatqOwyR405ubi4Ay5Ytq9U44pVs8YpI1SXb6z7Z4q1LzGy1c65fpGUJ61pgZr83s11mtiFk3slm9rKZ5Qd/TwpZdruZfWRmH5jZkETFJSIiIiJ1QyL7yM4Gvh827zZgiXOuK7AkmMbMvgOMAHoG2/zazOolMDYRERERSXIJS2Sdc8uBPWGzhwFzgv/nAMND5s93zn3lnNsEfAQc21tZRERE5ASWl5eHmVXbIy8vr7YPqUpqehzZ1s65HQDOuR1mVjL+Q1vgjZD1tgbzRERERCSQl5dXbvJ5IvXnPV5+ECHS6L0R70Izs5uAmwA6dOiQyJhEJMn1mtOrQut/8stPKPig+oabadK9CZ1v7xz3+utHr6+2fYtI8qhoXVWeTz77JCHlwvFXT9V0IrvTzNoErbFtgF3B/K1A+5D12gHbIxXgnJsFzAI/akEigxWRE0tFkk4REal9NZ3I/gUYDcwI/i4Mmf+UmT0AZABdgbdqODYRERGR49rOP+/k84Wfx7XuhjEbyl2n5bCWtL70+P4Z2lgSlsia2TwgF0g3s63AHfgE9hkzGwt8ClwJ4Jz7h5k9A2wEjgLjnXPFiYpNREREJBm1vrR1Uiee1S1hiaxz7pooiwZFWf9u4O5ExSMiIiIidUsix5EVEREREUkYJbIiIiIikpSUyIqIiIhIUlIiKyIiIiJJSYmsiIiIiCQlJbIiIiIikpSUyIqIiIhIUlIiKyIiIiJJSYmsiIiIiCQlJbIiIiIikpSUyIqIiIhIUlIiKyIiIiJJSYmsiIiIiCQlJbIiIiIikpSUyIqIiIhIUlIiKyIiIiJJSYmsiIiIiCQlJbIiIiIikpSUyIqIiEiVPPzww2RlZdGzZ08eeughAPbs2cPgwYPp2rUrgwcPZu/evRG3XbRoEd27dyczM5MZM2aUzp80aRLZ2dmMGjWqdN6TTz7Jww8/nNiDkaSiRFZEREQqbcOGDfz2t7/lrbfe4t133+WFF14gPz+fGTNmMGjQIPLz8xk0aFCZJLWEc47x48fz0ksvsXHjRubNm8fGjRvZv38/K1euZN26dRQXF7N+/XoKCwuZPXs248aNq4WjlOOVElkRERGptPfee48BAwbQpEkT6tevz7nnnsuf//xnFi5cyOjRowEYPXo0CxYsOGbbL7/8kszMTDp37kyDBg0YMWIECxcuJCUlhaKiIpxzFBYWkpqayr333sstt9xCampqTR+iHMeUyIqIiEilZWVlsXz5cnbv3k1BQQEvvvgiW7ZsYefOnbRp0waANm3asGvXrmO2LSoqon379qXT7dq1Y9u2baSlpXH55ZfTp08fOnXqRPPmzXn77bcZNmxYjR2XJIf6tR2AiIiIJK8ePXowadIkBg8eTNOmTenduzf161c+vTAzACZOnMjEiRMBuPHGG5k2bRqPP/44ixcvJjs7m8mTJ1dL/JLc1CIrIiIiVTJ27Fjeeecdli9fzsknn0zXrl1p3bo1O3bsAGDHjh20atXqmO0aNGjAli1bSqe3bt1KRkZGmXXWrFkDQLdu3XjiiSd45pln2LBhA/n5+Qk8IkkWSmRFRESkSkq6DXz66ac8//zzXHPNNQwdOpQ5c+YAMGfOnIjdApo1a0Z+fj6bNm2iqKiI+fPnM3To0DLrTJkyhWnTpnHkyBGKi4sBSElJoaCgIMFHJclAXQtERESkSi6//HJ2795Namoqjz76KCeddBK33XYbV111Fb/73e/o0KEDzz77LADbt2/nxhtvBHw3gpkzZzJkyBCKi4u54YYb6NmzZ2m5CxYsoH///qWttAMHDqRXr15kZ2fTu3fvmj9QOe4okRUREZEqee21146Zd8opp7BkyZJj5mdkZPDiiy+Sm5sLwEUXXcRFF10Usdzhw4czfPjw0un77ruP++67r3qCljpBXQtEREREJCkpkRURERGRpKREVkRERESSkvrIioiISLk63vbXai3vs092J6RcgM0zLq72MuX4pBZZEREREUlKSmRFREREJCkpkRURERGRpKREVkRERESSkhJZEREREUlKSmRFREREJCkpkRURERGRpKREVkRERESSkhJZkePcgw8+SM+ePcnKyuKaa67h8OHDpcvuu+8+zIwvvvgi4raLFi2ie/fuZGZmMmPGjNL5kyZNIjs7m1GjRpXOe/LJJ3n44YcTdyAiIiLVTImsyHFs27ZtPPLII6xatYoNGzZQXFzM/PnzAdiyZQsvv/wyHTp0iLitc47x48fz0ksvsXHjRubNm8fGjRvZv38/K1euZN26dRQXF7N+/XoKCwuZPXs248aNq8nDExERqRIlsiLHuaNHj1JYWMjRo0cpKCggIyMDgJ/97Gfcc889mFnE7b788ksyMzPp3LkzDRo0YMSIESxcuJCUlBSKiopwzlFYWEhqair33nsvt9xyC6mpqTV5aCIiIlWiRFbkONa2bVtuvfVWOnToQJs2bWjevDkXXnghf/nLX2jbti29e/eOum1RURHt27cvnW7Xrh3btm0jLS2Nyy+/nD59+tCpUyeaN2/O22+/zbBhw2rikERERKpN/doOQESi27t3LwsXLmTTpk20aNGCK6+8kieeeIJHH32UxYsXV7i8ktbbiRMnMnHiRABuvPFGpk2bxuOPP87ixYvJzs5m8uTJ1XocIiIiiaAWWZHj2CuvvEKnTp1o2bIlqampXHbZZfzhD39g06ZN9O7dm44dO7J161b69u3LZ599VmbbBg0asGXLltLprVu3lnZLKLFmzRoAunXrxhNPPMEzzzzDhg0byM/PT/zBiYiIVJFaZEWOYx06dOCNN96goKCAxo0bs2TJEi677DKWLl1auk7Hjh1ZtWoV6enpZbZt1qwZ+fn5bNq0ibZt2zJ//nyeeuqpMutMmTKFWbNmceTIEYqLiwFISUmhoKAg8QcnIiJSRWqRFTmOffe73+WKK66gb9++9OrVi6+//pqbbrop6vrbt2/noosuAnw3gpkzZzJkyBB69OjBVVddRc+ePUvXXbBgAf379ycjI4MWLVowcOBAevXqhZnF7HsrIiJyvFCLrMhxburUqUydOjXq8s2bN5f+n5GRwYsvvkhubi4AF110UWliG2748OEMHz68dPq+++7jvvvuq5aYRUREaoJaZEVEREQkKSmRFREREZGkpERWRERERJKSElkRERERSUq62UuklnW87a/VXuZnn+xOWNmbZ1xc7WWKiIhUhlpkRURERCQpKZEVkWrz4IMP0rNnT7Kysrjmmms4fPgwe/bsYfDgwXTt2pXBgwezd+/eiNvu2bOH7t27k5mZyYwZM0rnT5o0iezsbEaNGlU678knn+Thhx9O+PGISN1Ulbpq0aJFqquOI0pkRaRabNu2jUceeYRVq1axYcMGiouLmT9/PjNmzGDQoEHk5+czaNCgMhV/Cecc+fn5vPTSS2zcuJF58+axceNG9u/fz8qVK1m3bh3FxcWsX7+ewsJCZs+ezbhx42rhKEUk2VW1rho/frzqquOIElkRqTZHjx6lsLCQo0ePUlBQQEZGBgsXLmT06NEAjB49mgULFhyz3Zdffknjxo3p3LkzDRo0YMSIESxcuJCUlBSKiopwzlFYWEhqair33nsvt9xyC6mpqTV9eCJSR1SlrsrMzFRddRxRIisi1aJt27bceuutdOjQgTZt2tC8eXMuvPBCdu7cSZs2bQBo06YNu3btOmbboqIiGjZsWDrdrl07tm3bRlpaGpdffjl9+vShU6dONG/enLfffpthw4bV2HGJSN1S1bqqffv2pdOqq2qfElkRqRZ79+5l4cKFbNq0ie3bt3Po0CH++Mc/Vro8MwNg4sSJrF27lvvvv58pU6Ywbdo0Hn/8ca666iruuuuu6gpfRE4QqqvqFiWyIlItXnnlFTp16kTLli1JTU3lsssuY+XKlbRu3ZodO3YAsGPHDlq1anXMtg0aNOCrr74qnd66dSsZGRll1lmzZg0A3bp144knnuCZZ55hw4YN5OfnJ/CoRKSuqWpdtWXLltJp1VW1T4msiFSLDh068MYbb1BQUIBzjiVLltCjRw+GDh3KnDlzAJgzZ07Er9qaNWtGYWEhmzZtoqioiPnz5zN06NAy65S0cBw5coTi4mIAUlJSKCgoSPzBiUidUdW6Kj8/X3XVcUQ/iCAi1eK73/0uV1xxBX379qV+/fr06dOHm266iYMHD3LVVVfxu9/9jg4dOvDss88CsH37dm688UZefPFFzIzMzEyGDBlCcXExN9xwAz179iwte8GCBfTv37+05WPgwIH06tWL7OxsevfuXSvHKyLJqbJ1FfhuBDNnzlRddRwx51xtx1Bp/fr1c6tWrartMESqJCG/7PXUbQB8+9pjh4+pqkT8sldubi4Ay5Ytq9Zye83pVa3lJdr60etrOwSRqKq7rlI99Y1kqqtqo54ys9XOuX6RltVK1wIz+5mZ/cPMNpjZPDNrZGYnm9nLZpYf/D2pNmITERERkeRQ44msmbUFbgH6OeeygHrACOA2YIlzriuwJJgWEREREYmotm72qg80NrP6QBNgOzAMmBMsnwMMr6XYRERERCQJ1PjNXs65bWZ2H/ApUAgsds4tNrPWzrkdwTo7zOzYcS8AM7sJuAn8nYciUsPymld/mZsPJabsTqojRETqshpPZIO+r8OATsA+4Fkz+2G82zvnZgGzwN/slZAgRUREJHkl0wdu0IfuKqiNrgUXAJucc587544AzwNnAjvNrA1A8PfY34YTEREREQnURiL7KTDAzJqY/123QcB7wF+A0cE6o4GFtRCbiIiIiCSJ2ugj+6aZ/Ql4BzgKrMF3FWgKPGNmY/HJ7pU1HZuIiIiIJI9a+WUv59wdwB1hs7/Ct86KiIiIiJSrtobfEhERERGpEiWyIiIiIpKUlMiKiIiISFJSIisiIiIiSUmJrIiIiIgkJSWyIiIiIpKUlMiKiIiISFJSIisiIiIiSUmJrIiIiIgkJSWyIiIiIpKUoiayZnZSTQYiIiIiIlIR9WMs+8DMPgdWAq8DK51zH9ZMWCIiIiIisUVtkXXOtQIuxSexZwLPm9lOM1toZhNrKkARERERkUhitcgStMB+CMw2sy7ARcBPgAuBexIfnoiIiIhIZFETWTM7E98SOxBoD3wCvAH8EHinRqITEREREYkiVovsCnzC+gCwwDlXUDMhiYiIiIiUL1Yim4FvkT0T+LGZ1ccntn8H/u6c+6QG4hMRERERiShqIuuc+wx4PnhgZk2AG4CpQCegXk0EKCIiIiISSaw+ss3x/WNLWmX7AB8B/4sfyUBEREREpNbE6lrwEf7mrpXAncBbzrnCGolKRERERKQcsRLZ3znnbquxSEREREREKiDqDyLgx4oVERERETkuxWqRrWdmJwEWaaFzbk9iQhIRERERKV+sRPY0YDWRE1kHdE5IRCIiIiIicYiVyG50zvWpsUhERERERCogVh9ZEREREZHjVqxE9uEai0JEREREpIKiJrLOudlmNtrM3jGzQ8FjlZmNqskARUREREQiifXLXqOAnwL/DryDv+mrL3CvmeGce6JmQhQREREROVasrgXjgEudc0udc/udc/ucc/8HXB4sExGROiAvLw8zq7ZHXl5ebR+SiJwgYo1a0Mw5tzl8pnNus5k1S1xIIiJSk/Ly8spNPnNzcwFYtmxZwuMREYlXrBbZwkouExERERFJuFgtsj3MbF2E+YZ+DEFEREREalnMRLbGohARERERqaCoiaxz7p81GYiIiIiISEXol71EREREJCkpkRURERGRpBSrj2wpM2sAdAsmP3DOHUlcSCIiIiIi5Ss3kTWzXGAOsBk/YkF7MxvtnFue2NBERERERKKLp0X2fuBC59wHAGbWDZgHnJ7IwEREREREYomnj2xqSRIL4Jz7EEhNXEgiIiIiIuWLp0V2lZn9DngymB4JrE5cSCIiIiIi5Ysnkb0ZGA/cgu8juxx4NJFBiYiIiIiUJ55E9sfOuQeAB0pmmNlPgIcTFpWIiIiISDni6SM7OsK8MdUch4iIiIhIhURtkTWza4BrgU5m9peQRWnA7kQHJiIiIiISS6yuBSuBHUA6fgiuEgeAdYkMSkRERESkPFETWefcP4F/AgNrLhwRERERkfjE00dWREREROS4o0RWRERERJJSXImsmTU2s+6JDkZEREREJF7lJrJm9gNgLbAomM4JG8VARERERKTGxdMimwecAewDcM6tBTomLiQRERERkfLFk8gedc7tT3gkIiIiIiIVEM9P1G4ws2uBembWFbgFP8asiIiIiEitiadFdgLQE/gKeArYD/w0kUGJiIiIiJQnnhbZ7s65/wT+M9HBiIiIiIjEK54W2QfM7H0zu9PMeiY8IhERERGROJSbyDrnzgNygc+BWWa23swmJzowEREREZFY4vpBBOfcZ865R4Af48eU/a+ERiUiIiIiUo54fhChh5nlmdkGYCZ+xIJ2CY9MRERERCSGeG72+gMwD7jQObc9wfGIiIiIiMSl3ETWOTegundqZi2Ax4EswAE3AB8AT+N/NWwzcJVzbm9171tERERE6oaoXQvM7Jng73ozWxfyWG9m66q434eBRc6504DewHvAbcAS51xXYEkwLSIiIiISUawW2Z8Efy+pzh2aWTPgHGAMgHOuCCgys2H40REA5gDLgEnVuW8RERFJvH0r5rL/9XlxrfvPX5WfZjQ/6xpanD2yqmFJHRQ1kXXO7Qj+HeecK5NQmtmvqHyS2Rk/lNcfzKw3sBqfNLcu2adzboeZtYq0sZndBNwE0KFDh0qGICIiIonS4uyRSjylRsQz/NbgCPP+pQr7rA/0BX7jnOsDHKIC3Qicc7Occ/2cc/1atmxZhTBEREREJJnF6iN7s5mtB7qH9ZHdBFSlj+xWYKtz7s1g+k/4xHanmbUJ9t0G2FWFfYiIiIhIHRerj+xTwEvALynbYnrAObensjt0zn1mZlvMrLtz7gNgELAxeIwGZgR/F1Z2HyIiIiJS98XqI7sf2A9cAxD0WW0ENDWzps65T6uw3wnAXDNrAHwCXI9vHX7GzMYCnwJXVqF8EREREanjyh1H1sx+ADwAZOC/7j8VP1xWz8ru1Dm3FugXYdGgypYpIiIiIieWeG72ugsYAHzonOuETzZfT2hUIiIiIiLliCeRPeKc2w2kmFmKc24pkJPguEREREREYiq3awGwz8yaAsvx/Vp3AUcTG5aIiIiISGzxtMgOAwqBnwGLgI+BHyQyKBERERGR8pTbIuucOxQyOSeBsYiIiIiIxC1qImtmBwAXOiuYNsA555olODYRERERkahijSObVpOBiIiIiIhURDx9ZDGzs83s+uD/dDPrlNiwRERERERiKzeRNbM7gEnA7cGsBsAfExmUiIiIiEh54mmRvRQYChwCcM5tB9TtQERERERqVTyJbJFzzhHc+GVm30psSCIiIiIi5YsnkX3GzP4HaGFmPwKWAI8nNiwRERERkdjiGUf2PjMbDHwJdAemOOdeTnhkIiIiIiIxxExkzawecFKQuL5sZg2AMWb2nnOuR41EKCIiIiISQdSuBWY2AtgDrDOzV83sPOAT4F+AkTUUn4iIVLOOHTvSq1cvcnJy6NevHwDPPvssPXv2JCUlhVWrVkXddtGiRXTv3p3MzExmzJhROn/SpElkZ2czatSo0nlPPvkkDz/8cOIOREROeLFaZCcDpzvnPjKzvsDfgRHOuT/XTGgiIpIoS5cuJT09vXQ6KyuL559/nn/913+Nuo1zjvHjx/Pyyy/Trl07+vfvz9ChQ2nbti0rV65k3bp1jBw5kvXr15OZmcns2bNZtGhRTRyOiJygYt3sVeSc+wjAOfcOsElJrIhI3dSjRw+6d+8ec50vv/ySzMxMOnfuTIMGDRgxYgQLFy4kJSWFoqIinHMUFhaSmprKvffeyy233EJqamoNHYGInIhiJbKtzOzfSx5A07BpERFJQmbGhRdeyOmnn86sWbPi3q6oqIj27duXTrdr145t27aRlpbG5ZdfTp8+fejUqRPNmzfn7bffZtiwYYkIX0SkVKyuBb+l7A8fhE+LSC3Yt2Iu+1+fF9e6//zVJeWu0/ysa2hxtrq9n0hef/11MjIy2LVrF4MHD+a0007jnHPOqVRZZgbAxIkTmThxIgA33ngj06ZN4/HHH2fx4sVkZ2czefLkaotfRKRE1ETWOTe1JgMRkfi0OHukEk+pkoyMDABatWrFpZdeyltvvRVXItugQQO2bNlSOr1169bSskqsWbMGgG7duvGTn/yE5cuXM2LECPLz8+natWs1HoWISHw/iCAiInXEoUOHOHDgQOn/ixcvJisrK65tmzVrRn5+Pps2baKoqIj58+czdOjQMutMmTKFadOmceTIEYqLiwFISUmhoKCgeg9ERAQlsiIiJ5SdO3dy9tln07t3b8444wwuvvhivv/97/PnP/+Zdu3a8fe//52LL76YIUOGALB9+3YuuugiwHcjmDlzJkOGDKFHjx5cddVV9OzZs7TsBQsW0L9/fzIyMmjRogUDBw6kV69emBm9e/euleMVkbrNnHO1HUOl9evXz8Ua71AkGXS87a+1HUKFbG50bbWXmTv7EADLxnyrWsvt1alDtZaXaOtHr6/tEKLKzc0FYNmyZbUah9SeZKqrkqmeguSqq2qjnjKz1c65fpGWxd0ia2YDzOz/zOx1MxtefeGJiIiIiFRc1Ju9zOzbzrnPQmb9OzAUMGAlsCDBsYmIiIiIRBVr+K3HzGw1cK9z7jCwD7gW+Br4siaCExERERGJJtbwW8PN7AfAC2Y2B/gpPpFtAqhrgYjIcSoRfRk/+2R3wsrePOPiai9TRE4MMfvIOuf+FxgCtACeBz5wzj3inPu8JoITEREREYkmaiJrZkPNbAXwf8AGYARwqZnNM7MuNRWgiIiIiEgksfrI3gUMBBoDLzrnzgD+3cy6AnfjE1sRERERkVoRK5Hdj09WGwO7SmY65/JREisizVtGmAAAHilJREFUIiIitSxWH9lL8Td2HcXf5CUiIiIictyINWrBF8B/12AsIiIiIiJxi/uXvUREREREjidKZP+/vfsPs6q67z3+/o4yKjeAMQHDOHiVIIrKz0iUxiukSQqlKQwSDcgNKiBFk0pi8mCqMY5cTGmACk1QSxQRbjI+JGlFFPxxTdBERMGAglgyDdDww5+1QJSRAVz3j3OYzsAgIPPjbHy/nmeeOXvttfdZy1luPmeddc6WJElSJhlkJUmSlEkGWUmSJGWSQVaSJEmZZJCVJElSJhlkJUmSlEkGWUmSJGXSB92iVpIk6ZhXvuQ9bnuq+rDqxm07Dlnn1r7FlPc78WibpcNgkJUkSR9p5f1ONHhmlEsLJEmSlEkGWUmSJGWSSwskNSrXnkmSGotBVlKjcu2ZJKmxuLRAkiRJmWSQlSRJUiYZZCVJkpRJBllJkiRlkkFWkiRJmWSQlSRJUiYZZCVJkpRJBllJkiRlkkFWkiRJmWSQlSRJUiYZZCVJkpRJBllJkiRlkkFWkiRJmWSQlSRJUiYZZCVJkpRJBllJUqaUl5cTEQ32U15e3txdkvQhNVuQjYjjImJlRDyc3z4lIp6IiMr87483V9skSYWrvLyclNIH/vTt25e+ffsesl5KySArZVhzzsiOB16ptf1d4MmU0lnAk/ltSZIkqV7NEmQjohT4K+CeWsWDgfvzj+8Hypq6XZIkScqO5pqRnQ5MAN6vVXZqSulVgPzvdvUdGBFjI2JFRKx48803G7+lkiRJKkhNHmQj4svAGymlFz7M8SmlWSmlC1JKF7Rt27aBWydJkqSsOL4ZnvNzwKCIGAicCLSOiP8LvB4R7VNKr0ZEe+CNZmibJEmSMqLJZ2RTSn+XUipNKZ0BDAN+lVL638BDwJX5alcCC5q6bZIkScqOQvoe2cnAlyKiEvhSfluSJEmqV3MsLaiRUloCLMk//k/gC83ZHkmSJGVHIc3ISpIkSYfNICtJkqRMMshKkiQpkwyykiRJyiSDrCRJkjLJICtJkqRMMshKkiQpkwyykiRJyiSDrCRJkjLJICtJkqRMMshKkiQpkwyykiRJyiSDrCRJkjLJICtJkqRMMshKkiQpkwyykiRJyiSDrCRJkjLJICtJkqRMMshKkiQpkwyykiRJyiSDrCRJkjLJICtJkqRMMshKkiQpk45v7gZIkprXtt/+lO3PVBxW3f/4hy8fsk6bzw3n5ItHHG2zJOmQDLKS9BF38sUjDJ6SMsmlBZKkgrVp0yY+//nP06VLF8477zxmzJgBwKpVq7jooovo0aMHF1xwAc8//3y9xz/66KOcffbZdOrUicmTJ9eU33jjjXTr1o2RI0fWlM2bN6/m/JKywSArSSpYxx9/PNOmTeOVV15h2bJlzJw5k7Vr1zJhwgRuvfVWVq1axcSJE5kwYcIBx6aU+PrXv87ixYtZu3YtFRUVrF27lu3bt7N06VJeeukl9u7dy+rVq6mqqmLOnDlcd911zdBLSR+WQVaSVLDat29Pr169AGjVqhVdunRhy5YtRAQ7duwAYPv27ZSUlBxw7I4dO+jUqRMdO3akuLiYYcOGsWDBAoqKiqiurialRFVVFS1atGDKlClcf/31tGjRokn7J+nouEZWkpQJGzduZOXKlVx44YVMnz6d/v37853vfIf333+fpUuXHlC/urqaDh061GyXlpby3HPP0apVK4YOHUrPnj35whe+QJs2bVi+fDnf//73m7I7khqAM7KSpIL3zjvvMHToUKZPn07r1q256667uOOOO9i0aRN33HEHo0ePPqzzRAQAEyZMYNWqVUybNo1bbrmFiRMncs8993D55ZczadKkxuyKpAZkkJUkFbTdu3czdOhQRowYwaWXXgrA/fffX/P4sssuq/fDXsXFxWzatKlme/PmzQcsQVi5ciUAnTt3Zu7cucyfP581a9ZQWVnZWN2R1IAMspKkgpVSYvTo0XTp0oUbbrihprykpISnnnoKgF/96lecddZZBxzbunVrKisr2bBhA9XV1TzwwAMMGjSoTp19s7G7d+9m7969ABQVFbFz585G7JWkhuIaWUlSwXrmmWeYN28eXbt2pUePHgD84Ac/4Cc/+Qnjx49nz549nHjiicyaNQuArVu3MmbMGCC3jODHP/4x/fv3Z+/evYwaNYrzzjuv5twPPvggvXv3rpml7dOnD127dqVbt2507969iXsq6cMwyEqSCtbFF19MSqnefS+88MIBZSUlJSxatIh+/foBMHDgQAYOHFjv8WVlZZSVldVsT506lalTpx59oyU1GZcWSJIkKZMMspIkScoklxZIkppXeZuGP+fGdxvn3OXbG/Z8ko6KM7KSJEnKJIOsJEmSMskgK0mSpEwyyEqSJCmTDLKSJEnKJIOsJEmSMskgK0mSpEwyyEqS1EjKy8uJiAb7KS8vb+4uSQXFGyJIktRIysvLDxk++/XrB8CSJUsavT3SscYZWUmSJGWSQVaSJEmZZJCVJElSJhlkJUmSlEkGWUmSJGWSQVaSJEmZZJCVJElSJhlkJUmSlEkGWUmSJGWSQVaSJEmZZJCVJElSJhlkJUlqAKNGjaJdu3acf/75NWUvvvgiffr0oWvXrvz1X/81O3bsqPfYt99+m7PPPptOnToxefLkmvIbb7yRbt26MXLkyJqyefPmMWPGjMbriJQhBllJkhrAVVddxaOPPlqnbMyYMUyePJnVq1czZMgQpkyZcsBxKSUqKytZvHgxa9eupaKigrVr17J9+3aWLl3KSy+9xN69e1m9ejVVVVXMmTOH6667rqm6JRU0g6wkSQ3gkksu4ZRTTqlTtm7dOi655BIAvvSlL/HLX/7ygON27NjBSSedRMeOHSkuLmbYsGEsWLCAoqIiqqurSSlRVVVFixYtmDJlCtdffz0tWrRokj5Jhc4gK0lSIzn//PN56KGHAPj5z3/Opk2bDqhTXV3NCSecULNdWlrKli1baNWqFUOHDqVnz56ceeaZtGnThuXLlzN48OAma79U6AyykiQ1ktmzZzNz5kw+85nP8Kc//Yni4uLDOi4iAJgwYQKrVq1i2rRp3HLLLUycOJF77rmHyy+/nEmTJjVm06VMaPIgGxEdIuLXEfFKRLwcEePz5adExBMRUZn//fGmbpskSQ3pnHPO4fHHH+eFF15g+PDhfPrTnz6gTnFxMbt27arZ3rx5MyUlJXXqrFy5EoDOnTszd+5c5s+fz5o1a6isrGzcDkgFrjlmZPcA304pdQEuAr4eEecC3wWeTCmdBTyZ35YkKbPeeOMNAN5//30mTZrEuHHjDqjTunVrqqqq2LBhA9XV1TzwwAMMGjSoTp19s7G7d+9m7969ABQVFbFz587G74RUwJo8yKaUXk0p/S7/+E/AK8BpwGDg/ny1+4Gypm6bJEkf1vDhw+nTpw/r1q2jtLSUe++9l4qKCjp37sw555xDSUkJV199NQBbt25l4MCBQG4ZQadOnejfvz9dunTh8ssv57zzzqs574MPPkjv3r0pKSnh5JNPrvk6r4ige/fuzdJXqVAc35xPHhFnAD2B54BTU0qvQi7sRkS7gxwzFhgLcPrppzdNQyVJOoSKiop6y8ePH39AWUlJCYsWLarZ/sQnPsGSJUvqPb6srIyysv+e25k6dSpTp049usZKx4hm+7BXRHwM+CXwzZRS/d8QXY+U0qyU0gUppQvatm3beA2UJElSQWuWIBsRLciF2J+mlP4lX/x6RLTP728PvNEcbZMkSVI2NMe3FgRwL/BKSukfa+16CLgy//hKYEFTt02SJEnZ0RxrZD8HfA1YHRGr8mU3AZOB+RExGvgjcFkztE2SJEkZ0eRBNqX0WyAOsvsLTdkWSZKORNf7uzb4Ode/tr7Rzr36ytUNfk6pkHhnr0ZWXl5ORDTYT3l5eXN3SZIkqSA069dvfRSUl5cfMnz269cP4KBfvSJJkqQDOSMrSZKkTDLISpIkKZMMsg1o1KhRtGvXjvPPP/+AfVOnTiUieOutt+o99u233+bss8+mU6dOTJ48uab8xhtvpFu3bowcObKmbN68ecyYMaPhOyBJkpQhBtkGdNVVV/Hoo48eUL5p0yaeeOKJg95SN6VEZWUlixcvZu3atVRUVLB27Vq2b9/O0qVLeemll9i7dy+rV6+mqqqKOXPmcN111zV2dyRJkgqaQbYBXXLJJZxyyikHlH/rW9/ihz/8Ibl7QRxox44dnHTSSXTs2JHi4mKGDRvGggULKCoqorq6mpQSVVVVtGjRgilTpnD99dfTokWLxu6OJElSQTPINrKHHnqI0047je7dux+0TnV1NSeccELNdmlpKVu2bKFVq1YMHTqUnj17cuaZZ9KmTRuWL1/O4MGDm6LpkiRJBc2v32pEO3fu5Pbbb+fxxx8/4mP3zd5OmDCBCRMmADBmzBgmTpzIPffcw+OPP063bt343ve+16BtliRJygpnZBvRH/7wBzZs2ED37t0544wz2Lx5M7169eK1116rU6+4uJhdu3bVbG/evJmSkpI6dVauXAlA586dmTt3LvPnz2fNmjVUVlY2fkckSZIKkDOyjahr16688cYbNdtnnHEGK1as4JOf/GSdeq1bt6aqqooNGzZw2mmn8cADD/Czn/2sTp1bbrmFWbNmsXv3bvbu3QtAUVERO3fubPyOSFIBKV/yHrc9VX1YdeO2HYesc2vfYsr7nXi0zZLUDAyyDWj48OEsWbKEt956i9LSUm677TZGjx5db92tW7cyZswYFi1aRETQqVMn+vfvz969exk1ahTnnXdeTd0HH3yQ3r1718zS9unTh65du9KtW7cPXHsrScei8n4nGjwlAQbZBlVRUfGB+zdu3FjzuKSkhEWLFtVsf+ITnzjoLWrLysooKyur2Z46dSpTp049qrZKkiRlnWtkJUmSlEkGWUmSJGWSQVaSJEmZ5BrZI3TGdx9p8HO+tv4/G+3cGyf/VYOfU5IkqRA4IytJkqRMMshKkiQpkwyykiRJyiSDrCRJkjLJICtJkqRMMshKkiQpkwyykiRJyiSDrCRJkjLJICtJkqRM8s5ekiQ1ktf/9XXeXPDmYdVdc9WaQ9ZpO7gtpw459WibJR0zDLKSJDWSU4ecavCUGpFLCyRJkpRJBllJkiRlkkFWkiRJmWSQlSRJUib5Ya9Gtu23P2X7MxWHVfc//uHLh6zT5nPDOfniEUfbrHpt27aNMWPGsGbNGiKC2bNn06dPn5r9KSXGjx/PokWLaNmyJXPmzKFXr168+eabDBkyhG3btjFp0iTKysoAGDx4MHfddRclJSWN0l5JkvTRZpBtZCdfPKLRgmdDGz9+PAMGDOAXv/gF1dXV7Ny5s87+xYsXU1lZSWVlJc899xzXXnstzz33HBUVFVx55ZUMGzaMAQMGUFZWxsKFC+nVq5chVpIkNRqDrADYsWMHTz/9NHPmzAGguLiY4uLiOnUWLFjAyJEjiQguuugitm3bxquvvkqLFi2oqqpi165dFBUVsWfPHqZPn87ChQuboSeSJOmjwjWyAmD9+vW0bduWq6++mp49ezJmzBjefffdOnW2bNlChw4darZLS0vZsmULV1xxBY899hgDBgygvLycO++8k5EjR9KyZcum7oYkSfoIcUZWAOzZs4cVK1awfPlyAFatWsW99957QL1HHnmkznbv3r3rbH/xi1+seTxjxgw6duzIt7/97TprbSVJkhqCM7ICcrOrp59+OiklUko8/fTTDBw4sGY7pcTYsWPp0qULffv2JaVE586d2bp1a5063/zmN1myZAmzZs1i7NixzJ49m5tuuqm5uydJko5BBlkB8KlPfYoOHTqwbt06AJ588knOPffcOnUGDRrEa6+9RkqJZcuW0aZNG9q3b1+zv7Kykq1bt9K3b1927txJUVEREcF7773XpH2RJEkfDS4tUI0f/ehHjBgxgurqajp27Mh9993H3XffDcC4ceMYOHAgJ510Es8//zzXXHMN9913X53jb775Zm6//XYAhg8fTllZGTNmzGDixIlN3hdJknTsM8iqRo8ePVixYkWdsnHjxtU8jgjOOussAJYsWXLA8fPnz6953K5dO5YuXdo4DZUkScKlBZIkScoog6wkSZIyySArSZKkTDLISpIkKZMMspIkScokv7XgWFfepmHPt/HdxjkvQPn2w6q2adMmRo4cyWuvvUZRURFjx45l/PjxdeqklBg/fjyLFi2iZcuWzJkzh169evHmm28yZMgQtm3bxqRJkygrKwNg8ODB3HXXXZSUlDR4tyRJUuNwRlaZc/zxxzNt2jReeeUVli1bxsyZM1m7dm2dOosXL6ayspLKykpmzZrFtddeC0BFRQVXXnklzz77LFOmTAFg4cKF9OrVyxArSVLGOCOrzGnfvn3NHcVatWpFly5d2LJlS507kS1YsICRI0cSEVx00UVs27aNV199lRYtWlBVVcWuXbsoKipiz549TJ8+nYULFzZXdyRJ0ofkjKwybePGjaxcuZILL7ywTvmWLVvo0KFDzXZpaSlbtmzhiiuu4LHHHmPAgAGUl5dz5513MnLkSFq2bNnUTZckSUfJIKvMeueddxg6dCjTp0+ndevWdfallA6oHxG0adOGRx55hBUrVtCrVy8efvhhhg4dyjXXXMNXvvIVnn322aZqviRJOkoGWWXS7t27GTp0KCNGjODSSy89YH9paSmbNm2q2d68efMBa2AnTpzIzTffTEVFBZ/5zGeYPXs2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\n",
      "text/plain": [
       "<Figure size 720x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "labels = ['Computing time', 'Recall', 'Precision-50', 'Precision-10%']\n",
    "pseudo_values = [\n",
    "    100 * (np.mean(total_lsh_times) / np.mean(total_dtw_times)),\n",
    "    100 * np.mean(total_recall_pseudo), \n",
    "    100 * np.mean(total_precision_pseudo), \n",
    "    100 * np.mean(total_precision2_pseudo)\n",
    "]\n",
    "pseudo_error = [\n",
    "    100 * (np.std(total_lsh_times) / np.mean(total_dtw_times)), \n",
    "    100 * np.std(total_recall_pseudo), \n",
    "    100 * np.std(total_precision_pseudo), \n",
    "    100 * np.std(total_precision2_pseudo)\n",
    "]\n",
    "pseudo_ed_values = [\n",
    "    100 * (np.mean(total_lsh_times_ed) / np.mean(total_dtw_times)),\n",
    "    100 * np.mean(total_recall_pseudo_ed), \n",
    "    100 * np.mean(total_precision_pseudo_ed), \n",
    "    100 * np.mean(total_precision2_pseudo_ed)\n",
    "]\n",
    "pseudo_ed_error = [\n",
    "    100 * (np.std(total_lsh_times_ed) / np.mean(total_dtw_times)), \n",
    "    100 * np.std(total_recall_pseudo_ed), \n",
    "    100 * np.std(total_precision_pseudo_ed), \n",
    "    100 * np.std(total_precision2_pseudo_ed)\n",
    "]\n",
    "ed_values = [\n",
    "    100 * (np.mean(total_ed_times) / np.mean(total_dtw_times)),\n",
    "    100 * np.mean(total_recall_ed), \n",
    "    100 * np.mean(total_precision_ed), \n",
    "    100 * np.mean(total_precision2_ed)\n",
    "]\n",
    "ed_error = [\n",
    "    100 * (np.std(total_ed_times) / np.mean(total_dtw_times)), \n",
    "    100 * np.std(total_recall_ed), \n",
    "    100 * np.std(total_precision_ed), \n",
    "    100 * np.std(total_precision2_ed)\n",
    "]\n",
    "\n",
    "x = 1.2 * np.arange(len(labels))  # the label locations\n",
    "width = 0.35  # the width of the bars\n",
    "\n",
    "fig, ax = plt.subplots()\n",
    "fig.set_size_inches(10, 7)\n",
    "rects1 = ax.bar(x - width, pseudo_values, width, yerr=pseudo_error, capsize=10, label='PSEUDo (DTW)')\n",
    "rects2 = ax.bar(x, pseudo_ed_values, width, yerr=pseudo_ed_error, capsize=10, label='PSEUDo (ED)')\n",
    "rects3 = ax.bar(x + width, ed_values, width, yerr=ed_error, capsize=10, label='ED')\n",
    "\n",
    "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
    "ax.set_ylabel('% Relative to DTW')\n",
    "ax.set_title('Recall and precision of PSEUDo (and ED) compared to DTW [Synthetic: M=200.000, T=100, d=16]')\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(labels)\n",
    "ax.legend()\n",
    "\n",
    "\n",
    "def autolabel(rects):\n",
    "    \"\"\"Attach a text label above each bar in *rects*, displaying its height.\"\"\"\n",
    "    for rect in rects:\n",
    "        height = round(rect.get_height(),0)\n",
    "        ax.annotate('{}'.format(height)+'%',\n",
    "                    xy=(rect.get_x() + rect.get_width() / 2, height),\n",
    "                    xytext=(0, 3),  # 3 points vertical offset\n",
    "                    textcoords=\"offset points\",\n",
    "                    ha='center', va='bottom')\n",
    "\n",
    "\n",
    "autolabel(rects1)\n",
    "autolabel(rects2)\n",
    "autolabel(rects3)\n",
    "\n",
    "fig.tight_layout()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}