{ "cells": [ { "cell_type": "markdown", "id": "bafc2f4e-05a3-4120-bcd6-5d1f5fb91cd9", "metadata": {}, "source": [ "# Distinguishing countermeasures by output" ] }, { "cell_type": "code", "execution_count": 204, "id": "33ee6084-2ac3-4f95-9610-0fbc06026538", "metadata": {}, "outputs": [], "source": [ "import io\n", "import random\n", "import itertools\n", "import cypari2\n", "\n", "from matplotlib import pyplot as plt\n", "from collections import Counter\n", "from tqdm.auto import tqdm, trange\n", "\n", "from pyecsca.misc.utils import TaskExecutor\n", "from pyecsca.ec.mod import mod, RandomModAction\n", "from pyecsca.ec.point import Point\n", "from pyecsca.ec.model import ShortWeierstrassModel\n", "from pyecsca.ec.params import load_params_ectester\n", "from pyecsca.ec.mult import LTRMultiplier\n", "from pyecsca.ec.context import local, DefaultContext\n", "from pyecsca.ec.countermeasures import GroupScalarRandomization, AdditiveSplitting, MultiplicativeSplitting, EuclideanSplitting, BrumleyTuveri\n", "\n", "%matplotlib ipympl" ] }, { "cell_type": "code", "execution_count": 205, "id": "b1b9596c-1eba-4ace-af84-8cb279d84cc2", "metadata": {}, "outputs": [], "source": [ "model = ShortWeierstrassModel()\n", "coords = model.coordinates[\"projective\"]" ] }, { "cell_type": "code", "execution_count": 206, "id": "b0afb195-8390-44c5-931e-75a70ccd4e9e", "metadata": {}, "outputs": [], "source": [ "add = coords.formulas[\"add-2015-rcb\"]\n", "dbl = coords.formulas[\"dbl-2015-rcb\"]\n", "mult = LTRMultiplier(add, dbl, complete=False)" ] }, { "cell_type": "code", "execution_count": 207, "id": "52c877e1-5021-4ec2-9daa-dd20bec6bcb2", "metadata": {}, "outputs": [], "source": [ "gsr = GroupScalarRandomization(mult)\n", "asplit = AdditiveSplitting(mult)\n", "msplit = MultiplicativeSplitting(mult)\n", "esplit = EuclideanSplitting(mult)\n", "bt = BrumleyTuveri(mult)" ] }, { "cell_type": "markdown", "id": "27626337-dcbc-497c-a54e-02d50e2b8f34", "metadata": {}, "source": [ "## 3n test" ] }, { "cell_type": "code", "execution_count": 208, "id": "c3088419-161b-4193-a1b6-6f623f217fcd", "metadata": {}, "outputs": [], "source": [ "key3n = 0x20959f2b437de1e522baf6d814911938157390d3ea5118660b852ab0d5387006\n", "params3n = load_params_ectester(io.BytesIO(b\"0xc381bb0394f34b5ed061c9107b66974f4d0a8ec89b9fe73b98b6d1368c7d974d,0x5ca6c5ee0a10097af291a8f125303fb1a3e35e8100411902245d691e0e5cb497,0x385a5a8bb8af94721f6fd10b562606d9b9df931f7fd966e96859bb9bd7c05836,0x4616af1898b92cac0f902a9daee24bbae63571cead270467c6a7886ced421f5e,0x34e896bdb1337e0ae5960fa3389fb59c2c8d6c7dbfd9aac33a844f8f98e433ef,0x412b3e5686fbc3ca4575edb0292232702ae721a7d4a230cc170a5561aa70e00f,0x01\"), \"projective\")\n", "bits3n = params3n.full_order.bit_length()\n", "point3n = Point(X=mod(0x4a48addb2e471767b7cd0f6f1d4c27fe46f4a828fc20f950bd1f72c939b36a84, params3n.curve.prime),\n", " Y=mod(0x13384d38c353f862832c0f067e46a3e510bb6803c20745dfb31929f4a18d890d, params3n.curve.prime),\n", " Z=mod(1, params3n.curve.prime), model=coords)" ] }, { "cell_type": "code", "execution_count": 209, "id": "a8dde7e6-cd48-4f99-9677-23a19e4c2e5b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "prime:\t0xc381bb0394f34b5ed061c9107b66974f4d0a8ec89b9fe73b98b6d1368c7d974d\n", "a:\t0x5ca6c5ee0a10097af291a8f125303fb1a3e35e8100411902245d691e0e5cb497\n", "b:\t0x385a5a8bb8af94721f6fd10b562606d9b9df931f7fd966e96859bb9bd7c05836\n", "G:\t[0x4616af1898b92cac0f902a9daee24bbae63571cead270467c6a7886ced421f5e,\n", "\t 0x34e896bdb1337e0ae5960fa3389fb59c2c8d6c7dbfd9aac33a844f8f98e433ef]\n", "n:\t0x412b3e5686fbc3ca4575edb0292232702ae721a7d4a230cc170a5561aa70e00f\n", "3n:\t0xc381bb0394f34b5ed061c9107b66975080b564f77de69264451f0024ff52a02d\n", "\n", "P:\t[0x4a48addb2e471767b7cd0f6f1d4c27fe46f4a828fc20f950bd1f72c939b36a84,\n", "\t 0x13384d38c353f862832c0f067e46a3e510bb6803c20745dfb31929f4a18d890d]\n" ] } ], "source": [ "print(f\"prime:\\t0x{params3n.curve.prime:x}\")\n", "print(f\"a:\\t0x{params3n.curve.parameters['a']:x}\")\n", "print(f\"b:\\t0x{params3n.curve.parameters['b']:x}\")\n", "print(f\"G:\\t[0x{params3n.generator.X:x},\\n\\t 0x{params3n.generator.Y:x}]\")\n", "print(f\"n:\\t0x{params3n.order:x}\")\n", "print(f\"3n:\\t0x{3 * params3n.order:x}\")\n", "print(f\"\\nP:\\t[0x{point3n.X:x},\\n\\t 0x{point3n.Y:x}]\")" ] }, { "cell_type": "code", "execution_count": 210, "id": "cd6f8500-7509-45b0-8b23-471ee5014f42", "metadata": {}, "outputs": [], "source": [ "def generate_scalars_mod3(rem, samples):\n", " scalars = []\n", " while True:\n", " scalar = random.randint(0, params3n.full_order)\n", " if scalar % 3 == rem:\n", " scalars.append(scalar)\n", " if len(scalars) == samples:\n", " break\n", " return scalars\n", "\n", "def test_3n(countermeasure, scalars):\n", " ctr = Counter()\n", " for k in tqdm(scalars, leave=False):\n", " mult.init(params3n, point3n)\n", " kP = mult.multiply(k).to_affine()\n", " mult.init(params3n, point3n)\n", " knP = mult.multiply(k + params3n.full_order).to_affine()\n", " mult.init(params3n, point3n)\n", " k2nP = mult.multiply(k + 2 * params3n.full_order).to_affine()\n", "\n", " countermeasure.init(params3n, point3n)\n", " res = countermeasure.multiply(k)\n", " aff = res.to_affine()\n", " if aff.equals(kP):\n", " ctr[\"k\"] += 1\n", " elif aff.equals(knP):\n", " ctr[\"k + 1n\"] += 1\n", " elif aff.equals(k2nP):\n", " ctr[\"k + 2n\"] += 1\n", " else:\n", " ctr[aff] += 1\n", " for name, count in sorted(ctr.items()):\n", " print(f\"{name}:\\t{count}\")\n", "\n", "def test_3n_fixed_scalar(countermeasure, samples):\n", " test_3n(countermeasure, [key3n for _ in range(samples)])\n", "\n", "def test_3n_random_scalar(countermeasure, samples):\n", " test_3n(countermeasure, [random.randint(0, params3n.full_order) for _ in range(samples)])\n", "\n", "def test_3n_random_scalar_projected(countermeasure, samples):\n", " print(\"k = 0 mod 3\")\n", " test_3n(countermeasure, generate_scalars_mod3(0, samples))\n", " print()\n", " print(\"k = 1 mod 3\")\n", " test_3n(countermeasure, generate_scalars_mod3(1, samples))\n", " print()\n", " print(\"k = 2 mod 3\")\n", " test_3n(countermeasure, generate_scalars_mod3(2, samples))" ] }, { "cell_type": "markdown", "id": "46b8f74a-433d-48c9-b5b9-6bb7d2731246", "metadata": {}, "source": [ "### Fixed scalar experiments" ] }, { "cell_type": "markdown", "id": "fc82d4b9-91cd-423c-83aa-89721efa1ae9", "metadata": {}, "source": [ "#### Group scalar randomization" ] }, { "cell_type": "code", "execution_count": 211, "id": "86532d50-2db7-4370-b449-c545b330a852", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b0a80fc9b8f14b2992f10c7bb66a9a83", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/1000 [00:00 t:\n", " candidates.add(divisor)\n", " #print(f\"Candidates: {len(candidates)}, {r in candidates}\")\n", " candidate_amounts.append(len(candidates))\n", " if len(candidates) == 1:\n", " candidate = candidates.pop()\n", " print(\"Only one candidate, we got the mask:\", candidate, candidate == r)\n", " #print(\"--\")\n", "print(f\"Total recovered masks: {len(list(filter(lambda a: a == 1, candidate_amounts)))} out of {tries}\")" ] }, { "cell_type": "code", "execution_count": 237, "id": "6274ff91-325f-4c6b-a4d7-d66b994d730f", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "d557894c3fbd4543b9fd6c240b676212", "version_major": 2, "version_minor": 0 }, "image/png": "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3LlauHCh3WpI0iOPPKLMzEytXLlSe/fu1cCBAxUXF6fjx4/XabyrHWtz587Vyy+/rKVLl2rnzp1q3ry54uPj9dNPP9ll/LKyMvXr10/PPfdcneZ/tRrnzp1Tbm6uUlNTlZubq3feeUcHDhzQ0KFD7VZDkjp37qxFixZp7969+vTTTxUSEqKBAwfq5MmTdqtx0fr167Vjxw4FBATUah9qWmPQoEGVjpe33nrLbuMfPnxY/fr1U9euXbV161Z9+eWXSk1NrfHjx2pS45dzLygo0GuvvSaLxaL77rvPbjWSk5OVkZGhVatWaf/+/Zo0aZLGjRunDRs22KWGYRhKSEjQv/71L7377rvas2ePgoODFRcXV+PPqZp8zk2ePFnvvfee1q1bp23btunEiRO69957a7wPTZqBWuvTp48xduxY288VFRVGQECAkZaW5pB6koz169c7ZOyLiouLDUnGtm3bHFrnxhtvNF599VW7j3vmzBkjNDTUyMzMNPr3729MnDjRbmPPmjXLCA8Pt9t4l5oyZYrRr18/h41flYkTJxo33XSTYbVa7TbmkCFDjIcffrhS27333muMHDnSbjXOnTtnODs7Gxs3bqzU3qtXL+PJJ5+85vEvPdasVqvh7+9vPP/887a206dPG+7u7sZbb711zeP/0pEjRwxJxp49e2o9bk1rXLRr1y5DknH06FGH1SgpKTEkGZs3b7ZrjX//+99Gu3btjH379hnBwcHGiy++WKfxq6uRlJRk3HPPPXUe82rjJyYmGr/97W/tMn51NS51zz33GHfeeadda3Tv3t344x//WKntWo7DS2scOHDAkGTs27fP1lZRUWG0bt3aWL58eZ1qXPo5d/r0acPV1dVYt26drc/+/fsNSUZ2dnadajQlrADW0vnz55WTk6O4uDhbm5OTk+Li4pSdnd2AM7s2JSUlkuSwL8auqKjQmjVrVFZW5pCv2hs7dqyGDBlS6f8Xezp06JACAgLUsWNHjRw5slana65mw4YNioqK0v333682bdrolltu0fLly+02/qXOnz+vVatW6eGHH5bFYrHbuLGxscrKytLBgwclSV988YU+/fRTDR482G41/vOf/6iiouKy1RJPT0+7rspedOTIERUWFlb678rHx0fR0dFN/ni3WCxq0aKFQ8Y/f/68li1bJh8fH4WHh9ttXKvVqgcffFApKSnq3r273ca91NatW9WmTRt16dJFv/vd7/Tdd9/ZZVyr1apNmzapc+fOio+PV5s2bRQdHW33S3x+qaioSJs2bdKYMWPsOm5sbKw2bNig48ePyzAMbdmyRQcPHtTAgQPtMn55ebkkVTrWnZyc5O7uXudj/dLPuZycHF24cKHS8d21a1cFBQU16eO7pgiAtXTq1ClVVFTYvoHkIj8/PxUWFjbQrK6N1WrVpEmT1Ldv30rflGIPe/fu1Q033CB3d3c99thjWr9+vbp162bXGmvWrFFubq7tawDtLTo6Wm+88YYyMjK0ZMkSHTlyRLfddpvOnDljl/H/9a9/acmSJQoNDdWHH36o3/3ud5owYYL+8pe/2GX8S6Wnp+v06dN66KGH7Dru1KlTNXz4cHXt2lWurq665ZZbNGnSJI0cOdJuNby8vBQTE6M//elPOnHihCoqKrRq1SplZ2eroKDAbnUuunhMX0/H+08//aQpU6ZoxIgR8vb2tuvYGzdu1A033CAPDw+9+OKLyszMlK+vr93Gf+655+Ti4qIJEybYbcxLDRo0SG+++aaysrL03HPPadu2bRo8eLAqKiqueezi4mKdPXtWc+bM0aBBg/TRRx9p2LBhuvfee7Vt2zY7zP5yf/nLX+Tl5WX305oLFy5Ut27d1L59e7m5uWnQoEFavHixbr/9druMfzGITZs2TT/88IPOnz+v5557Tv/+97/rdKxX9TlXWFgoNze3y/4h1JSP79poMl8FB8cZO3as9u3b55AVlC5duigvL08lJSX629/+pqSkJG3bts1uIfDYsWOaOHGiMjMza3UNTW38cgWrZ8+eio6OVnBwsN5++227/KvaarUqKipKzz77rCTplltu0b59+7R06VIlJSVd8/iXWrFihQYPHlyn66eu5O2339Zf//pXrV69Wt27d1deXp4mTZqkgIAAu+7HypUr9fDDD6tdu3ZydnZWr169NGLECOXk5NitxvXqwoULeuCBB2QYhpYsWWL38e+44w7l5eXp1KlTWr58uR544AHt3LlTbdq0ueaxc3Jy9NJLLyk3N9euK9eXGj58uO3PYWFh6tmzp2666SZt3bpVAwYMuKaxrVarJOmee+7R5MmTJUkRERHavn27li5dqv79+1/T+FV57bXXNHLkSLv//bhw4ULt2LFDGzZsUHBwsD7++GONHTtWAQEBdjkT4+rq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h4SEaMGCBVfcxmKxKD093aHzakxq8jsBAL4KDkCT9fnnn6t58+Z2HfOpp55Senq68vLy7DouADQmBEAATVbr1q0begoA0CRxChhArVmtVs2dO1edOnWSu7u7goKC9Mwzz9jenzJlijp37qxmzZqpY8eOSk1N1YULF2zvP/XUU4qIiNDKlSsVEhIiHx8fDR8+XGfOnLH1KSsr06hRo3TDDTeobdu2VX6p/aWnOw8dOqTbb79dHh4e6tatmzIzMy/b5kpze+ONNzR79mx98cUXslgsslgseuONNyRJp0+f1iOPPKLWrVvL29tbd955p7744otqf0fffvutLBaL3n77bd12223y9PRU7969dfDgQX3++eeKiorSDTfcoMGDB+vkyZO27T7//HP9+te/lq+vr3x8fNS/f3/l5uba3jcMQ0899ZSCgoLk7u6ugIAATZgwodp5vPrqq2rRooWysrIkSX/7298UFhYmT09PtWrVSnFxcSorK6t2ewDXJ1YAAdTatGnTtHz5cr344ovq16+fCgoK9M0339je9/Ly0htvvKGAgADt3btXjz76qLy8vPTEE0/Y+hw+fFjp6enauHGjfvjhBz3wwAOaM2eOLUimpKRo27Ztevfdd9WmTRtNnz5dubm5ioiIqHJOVqtV9957r/z8/LRz506VlJRUul6wJnNLTEzUvn37lJGRoc2bN0uSfHx8JEn333+/PD099cEHH8jHx0evvPKKBgwYoIMHD6ply5bV/q5mzZqlBQsWKCgoSA8//LD++7//W15eXnrppZfUrFkzPfDAA5o5c6aWLFkiSTpz5oySkpK0cOFCGYahefPm6a677tKhQ4fk5eWlv//973rxxRe1Zs0ade/eXYWFhdUG0blz52ru3Ln66KOP1KdPHxUUFGjEiBGaO3euhg0bpjNnzuiTTz4RXwkPmJABALVQWlpquLu7G8uXL6/xNs8//7wRGRlp+3nWrFlGs2bNjNLSUltbSkqKER0dbRiGYZw5c8Zwc3Mz3n77bdv73333neHp6WlMnDjR1hYcHGy8+OKLhmEYxocffmi4uLgYx48ft73/wQcfGJKM9evX12pu4eHhlfp88sknhre3t/HTTz9Var/pppuMV155pcpxjxw5YkgyXn31VVvbW2+9ZUgysrKybG1paWlGly5dqp1fRUWF4eXlZbz33nuGYRjGvHnzjM6dOxvnz5+vsv/F38kTTzxhtG3b1ti3b5/tvZycHEOS8e2331ZbD4A5sAIIoFb279+v8vJyDRgwoNo+a9eu1csvv6zDhw/r7Nmz+s9//iNvb+9KfUJCQuTl5WX7uW3btiouLpb08+rg+fPnFR0dbXu/ZcuW6tKlyxXnFRgYqICAAFtbTExMneZ2qS+++EJnz55Vq1atKrX/+OOPOnz48BW37dmzp+3Pfn5+kqSwsLBKbRf3W5KKioo0Y8YMbd26VcXFxaqoqNC5c+eUn58v6eeVyAULFqhjx44aNGiQ7rrrLt19991ycfn/v87nzZunsrIy7d69Wx07drS1h4eHa8CAAQoLC1N8fLwGDhyo3/zmN7rxxhuvuA8Arj9cAwigVjw9Pa/4fnZ2tkaOHKm77rpLGzdu1J49e/Tkk0/q/Pnzlfq5urpW+tlischqtdp9vnWZ26XOnj2rtm3bKi8vr9LrwIEDSklJueK2v9xPi8VSZdsv9zspKUl5eXl66aWXtH37duXl5alVq1a2OQYGBurAgQP685//LE9PT/3+97/X7bffXukay9tuu00VFRV6++23K83F2dlZmZmZ+uCDD9StWzctXLhQXbp00ZEjR67ymwNwvSEAAqiV0NBQeXp62m4quNT27dsVHBysJ598UlFRUQoNDdXRo0drVeOmm26Sq6urdu7caWv74YcfdPDgwWq3ufnmm3Xs2DEVFBTY2nbs2FHrubm5uamioqJSW69evVRYWCgXFxd16tSp0svX17dW+3Y1n332mSZMmKC77rpL3bt3l7u7u06dOlWpj6enp+6++269/PLL2rp1q7Kzs7V3717b+3369NEHH3ygZ599Vi+88EKlbS0Wi/r27avZs2drz549cnNz0/r16+26DwAaP04BA6gVDw8PTZkyRU888YTc3NzUt29fnTx5Ul999ZXGjBmj0NBQ5efna82aNerdu7c2bdpU64Bxww03aMyYMUpJSVGrVq3Upk0bPfnkk3Jyqv7frHFxcercubOSkpL0/PPPq7S0VE8++WSlPjWZW0hIiI4cOaK8vDy1b99eXl5eiouLU0xMjBISEjR37lx17txZJ06c0KZNmzRs2DBFRUXVav+uJDQ0VCtXrlRUVJRKS0uVkpJSadX1jTfeUEVFhaKjo9WsWTOtWrVKnp6eCg4OrjRObGys3n//fQ0ePFguLi6aNGmSdu7cqaysLA0cOFBt2rTRzp07dfLkSd188812mz+ApoEVQAC1lpqaqscff1wzZ87UzTffrMTERNt1bEOHDtXkyZM1btw4RUREaPv27UpNTa11jeeff1633Xab7r77bsXFxalfv36KjIystr+Tk5PWr1+vH3/8UX369NEjjzxS6dE0NZ3bfffdp0GDBumOO+5Q69at9dZbb8lisej999/X7bffrtGjR6tz584aPny4jh49aruuz15WrFihH374Qb169dKDDz6oCRMmqE2bNrb3W7RooeXLl6tv377q2bOnNm/erPfee++y6xMlqV+/ftq0aZNmzJihhQsXytvbWx9//LHuuusude7cWTNmzNC8efM0ePBgu+4DgMbPYhjc/w8AAGAmrAACAACYDAEQAADAZAiAAAAAJkMABAAAMBkCIAAAgMkQAAEAAEyGAAgAAGAyBEAAAACTIQACAACYDAEQAADAZAiAAAAAJkMABAAAMJn/A1cXgnhL9+q3AAAAAElFTkSuQmCC", "text/html": [ "\n", "
\n", "
\n", " Figure\n", "
\n", " \n", "
\n", " " ], "text/plain": [ "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "max_amount = max(candidate_amounts)\n", "fig = plt.subplots()\n", "plt.hist(candidate_amounts, range=(1, max_amount), align=\"left\", density=True, bins=range(1, max_amount))#, bins=list(range(20)) + list(range(20, 100, 5)) + list(range(100, max(candidate_amounts), 10)))\n", "plt.xlabel(\"candidate masks\")\n", "plt.ylabel(\"proportion\")\n", "plt.xticks(range(max_amount))\n", "plt.xlim(0, 20);\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 238, "id": "9f22ca9d-bdc2-4ea5-b2bc-249a256bb8ad", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c64139ea7ed94a1ea0e153e7728d34eb", "version_major": 2, "version_minor": 0 }, "image/png": "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XAoICDinPCAgwH0ZtjFycnKUnZ2tpKQkJScna+HChaqtrdWYMWMkSaNHj1bnzp2Vn58vSZo4caIGDx6s+fPna9iwYSooKNDOnTu1fPlyd5u5ubnKysrSLbfcoh/+8IfasGGDXnnlFW3evPnSJg0AANDKefUM4I9+9CNNnDhRhw4dcpcdPHhQkydP1q233trodrKysvT4449r1qxZSkhI0K5du7Rhwwb3gx4VFRU6fPiwu35aWppWrlyp5cuXq1+/fvrrX/+qwsJC9enTx11nxIgRWrZsmebNm6f4+Hg9++yz+tvf/qZBgwY1wcwBAABaL6++B/DAgQMaPny4PvzwQ/fDEgcOHFCfPn308ssv65prrvFW182C9wACAOB7eA+gly8Bx8TEqLS0VK+//ro+/vhjSd88aJGenu7NbgEAAHAeXg2AkuRwOPTjH/9YP/7xj73dFQAAABrB6y+CLioq0k9+8hNdd911uu666/STn/xEr7/+ure7BQAAQAO8GgCffvppDRkyRO3bt9fEiRM1ceJEhYaGaujQoVqyZIk3uwYAAEADvHoJ+NFHH9UTTzyhcePGucsmTJiggQMH6tFHH9UDDzzgze4BAABQD6+eATx+/LiGDBlyTvltt92mmpoab3YNAACABng1AA4fPlxr1qw5p/wf//iHfvKTn3izawAAADTAq5eAe/furTlz5mjz5s3uP8O2detWvfvuu3rwwQf11FNPues29m8DAwAA4NJ49UXQ3bp1a9wgHA59+umn3hqG1/AiaAAAfA8vgvbyGcB9+/Z5s3kAAABcBK+/B/AsY4y8eLIRAAAAjeT1APj8888rPj5eISEhCgkJUd++ffXCCy94u1sAAAA0wKuXgBcsWKC8vDyNGzdOAwcOlCS98847uv/++3X06FFNnjzZm90DAACgHl4NgIsWLdLSpUs1evRod9nw4cN1ww036OGHHyYAAgAAtACvXgI+fPiw0tLSzilPS0vT4cOHvdk1AAAAGuDVANi9e3e99NJL55SvWrVKcXFx3uwaAAAADfDqJeBHHnlEWVlZeuutt9z3AL777rsqKiqqNxgCAADA+7x6BvDOO+/U9u3bFRERocLCQhUWFioiIkLbt2/XiBEjvNk1AAAAGuC1M4BnzpzRf//3fysvL08rVqzwVjcAAAC4QF47AxgQEKC//e1v3moeAAAAF8mrl4AzMzNVWFjozS4AAABwgbz6EEhcXJx++9vf6t1331ViYqKuuOIKj/0TJkzwZvcAAACoh1cD4B//+EeFh4erpKREJSUlHvscDgcBEAAAoAV4NQDu27fP/bMxRtI3wQ8AAAAtx6v3AErfnAXs06ePgoODFRwcrD59+ujZZ5/1drcAAABogFfPAM6aNUsLFizQ+PHjlZqaKkkqLi7W5MmTVVFRod/+9rfe7B4AAAD18GoAXLp0qZ555hmNHDnSXTZ8+HD17dtX48ePJwACAAC0AK9eAj5z5oySkpLOKU9MTNTXX3/tza4vG7FT17X0EAAAwGXGqwHw3nvv1dKlS88pX758uUaNGuXNrgEAANAAr14Clr55COS1117TTTfdJEnatm2bKioqNHr0aOXk5LjrLViwwNtDAQAAgLwcAMvLy9W/f39J0ieffCJJioiIUEREhMrLy931eDUMAABA8/FqANy0aZM3mwcAAMBF8Pp7AAEAANC6EAABAAAsQwAEAACwDAEQAADAMgRAAAAAyxAAAQAALEMABAAAsAwBEAAAwDIEQAAAAMsQAAEAACxDAAQAALAMARAAAMAyBEAAAADLEAABAAAsQwAEAACwDAEQAADAMgRAAAAAyxAAAQAALONTAXDJkiWKjY1VcHCwUlJStH379vPWX716tXr27Kng4GDFx8dr/fr1Dda9//775XA4tHDhwiYeNQAAQOviMwFw1apVysnJ0ezZs1VaWqp+/fopIyND1dXV9dbfsmWLRo4cqbFjx6qsrEyZmZnKzMxUeXn5OXXXrFmjrVu3Kjo62tvTAAAAaHE+EwAXLFig++67T2PGjFHv3r21bNkytW3bVn/605/qrf/kk09qyJAhys3NVa9evfS73/1O/fv31+LFiz3qHTx4UOPHj9df/vIXBQQENMdUAAAAWpRPBMDTp0+rpKRE6enp7jI/Pz+lp6eruLi43mOKi4s96ktSRkaGR32Xy6V7771Xubm5uuGGG7wzeAAAgFbGv6UH0BhHjx5VXV2dIiMjPcojIyP18ccf13tMZWVlvfUrKyvdn//nf/5H/v7+mjBhQqPGcerUKZ06dcr92el0NnYKAAAArYZPnAH0hpKSEj355JN67rnn5HA4GnVMfn6+wsLC3FtMTIyXRwkAAND0fCIARkREqE2bNqqqqvIor6qqUlRUVL3HREVFnbf+22+/rerqanXp0kX+/v7y9/fX/v379eCDDyo2NrbeNqdNm6aamhr3duDAgUufHAAAQDPziQAYGBioxMREFRUVuctcLpeKioqUmppa7zGpqake9SVp48aN7vr33nuv3n//fe3atcu9RUdHKzc3V//85z/rbTMoKEihoaEeGwAAgK/xiXsAJSknJ0fZ2dlKSkpScnKyFi5cqNraWo0ZM0aSNHr0aHXu3Fn5+fmSpIkTJ2rw4MGaP3++hg0bpoKCAu3cuVPLly+XJHXo0EEdOnTw6CMgIEBRUVHq0aNH804OAACgGflMAMzKytKRI0c0a9YsVVZWKiEhQRs2bHA/6FFRUSE/v3+f0ExLS9PKlSs1c+ZMTZ8+XXFxcSosLFSfPn1aagoAAACtgsMYY1p6EL7K6XQqLCxMNTU1XrkcHDt1nSTps7nDmrxtAABs5e3vb1/gE/cA2u5sEAQAAGgKBEAAAADLEAABAAAsQwAEAACwDAEQAADAMgRAAAAAyxAAAQAALEMABAAAsAwBEAAAwDIEQAAAAMsQAAEAACxDAAQAALAMARAAAMAyBEAAAADLEAABAAAsQwAEAACwDAEQAADAMgRAAAAAyxAAAQAALEMABAAAsAwBEAAAwDIEQAAAAMsQAAEAACxDAAQAALAMARAAAMAyBEAAAADLEAABAAAsQwAEAACwDAEQAADAMgRAAAAAyxAAAQAALEMA9BGxU9e19BAAAMBlggAIAABgGQIgAACAZQiAAAAAliEAAgAAWIYACAAAYBkCIAAAgGUIgAAAAJYhAAIAAFiGAAgAAGAZAiAAAIBlCIAAAACWIQACAABYhgAIAABgGQIgAACAZQiAAAAAliEAAgAAWIYACAAAYBkCoA+JnbqupYcAAAAuAwRAAAAAy/hUAFyyZIliY2MVHByslJQUbd++/bz1V69erZ49eyo4OFjx8fFav369e9+ZM2c0ZcoUxcfH64orrlB0dLRGjx6tQ4cOeXsaAAAALcpnAuCqVauUk5Oj2bNnq7S0VP369VNGRoaqq6vrrb9lyxaNHDlSY8eOVVlZmTIzM5WZmany8nJJ0hdffKHS0lLl5eWptLRUf//737V7924NHz68OacFAADQ7BzGGNPSg2iMlJQUDRgwQIsXL5YkuVwuxcTEaPz48Zo6deo59bOyslRbW6u1a9e6y2666SYlJCRo2bJl9faxY8cOJScna//+/erSpcv3jsnpdCosLEw1NTUKDQ29yJk1rL57/j6bO6zJ+wEAwCbe/v72BT5xBvD06dMqKSlRenq6u8zPz0/p6ekqLi6u95ji4mKP+pKUkZHRYH1JqqmpkcPhUHh4eJOMGwAAoDXyiQB49OhR1dXVKTIy0qM8MjJSlZWV9R5TWVl5QfW/+uorTZkyRSNHjmzwvwZOnTolp9PpsbUEngYGAACXwicCoLedOXNGd999t4wxWrp0aYP18vPzFRYW5t5iYmKacZQAAABNwycCYEREhNq0aaOqqiqP8qqqKkVFRdV7TFRUVKPqnw1/+/fv18aNG897L8C0adNUU1Pj3g4cOHCRMwIAAGg5PhEAAwMDlZiYqKKiIneZy+VSUVGRUlNT6z0mNTXVo74kbdy40aP+2fC3d+9evf766+rQocN5xxEUFKTQ0FCPDQAAwNf4t/QAGisnJ0fZ2dlKSkpScnKyFi5cqNraWo0ZM0aSNHr0aHXu3Fn5+fmSpIkTJ2rw4MGaP3++hg0bpoKCAu3cuVPLly+X9E34+/nPf67S0lKtXbtWdXV17vsDr7rqKgUGBrbMRAEAALzMZwJgVlaWjhw5olmzZqmyslIJCQnasGGD+0GPiooK+fn9+4RmWlqaVq5cqZkzZ2r69OmKi4tTYWGh+vTpI0k6ePCgXn75ZUlSQkKCR1+bNm3SD37wg2aZFwAAQHPzmfcAtkYt9R7A2KnreB8gAAAXifcA+sg9gAAAAGg6BEAAAADLEAABAAAsQwAEAACwDAEQAADAMgRAAAAAyxAAfVh9r4kBAAD4PgRAAAAAyxAAAQAALEMA9FFc/gUAABeLAAgAAGAZAiAAAIBlCIA+hku/AADgUhEAAQAALEMABAAAsAwBEAAAwDIEQAAAAMsQAAEAACxDAAQAALAMARAAAMAyBEAAAADLEAB9HC+GBgAAF4oACAAAYBkCIAAAgGUIgAAAAJYhAAIAAFiGAAgAAGAZAiAAAIBlCIAAAACWIQACAABYhgAIAABgGQIgAACAZQiAAAAAliEAAgAAWIYAeBmInbqupYcAAAB8CAEQAADAMgRAAAAAyxAAAQAALEMABAAAsAwBEAAAwDIEwMtIUz8NzNPFAABcngiAAAAAliEAAgAAWIYAeJlqzOXb79bhki8AAHYgAAIAAFiGAAgAAGAZAuBl4uzl2/Ndxm3Mvsa0AwAAfBsBEAAAwDIEQAAAAMsQAC9DF3L59kIv9dZX35dfQM2lbngL/24BaM0IgAAAAJbxqQC4ZMkSxcbGKjg4WCkpKdq+fft5669evVo9e/ZUcHCw4uPjtX79eo/9xhjNmjVLnTp1UkhIiNLT07V3715vTgEAAKDF+UwAXLVqlXJycjR79myVlpaqX79+ysjIUHV1db31t2zZopEjR2rs2LEqKytTZmamMjMzVV5e7q4zb948PfXUU1q2bJm2bdumK664QhkZGfrqq6+aa1peFTt1nXv7btmFtPHtf3637Hz7v/vzhYzhfE8lN3Ts+S5PX+i+73Oh69iY9s73uaF9jVmfi30y/FJw+RMAWjefCYALFizQfffdpzFjxqh3795atmyZ2rZtqz/96U/11n/yySc1ZMgQ5ebmqlevXvrd736n/v37a/HixZK+Ofu3cOFCzZw5Uz/96U/Vt29fPf/88zp06JAKCwubcWYAAADNy7+lB9AYp0+fVklJiaZNm+Yu8/PzU3p6uoqLi+s9pri4WDk5OR5lGRkZ7nC3b98+VVZWKj093b0/LCxMKSkpKi4u1j333HNOm6dOndKpU6fcn2tqaiRJTqfzoud2Pq5TXzRZW06n06O9b38+375vfz7fMWfLzo67vp+/O6+G1u27fXz7nw21eb6+vnv8d/s531ga8n1zuFDfHV9Dc/zuvsasT2PbakreateXsAZA63X2/5vGmBYeSQsyPuDgwYNGktmyZYtHeW5urklOTq73mICAALNy5UqPsiVLlpiOHTsaY4x59913jSRz6NAhjzp33XWXufvuu+ttc/bs2UYSGxsbGxsb22WwHThw4GKjic/ziTOArcW0adM8ziq6XC4dO3ZMHTp0kMPhaLJ+nE6nYmJidODAAYWGhjZZu2gYa978WPPmx5o3L9a7+TV2zY0xOnHihKKjo5txdK2LTwTAiIgItWnTRlVVVR7lVVVVioqKqveYqKio89Y/+8+qqip16tTJo05CQkK9bQYFBSkoKMijLDw8/EKmckFCQ0P5pdHMWPPmx5o3P9a8ebHeza8xax4WFtZMo2mdfOIhkMDAQCUmJqqoqMhd5nK5VFRUpNTU1HqPSU1N9agvSRs3bnTX79atm6KiojzqOJ1Obdu2rcE2AQAALgc+cQZQknJycpSdna2kpCQlJydr4cKFqq2t1ZgxYyRJo0ePVufOnZWfny9JmjhxogYPHqz58+dr2LBhKigo0M6dO7V8+XJJksPh0KRJk/T73/9ecXFx6tatm/Ly8hQdHa3MzMyWmiYAAIDX+UwAzMrK0pEjRzRr1ixVVlYqISFBGzZsUGRkpCSpoqJCfn7/PqGZlpamlStXaubMmZo+fbri4uJUWFioPn36uOs89NBDqq2t1a9+9SsdP35cgwYN0oYNGxQcHNzs8/u2oKAgzZ49+5zLzfAe1rz5sebNjzVvXqx382PNG89hjM3PQAMAANjHJ+4BBAAAQNMhAAIAAFiGAAgAAGAZAiAAAIBlCICt0JIlSxQbG6vg4GClpKRo+/btLT0kn5Sfn68BAwaoffv26tixozIzM7V7926POl999ZUeeOABdejQQe3atdOdd955zgvEKyoqNGzYMLVt21YdO3ZUbm6uvv766+acik+aO3eu+3VLZ7HeTe/gwYP6z//8T3Xo0EEhISGKj4/Xzp073fuNMZo1a5Y6deqkkJAQpaena+/evR5tHDt2TKNGjVJoaKjCw8M1duxYnTx5srmn4hPq6uqUl5enbt26KSQkRNddd51+97vfefxNWdb80rz11lu64447FB0dLYfDocLCQo/9TbW+77//vm6++WYFBwcrJiZG8+bN8/bUWpcW/DN0qEdBQYEJDAw0f/rTn8yHH35o7rvvPhMeHm6qqqpaemg+JyMjw/z5z3825eXlZteuXWbo0KGmS5cu5uTJk+46999/v4mJiTFFRUVm586d5qabbjJpaWnu/V9//bXp06ePSU9PN2VlZWb9+vUmIiLCTJs2rSWm5DO2b99uYmNjTd++fc3EiRPd5ax30zp27Jjp2rWr+a//+i+zbds28+mnn5p//vOf5n//93/ddebOnWvCwsJMYWGhee+998zw4cNNt27dzJdffumuM2TIENOvXz+zdetW8/bbb5vu3bubkSNHtsSUWr05c+aYDh06mLVr15p9+/aZ1atXm3bt2pknn3zSXYc1vzTr1683M2bMMH//+9+NJLNmzRqP/U2xvjU1NSYyMtKMGjXKlJeXmxdffNGEhISYP/zhD801zRZHAGxlkpOTzQMPPOD+XFdXZ6Kjo01+fn4LjuryUF1dbSSZN9980xhjzPHjx01AQIBZvXq1u85HH31kJJni4mJjzDe/iPz8/ExlZaW7ztKlS01oaKg5depU807AR5w4ccLExcWZjRs3msGDB7sDIOvd9KZMmWIGDRrU4H6Xy2WioqLMY4895i47fvy4CQoKMi+++KIxxph//etfRpLZsWOHu86rr75qHA6HOXjwoPcG76OGDRtmfvGLX3iU/exnPzOjRo0yxrDmTe27AbCp1vfpp582V155pcfvlSlTppgePXp4eUatB5eAW5HTp0+rpKRE6enp7jI/Pz+lp6eruLi4BUd2eaipqZEkXXXVVZKkkpISnTlzxmO9e/bsqS5durjXu7i4WPHx8e4XjktSRkaGnE6nPvzww2Ycve944IEHNGzYMI91lVhvb3j55ZeVlJSku+66Sx07dtSNN96oZ555xr1/3759qqys9FjzsLAwpaSkeKx5eHi4kpKS3HXS09Pl5+enbdu2Nd9kfERaWpqKioq0Z88eSdJ7772nd955R7fffrsk1tzbmmp9i4uLdcsttygwMNBdJyMjQ7t379bnn3/eTLNpWT7zl0BscPToUdXV1Xl8+UlSZGSkPv744xYa1eXB5XJp0qRJGjhwoPuvwVRWViowMFDh4eEedSMjI1VZWemuU9//Hmf3wVNBQYFKS0u1Y8eOc/ax3k3v008/1dKlS5WTk6Pp06drx44dmjBhggIDA5Wdne1es/rW9Ntr3rFjR4/9/v7+uuqqq1jzekydOlVOp1M9e/ZUmzZtVFdXpzlz5mjUqFGSxJp7WVOtb2Vlpbp163ZOG2f3XXnllV4Zf2tCAIQVHnjgAZWXl+udd95p6aFctg4cOKCJEydq48aNLf7nFG3hcrmUlJSkRx99VJJ04403qry8XMuWLVN2dnYLj+7y9NJLL+kvf/mLVq5cqRtuuEG7du3SpEmTFB0dzZrDp3AJuBWJiIhQmzZtznkqsqqqSlFRUS00Kt83btw4rV27Vps2bdI111zjLo+KitLp06d1/Phxj/rfXu+oqKh6//c4uw//VlJSourqavXv31/+/v7y9/fXm2++qaeeekr+/v6KjIxkvZtYp06d1Lt3b4+yXr16qaKiQtK/1+x8v1OioqJUXV3tsf/rr7/WsWPHWPN65ObmaurUqbrnnnsUHx+ve++9V5MnT1Z+fr4k1tzbmmp9+V1DAGxVAgMDlZiYqKKiIneZy+VSUVGRUlNTW3BkvskYo3HjxmnNmjV64403zjndn5iYqICAAI/13r17tyoqKtzrnZqaqg8++MDjl8nGjRsVGhp6zhev7W699VZ98MEH2rVrl3tLSkrSqFGj3D+z3k1r4MCB57zaaM+ePerataskqVu3boqKivJYc6fTqW3btnms+fHjx1VSUuKu88Ybb8jlciklJaUZZuFbvvjiC/n5eX51tmnTRi6XSxJr7m1Ntb6pqal66623dObMGXedjRs3qkePHlZc/pXEa2Bam4KCAhMUFGSee+45869//cv86le/MuHh4R5PRaJxfv3rX5uwsDCzefNmc/jwYff2xRdfuOvcf//9pkuXLuaNN94wO3fuNKmpqSY1NdW9/+xrSW677Taza9cus2HDBnP11VfzWpJG+vZTwMaw3k1t+/btxt/f38yZM8fs3bvX/OUvfzFt27Y1K1ascNeZO3euCQ8PN//4xz/M+++/b37605/W+8qMG2+80Wzbts288847Ji4ujleSNCA7O9t07tzZ/RqYv//97yYiIsI89NBD7jqs+aU5ceKEKSsrM2VlZUaSWbBggSkrKzP79+83xjTN+h4/ftxERkaae++915SXl5uCggLTtm1bXgODlrVo0SLTpUsXExgYaJKTk83WrVtbekg+SVK925///Gd3nS+//NL85je/MVdeeaVp27atGTFihDl8+LBHO5999pm5/fbbTUhIiImIiDAPPvigOXPmTDPPxjd9NwCy3k3vlVdeMX369DFBQUGmZ8+eZvny5R77XS6XycvLM5GRkSYoKMjceuutZvfu3R51/u///s+MHDnStGvXzoSGhpoxY8aYEydONOc0fIbT6TQTJ040Xbp0McHBwebaa681M2bM8HidCGt+aTZt2lTv7+7s7GxjTNOt73vvvWcGDRpkgoKCTOfOnc3cuXOba4qtgsOYb72+HAAAAJc97gEEAACwDAEQAADAMgRAAAAAyxAAAQAALEMABAAAsAwBEAAAwDIEQAAAAMsQAAH4jB/84AeaNGmS+3NsbKwWLlx43mMcDocKCwu9Oq7WpDFrAgD+LT0AALhYO3bs0BVXXNGkbT788MMqLCzUrl27mrRdAGhNCIAAfNbVV1/d0kMAAJ/EJWAAF8zlcmnevHnq3r27goKC1KVLF82ZM8e9f8qUKbr++uvVtm1bXXvttcrLy9OZM2fc+x9++GElJCTohRdeUGxsrMLCwnTPPffoxIkT7jq1tbUaPXq02rVrp06dOmn+/PnnjOO7lzv37t2rW265RcHBwerdu7c2btx4zjHnG9tzzz2nRx55RO+9954cDoccDoeee+45SdLx48f1y1/+UldffbVCQ0P1ox/9SO+9916Da/TZZ5/J4XDopZde0s0336yQkBANGDBAe/bs0Y4dO5SUlKR27drp9ttv15EjR9zH7dixQz/+8Y8VERGhsLAwDR48WKWlpe79xhg9/PDD6tKli4KCghQdHa0JEyY0OI5nn31W4eHhKioqkiT99a9/VXx8vEJCQtShQwelp6ertra2weMBXJ44Awjggk2bNk3PPPOMnnjiCQ0aNEiHDx/Wxx9/7N7fvn17Pffcc4qOjtYHH3yg++67T+3bt9dDDz3krvPJJ5+osLBQa9eu1eeff667775bc+fOdQfJ3Nxcvfnmm/rHP/6hjh07avr06SotLVVCQkK9Y3K5XPrZz36myMhIbdu2TTU1NR73CzZmbFlZWSovL9eGDRv0+uuvS5LCwsIkSXfddZdCQkL06quvKiwsTH/4wx906623as+ePbrqqqsaXKvZs2dr4cKF6tKli37xi1/oP/7jP9S+fXs9+eSTatu2re6++27NmjVLS5culSSdOHFC2dnZWrRokYwxmj9/voYOHaq9e/eqffv2+tvf/qYnnnhCBQUFuuGGG1RZWdlgEJ03b57mzZun1157TcnJyTp8+LBGjhypefPmacSIETpx4oTefvtt8SfhAQsZALgATqfTBAUFmWeeeabRxzz22GMmMTHR/Xn27Nmmbdu2xul0ustyc3NNSkqKMcaYEydOmMDAQPPSSy+59//f//2fCQkJMRMnTnSXde3a1TzxxBPGGGP++c9/Gn9/f3Pw4EH3/ldffdVIMmvWrLmgsfXr18+jzttvv21CQ0PNV1995VF+3XXXmT/84Q/1trtv3z4jyTz77LPushdffNFIMkVFRe6y/Px806NHjwbHV1dXZ9q3b29eeeUVY4wx8+fPN9dff705ffp0vfXPrslDDz1kOnXqZMrLy937SkpKjCTz2WefNdgfADtwBhDABfnoo4906tQp3XrrrQ3WWbVqlZ566il98sknOnnypL7++muFhoZ61ImNjVX79u3dnzt16qTq6mpJ35wdPH36tFJSUtz7r7rqKvXo0eO844qJiVF0dLS7LDU19aLG9l3vvfeeTp48qQ4dOniUf/nll/rkk0/Oe2zfvn3dP0dGRkqS4uPjPcrOzluSqqqqNHPmTG3evFnV1dWqq6vTF198oYqKCknfnIlcuHChrr32Wg0ZMkRDhw7VHXfcIX//f/86nz9/vmpra7Vz505de+217vJ+/frp1ltvVXx8vDIyMnTbbbfp5z//ua688srzzgHA5Yd7AAFckJCQkPPuLy4u1qhRozR06FCtXbtWZWVlmjFjhk6fPu1RLyAgwOOzw+GQy+Vq8vFezNi+6+TJk+rUqZN27drlse3evVu5ubnnPfbb83Q4HPWWfXve2dnZ2rVrl5588klt2bJFu3btUocOHdxjjImJ0e7du/X0008rJCREv/nNb3TLLbd43GN58803q66uTi+99JLHWNq0aaONGzfq1VdfVe/evbVo0SL16NFD+/bt+56VA3C5IQACuCBxcXEKCQlxP1TwXVu2bFHXrl01Y8YMJSUlKS4uTvv377+gPq677joFBARo27Zt7rLPP/9ce/bsafCYXr166cCBAzp8+LC7bOvWrRc8tsDAQNXV1XmU9e/fX5WVlfL391f37t09toiIiAua2/d59913NWHCBA0dOlQ33HCDgoKCdPToUY86ISEhuuOOO/TUU09p8+bNKi4u1gcffODen5ycrFdffVWPPvqoHn/8cY9jHQ6HBg4cqEceeURlZWUKDAzUmjVrmnQOAFo/LgEDuCDBwcGaMmWKHnroIQUGBmrgwIE6cuSIPvzwQ40dO1ZxcXGqqKhQQUGBBgwYoHXr1l1wwGjXrp3Gjh2r3NxcdejQQR07dtSMGTPk59fwf7Omp6fr+uuvV3Z2th577DE5nU7NmDHDo05jxhYbG6t9+/Zp165duuaaa9S+fXulp6crNTVVmZmZmjdvnq6//nodOnRI69at04gRI5SUlHRB8zufuLg4vfDCC0pKSpLT6VRubq7HWdfnnntOdXV1SklJUdu2bbVixQqFhISoa9euHu2kpaVp/fr1uv322+Xv769JkyZp27ZtKioq0m233aaOHTtq27ZtOnLkiHr16tVk4wfgGzgDCOCC5eXl6cEHH9SsWbPUq1cvZWVlue9jGz58uCZPnqxx48YpISFBW7ZsUV5e3gX38dhjj+nmm2/WHXfcofT0dA0aNEiJiYkN1vfz89OaNWv05ZdfKjk5Wb/85S89Xk3T2LHdeeedGjJkiH74wx/q6quv1osvviiHw6H169frlltu0ZgxY3T99dfrnnvu0f79+9339TWVP/7xj/r888/Vv39/3XvvvZowYYI6duzo3h8eHq5nnnlGAwcOVN++ffX666/rlVdeOef+REkaNGiQ1q1bp5kzZ2rRokUKDQ3VW2+9paFDh+r666/XzJkzNX/+fN1+++1NOgcArZ/DGJ7/BwAAsAlnAAEAACxDAAQAALAMARAAAMAyBEAAAADLEAABAAAsQwAEAACwDAEQAADAMgRAAAAAyxAAAQAALEMABAAAsAwBEAAAwDIEQAAAAMv8fwxUUx3rxqQ1AAAAAElFTkSuQmCC", "text/html": [ "\n", "
\n", "
\n", " Figure\n", "
\n", " \n", "
\n", " " ], "text/plain": [ "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "max_amount = max(candidate_amounts)\n", "fig = plt.subplots()\n", "plt.hist(candidate_amounts, range=(1, max_amount), align=\"left\", density=True, bins=range(1, max_amount))#, bins=list(range(20)) + list(range(20, 100, 5)) + list(range(100, max(candidate_amounts), 10)))\n", "plt.xlabel(\"candidate masks\")\n", "plt.ylabel(\"proportion\")\n", "plt.show()" ] } ], "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.3" } }, "nbformat": 4, "nbformat_minor": 5 }