2024-11-25 19:28:53 +04:00

648 lines
122 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "3f485372-2531-4a49-8d15-5b26e9018a6a",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from dataclasses import dataclass\n",
"from pyvis.network import Network\n",
"from pyvis.options import Layout\n",
"import networkx as nx"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "8ea18f7d-34a8-4de8-b18f-e93329825840",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class Block:\n",
" id: int\n",
" t: float\n",
" height: int\n",
" parent: int\n",
" leader: int"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "cabf7946-8382-4102-b730-d74ed42ceb38",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class NetworkParams:\n",
" mixnet_delay_mean: int # seconds\n",
" mixnet_delay_var: int\n",
" broadcast_delay_mean: int # second\n",
" pol_proof_time: int # seconds\n",
"\n",
" def sample_mixnet_delay(self):\n",
" scale = self.mixnet_delay_var / self.mixnet_delay_mean\n",
" shape = self.mixnet_delay_mean / scale\n",
" return np.random.gamma(shape=shape, scale=scale)\n",
" \n",
" def sample_broadcast_delay(self, blocks):\n",
" return np.random.exponential(self.broadcast_delay_mean, size=blocks.shape)\n",
"\n",
" def block_arrival_time(self, block_time):\n",
" return self.pol_proof_time + self.sample_mixnet_delay() + self.sample_broadcast_delay(block_time) + block_time"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "4e9df29f-fb4a-4dfb-a7b4-b8b4b0a6e6b7",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class Params:\n",
" TIME: int\n",
" MEAN_BLOCK_TIME: int\n",
" honest_hash_power: np.array\n",
" adversary_control: float\n",
"\n",
" @property\n",
" def N(self):\n",
" return len(self.hash_power)\n",
"\n",
" @property\n",
" def hash_power(self):\n",
" return np.append(self.honest_hash_power, self.honest_hash_power.sum() / (1/self.adversary_control - 1))\n",
" \n",
" @property\n",
" def relative_hash_power(self):\n",
" return self.hash_power / self.hash_power.sum()\n",
"\n",
" def next_block(self):\n",
" return np.random.exponential(self.MEAN_BLOCK_TIME / self.relative_hash_power)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "5616a037-ef12-44a0-915f-ca64e774c549",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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rOaZZ57RLbfcIqfTqVtuuUUtLS2XfdxUkYz3s6qqSg6HI2LxeDwx7Vcyxfo9PXr0qO68805NmTJFDodDtbW1MTkuAKQUy2DNzc1WRkaGtWvXLuvYsWPWww8/bGVlZVnvvPPOiPVvv/22NWHCBOvhhx+2jh07Zu3atcvKyMiw/v3f/92uaW9vt9LS0qzq6mrrzTfftKqrq6309HTr4MGDoz5uqkjW+/nII49Yt956q9XT02Mvvb29ce9vIsTjPX3ttdesdevWWT//+c8tj8djbdu27bKPi9Tw3e9+17rqqqusO++8M9lNiYtQKGRNnz7dKikpsYqKiqydO3cmu0lxkZaWZpWUlFglJSXW8uXLk92cmPvf//1fu38lJSXWuHHjrJaWlpgfx+iA8td//dfWQw89FLHupptusjZs2DBi/fr1662bbropYt2KFSus2267zX69ePFi62//9m8jaubNm2fdc889oz5uqkjW+/nII49YJSUll9l6M8XjPf1z11133YgBZaz+jl7pfvvb31rPPffcmA0oH3/8sXX69GnLsizr9OnTVkFBgfX+++8nuVWxl5eXl+wmJEx/f7+Vl5dnDQwMxHzfxp7iOf9o9YqKioj1n/do9f/8z/8cVj9v3jy9/vrrOnv27OfWnN/naI6bCpL1fp53/Phxeb1eFRQU6J577tHbb799uV1Kuni9p/E4LlLDt771LWVnZye7GXGTlpamCRMmSJI++ugjnTt3Tha34kppzz33nObMmaOsrKyY79vYgDKaR6sHAoER6z/++GO9//77n1tzfp+jOW4qSNb7KUkzZszQ008/rZdeekm7du1SIBBQWVmZPvjgg1h0LWni9Z7G47iIv1deeUV33HGHvF6vHA6Hnn322WE1qX7dUCz6+OGHH6qkpETXXHON1q9fr/z8/AS1/tLEoo+hUEilpaX6xje+oba2tgS1/NLF8nf1X//1X3X33XfHpZ3GBpTzonm0+sXqL1x/KfuM9ripIhnvZ2Vlpe68804VFxervLxce/fulSQ1NDSMrhOGicd7Go/jIr5Onz6tkpISbd++fcTtv/jFL+T3+7Vp0yYdOnRIt99+uyorK3Xy5MkEt3T0YtHHq666Sv/93/+trq4uNTU16b333ktU8y9JLPp44sQJdXZ26mc/+5n+/u//XqFQKFHNvySx+l0NhUJ69dVX9e1vfzsu7TQ2oIzm0eoej2fE+vT0dOXl5X1uzfl9jua4qSBZ7+dIsrKyVFxcrOPHj4+mK8aI13saj+Mi/iorK/XYY49p0aJFI27funWrli9frgcffFA333yzamtr5fP5tGPHjgS3dPRi2Ue3262pU6fqlVdeiXezoxKLPnq9XklSUVGRbrnlFv3hD39ISNsvVaz+H3/1q19p3rx5GjduXFzaaWxAGc2j1WfOnDmsft++fZo+fboyMjI+t+b8PsfqI92T9X6OJBwO680339TkyZNH0xVjxOs9jcdxkVxXwnVDl9LH9957z55NCIVCeuWVV3TjjTcmvK2jdSl97OvrUzgcliSdOnVKx44d0/XXX5/wto5WNL+r8Ty9Iyk1vmZcV1dnHTt2zPL7/VZWVpZ14sQJy7Isa8OGDdb9999v15//CucPfvAD69ixY1ZdXd2wr3C++uqrVlpamrV582brzTfftDZv3nzRrxlf7LipKlnv59q1a62XX37Zevvtt62DBw9aCxYssLKzs1P+/bSs+Lyn4XDYOnTokHXo0CFr8uTJ1rp166xDhw5Zx48fv+TjIrkkRXzt8k9/+pMlyXr11Vcj6n7yk59YN9xwg/26oqLCys/Pt8aPH2/95V/+pfXaa68lqslRG00fX3/9daukpMSaOnWqVVxcbD311FOJbHLURtPHV1991SoqKrKmTp1qlZSUxOXrt7E02t/VDz/80Jo0aZIVDofj17a47TlG/uVf/sW67rrrrMzMTOurX/2q1dbWZm9bunSpNWvWrIj6l19+2Zo2bZqVmZlpTZkyxdqxY8ewff7bv/2bdeONN1oZGRnWTTfdZD3zzDNRHTeVJeP9vPvuu63JkydbGRkZltfrtRYtWmQdPXo0Lv1Lhli/p11dXZakYcuF+xmrv6NjwcUG/fb29oi6xx57zLrxxhsT3LrYoI+foY9xaltCjwYAV4ALB/1wOGylpaVZv/zlLyPqvv/971vf/OY3E9y62KCPn6GP8WHsNSgAMFZcCdcN0Uf6GGvpCT0aAIxRAwMD+uMf/2i/7urq0htvvKHc3Fxde+21WrNmje6//35Nnz5dM2fO1M6dO3Xy5Ek99NBDSWx1dOgjfUyohM7XAMAY9bvf/W7Ea4eWLl1q16T6dUP08VP0MTEclsV9hgEAgFm4BgUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4/w/rIolILqRbDwAAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 640x480 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"np.random.seed(0)\n",
"\n",
"params = Params(\n",
" TIME=60 * 60, # seconds\n",
" MEAN_BLOCK_TIME=10*60,\n",
" honest_hash_power = np.random.pareto(10, size=1000),\n",
" adversary_control=0.001,\n",
")\n",
"ax = plt.subplot(121)\n",
"ax.hist(params.relative_hash_power)\n",
"ax = plt.subplot(122)\n",
"next_block_times = params.next_block()\n",
"ax.hist(next_block_times / 60, bins=1000)\n",
"ax.set_xscale(\"log\")\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "48c54c25-c7b4-47f9-a00a-5f10997cf185",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABjUAAAMWCAYAAAC5gwQ2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAADKsklEQVR4nOzdf3zP9f7/8fvbftnY3mzsV82mYn5sJIrhhDC/RlGtWg0ROgoLiTqFc2TooHM4JDlUSKdTzlFqmWIlPxsrJKnjZ20mzXuIbbbX948+Xt/e9sPGtvfe2+16ubwuF6/n6/F6vR/P1969ez3fj/fz9bIYhmEIAAAAAAAAAACgiqvl6AQAAAAAAAAAAABKg6IGAAAAAAAAAABwChQ1AAAAAAAAAACAU6CoAQAAAAAAAAAAnAJFDQAAAAAAAAAA4BQoagAAAAAAAAAAAKdAUQMAAAAAAAAAADgFihoAAAAAAAAAAMApUNQAAAAAAAAAAABOgaIGAKCQI0eOyGKxaMWKFY5ORZK0aNGicsnFYrFo2rRpZd6vqp0PAAAA1BzTpk2TxWLRzz//fNXYrl27qmvXrhWWS1hYmGJiYip938q2YsUKWSwWHTlyxGE5rF69Wi+//HK5HvN63h9hYWEaOnRoueYDANeKogYAoJCgoCBt27ZN/fr1c3QqksqvqAEAAAAAzqAiihoAUF24OjoBAEDV4+HhoQ4dOjg6DQAAAABQXl6eLBaLXF35GgsAwEwNAKiWLk9R//rrr3X//ffLarXK19dX48eP16VLl3Tw4EH17t1b3t7eCgsL05w5c+z2v/J2SxcvXlSbNm10yy23yGazmXEZGRkKDAxU165dlZ+fL0kaOnSo6tatq++//159+/ZV3bp1FRISogkTJignJ8fudXJzczVjxgw1a9ZMHh4eatiwoR599FGdOnXKjAkLC9P+/fuVkpIii8Uii8WisLCwEvufnZ2tESNGyM/PT3Xr1lXv3r313XffFRl76NAhxcXFyd/fXx4eHmrevLn+8Y9/XPUcf//993r00UfVpEkTeXl56YYbblD//v21d+9eM+bcuXOqV6+eRo0aVWj/I0eOyMXFRS+99NJVXwsAAAA4fvy4Bg0aJB8fH1mtVj3yyCN2183F+eWXXzR69GjdcMMNcnd310033aTnnnuu0LV5QUGBFixYoFtvvVWenp6qV6+eOnTooHXr1pV4/EWLFsnV1VVTp04tVT/Wrl2rVq1aqXbt2rrpppv097//3W775s2bZbFY9Oabb2rChAm64YYb5OHhoe+//16S9M9//lOtW7dW7dq15evrq4EDB+rAgQN2x/jyyy/14IMPKiwsTJ6engoLC9NDDz2ko0ePFspn+/bt6tSpk2rXrq3g4GBNmTJFeXl5Rea+evVqRUVFqW7duqpbt65uvfVWLVu2zNyenJysu+++WzfeeKNq166tW265RaNGjSp067BTp05p5MiRCgkJMcdBnTp10saNGyX9dpuo9evX6+jRo+YYyGKxlOr8SpJhGJozZ45CQ0NVu3Zt3Xbbbfroo4+KjM3OztbEiRPVuHFjubu764YbblBCQoLOnz9f4mtcvHhREyZM0K233mqON6OiovTf//7XLq579+5q1qyZDMMolOMtt9xSZe4OAMC5UOIGgGosNjZWjzzyiEaNGqXk5GTNmTNHeXl52rhxo0aPHq2JEydq9erVeuaZZ3TLLbdo0KBBRR6ndu3a+te//qW2bdtq2LBhevfdd1VQUKCHH35YhmHorbfekouLixmfl5enAQMGaPjw4ZowYYI+++wz/eUvf5HVatULL7wg6bdB0913363PP/9ckyZNUseOHXX06FFNnTpVXbt21ZdffilPT0+tXbtW9913n6xWqxYtWiTpt5kkxTEMQ/fcc4+2bt2qF154Qbfffru++OIL9enTp1DsN998o44dO6pRo0aaO3euAgMD9fHHH2vs2LH6+eefSxyY/fTTT/Lz89OsWbPUsGFD/fLLL3r99dfVvn177dmzR+Hh4apbt66GDRumV199VXPmzJHVajX3X7Rokdzd3TVs2LCS/4gAAACApIEDByo2NlaPP/649u/fr+eff17ffPONduzYITc3tyL3uXjxorp166YffvhB06dPV6tWrfT5558rMTFRaWlpWr9+vRk7dOhQrVy5UsOHD9ef//xnubu7a/fu3cU+V8IwDD399NP6+9//rtdee61Uz1tIS0tTQkKCpk2bpsDAQK1atUrjxo1Tbm6uJk6caBc7ZcoURUVF6ZVXXlGtWrXk7++vxMREPfvss3rooYeUmJio06dPa9q0aYqKitKuXbvUpEkTSb/9gCg8PFwPPvigfH19lZ6ersWLF+v222/XN998owYNGkj6bTzQvXt3hYWFacWKFfLy8tKiRYu0evXqQrm/8MIL+stf/qJBgwZpwoQJslqt2rdvn12h5IcfflBUVJQee+wxWa1WHTlyRPPmzVPnzp21d+9e8+8UHx+v3bt368UXX1TTpk115swZ7d69W6dPn5b021hh5MiR+uGHH7R27dqrntcrTZ8+XdOnT9fw4cN133336fjx4xoxYoTy8/MVHh5uxv3666/q0qWLTpw4oWeffVatWrXS/v379cILL2jv3r3auHFjscWUnJwc/fLLL5o4caJuuOEG5ebmauPGjRo0aJCWL1+uwYMHS5LGjRunu+++W5988ol69Ohh7v/RRx/phx9+KFTUAoBSMQAA1c7UqVMNScbcuXPt2m+99VZDkvHee++ZbXl5eUbDhg2NQYMGmW2HDx82JBnLly+32//tt982JBkvv/yy8cILLxi1atUyNmzYYBczZMgQQ5Lxr3/9y669b9++Rnh4uLn+1ltvGZKMd9991y5u165dhiRj0aJFZlvLli2NLl26lKrvH330kSHJ+Nvf/mbX/uKLLxqSjKlTp5ptvXr1Mm688UbDZrPZxT755JNG7dq1jV9++aXE8/F7ly5dMnJzc40mTZoYTz31lNn+ww8/GLVq1TLmz59vtl24cMHw8/MzHn300VL1CQAAADXX5Wv7319jGoZhrFq1ypBkrFy50mzr0qWL3XXzK6+8UuS1+ezZsw1J5rX8Z599ZkgynnvuuRJzCQ0NNfr162f8+uuvxr333mtYrVZj48aNpepHaGioYbFYjLS0NLv2nj17Gj4+Psb58+cNwzCMTZs2GZKMO++80y4uKyvL8PT0NPr27WvXfuzYMcPDw8OIi4sr9rUvXbpknDt3zqhTp47dOOGBBx4wPD09jYyMDLvYZs2aGZKMw4cPG4ZhGP/73/8MFxcX4+GHHy5VXw3DMAoKCoy8vDzj6NGjhiTjv//9r7mtbt26RkJCQon79+vXzwgNDS31612WlZVl1K5d2xg4cKBd+xdffGFIsnt/JCYmGrVq1TJ27dplF/vvf//bkGR8+OGHZltoaKgxZMiQYl/30qVLRl5enjF8+HCjTZs2Znt+fr5x0003GXfffbddfJ8+fYybb77ZKCgoKHMfAYDbTwFANRYTE2O33rx5c1ksFrtZC66urrrllluKnIp9pdjYWP3xj3/U008/rRkzZujZZ59Vz549C8VZLBb179/frq1Vq1Z2r/HBBx+oXr166t+/vy5dumQut956qwIDA7V58+Yy9vY3mzZtkiQ9/PDDdu1xcXF26xcvXtQnn3yigQMHysvLyy6Hvn376uLFi9q+fXuxr3Pp0iXNnDlTLVq0kLu7u1xdXeXu7q5Dhw7ZTX+/6aabFBMTo0WLFplTrlevXq3Tp0/rySefvKY+AgAAoOa58vo2NjZWrq6u5vVvUT799FPVqVNH9913n1375VkVn3zyiSSZtyZ64oknrprH6dOnddddd2nnzp3asmWLunfvXuo+tGzZUq1bt7Zri4uLU3Z2tnbv3m3Xfu+999qtb9u2TRcuXCg0IyQkJER33XWX2Rfpt9vAXp6N7urqKldXV9WtW1fnz5+3u1bftGmTunfvroCAALPNxcVFDzzwgN1rJCcnKz8//6rnJzMzU48//rhCQkLk6uoqNzc3hYaGSpLd695xxx1asWKFZsyYoe3btxd7u6trsW3bNl28eLHQ+6Vjx45mLpd98MEHioiI0K233mo3HurVq5csFstVx2TvvPOOOnXqpLp165r9XbZsmV1fa9WqpSeffFIffPCBjh07Jum3GS1JSUkaPXp0mW6rBQCXUdQAgGrM19fXbt3d3V1eXl6qXbt2ofaLFy+W6pjDhg1TXl6eXF1dNXbs2CJjinoNDw8Pu9c4efKkzpw5I3d3d7m5udktGRkZhe47W1qnT5+Wq6ur/Pz87NoDAwMLxV26dEkLFiwo9Pp9+/aVpBJzGD9+vJ5//nndc889ev/997Vjxw7t2rVLrVu31oULF+xix40bp0OHDik5OVmS9I9//ENRUVG67bbbrqmPAAAAqHmuvJ69fM17+ZZFRTl9+rQCAwMLfXHs7+8vV1dXc99Tp07JxcWl0GsU5bvvvtOOHTvUp08fRUREXFcfft92ZT+CgoIK9aWodkkKDg622z8uLk4LFy7UY489po8//lg7d+7Url271LBhQ7tr9cvn52p5Xn52yY033lhs3woKChQdHa333ntPkyZN0ieffKKdO3eaP5T6/eu+/fbbGjJkiF577TVFRUXJ19dXgwcPVkZGRrHHL63L56E0/Tp58qS+/vrrQuMhb29vGYZR4njovffeU2xsrG644QatXLlS27Zt065duzRs2LBCY8thw4bJ09NTr7zyiqTfxkOenp7cihfANeOZGgCAUjt//rzi4+PVtGlTnTx5Uo899lihB8GVVoMGDeTn56ekpKQit3t7e1/Tcf38/HTp0iWdPn3arrBx5QChfv36cnFxUXx8fLG/uGrcuHGxr7Ny5UoNHjxYM2fOtGv/+eefVa9ePbu2u+66SxEREVq4cKHq1q2r3bt3a+XKlWXsGQAAAGqyjIwM3XDDDeZ6Ude8V/Lz89OOHTtkGIZdYSMzM1OXLl0yny3RsGFD5efnKyMjo8iiwe9FRUXp/vvv1/DhwyVJixcvVq1apfvNbFFf2l9uu7IfVxZiLm9PT08vdIyffvrJ7IvNZtMHH3ygqVOnavLkyWbM5WdAXHnMknK6rGHDhpKkEydOKCQkpMi+7du3T1999ZVWrFihIUOGmO2XH3D+ew0aNNDLL7+sl19+WceOHdO6des0efJkZWZmFjs+Kq3L56m4foWFhdnl4enpqX/+859FHuvyOS3KypUr1bhxY7399tt2f6srH0AvSVar1SziTJw4UcuXL1dcXFyhcRMAlBYzNQAApfb444/r2LFjeu+997Rs2TKtW7dO8+fPv6ZjxcTE6PTp08rPz1e7du0KLb9/gJ2Hh0eh2Q/F6datmyRp1apVdu1XPuzPy8tL3bp10549e9SqVasicyhpgGixWAo9sHz9+vX68ccfi4wfO3as1q9frylTpiggIED3339/qfoDAAAASIWvb//1r3/p0qVL6tq1a7H7dO/eXefOndN//vMfu/Y33njD3C7JvD3t4sWLS5XLkCFDtGbNGvOB0Pn5+aXab//+/frqq6/s2lavXi1vb++rzmKOioqSp6dnoR8HnThxQp9++qnZF4vFIsMwCl2rv/baa4Xy7Natmz755BOdPHnSbMvPz9fbb79tFxcdHS0XF5cSz8/lL/avfN0lS5aU2K9GjRrpySefVM+ePe1uwVWWMdDvdejQQbVr1y70ftm6dWuhWw7HxMTohx9+kJ+fX5Hjod8XQK5ksVjk7u5uV9DIyMgo9kdvY8eO1c8//6z77rtPZ86c4Va8AK4LMzUAAKXy2muvaeXKlVq+fLlatmypli1b6sknn9QzzzyjTp066Y477ijT8R588EGtWrVKffv21bhx43THHXfIzc1NJ06c0KZNm3T33Xdr4MCBkqTIyEitWbNGb7/9tm666SbVrl1bkZGRRR43Ojpad955pyZNmqTz58+rXbt2+uKLL/Tmm28Wiv3b3/6mzp076w9/+IP++Mc/KiwsTGfPntX333+v999/X59++mmx+cfExGjFihVq1qyZWrVqpdTUVL300kvFTkl/5JFHNGXKFH322Wf605/+JHd39zKdLwAAANRs7733nlxdXdWzZ0/t379fzz//vFq3bq3Y2Nhi9xk8eLD+8Y9/aMiQITpy5IgiIyO1ZcsWzZw5U3379lWPHj0kSX/4wx8UHx+vGTNm6OTJk4qJiZGHh4f27NkjLy8vjRkzptCx77vvPnl5eem+++7ThQsX9NZbb131Gjc4OFgDBgzQtGnTFBQUpJUrVyo5OVmzZ8+Wl5dXifvWq1dPzz//vJ599lkNHjxYDz30kE6fPq3p06erdu3amjp1qiTJx8dHd955p1566SU1aNBAYWFhSklJ0bJlywrNDPjTn/6kdevW6a677tILL7wgLy8v/eMf/9D58+ft4sLCwvTss8/qL3/5iy5cuKCHHnpIVqtV33zzjX7++WdNnz5dzZo1080336zJkyfLMAz5+vrq/fffN29Be5nNZlO3bt0UFxenZs2aydvbW7t27VJSUpIGDRpkxkVGRuq9997T4sWL1bZtW9WqVUvt2rUr8RxJv81InzhxombMmKHHHntM999/v44fP65p06YVuv1UQkKC3n33Xd1555166qmn1KpVKxUUFOjYsWPasGGDJkyYoPbt2xf5OjExMXrvvfc0evRo3XfffTp+/Lj+8pe/KCgoSIcOHSoU37RpU/Xu3VsfffSROnfuXOjZKgBQJg59TDkAoEJMnTrVkGScOnXKrn3IkCFGnTp1CsV36dLFaNmypbl++PBhQ5KxfPlywzAM4+uvvzY8PT2NIUOG2O138eJFo23btkZYWJiRlZVV4mtczun38vLyjL/+9a9G69atjdq1axt169Y1mjVrZowaNco4dOiQGXfkyBEjOjra8Pb2NiQZoaGhJfb/zJkzxrBhw4x69eoZXl5eRs+ePY1vv/3WkGRMnTrVLvbw4cPGsGHDjBtuuMFwc3MzGjZsaHTs2NGYMWNGsefDMAwjKyvLGD58uOHv7294eXkZnTt3Nj7//HOjS5cuRpcuXYrMa+jQoYarq6tx4sSJEvMHAAAALrt8HZ2ammr079/fqFu3ruHt7W089NBDxsmTJ+1ii7oWPX36tPH4448bQUFBhqurqxEaGmpMmTLFuHjxol1cfn6+MX/+fCMiIsJwd3c3rFarERUVZbz//vtmTGhoqNGvXz+7/TZt2mTUrVvX6N27t/Hrr78W24/L+/773/82WrZsabi7uxthYWHGvHnzCh1PkvHOO+8UeZzXXnvNaNWqlZnj3Xffbezfv98u5sSJE8a9995r1K9f3/D29jZ69+5t7Nu3zwgNDS00pvniiy+MDh06GB4eHkZgYKDx9NNPG6+++qohyTh8+LBd7BtvvGHcfvvt5tilTZs2dmOEb775xujZs6fh7e1t1K9f37j//vuNY8eO2Y1DLl68aDz++ONGq1atDB8fH8PT09MIDw83pk6dapw/f9481i+//GLcd999Rr169QyLxVJoLFWSgoICIzEx0QgJCTHc3d2NVq1aGe+//36R749z584Zf/rTn4zw8HDznEZGRhpPPfWUkZGRYcYVde5mzZplhIWFGR4eHkbz5s2NpUuXFjnuu2zFihWGJGPNmjWl7gsAFMViGIbhkGoKAAA1SG5ursLCwtS5c2f961//cnQ6AAAAAFCp7r33Xm3fvl1HjhyRm5ubo9MB4MS4/RQAABXo1KlTOnjwoJYvX66TJ0/aPawQAAAAAKqznJwc7d69Wzt37tTatWs1b948ChoArhtFDQAAKtD69ev16KOPKigoSIsWLbrqAxABAAAAoCj5+fkq6YYrFotFLi4ulZjR1aWnp6tjx47y8fHRqFGjinw+CwCUFbefAgAAAAAAAKq4rl27KiUlpdjtoaGhOnLkSOUlBAAOQlEDAAAAAAAAqOIOHjyos2fPFrvdw8NDkZGRlZgRADgGRQ0AAAAAAAAAAOAUajk6AQAAAAAAAAAAgNLgQeGlVFBQoJ9++kne3t6yWCyOTgcAAACoVIZh6OzZswoODlatWvw2qiIw5gAAAEBNVuoxh4FSOX78uCGJhYWFhYWFhYWFpUYvx48fd/SluSklJcWIiYkxgoKCDEnG2rVrzW25ubnGpEmTjIiICMPLy8sICgoy4uPjjR9//NHuGBcvXjSefPJJw8/Pz/Dy8jL69+9fqI+//PKL8cgjjxg+Pj6Gj4+P8cgjjxhZWVl2MUePHjViYmIMLy8vw8/PzxgzZoyRk5NTpv4w5mBhYWFhYWFhYWG5+piDmRql5O3tLUk6fvy4fHx8HJwNAAAAULmys7MVEhJiXhdXBefPn1fr1q316KOP6t5777Xb9uuvv2r37t16/vnn1bp1a2VlZSkhIUEDBgzQl19+acYlJCTo/fff15o1a+Tn56cJEyYoJiZGqampcnFxkSTFxcXpxIkTSkpKkiSNHDlS8fHxev/99yVJ+fn56tevnxo2bKgtW7bo9OnTGjJkiAzD0IIFC0rdH8YcAAAAqMlKO+bgQeGllJ2dLavVKpvNxgADAAAANU5Vvx62WCxau3at7rnnnmJjdu3apTvuuENHjx5Vo0aNZLPZ1LBhQ7355pt64IEHJEk//fSTQkJC9OGHH6pXr146cOCAWrRooe3bt6t9+/aSpO3btysqKkrffvutwsPD9dFHHykmJkbHjx9XcHCwJGnNmjUaOnSoMjMzS32+qvo5BgAAACpSaa+HuRkuAAAAgBrBZrPJYrGoXr16kqTU1FTl5eUpOjrajAkODlZERIS2bt0qSdq2bZusVqtZ0JCkDh06yGq12sVERESYBQ1J6tWrl3JycpSamlpsPjk5OcrOzrZbAAAAAJSMogYAAACAau/ixYuaPHmy4uLizF99ZWRkyN3dXfXr17eLDQgIUEZGhhnj7+9f6Hj+/v52MQEBAXbb69evL3d3dzOmKImJibJareYSEhJyXX0EAAAAagKKGgAAAACqtby8PD344IMqKCjQokWLrhpvGIYsFou5/vt/X0/MlaZMmSKbzWYux48fv2puAAAAQE1HUQMAAABAtZWXl6fY2FgdPnxYycnJdvfmDQwMVG5urrKysuz2yczMNGdeBAYG6uTJk4WOe+rUKbuYK2dkZGVlKS8vr9AMjt/z8PCQj4+P3QIAAACgZBQ1AAAAAFRLlwsahw4d0saNG+Xn52e3vW3btnJzc1NycrLZlp6ern379qljx46SpKioKNlsNu3cudOM2bFjh2w2m13Mvn37lJ6ebsZs2LBBHh4eatu2bUV2EQAAAKhxXB2dAAAAAABci3Pnzun777831w8fPqy0tDT5+voqODhY9913n3bv3q0PPvhA+fn55mwKX19fubu7y2q1avjw4ZowYYL8/Pzk6+uriRMnKjIyUj169JAkNW/eXL1799aIESO0ZMkSSdLIkSMVExOj8PBwSVJ0dLRatGih+Ph4vfTSS/rll180ceJEjRgxgtkXAAAAQDmjqAEAAADAKX355Zfq1q2buT5+/HhJ0pAhQzRt2jStW7dOknTrrbfa7bdp0yZ17dpVkjR//ny5uroqNjZWFy5cUPfu3bVixQq5uLiY8atWrdLYsWMVHR0tSRowYIAWLlxobndxcdH69es1evRoderUSZ6enoqLi9Nf//rXiug2AAAAUKNZDMMwHJ2EM8jOzpbVapXNZuPXVgAAAKhxuB6ueJxjAAAA1GSlvR7mmRoAAAAAAAAAAMApUNQAAAAAAAAAAABOgaIGAAAAAAAAAABwCg4taixevFitWrWSj4+PfHx8FBUVpY8++sjcPnToUFksFrulQ4cOdsfIycnRmDFj1KBBA9WpU0cDBgzQiRMn7GKysrIUHx8vq9Uqq9Wq+Ph4nTlzpjK6CAAAAAAAAAAAyomrI1/8xhtv1KxZs3TLLbdIkl5//XXdfffd2rNnj1q2bClJ6t27t5YvX27u4+7ubneMhIQEvf/++1qzZo38/Pw0YcIExcTEKDU1VS4uLpKkuLg4nThxQklJSZKkkSNHKj4+Xu+//35ldBNADRY2eX2x247M6leJmQAAAFxdcdcuXLcAAACgqnBoUaN///526y+++KIWL16s7du3m0UNDw8PBQYGFrm/zWbTsmXL9Oabb6pHjx6SpJUrVyokJEQbN25Ur169dODAASUlJWn79u1q3769JGnp0qWKiorSwYMHFR4eXoE9BAAAAAAAAAAA5cWhRY3fy8/P1zvvvKPz588rKirKbN+8ebP8/f1Vr149denSRS+++KL8/f0lSampqcrLy1N0dLQZHxwcrIiICG3dulW9evXStm3bZLVazYKGJHXo0EFWq1Vbt24ttqiRk5OjnJwccz07O7u8uwzAyTDrAgAAAAAAAHAshxc19u7dq6ioKF28eFF169bV2rVr1aJFC0lSnz59dP/99ys0NFSHDx/W888/r7vuukupqany8PBQRkaG3N3dVb9+fbtjBgQEKCMjQ5KUkZFhFkF+z9/f34wpSmJioqZPn16OPQVQlVCgAAAAAAAAAJyPw4sa4eHhSktL05kzZ/Tuu+9qyJAhSklJUYsWLfTAAw+YcREREWrXrp1CQ0O1fv16DRo0qNhjGoYhi8Virv/+38XFXGnKlCkaP368uZ6dna2QkJCydg8AAAAAAAAAAJQThxc13N3dzQeFt2vXTrt27dLf/vY3LVmypFBsUFCQQkNDdejQIUlSYGCgcnNzlZWVZTdbIzMzUx07djRjTp48WehYp06dUkBAQLF5eXh4yMPD47r6BsA5lTSLAwAAAAAAAIDj1HJ0AlcyDMPuWRa/d/r0aR0/flxBQUGSpLZt28rNzU3JyclmTHp6uvbt22cWNaKiomSz2bRz504zZseOHbLZbGYMAAAAAAAAAACo+hw6U+PZZ59Vnz59FBISorNnz2rNmjXavHmzkpKSdO7cOU2bNk333nuvgoKCdOTIET377LNq0KCBBg4cKEmyWq0aPny4JkyYID8/P/n6+mrixImKjIxUjx49JEnNmzdX7969NWLECHP2x8iRIxUTE1PsQ8IBVB/MugAAAAAAAACqD4cWNU6ePKn4+Hilp6fLarWqVatWSkpKUs+ePXXhwgXt3btXb7zxhs6cOaOgoCB169ZNb7/9try9vc1jzJ8/X66uroqNjdWFCxfUvXt3rVixQi4uLmbMqlWrNHbsWEVHR0uSBgwYoIULF1Z6fwEAAAAAAAAAwLVzaFFj2bJlxW7z9PTUxx9/fNVj1K5dWwsWLNCCBQuKjfH19dXKlSuvKUcAAAAAAAAAAFA1VLlnagAAAAAAAAAAABTFoTM1AKC64NkdAAAAAAAAQMVjpgYAAAAAAAAAAHAKFDUAAAAAAAAAAIBToKgBAAAAAAAAAACcAkUNAAAAAAAAAADgFHhQOIBqgQd1AwAAAAAAANUfMzUAAAAAAAAAAIBTYKYGAKfBbAwAAAAAAACgZqOoAQAOUlyR5sisfpWcCQAAAAAAAOAcuP0UAAAAAAAAAABwChQ1AAAAAAAAAACAU6CoAQAAAAAAAAAAnALP1ACAKqakB6LzvA0AAAAAAADUZBQ1AFQ5JX2pDwAAAAAAAKDm4vZTAAAAAAAAAADAKVDUAAAAAAAAAAAAToGiBgAAAAAAAAAAcAoUNQAAAAAAAAAAgFOgqAEAAAAAAAAAAJwCRQ0AAAAAAAAAAOAUKGoAAAAAAAAAAACnQFEDAAAAAAAAAAA4BYoaAAAAAAAAAADAKVDUAAAAAAAAAAAAToGiBgAAAAAAAAAAcAqujk4AAAAAAFC1hU1eX+y2I7P6VWImAAAAqOmYqQEAAADAKX322Wfq37+/goODZbFY9J///Mduu2EYmjZtmoKDg+Xp6amuXbtq//79djE5OTkaM2aMGjRooDp16mjAgAE6ceKEXUxWVpbi4+NltVpltVoVHx+vM2fO2MUcO3ZM/fv3V506ddSgQQONHTtWubm5FdFtAAAAoEajqAEAAADAKZ0/f16tW7fWwoULi9w+Z84czZs3TwsXLtSuXbsUGBionj176uzZs2ZMQkKC1q5dqzVr1mjLli06d+6cYmJilJ+fb8bExcUpLS1NSUlJSkpKUlpamuLj483t+fn56tevn86fP68tW7ZozZo1evfddzVhwoSK6zwAAABQQ3H7KQAAAABOqU+fPurTp0+R2wzD0Msvv6znnntOgwYNkiS9/vrrCggI0OrVqzVq1CjZbDYtW7ZMb775pnr06CFJWrlypUJCQrRx40b16tVLBw4cUFJSkrZv36727dtLkpYuXaqoqCgdPHhQ4eHh2rBhg7755hsdP35cwcHBkqS5c+dq6NChevHFF+Xj41MJZwMAAACoGZipAQAAAKDaOXz4sDIyMhQdHW22eXh4qEuXLtq6daskKTU1VXl5eXYxwcHBioiIMGO2bdsmq9VqFjQkqUOHDrJarXYxERERZkFDknr16qWcnBylpqYWm2NOTo6ys7PtFgAAAAAlc2hRY/HixWrVqpV8fHzk4+OjqKgoffTRR+b2yrwHLgAAAIDqIyMjQ5IUEBBg1x4QEGBuy8jIkLu7u+rXr19ijL+/f6Hj+/v728Vc+Tr169eXu7u7GVOUxMREc4xitVoVEhJSxl4CAAAANY9Dixo33nijZs2apS+//FJffvml7rrrLt19991m4aKy7oELAM4ibPL6IhcAAFA0i8Vit24YRqG2K10ZU1T8tcRcacqUKbLZbOZy/PjxEvMCAAAA4OCiRv/+/dW3b181bdpUTZs21Ysvvqi6detq+/bthe6BGxERoddff12//vqrVq9eLUnmPXDnzp2rHj16qE2bNlq5cqX27t2rjRs3SpJ5D9zXXntNUVFRioqK0tKlS/XBBx/o4MGDjuw+AAAAgAoSGBgoSYVmSmRmZpqzKgIDA5Wbm6usrKwSY06ePFno+KdOnbKLufJ1srKylJeXV2gGx+95eHiYs9YvLwAAAABKVmWeqZGfn681a9bo/PnzioqKqtR74AKofMXNOGDWAQAAKA+NGzdWYGCgkpOTzbbc3FylpKSoY8eOkqS2bdvKzc3NLiY9PV379u0zY6KiomSz2bRz504zZseOHbLZbHYx+/btU3p6uhmzYcMGeXh4qG3bthXaTwAAAKCmcXV0Anv37lVUVJQuXryounXrau3atWrRooVZcCjqHrhHjx6VVH73wC1KTk6OcnJyzHUe2gcAAABULefOndP3339vrh8+fFhpaWny9fVVo0aNlJCQoJkzZ6pJkyZq0qSJZs6cKS8vL8XFxUmSrFarhg8frgkTJsjPz0++vr6aOHGiIiMj1aNHD0lS8+bN1bt3b40YMUJLliyRJI0cOVIxMTEKDw+XJEVHR6tFixaKj4/XSy+9pF9++UUTJ07UiBEjmH0BAAAAlDOHFzXCw8OVlpamM2fO6N1339WQIUOUkpJibq+se+BeKTExUdOnTy9tNwAAAABUsi+//FLdunUz18ePHy9JGjJkiFasWKFJkybpwoULGj16tLKystS+fXtt2LBB3t7e5j7z58+Xq6urYmNjdeHCBXXv3l0rVqyQi4uLGbNq1SqNHTvWnCE+YMAALVy40Nzu4uKi9evXa/To0erUqZM8PT0VFxenv/71rxV9CgAAAIAax+FFDXd3d91yyy2SpHbt2mnXrl3629/+pmeeeUbSbzMtgoKCzPji7oH7+9kamZmZ5lTw0twDtyhTpkwxB0XSbzM1QkJCrqOnAFBxirtt15FZ/So5EwAAKk/Xrl1lGEax2y0Wi6ZNm6Zp06YVG1O7dm0tWLBACxYsKDbG19dXK1euLDGXRo0a6YMPPrhqzgAAAACuT5V5psZlhmEoJyenUu+BWxQe2gcAAAAAAAAAQNXi0Jkazz77rPr06aOQkBCdPXtWa9as0ebNm5WUlCSLxVJp98AFAAAAAAAAAABVn0OLGidPnlR8fLzS09NltVrVqlUrJSUlqWfPnpJUaffABQAAAAAAAAAAVZ/FKOkmtDBlZ2fLarXKZrNxKyqgHBT3DAiUL56pAQAoL1wPV7yqcI6v5RqN6w0AAACUh9JeD1e5Z2oAAAAAAAAAAAAUhaIGAAAAAAAAAABwChQ1AAAAAAAAAACAU6CoAQAAAAAAAAAAnAJFDQAAAAAAAAAA4BQoagAAAAAAAAAAAKdAUQMAAAAAAAAAADgFihoAAAAAAAAAAMApUNQAAAAAAAAAAABOwdXRCQCo3sImr3d0CgAAAAAAAACqCWZqAAAAAAAAAAAAp0BRAwAAAAAAAAAAOAWKGgAAAAAAAAAAwClQ1AAAAAAAAAAAAE6BogYAAAAAAAAAAHAKro5OAAAAAADgvMImry+y/cisfpWcCQAAAGoCZmoAAAAAAAAAAACnwEwNAKjGivvlpMSvJwEAAAAAAOB8mKkBAAAAAAAAAACcAkUNAAAAAAAAAADgFLj9FIDrVtItjgAAAAAAAACgvDBTAwAAAAAAAAAAOAWKGgAAAAAAAAAAwClQ1AAAAAAAAAAAAE6BogYAAAAAAAAAAHAKFDUAAAAAAAAAAIBTcHV0AgAAxwibvL7I9iOz+lVyJgAAAAAAAEDpMFMDAAAAAAAAAAA4BYoaAAAAAAAAAADAKVDUAAAAAAAAAAAAToGiBgAAAAAAAAAAcAoOLWokJibq9ttvl7e3t/z9/XXPPffo4MGDdjFDhw6VxWKxWzp06GAXk5OTozFjxqhBgwaqU6eOBgwYoBMnTtjFZGVlKT4+XlarVVarVfHx8Tpz5kxFdxEAAAAAAAAAAJQThxY1UlJS9MQTT2j79u1KTk7WpUuXFB0drfPnz9vF9e7dW+np6eby4Ycf2m1PSEjQ2rVrtWbNGm3ZskXnzp1TTEyM8vPzzZi4uDilpaUpKSlJSUlJSktLU3x8fKX0EwAAAAAAAAAAXD9XR754UlKS3fry5cvl7++v1NRU3XnnnWa7h4eHAgMDizyGzWbTsmXL9Oabb6pHjx6SpJUrVyokJEQbN25Ur169dODAASUlJWn79u1q3769JGnp0qWKiorSwYMHFR4eXkE9BAAAAAAAAAAA5cWhRY0r2Ww2SZKvr69d++bNm+Xv76969eqpS5cuevHFF+Xv7y9JSk1NVV5enqKjo8344OBgRUREaOvWrerVq5e2bdsmq9VqFjQkqUOHDrJardq6dStFDaCUwiavd3QKAAAAAAAAAGqwKlPUMAxD48ePV+fOnRUREWG29+nTR/fff79CQ0N1+PBhPf/887rrrruUmpoqDw8PZWRkyN3dXfXr17c7XkBAgDIyMiRJGRkZZhHk9/z9/c2YK+Xk5CgnJ8dcz87OLo9uAgAAAAAAAACAa+TQZ2r83pNPPqmvv/5ab731ll37Aw88oH79+ikiIkL9+/fXRx99pO+++07r15f8i3HDMGSxWMz13/+7uJjfS0xMNB8qbrVaFRIScg29AgAAAOAoly5d0p/+9Cc1btxYnp6euummm/TnP/9ZBQUFZoxhGJo2bZqCg4Pl6emprl27av/+/XbHycnJ0ZgxY9SgQQPVqVNHAwYM0IkTJ+xisrKyFB8fb44f4uPjdebMmcroJgAAAFCjVImixpgxY7Ru3Tpt2rRJN954Y4mxQUFBCg0N1aFDhyRJgYGBys3NVVZWll1cZmamAgICzJiTJ08WOtapU6fMmCtNmTJFNpvNXI4fP34tXQMAAADgILNnz9Yrr7yihQsX6sCBA5ozZ45eeuklLViwwIyZM2eO5s2bp4ULF2rXrl0KDAxUz549dfbsWTMmISFBa9eu1Zo1a7RlyxadO3dOMTExys/PN2Pi4uKUlpampKQkJSUlKS0tTfHx8ZXaXwAAAKAmcGhRwzAMPfnkk3rvvff06aefqnHjxlfd5/Tp0zp+/LiCgoIkSW3btpWbm5uSk5PNmPT0dO3bt08dO3aUJEVFRclms2nnzp1mzI4dO2Sz2cyYK3l4eMjHx8duAQAAAOA8tm3bprvvvlv9+vVTWFiY7rvvPkVHR+vLL7+U9Nt45OWXX9Zzzz2nQYMGKSIiQq+//rp+/fVXrV69WtJvz/1btmyZ5s6dqx49eqhNmzZauXKl9u7dq40bN0qSDhw4oKSkJL322muKiopSVFSUli5dqg8++EAHDx50WP8BAACA6sihRY0nnnhCK1eu1OrVq+Xt7a2MjAxlZGTowoULkqRz585p4sSJ2rZtm44cOaLNmzerf//+atCggQYOHChJslqtGj58uCZMmKBPPvlEe/bs0SOPPKLIyEj16NFDktS8eXP17t1bI0aM0Pbt27V9+3aNGDFCMTExPCQcAAAAqKY6d+6sTz75RN99950k6auvvtKWLVvUt29fSdLhw4eVkZGh6Ohocx8PDw916dJFW7dulSSlpqYqLy/PLiY4OFgRERFmzLZt22S1WtW+fXszpkOHDrJarWYMAAAAgPLh0AeFL168WJLUtWtXu/bly5dr6NChcnFx0d69e/XGG2/ozJkzCgoKUrdu3fT222/L29vbjJ8/f75cXV0VGxurCxcuqHv37lqxYoVcXFzMmFWrVmns2LHmYGTAgAFauHBhxXcSAAAAgEM888wzstlsatasmVxcXJSfn68XX3xRDz30kCQpIyNDkgrdkjYgIEBHjx41Y9zd3VW/fv1CMZf3z8jIkL+/f6HX9/f3N2OKkpOTo5ycHHM9Ozv7GnoJAAAA1CwOLWoYhlHidk9PT3388cdXPU7t2rW1YMECu3vjXsnX11crV64sc44AUNOETV5f7LYjs/pVYiYAAFyft99+25wZ3rJlS6WlpSkhIUHBwcEaMmSIGWexWOz2MwyjUNuVrowpKv5qx0lMTNT06dNL2x2nwzUFAAAAKkKVeFA4AAAAAJS3p59+WpMnT9aDDz6oyMhIxcfH66mnnlJiYqIkKTAwUJIKzabIzMw0Z28EBgYqNzdXWVlZJcacPHmy0OufOnWq0CyQ35syZYpsNpu5HD9+/No7CwAAANQQFDUAAAAAVEu//vqratWyH/K4uLiooKBAktS4cWMFBgYqOTnZ3J6bm6uUlBR17NhRktS2bVu5ubnZxaSnp2vfvn1mTFRUlGw2m3bu3GnG7NixQzabzYwpioeHh3x8fOwWAAAAACVz6O2nAAAAAKCi9O/fXy+++KIaNWqkli1bas+ePZo3b56GDRsm6bdbRiUkJGjmzJlq0qSJmjRpopkzZ8rLy0txcXGSJKvVquHDh2vChAny8/OTr6+vJk6cqMjISPXo0UOS1Lx5c/Xu3VsjRozQkiVLJEkjR45UTEyMwsPDHdN5AAAAoJqiqAEAAACgWlqwYIGef/55jR49WpmZmQoODtaoUaP0wgsvmDGTJk3ShQsXNHr0aGVlZal9+/basGGDvL29zZj58+fL1dVVsbGxunDhgrp3764VK1bIxcXFjFm1apXGjh2r6OhoSdKAAQO0cOHCyussAAAAUENYjKs9rRuSpOzsbFmtVtlsNqaFo8Yq6WGPqBl4qCcA1FxcD1e8qnCOK+t6j2sKAAAAXKm018PM1AAAlFpxX3TwxQQAAAAAAAAqAw8KBwAAAAAAAAAAToGiBgAAAAAAAAAAcAoUNQAAAAAAAAAAgFOgqAEAAAAAAAAAAJwCRQ0AAAAAAAAAAOAUKGoAAAAAAAAAAACnQFEDAAAAAAAAAAA4BYoaAAAAAAAAAADAKVDUAAAAAAAAAAAATqHMRY3Dhw9XRB4AAAAAagDGEwAAAACuR5mLGrfccou6deumlStX6uLFixWREwAAAIBqivEEAAAAgOtR5qLGV199pTZt2mjChAkKDAzUqFGjtHPnzorIDYCDhE1eX+QCAABwvRhPAAAAALgeZS5qREREaN68efrxxx+1fPlyZWRkqHPnzmrZsqXmzZunU6dOVUSeAAAAAKoBxhMAAAAArsc1Pyjc1dVVAwcO1L/+9S/Nnj1bP/zwgyZOnKgbb7xRgwcPVnp6ennmCQAAAKAaYTwBAAAA4Fpcc1Hjyy+/1OjRoxUUFKR58+Zp4sSJ+uGHH/Tpp5/qxx9/1N13312eeQIAAACoRhhPAAAAALgWrmXdYd68eVq+fLkOHjyovn376o033lDfvn1Vq9Zv9ZHGjRtryZIlatasWbknCwAAAMC5MZ4AAAAAcD3KXNRYvHixhg0bpkcffVSBgYFFxjRq1EjLli277uQAAAAAVC+MJwAAAABcjzIXNQ4dOnTVGHd3dw0ZMuSaEgIAAABQfTGegCSFTV5fZPuRWf0qORMAAAA4mzI/U2P58uV65513CrW/8847ev3118slKQAAAADVE+MJAAAAANejzEWNWbNmqUGDBoXa/f39NXPmzHJJCgAAAED1xHgCAAAAwPUoc1Hj6NGjaty4caH20NBQHTt2rFySAgAAAFA9MZ4AAAAAcD3K/EwNf39/ff311woLC7Nr/+qrr+Tn51deeQEAnEhx98WWuDc2AMAe4wkAAAAA16PMMzUefPBBjR07Vps2bVJ+fr7y8/P16aefaty4cXrwwQcrIkcAAAAA1QTjCQAAAADXo8wzNWbMmKGjR4+qe/fucnX9bfeCggINHjyYe+ACTqSkX9YDAABUFMYTAAAAAK5HmYsa7u7uevvtt/WXv/xFX331lTw9PRUZGanQ0NCKyA8AAABANcJ4AgAAAMD1KHNR47KmTZuqadOm5ZkLAAAAgBqC8QQAAACAa1HmZ2rk5+dr2bJliouLU48ePXTXXXfZLWWRmJio22+/Xd7e3vL399c999yjgwcP2sUYhqFp06YpODhYnp6e6tq1q/bv328Xk5OTozFjxqhBgwaqU6eOBgwYoBMnTtjFZGVlKT4+XlarVVarVfHx8Tpz5kxZuw8AAADgOpTneAIAAABAzVPmosa4ceM0btw45efnKyIiQq1bt7ZbyiIlJUVPPPGEtm/fruTkZF26dEnR0dE6f/68GTNnzhzNmzdPCxcu1K5duxQYGKiePXvq7NmzZkxCQoLWrl2rNWvWaMuWLTp37pxiYmKUn59vxsTFxSktLU1JSUlKSkpSWlqa4uPjy9p9AAAAANehPMcTAAAAAGoei2EYRll2aNCggd544w317du33JM5deqU/P39lZKSojvvvFOGYSg4OFgJCQl65plnJP02KyMgIECzZ8/WqFGjZLPZ1LBhQ7355pt64IEHJEk//fSTQkJC9OGHH6pXr146cOCAWrRooe3bt6t9+/aSpO3btysqKkrffvutwsPDr5pbdna2rFarbDabfHx8yr3vQGXjQeGoLEdm9XN0CgCAclBe18MVOZ5wdlVhzOHoa0SuGwAAAGqu0l4Pl3mmhru7u2655ZbrSq44NptNkuTr6ytJOnz4sDIyMhQdHW3GeHh4qEuXLtq6daskKTU1VXl5eXYxwcHBioiIMGO2bdsmq9VqFjQkqUOHDrJarWbMlXJycpSdnW23AAAAALg+FTmeAAAAAFD9lbmoMWHCBP3tb39TGSd4XJVhGBo/frw6d+6siIgISVJGRoYkKSAgwC42ICDA3JaRkSF3d3fVr1+/xBh/f/9Cr+nv72/GXCkxMdF8/obValVISMj1dRAAAABAhY0nAAAAANQMrmXdYcuWLdq0aZM++ugjtWzZUm5ubnbb33vvvWtK5Mknn9TXX3+tLVu2FNpmsVjs1g3DKNR2pStjioov6ThTpkzR+PHjzfXs7GwKGwAAAMB1qqjxBAAAAICaocxFjXr16mngwIHlmsSYMWO0bt06ffbZZ7rxxhvN9sDAQEm/zbQICgoy2zMzM83ZG4GBgcrNzVVWVpbdbI3MzEx17NjRjDl58mSh1z116lShWSCXeXh4yMPD4/o7BwAAAMBUEeMJAAAAADVHmYsay5cvL7cXNwxDY8aM0dq1a7V582Y1btzYbnvjxo0VGBio5ORktWnTRpKUm5urlJQUzZ49W5LUtm1bubm5KTk5WbGxsZKk9PR07du3T3PmzJEkRUVFyWazaefOnbrjjjskSTt27JDNZjMLHwAAAAAqXnmOJwAAAADUPGUuakjSpUuXtHnzZv3www+Ki4uTt7e3fvrpJ/n4+Khu3bqlPs4TTzyh1atX67///a+8vb3N51tYrVZ5enrKYrEoISFBM2fOVJMmTdSkSRPNnDlTXl5eiouLM2OHDx+uCRMmyM/PT76+vpo4caIiIyPVo0cPSVLz5s3Vu3dvjRgxQkuWLJEkjRw5UjExMQoPD7+WUwAAKKWwyeuLbD8yq18lZwIAqCrKazwBAAAAoOYpc1Hj6NGj6t27t44dO6acnBz17NlT3t7emjNnji5evKhXXnml1MdavHixJKlr16527cuXL9fQoUMlSZMmTdKFCxc0evRoZWVlqX379tqwYYO8vb3N+Pnz58vV1VWxsbG6cOGCunfvrhUrVsjFxcWMWbVqlcaOHavo6GhJ0oABA7Rw4cKydh8AAADAdSjP8QQAAACAmqfMRY1x48apXbt2+uqrr+Tn52e2Dxw4UI899liZjmUYxlVjLBaLpk2bpmnTphUbU7t2bS1YsEALFiwoNsbX11crV64sU34AAAAAyld5jicAAAAA1DxlLmps2bJFX3zxhdzd3e3aQ0ND9eOPP5ZbYgAAAACqH8YTAAAAAK5HrbLuUFBQoPz8/ELtJ06csLslFAAAAABcifEEAAAAgOtR5qJGz5499fLLL5vrFotF586d09SpU9W3b9/yzA0AAABANcN4AgAAAMD1KHNRY/78+UpJSVGLFi108eJFxcXFKSwsTD/++KNmz55dETkCAAAAqCYqezzx448/6pFHHpGfn5+8vLx06623KjU11dxuGIamTZum4OBgeXp6qmvXrtq/f7/dMXJycjRmzBg1aNBAderU0YABA3TixAm7mKysLMXHx8tqtcpqtSo+Pl5nzpwp9/5Ud2GT1xe7AAAAANI1PFMjODhYaWlpeuutt7R7924VFBRo+PDhevjhh+Xp6VkROQIAAACoJipzPJGVlaVOnTqpW7du+uijj+Tv768ffvhB9erVM2PmzJmjefPmacWKFWratKlmzJihnj176uDBg+btsBISEvT+++9rzZo18vPz04QJExQTE6PU1FS5uLhIkuLi4nTixAklJSVJkkaOHKn4+Hi9//775donAAAAoKazGIZhODoJZ5CdnS2r1SqbzSYfHx9HpwNcN37tBkc7Mqufo1MAAJSBM14PT548WV988YU+//zzIrcbhqHg4GAlJCTomWeekfTbrIyAgADNnj1bo0aNks1mU8OGDfXmm2/qgQcekCT99NNPCgkJ0YcffqhevXrpwIEDatGihbZv36727dtLkrZv366oqCh9++23Cg8PL1W+VeEcV+VrRK4dAAAAqrfSXg+XeabGG2+8UeL2wYMHl/WQAAAAAGqIyhxPrFu3Tr169dL999+vlJQU3XDDDRo9erRGjBghSTp8+LAyMjIUHR1t7uPh4aEuXbpo69atGjVqlFJTU5WXl2cXExwcrIiICG3dulW9evXStm3bZLVazYKGJHXo0EFWq1Vbt24ttqiRk5OjnJwccz07O7vc+g4AAABUV2UuaowbN85u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GoMHD9ayZcs0a9as637xPn36qE+fPiXGeHh4KDAwsMhtNptNy5Yt05tvvqkePXpIklauXKmQkBBt3LhRvXr10oEDB5SUlKTt27erffv2kqSlS5cqKipKBw8eVHh4+HX3AwAAAEDplOd4AgAAAEDNU+aiRm5url577TUlJyerXbt2qlOnjt32efPmlVtykrR582b5+/urXr166tKli1588UX5+/tLklJTU5WXl6fo6GgzPjg4WBEREdq6dat69eqlbdu2yWq1mgUNSerQoYOsVqu2bt1abFEjJydHOTk55np2dna59gsAAACoiSp7PAEAAACgeilVUePrr79WRESEatWqpX379um2226TJH333Xd2ceU9jbxPnz66//77FRoaqsOHD+v555/XXXfdpdTUVHl4eCgjI0Pu7u6qX7++3X4BAQHKyMiQJGVkZJhFkN/z9/c3Y4qSmJio6dOnl2t/AADXJ2zy+iLbj8zqV8mZAADKwlHjCdQMXB8AAADULKUqarRp00bp6eny9/fX0aNHtWvXLvn5+VV0bnrggQfMf0dERKhdu3YKDQ3V+vXrNWjQoGL3MwzDbkBU1ODoypgrTZkyRePHjzfXs7OzFRISUtYuAAAAADWeo8YTAAAAAKqfUj0ovF69ejp8+LAk6ciRIyooKKjQpIoTFBSk0NBQHTp0SJIUGBio3NxcZWVl2cVlZmYqICDAjDl58mShY506dcqMKYqHh4d8fHzsFgAAAABlV1XGEwAAAACcX6lmatx7773q0qWLgoKCZLFY1K5dO7m4uBQZ+7///a9cE/y906dP6/jx4woKCpIktW3bVm5ubkpOTlZsbKwkKT09Xfv27dOcOXMkSVFRUbLZbNq5c6fuuOMOSdKOHTtks9nUsWPHCssVAAAAwG+qyngCAAAAgPMrVVHj1Vdf1aBBg/T9999r7NixGjFihLy9va/7xc+dO6fvv//eXD98+LDS0tLk6+srX19fTZs2Tffee6+CgoJ05MgRPfvss2rQoIEGDhwoSbJarRo+fLgmTJggPz8/+fr6auLEiYqMjFSPHj0kSc2bN1fv3r01YsQILVmyRJI0cuRIxcTEFPuQcAAAAADlp6LGEwAAAABqnlIVNSSpd+/ekqTU1FSNGzeuXAYhX375pbp162auX36GxZAhQ7R48WLt3btXb7zxhs6cOaOgoCB169ZN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2rWr9u/ff9Xjvvvuu2rRooU8PDzUokULrV27toJ64FxKOt95eXl65plnFBkZqTp16ig4OFiDBw/WTz/9VOIxV6xYUeT7/uLFixXcG+dwtff40KFDC527Dh06XPW4vMeLdrXzXdR71WKx6KWXXir2mLzHi5eYmKjbb79d3t7e8vf31z333KODBw/axfA5Dvxm0aJFaty4sWrXrq22bdvq888/d3RKuEJpPtNQ9SQmJspisSghIcHRqaAIP/74ox555BH5+fnJy8tLt956q1JTUx2dFq5w6dIl/elPf1Ljxo3l6empm266SX/+859VUFDg6NRqtIr6vgTlryK+a3E0ihoody1btlR6erq57N27t9jYw4cPq2/fvvrDH/6gPXv26Nlnn9XYsWP17rvvVmLGzmvXrl125zo5OVmSdP/995e438GDB+32a9KkSWWk65TOnz+v1q1ba+HChUVunzNnjubNm6eFCxdq165dCgwMVM+ePXX27Nlij7lt2zY98MADio+P11dffaX4+HjFxsZqx44dFdUNp1HS+f7111+1e/duPf/889q9e7fee+89fffddxowYMBVj+vj42P3nk9PT1ft2rUrogtO52rvcUnq3bu33bn78MMPSzwm7/HiXe18X/k+/ec//ymLxaJ77723xOPyHi9aSkqKnnjiCW3fvl3Jycm6dOmSoqOjdf78eTOGz3FAevvtt5WQkKDnnntOe/bs0R/+8Af16dNHx44dc3Rq+J3SfKahatm1a5deffVVtWrVytGpoAhZWVnq1KmT3Nzc9NFHH+mbb77R3LlzVa9ePUenhivMnj1br7zyihYuXKgDBw5ozpw5eumll7RgwQJHp1ajVcT3JagYFfVdi0MZQDmaOnWq0bp161LHT5o0yWjWrJld26hRo4wOHTqUc2Y1w7hx44ybb77ZKCgoKHL7pk2bDElGVlZW5SZWTUgy1q5da64XFBQYgYGBxqxZs8y2ixcvGlar1XjllVeKPU5sbKzRu3dvu7ZevXoZDz74YLnn7MyuPN9F2blzpyHJOHr0aLExy5cvN6xWa/kmV00Vdc6HDBli3H333WU6Du/x0inNe/zuu+827rrrrhJjeI+XXmZmpiHJSElJMQyDz3HgsjvuuMN4/PHH7dqaNWtmTJ482UEZoTSu/ExD1XL27FmjSZMmRnJystGlSxdj3Lhxjk4J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oCDNnTu3UExubq4mTZqkG264QXXq1FH79u21efPmYo/5ww8/6O6771ZAQIDq1q2r22+/XRs3bjS3//nPf1ZkZGSh/dq2basXXnih2OOuWbNGAwYMsGv797//rcjISHl6esrPz089evSwmza9fPlyNW/eXLVr11azZs20aNEiu/1PnDihBx98UL6+vqpTp47atWunHTt2mNsXL16sm2++We7u7goPD9ebb75pt7/FYtFrr72mgQMHysvLS02aNNG6devsYj788EM1bdpUnp6e6tatm44cOWK3/ejRo+rfv7/q16+vOnXqqGXLlvrwww/N7b169dLp06eVkpJS7LkBAAAAqirGHIw5AMBRKGoAQDX09NNPa9OmTVq7dq02bNigzZs3KzU11S7m0Ucf1RdffKE1a9bo66+/1v3336/evXvr0KFDRR7z3Llz6tu3rzZu3Kg9e/aoV69e6t+/v44dOyZJGjZsmL755hvt2rXL3Ofrr7/Wnj17NHTo0CKPmZWVpX379qldu3ZmW3p6uh566CENGzZMBw4c0ObNmzVo0CAZhiFJWrp0qZ577jm9+OKLOnDggGbOnKnnn39er7/+uplnly5d9NNPP2ndunX66quvNGnSJBUUFEiS1q5dq3HjxmnChAnat2+fRo0apUcffVSbNm2yy2369OmKjY3V119/rb59++rhhx/WL7/8Ikk6fvy4Bg0apL59+yotLU2PPfaYJk+ebLf/E088oZycHH322Wfau3evZs+erbp165rb3d3d1bp1a33++edF/xEBAACAKowxB2MOAHAYAwBQrZw9e9Zwd3c31qxZY7adPn3a8PT0NMaNG2cYhmF8//33hsViMX788Ue7fbt3725MmTLFMAzDWL58uWG1Wkt8rRYtWhgLFiww1/v06WP88Y9/NNcTEhKMrl27Frv/nj17DEnGsWPHzLbU1FRDknHkyJEi9wkJCTFWr15t1/aXv/zFiIqKMgzDMJYsWWJ4e3sbp0+fLnL/jh07GiNGjLBru//++42+ffua65KMP/3pT+b6uXPnDIvFYnz00UeGYRjGlClTjObNmxsFBQVmzDPPPGNIMrKysgzDMIzIyEhj2rRpxfbdMAxj4MCBxtChQ0uMAQAAAKoaxhyMOQDAkZipAQDVzA8//KDc3FxFRUWZbb6+vgoPDzfXd+/eLcMw1LRpU9WtW9dcUlJS9MMPPxR53PPnz2vSpElq0aKF6tWrp7p16+rbb781fzUlSSNGjNBbb72lixcvKi8vT6tWrdKwYcOKzfXChQuSpNq1a5ttrVu3Vvfu3RUZGan7779fS5cuVVZWliTp1KlTOn78uIYPH26X94wZM8y809LS1KZNG/n6+hb5mgcOHFCnTp3s2jp16qQDBw7YtbVq1cr8d506deTt7a3MzEzzGB06dJDFYjFjfn++JWns2LGaMWOGOnXqpKlTp+rrr78ulIunp6d+/fXXYs8PAAAAUBUx5mDMAQCO5OroBAAA5cv4vynTJSkoKJCLi4tSU1Pl4uJit+3305V/7+mnn9bHH3+sv/71r7rlllvk6emp++67T7m5uWZM//795eHhobVr18rDw0M5OTm69957i82jQYMGkn6bEt6wYUNJkouLi5KTk7V161Zt2LBBCxYs0HPPPacdO3bIy8tL0m/Twdu3b293rMv98PT0vGr/fz8wkH47Z1e2ubm5Fdrn8nTy0pzjxx57TL169dL69eu1YcMGJSYmau7cuRozZowZ88svv+jmm2++6rEAAACAqoQxB2MOAHAkZmoAQDVzyy23yM3NTdu3bzfbsrKy9N1335nrbdq0UX5+vjIzM3XLLbfYLYGBgUUe9/PPP9fQoUM1cOBARUZGKjAwsNCD6lxdXTVkyBAtX75cy5cv14MPPmgOCopy8803y8fHR998841du8ViUadOnTR9+nTt2bNH7u7uWrt2rQICAnTDDTfof//7X6G8GzduLOm3XzulpaWZ96K9UvPmzbVlyxa7tq1bt6p58+bF5nmlFi1a2J1fSYXWJSkkJESPP/643nvvPU2YMEFLly61275v3z61adOm1K8LAAAAVAWMORhzAIAjMVMDAKqZunXravjw4Xr66afl5+engIAAPffcc6pV6//XsZs2baqHH35YgwcP1ty5c9WmTRv9/PPP+vTTTxUZGam+ffsWOu4tt9yi9957T/3795fFYtHzzz9v/oro9x577DHzYv2LL74oMddatWqpR48e2rJly/9r787jvarq/fG/jgyHQTgKCAcUgRQ0BC2HFFNRQRBxxHJWHOprOSSBt8Thil4Dhxwqc+imoGlq5dCAmeB4vaQpzlpmBogJooggiodp//7wx7kdmZHDOR94Ph+Pz+Ph3nvttd7rw3I/9tnvz9orhx56aJLkqaeeykMPPZR+/fqlbdu2eeqpp/Luu+9W1zlixIh85zvfScuWLTNgwIBUVVXlmWeeyaxZszJ06NAcffTRGTlyZA499NCMGjUq7du3z3PPPZcOHTqkV69e+Y//+I8cccQR2XHHHdOnT5/8/ve/zz333JPx48ev8nf8rW99K1deeWWGDh2aU089NRMnTsyYMWNqlBkyZEgGDBiQbt26ZdasWXn44Ydr/BEzefLk/Otf/0rfvn1XuV0AAKgP/M3hbw6AOlV3y3kAUFs+/PDD4rjjjiuaNWtWtGvXrrj88suL3r17Vy/aVxRFMX/+/OI///M/i86dOxeNGjUqKisri8MOO6x48cUXi6JYetG+SZMmFfvss0/RtGnTomPHjsW11167VJ1L7LnnnkX37t1XKdYHHnig2HzzzYtFixYVRVEUr776atG/f/9is802K8rLy4tu3brVWBiwKIri9ttvL770pS8VjRs3LjbddNNir732Ku65557q45MnTy4OP/zwomXLlkWzZs2KnXfeuXjqqaeqj1933XXFF77whaJRo0ZFt27diltvvbVG/UmKe++9t8a+ioqKYvTo0dXbv//974utt966KC8vL/bcc8/i5ptvrrFo3xlnnFFstdVWRXl5ebHZZpsVxx9/fPHee+9Vnz9y5Miif//+q/QdAQBAfeNvDn9zANSVsqJYhZf0AcAqKooi2267bU499dQMHTp0lcrvtttuGTJkSI4++uh1EGHdq6qqSteuXXPHHXcstYAgAACwYv7mWDl/cwDrM2tqALDWzJgxI1dddVX+9a9/5aSTTlqlc8rKyvKzn/0sCxcurOXo6o8pU6bkvPPO88cFAACsJn9zrBp/cwDrMzM1AFhrysrK0qZNm/zoRz/KMcccU9fhAAAA6xl/cwAgqQEAAAAAAJQEr58CAAAAAABKgqQGAAAAAABQEiQ1AAAAAACAkiCpAQAAAAAAlARJDQAAAAAAoCRIagAAAAAAACVBUgMAAAAAACgJkhoAAAAAAEBJkNQAAAAAAABKgqQGAAAAAABQEiQ1AAAAAACAkiCpAQAAAAAAlARJDQAAAAAAoCRIagAAAAAAACVBUgMAgDo3YsSIlJWV5b333ltp2b333jt77713rcXSuXPnHHjggbVW/+dRVlaWESNG1Fr9kydPTllZWcaMGfO56xozZkzKysoyefLk1T73/vvvr9V+Jv/X1x/+8Ie12k599/HHH2fEiBF59NFHV6n82hwjq6Nz58458cQTV/u81e3fEj/5yU+y7bbbpry8PF26dMlFF12UBQsWrHb7AACsfZIaAABQIv785z/nG9/4Rl2HUevuv//+XHTRRXUdxgbh448/zkUXXbTKD/3bt2+fP//5zxk4cGDtBraWrG7/kuQHP/hBzjrrrAwaNCh/+tOfctppp2XkyJE5/fTTay9QAABWWcO6DgAAAPj04WuzZs2W2l8URT755JM0bdo0u+22Wx1EBv+nvLx8vR6HM2fOzCWXXJJvfvObGTlyZJJPZ4ctWLAg559/foYMGZLu3bvXcZQAABs2MzUAAKg3pk6dmkGDBqVly5apqKjIcccdl3fffXel573//vs57bTTsvnmm6dx48b5whe+kPPOOy9VVVU1yi1evDg/+clP8qUvfSlNmzbNJptskt122y2/+93vVlj/ddddl4YNG+bCCy9cYbm77ror/fr1S/v27dO0adN88YtfzDnnnJOPPvqoRrkTTzwxG2+8cV566aX069cvLVq0SJ8+fZJ8+oqpM844IzfccEO++MUvpry8PLfcckv1sSWvZXrhhRdSVlaWm266aak4/vjHP6asrKy6X//4xz9y0kknpWvXrmnWrFk233zzHHTQQXnppZdW2J/lWbx4cS655JJss8021d/j9ttvnx/96EcrPffmm2/ODjvskCZNmqRVq1Y57LDD8te//rXGd/PTn/60ur9LPst7jdVPf/rTbLTRRpkxY0b1viuvvDJlZWU1flm/ePHibLrpphk2bNhSdVx11VXp0qVLNt544/Tq1StPPvnkUmWeeeaZHHzwwWnVqlWaNGmSL3/5y/nVr35Vo8ySV2498sgj+fa3v502bdqkdevWGTRoUN5+++2Vfjf//Oc/c9RRR6VDhw4pLy9Pu3bt0qdPnzz//PM1yt11113p1atXmjdvno033jj9+/fPc889V6PMkjH2j3/8IwcccEA23njjdOzYMcOGDav+/2Ly5MnZbLPNkiQXXXRR9Xe9otc8Lev1U0teH/fKK6/k6KOPTkVFRdq1a5eTTz45s2fPXmm/n3vuuRx44IFp27ZtysvL06FDhwwcODBvvfXWCs978803c9xxx1Wf98UvfjFXXnllFi9evMb9e+CBB/LJJ5/kpJNOqrH/pJNOSlEUue+++1baHwAAapeZGgAA1BuHHXZYjjjiiHzrW9/KK6+8kgsuuCCvvvpqnnrqqTRq1GiZ53zyySfZZ5998sYbb+Siiy7K9ttvn//5n//JqFGj8vzzz2fs2LHVZU888cTcdtttOeWUU3LxxRencePGefbZZ5f7wLwoivzHf/xHfvzjH+fnP//5St/p//rrr+eAAw7IkCFD0rx58/ztb3/LZZddlr/85S95+OGHa5SdP39+Dj744Jx66qk555xzsnDhwupj9913X/7nf/4n//mf/5nKysq0bdt2qbZ22GGHfPnLX87o0aNzyimn1Dg2ZsyYtG3bNgcccECS5O23307r1q1z6aWXZrPNNsv777+fW265Jbvuumuee+65bLPNNivs12ddfvnlGTFiRM4///zstddeWbBgQf72t7/lgw8+WOF5o0aNyrnnnpujjz46o0aNysyZMzNixIj06tUrTz/9dLp27ZoLLrggH330UX7zm9/kz3/+c/W57du3X2adffv2TVEUeeihh3L00UcnScaPH5+mTZtm3Lhx1eWeeeaZfPDBB+nbt2+N83/6059m2223zTXXXJMkueCCC3LAAQdk0qRJqaioSJI88sgj2X///bPrrrvmhhtuSEVFRe68884ceeSR+fjjj5caF9/4xjcycODA/PKXv8zUqVPzH//xHznuuOOWGgOfdcABB2TRokW5/PLLs+WWW+a9997LhAkTanyvI0eOzPnnn5+TTjop559/fubPn58rrrgie+65Z/7yl7/UmEWwYMGCHHzwwTnllFMybNiwPP744/mv//qvVFRU5D//8z/Tvn37PPDAA9l///1zyimnVL/abEkiYHUdfvjhOfLII3PKKafkpZdeyvDhw5N8mshano8++ij77bdfunTpkp/+9Kdp165dpk+fnkceeSQffvjhcs979913s/vuu2f+/Pn5r//6r3Tu3Dl/+MMfcvbZZ+eNN97Iddddt0b9e/nll5MkPXv2rLG/ffv2adOmTfVxAADqUAEAAHXswgsvLJIU3/3ud2vsv/3224skxW233Va9r3fv3kXv3r2rt2+44YYiSfGrX/2qxrmXXXZZkaR48MEHi6Ioiscff7xIUpx33nkrjKVTp07FwIEDi48//rg4/PDDi4qKimL8+PGr3afFixcXCxYsKB577LEiSfHCCy9UHxs8eHCRpLj55puXOi9JUVFRUbz//vvLPHbhhRdWb//4xz8ukhSvvfZa9b7333+/KC8vL4YNG7bc2BYuXFjMnz+/6Nq1a43vfNKkSUWSYvTo0Svs24EHHlh86UtfWmGZ0aNHF0mKSZMmFUVRFLNmzSqaNm1aHHDAATXKvfnmm0V5eXlxzDHHVO87/fTTi9X5U2WLLbYoTj755KIoiqKqqqpo3rx58f3vf79IUkyZMqUoiqL4wQ9+UDRq1KiYO3dujb727NmzWLhwYXVdf/nLX4okxR133FG9b9ttty2+/OUvFwsWLFjqe2jfvn2xaNGiGn0+7bTTapS7/PLLiyTFtGnTltuH9957r0hSXHPNNcst8+abbxYNGzYszjzzzBr7P/zww6KysrI44ogjqvctGWOf/f/igAMOKLbZZpvq7XfffXepcbUiyxojS/7/vfzyy2uUPe2004omTZoUixcvXm59zzzzTJGkuO+++1bYbqdOnYrBgwdXb59zzjlFkuKpp56qUe7b3/52UVZWVv3/xOr275vf/GZRXl6+zGPdunUr+vXrt0r1AABQe7x+CgCAeuPYY4+tsX3EEUekYcOGeeSRR5Z7zsMPP5zmzZvna1/7Wo39S349/9BDDyX59JVMSVZpsd+ZM2dm3333zV/+8pc88cQT1a+GWpl//vOfOeaYY1JZWZkGDRqkUaNG6d27d5LUeMXSEocffvgy69l3332z6aabrrS9Y489NuXl5TVeBXTHHXekqqqqxutzFi5cmJEjR6Z79+5p3LhxGjZsmMaNG+f1119fZlwr85WvfCUvvPBCTjvttPzpT3/KnDlzVnrOn//858ybN2+pWQ0dO3bMvvvuW/3vtCb69OmT8ePHJ0kmTJiQjz/+OEOHDk2bNm2qZ2uMHz+++pVN/27gwIFp0KBB9fb222+fJJkyZUqST1/d9be//a16bC5cuLD6c8ABB2TatGl57bXXatR58MEH19j+bJ3L0qpVq2y11Va54oorctVVV+W5556rfo3SEn/605+ycOHCnHDCCTXiaNKkSXr37r3UYthlZWU56KCDloplRXF8Hsvq9yeffFLj1WCftfXWW2fTTTfN97///dxwww159dVXV6mthx9+ON27d89XvvKVGvtPPPHEFEWx0lkxK1JWVrZGxwAAWDckNQAAqDcqKytrbDds2DCtW7fOzJkzl3vOzJkzU1lZudTDxrZt26Zhw4bV57777rtp0KDBUm0sy9///vc89dRTGTBgQHr06LFKsc+dOzd77rlnnnrqqVxyySV59NFH8/TTT+eee+5JksybN69G+WbNmqVly5bLrGt5r1r6rFatWuXggw/OrbfemkWLFiX59NVTX/nKV7LddttVlxs6dGguuOCCHHroofn973+fp556Kk8//XR22GGHpeJaFcOHD88Pf/jDPPnkkxkwYEBat26dPn365JlnnlnuOUv+HZbVtw4dOqzw33hl+vbtmzfffDOvv/56xo8fny9/+ctp27Zt9t1334wfPz7z5s3LhAkTlnr1VJK0bt26xnZ5eXmS//v3euedd5IkZ599dho1alTjc9pppyVJ3nvvvdWqc1nKysry0EMPpX///rn88suz4447ZrPNNst3vvOd6tcwLYlll112WSqWu+66a6k4mjVrliZNmiwVyyeffLLcOD6PNel3RUVFHnvssXzpS1/Kueeem+222y4dOnTIhRdemAULFiz3vJkzZy53LC05viZat26dTz75JB9//PFSx95///20atVqjeoFAGDtsaYGAAD1xvTp07P55ptXby9cuDAzZ85c6mHpv2vdunWeeuqpFEVRI7ExY8aMLFy4MG3atEny6Xv0Fy1alOnTp680adCrV698/etfr16r4vrrr89GG63490APP/xw3n777Tz66KPVszOSLHedibX1a/CTTjopv/71rzNu3LhsueWWefrpp3P99dfXKHPbbbflhBNOyMiRI2vsf++997LJJpuscltLNGzYMEOHDs3QoUPzwQcfZPz48Tn33HPTv3//TJ06Nc2aNVvqnCX/htOmTVvq2Ntvv13977QmlsykGT9+fMaNG5f99tuvev/555+fxx9/PFVVVctMaqzMkriGDx+eQYMGLbPM6q5JsjydOnWqXvj973//e371q19lxIgRmT9/fm644YbqWH7zm9+kU6dOa6XN+qBnz5658847UxRFXnzxxYwZMyYXX3xxmjZtmnPOOWeZ57Ru3Xq5YynJGo+nJWtpvPTSS9l1112r90+fPj3vvffeKic5AQCoPWZqAABQb9x+++01tn/1q19l4cKF2XvvvZd7Tp8+fTJ37tzcd999Nfbfeuut1ceTZMCAAUmy1AP/5Rk8eHDuvPPOjB49OieccEL1TIjlWZKIWPLr9CVuvPHGVWpvTfXr1y+bb755Ro8endGjR6dJkybVC2b/e2yfjWvs2LH517/+9bnb32STTfK1r30tp59+et5///3lLrreq1evNG3aNLfddluN/W+99VYefvjhGq/4WpVf+P+79u3bp3v37rn77rszceLE6qTGfvvtl3fffTdXXXVVWrZsmV122WW1+7fNNtuka9eueeGFF7Lzzjsv89OiRYvVrndlunXrlvPPPz89e/bMs88+myTp379/GjZsmDfeeGO5sayu1f2ua1NZWVl22GGHXH311dlkk02q+70sffr0yauvvrpUmVtvvTVlZWXZZ599kqx+//bff/80adKkxivdkk9nQJWVleXQQw9d9Q4BAFArzNQAAKDeuOeee9KwYcPst99+eeWVV3LBBRdkhx12yBFHHLHcc0444YT89Kc/zeDBgzN58uT07NkzTzzxREaOHJkDDjig+tf5e+65Z44//vhccskleeedd3LggQemvLw8zz33XJo1a5Yzzzxzqbq/9rWvpVmzZvna176WefPm5Y477kjjxo2XGcfuu++eTTfdNN/61rdy4YUXplGjRrn99tvzwgsvrJ0vZzkaNGiQE044ofrB/aBBg1JRUVGjzIEHHpgxY8Zk2223zfbbb5+JEyfmiiuuyBZbbLFGbR500EHp0aNHdt5552y22WaZMmVKrrnmmnTq1Cldu3Zd5jmbbLJJLrjggpx77rk54YQTcvTRR2fmzJm56KKL0qRJk1x44YXVZZf8Wv6yyy7LgAED0qBBg2y//fbL/e6TTx9y/+QnP0nTpk3z1a9+NUnSpUuXdOnSJQ8++GAOPvjgNGy4Zn/+3HjjjRkwYED69++fE088MZtvvnnef//9/PWvf82zzz6bX//612tU77978cUXc8YZZ+TrX/96unbtmsaNG+fhhx/Oiy++WD1boXPnzrn44otz3nnn5Z///Gf233//bLrppnnnnXfyl7/8Jc2bN89FF120Wu22aNEinTp1ym9/+9v06dMnrVq1Sps2bdK5c+fP3adV8Yc//CHXXXddDj300HzhC19IURS555578sEHH1Qnp5blu9/9bm699dYMHDgwF198cTp16pSxY8fmuuuuy7e//e1069ZtjfrXqlWrnH/++bngggvSqlWr9OvXL08//XRGjBiRb3zjG+nevXttfA0AAKwGSQ0AAOqNe+65JyNGjMj1119fvcjxNddcs8KH2U2aNMkjjzyS8847L1dccUXefffdbL755jn77LNrPChPPv219Y477pibbropY8aMSdOmTdO9e/ece+65y63/gAMOyP3335+DDjoohxxySO655540bdp0qXKtW7fO2LFjM2zYsBx33HFp3rx5DjnkkNx1113Zcccd1/xLWQUnnXRSRo0alXfffbfGAuFL/OhHP0qjRo0yatSozJ07NzvuuGPuueeenH/++WvU3j777JO77747P//5zzNnzpxUVlZmv/32ywUXXJBGjRot97zhw4enbdu2+fGPf5y77rorTZs2zd57752RI0fWSIYcc8wx+d///d9cd911ufjii1MURSZNmrTCB+19+/bNT37yk+yxxx411pHo27dv/vu//3uNXj317/39y1/+kh/84AcZMmRIZs2aldatW6d79+4rTLitjsrKymy11Va57rrrMnXq1JSVleULX/hCrrzyyhoJt+HDh6d79+750Y9+VL0ofGVlZXbZZZd861vfWqO2b7rppvzHf/xHDj744FRVVWXw4MFLzVSoLV27ds0mm2ySyy+/PG+//XYaN26cbbbZJmPGjMngwYOXe95mm22WCRMmZPjw4Rk+fHjmzJmTL3zhC7n88sszdOjQGmVXt3/nnXdeWrRokZ/+9Kf54Q9/mMrKypxzzjk577zz1la3AQD4HMqKoijqOggAAAAAAICVsaYGAAAAAABQEiQ1AAAAAACAkiCpAQAAAAAAlARJDQAAAAAAoCRIagAAAAAAACVBUgMAAAAAACgJkhoAAAAAAEBJaFjXAZSKxYsX5+23306LFi1SVlZW1+EAAAAAAMB6oyiKfPjhh+nQoUM22mj58zEkNVbR22+/nY4dO9Z1GAAAAAAAsN6aOnVqtthii+Uel9RYRS1atEjy6RfasmXLOo4GAAAAAADWH3PmzEnHjh2rn8Uvj6TGKlryyqmWLVtKagAAAAAAQC1Y2fIPFgoHAAAAAABKgqQGAAAAAABQEiQ1AAAAAACAkiCpAQAAAAAAlARJDQAAAAAAoCRIagAAAAAAACVBUgMAAAAAACgJkhoAAAAAAEBJkNQAAAAAAABKgqQGAAAAAABQEiQ1AAAAAACAkiCpAQAAAAAAlISGdR0AAEAp63zO2NU+Z/KlA2shEgAAAFj/makBAAAAAACUBEkNAAAAAACgJEhqAAAAAAAAJUFSAwAAAAAAKAmSGgAAAAAAQEmQ1AAAAAAAAEqCpAYAAAAAAFASJDUAAAAAAICSUK+TGtdff3223377tGzZMi1btkyvXr3yxz/+sfr4iSeemLKyshqf3XbbrUYdVVVVOfPMM9OmTZs0b948Bx98cN5666113RUAAAAAAOBzqtdJjS222CKXXnppnnnmmTzzzDPZd999c8ghh+SVV16pLrP//vtn2rRp1Z/777+/Rh1DhgzJvffemzvvvDNPPPFE5s6dmwMPPDCLFi1a190BAAAAAAA+h4Z1HcCKHHTQQTW2f/CDH+T666/Pk08+me222y5JUl5ensrKymWeP3v27Nx00035xS9+kb59+yZJbrvttnTs2DHjx49P//79a7cDAAAAAADAWlOvZ2r8u0WLFuXOO+/MRx99lF69elXvf/TRR9O2bdt069Yt3/zmNzNjxozqYxMnTsyCBQvSr1+/6n0dOnRIjx49MmHChHUaPwAAAAAA8PnU65kaSfLSSy+lV69e+eSTT7Lxxhvn3nvvTffu3ZMkAwYMyNe//vV06tQpkyZNygUXXJB99903EydOTHl5eaZPn57GjRtn0003rVFnu3btMn369BW2W1VVlaqqqurtOXPmrP3OAQAAAAAAq6zeJzW22WabPP/88/nggw9y9913Z/DgwXnsscfSvXv3HHnkkdXlevTokZ133jmdOnXK2LFjM2jQoOXWWRRFysrKVtjuqFGjctFFF621fgAAAAAAAJ9PvX/9VOPGjbP11ltn5513zqhRo7LDDjvkRz/60TLLtm/fPp06dcrrr7+eJKmsrMz8+fMza9asGuVmzJiRdu3arbDd4cOHZ/bs2dWfqVOnrp0OAQAAAAAAa6TeJzU+qyiKGq+F+nczZ87M1KlT0759+yTJTjvtlEaNGmXcuHHVZaZNm5aXX345u++++wrbKS8vT8uWLWt8AAAAAACAulOvXz917rnnZsCAAenYsWM+/PDD3HnnnXn00UfzwAMPZO7cuRkxYkQOP/zwtG/fPpMnT865556bNm3a5LDDDkuSVFRU5JRTTsmwYcPSunXrtGrVKmeffXZ69uyZvn371nHvAAAAAACA1VGvkxrvvPNOjj/++EybNi0VFRXZfvvt88ADD2S//fbLvHnz8tJLL+XWW2/NBx98kPbt22efffbJXXfdlRYtWlTXcfXV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zDHHJEkqKipyyimnZNiwYXnooYfy3HPP5bjjjkvPnj3Tt2/ftdpvAAAAAACg9tTKQuFXX3119tlnn3Tv3j2ffPJJjjnmmLz++utp06bNMtfEWJ7rr78+SbL33nvX2D969OiceOKJSZLvfe97mTdvXk477bTMmjUru+66ax588MEaM0KuvvrqNGzYMEcccUTmzZuXPn36ZMyYMWnQoMHn7isAAAAAALBulBVFUdRGxfPmzcsdd9yRZ599NosXL86OO+6YY489tsbC4aVkzpw5qaioyOzZs72KCoBa0fmcsXUdAkC9NfnSgXUdAgAAUItW9Rl8rczUSJKmTZvm5JNPzsknn1xbTQAAAAAAABuQWklq3HrrrSs8fsIJJ9RGswAAAAAAwHqsVpIaZ511Vo3tBQsW5OOPP07jxo3TrFkzSQ0AAAAAAGC1bVQblc6aNavGZ+7cuXnttdeyxx57rNZC4QAAAAAAAEvU2poan9W1a9dceumlOe644/K3v/1tXTULAACsBzqfM3a1z7G4OAAArH9qZabG8jRo0CBvv/32umwSAAAAAABYT9TKTI3f/e53NbaLosi0adNy7bXX5qtf/WptNAkAAAAAAKznaiWpceihh9bYLisry2abbZZ99903V155ZW00CQAAAAAArOdqJamxePHi2qgWAABgla3JOhyJtTgAAKA+W6dragAAAAAAAKypWpmpMXTo0FUue9VVV9VGCABQp9b018EAAAAALF+tJDWee+65PPvss1m4cGG22WabJMnf//73NGjQIDvuuGN1ubKystpoHgAAAAAAWA/VSlLjoIMOSosWLXLLLbdk0003TZLMmjUrJ510Uvbcc88MGzasNpoFAAAAAADWY7WypsaVV16ZUaNGVSc0kmTTTTfNJZdckiuvvLI2mgQAAAAAANZztTJTY86cOXnnnXey3Xbb1dg/Y8aMfPjhh7XRJADUCmtjAAAAANQftTJT47DDDstJJ52U3/zmN3nrrbfy1ltv5Te/+U1OOeWUDBo0qDaaBAAAAAAA1nO1MlPjhhtuyNlnn53jjjsuCxYs+LShhg1zyimn5IorrqiNJgEAAAAAgPVcrSQ1mjVrluuuuy5XXHFF3njjjRRFka233jrNmzevjeYAAAAAAIANQK28fmqJadOmZdq0aenWrVuaN2+eoihqszkAAAAAAGA9VitJjZkzZ6ZPnz7p1q1bDjjggEybNi1J8o1vfCPDhg2rjSYBAAAAAID1XK0kNb773e+mUaNGefPNN9OsWbPq/UceeWQeeOCB2mgSAAAAAABYz9XKmhoPPvhg/vSnP2WLLbaosb9r166ZMmVKbTQJAAAAAACs52olqfHRRx/VmKGxxHvvvZfy8vLaaBIAAGCt6HzO2DU6b/KlA9dyJAAAwGfVyuun9tprr9x6663V22VlZVm8eHGuuOKK7LPPPrXRJAAAAAAAsJ6rlZkaV1xxRfbee+8888wzmT9/fr73ve/llVdeyfvvv5///d//rY0mAQAAAACA9VytzNTo3r17XnzxxXzlK1/Jfvvtl48++iiDBg3Kc889l6222qo2mgQAAAAAANZza32mxoIFC9KvX7/ceOONueiii9Z29QAAAAAAwAZqrc/UaNSoUV5++eWUlZWt7aoBAAAAAIANWK28fuqEE07ITTfdVBtVAwAAAAAAG6haWSh8/vz5+fnPf55x48Zl5513TvPmzWscv+qqq2qjWQBYrs7njK3rEAAAAAD4nNbaTI0XX3wxixcvTpK8/PLL2XHHHdOyZcv8/e9/z3PPPVf9ef7551er3scffzwHHXRQOnTokLKystx33301jp944okpKyur8dltt91qlKmqqsqZZ56ZNm3apHnz5jn44IPz1ltvfZ7uAgAAAAAA69ham6nx5S9/OdOmTUvbtm0zZcqUPP3002nduvXnrvejjz7KDjvskJNOOimHH374Msvsv//+GT16dPV248aNaxwfMmRIfv/73+fOO+9M69atM2zYsBx44IGZOHFiGjRo8LljBAAAAAAAat9aS2pssskmmTRpUtq2bZvJkydXz9r4vAYMGJABAwassEx5eXkqKyuXeWz27Nm56aab8otf/CJ9+/ZNktx2223p2LFjxo8fn/79+6+VOAEAAAAAgNq11pIahx9+eHr37p327dunrKwsO++883JnQfzzn/9cW80mSR599NG0bds2m2yySXr37p0f/OAHadu2bZJk4sSJWbBgQfr161ddvkOHDunRo0cmTJggqQEAAKwVa7J+0+RLB9ZCJAAAsP5aa0mNn/3sZxk0aFD+8Y9/5Dvf+U6++c1vpkWLFmur+uUaMGBAvv71r6dTp06ZNGlSLrjgguy7776ZOHFiysvLM3369DRu3DibbrppjfPatWuX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3/pWXYcGAAAAAACsgg0mqXHkkUdm5syZufjiizNt2rT06NEj999/fzp16lTXoQEAAAAAAKtgg0lqJMlpp52W0047rVbbKC8vz4UXXrjUq6tgfWB8sz4zvllfGdusz4xv1mfGN+sz45v1mfHN+qy+jO+yoiiKOo0AAAAAAABgFWxU1wEAAAAAAACsCkkNAAAAAACgJEhqAAAAAAAAJUFSYzWMGDEiZWVlNT6VlZUrPOexxx7LTjvtlCZNmuQLX/hCbrjhhnUULayezp07LzW+y8rKcvrppy+z/KOPPrrM8n/729/WceSwtMcffzwHHXRQOnTokLKystx33301jhdFkREjRqRDhw5p2rRp9t5777zyyisrrffuu+9O9+7dU15enu7du+fee++tpR7A8q1ofC9YsCDf//7307NnzzRv3jwdOnTICSeckLfffnuFdY4ZM2aZ1/RPPvmklnsD/2dl1+4TTzxxqTG62267rbRe127qg5WN72Vdg8vKynLFFVcst07XbuqDUaNGZZdddkmLFi3Stm3bHHrooXnttddqlHHvTala2fh2700pW5Xrd32+/5bUWE3bbbddpk2bVv156aWXllt20qRJOeCAA7Lnnnvmueeey7nnnpvvfOc7ufvuu9dhxLBqnn766Rpje9y4cUmSr3/96ys877XXXqtxXteuXddFuLBCH330UXbYYYdce+21yzx++eWX56qrrsq1116bp59+OpWVldlvv/3y4YcfLrfOP//5zznyyCNz/PHH54UXXsjxxx+fI444Ik899VRtdQOWaUXj++OPP86zzz6bCy64IM8++2zuueee/P3vf8/BBx+80npbtmxZ43o+bdq0NGnSpDa6AMu0smt3kuy///41xuj999+/wjpdu6kvVja+P3v9vfnmm1NWVpbDDz98hfW6dlPXHnvssZx++ul58sknM27cuCxcuDD9+vXLRx99VF3GvTelamXj2703pWxVrt9JPb7/LlhlF154YbHDDjuscvnvfe97xbbbbltj36mnnlrstttuazkyWPvOOuusYquttioWL168zOOPPPJIkaSYNWvWug0MVlOS4t57763eXrx4cVFZWVlceuml1fs++eSToqKiorjhhhuWW88RRxxR7L///jX29e/fvzjqqKPWesywqj47vpflL3/5S5GkmDJlynLLjB49uqioqFi7wcHnsKyxPXjw4OKQQw5ZrXpcu6mPVuXafcghhxT77rvvCsu4dlMfzZgxo0hSPPbYY0VRuPdm/fLZ8b0s7r0pVcsa3/X5/ttMjdX0+uuvp0OHDunSpUuOOuqo/POf/1xu2T//+c/p169fjX39+/fPM888kwULFtR2qLDG5s+fn9tuuy0nn3xyysrKVlj2y1/+ctq3b58+ffrkkUceWUcRwpqbNGlSpk+fXuP6XF5ent69e2fChAnLPW951/QVnQP1wezZs1NWVpZNNtlkheXmzp2bTp06ZYsttsiBBx6Y5557bt0ECKvh0UcfTdu2bdOtW7d885vfzIwZM1ZY3rWbUvTOO+9k7NixOeWUU1Za1rWb+mb27NlJklatWiVx78365bPje3ll3HtTipY3vuvr/bekxmrYddddc+utt+ZPf/pT/vu//zvTp0/P7rvvnpkzZy6z/PTp09OuXbsa+9q1a5eFCxfmvffeWxchwxq577778sEHH+TEE09cbpn27dvnZz/7We6+++7cc8892WabbdKnT588/vjj6y5QWAPTp09PkmVen5ccW955q3sO1LVPPvkk55xzTo455pi0bNlyueW23XbbjBkzJr/73e9yxx13pEmTJvnqV7+a119/fR1GCys2YMCA3H777Xn44Ydz5ZVX5umnn86+++6bqqqq5Z7j2k0puuWWW9KiRYsMGjRoheVcu6lviqLI0KFDs8cee6RHjx5J3Huz/ljW+P4s996UquWN7/p8/91wrda2nhswYED1f/fs2TO9evXKVlttlVtuuSVDhw5d5jmf/ZV7URTL3A/1yU033ZQBAwakQ4cOyy2zzTbbZJtttqne7tWrV6ZOnZof/vCH2WuvvdZFmPC5LOv6vLJr85qcA3VlwYIFOeqoo7J48eJcd911Kyy722671Vjw7atf/Wp23HHH/OQnP8mPf/zj2g4VVsmRRx5Z/d89evTIzjvvnE6dOmXs2LErfPjr2k2pufnmm3Pssceu9N3qrt3UN2eccUZefPHFPPHEE0sdc+9NqVvR+E7ce1Palje+6/P9t5kan0Pz5s3Ts2fP5WZSKysrl8pCzZgxIw0bNkzr1q3XRYiw2qZMmZLx48fnG9/4xmqfu9tuu/llAfVeZWVlkizz+vzZXxN89rzVPQfqyoIFC3LEEUdk0qRJGTdu3Ap/KbYsG220UXbZZRfXdOq19u3bp1OnTiscp67dlJr/+Z//yWuvvbZG9+Ku3dSlM888M7/73e/yyCOPZIsttqje796b9cHyxvcS7r0pZSsb3/+uPt1/S2p8DlVVVfnrX/+a9u3bL/N4r169Mm7cuBr7Hnzwwey8885p1KjRuggRVtvo0aPTtm3bDBw4cLXPfe6555b7/wPUF126dEllZWWN6/P8+fPz2GOPZffdd1/uecu7pq/oHKgLS/6oev311zN+/Pg1+iFFURR5/vnnXdOp12bOnJmpU6eucJy6dlNqbrrppuy0007ZYYcdVvtc127qQlEUOeOMM3LPPffk4YcfTpcuXWocd+9NKVvZ+E7ce1O6VmV8f1Z9uv/2+qnVcPbZZ+eggw7KlltumRkzZuSSSy7JnDlzMnjw4CTJ8OHD869//Su33nprkuRb3/pWrr322gwdOjTf/OY38+c//zk33XRT7rjjjrrsBizX4sWLM3r06AwePDgNG9a8PHx2fF9zzTXp3Llztttuu+qFxe++++7cfffddRE61DB37tz84x//qN6eNGlSnn/++bRq1SpbbrllhgwZkpEjR6Zr167p2rVrRo4cmWbNmuWYY46pPueEE07I5ptvnlGjRiVJzjrrrOy111657LLLcsghh+S3v/1txo8fv9zpx1BbVjS+O3TokK997Wt59tln84c//CGLFi2q/pVMq1at0rhx4yRLj++LLroou+22W7p27Zo5c+bkxz/+cZ5//vn89Kc/XfcdZIO1orHdqlWrjBgxIocffnjat2+fyZMn59xzz02bNm1y2GGHVZ/j2k19tbJ7kySZM2dOfv3rX+fKK69cZh2u3dRHp59+en75y1/mt7/9bVq0aFF931FRUZGmTZumrKzMvTcla2Xje+HChe69KVkrG99z586t3/ffBavsyCOPLNq3b180atSo6NChQzFo0KDilVdeqT4+ePDgonfv3jXOefTRR4svf/nLRePGjYvOnTsX119//TqOGlbdn/70pyJJ8dprry117LPj+7LLLiu22mqrokmTJsWmm25a7LHHHsXYsWPXYbSwfI888kiRZKnP4MGDi6IoisWLFxcXXnhhUVlZWZSXlxd77bVX8dJLL9Woo3fv3tXll/j1r39dbLPNNkWjRo2Kbbfdtrj77rvXUY/g/6xofE+aNGmZx5IUjzzySHUdnx3fQ4YMKbbccsuicePGxWabbVb069evmDBhwrrvHBu0FY3tjz/+uOjXr1+x2WabFY0aNSq23HLLYvDgwcWbb75Zow7Xbuqrld2bFEVR3HjjjUXTpk2LDz74YJl1uHZTHy3vvmP06NHVZdx7U6pWNr7de1PKVja+6/v9d9n/3wkAAAAAAIB6zZoaAAAAAABASZDUAAAAAAAASoKkBgAAAAAAUBIkNQAAAAAAgJIgqQEAAAAAAJQESQ0AAAAAAKAkSGoAAAAAAAAlQVIDAAAAAAAoCZIaAABAvdG5c+dcc801a62+yZMnp6ysLM8///xqnTdmzJhssskmay0OAABg7ZDUAAAA6o2nn346/+///b912uayEilHHnlk/v73v6/TOAAAgJVrWNcBAAAAG5YFCxakUaNGNfbNnz8/jRs3zmabbVZHUdXUtGnTNG3atK7DAAAAPsNMDQAAYI098MAD2WOPPbLJJpukdevWOfDAA/PGG29UH1/y+qdf/epX2XvvvdOkSZPcdtttOfHEE3PooYdm1KhR6dChQ7p165ak5qyJo48+OkcddVSN9hYsWJA2bdpk9OjRq9T+yuy9996ZMmVKvvvd76asrCxlZWVJln791IgRI/KlL30pN998c7bccstsvPHG+fa3v51Fixbl8ssvT2VlZdq2bZsf/OAHNeqfPXt2/t//+39p27ZtWrZsmX333TcvvPDCKscHAADUJKkBAACssY8++ihDhw7N008/nYceeigbbbRRDjvssCxevLhGue9///v5zne+k7/+9a/p379/kuShhx7KX//614wbNy5/+MMflqr72GOPze9+97vMnTu3et+f/vSnfPTRRzn88MNXq/3lueeee7LFFlvk4osvzrRp0zJt2rTlln3jjTfyxz/+MQ888EDuuOOO3HzzzRk4cGDeeuutPPbYY7nsssty/vnn58knn0ySFEWRgQMHZvr06bn//vszceLE7LjjjunTp0/ef//9VYoPAACoyeunAACANbYkubDETTfdlLZt2+bVV19Njx49qvcPGTIkgwYNqlG2efPm+fnPf57GjRsvs+7+/funefPmuffee3P88ccnSX75y1/moIMOSsuWLVer/eVp1apVGjRokBYtWqSysnKFZRcvXpybb745LVq0SPfu3bPPPvvktddey/3335+NNtoo22yzTS677LI8+uij2W233fLII4/kpZdeyowZM1JeXp4k+eEPf5j77rsvv/nNb9b52iEAALA+MFMDAABYY2+88UaOOeaYfOELX0jLli3TpUuXJMmbb75Zo9zOO++81Lk9e/ZcbkIjSRo1apSvf/3ruf3225N8Oivjt7/9bY499tjVbn9t6Ny5c1q0aFG93a5du3Tv3j0bbbRRjX0zZsxIkkycODFz585N69ats/HGG1d/Jk2atFqvyAIAAP6PmRoAAMAaO+igg9KxY8f893//dzp06JDFixenR48emT9/fo1yzZs3X+rcZe37rGOPPTa9e/fOjBkzMm7cuDRp0iQDBgxY7fbXhs8ubl5WVrbMfUtefbV48eK0b98+jz766FJ1/ft6HQAAwKqT1AAAANbIzJkz89e//jU33nhj9txzzyTJE088sVbb2H333dOxY8fcdddd+eMf/5ivf/3r1bM71lb7jRs3zqJFi9Zq3Emy4447Zvr06WnYsGE6d+681usHAIANkaQGAACwRjbddNO0bt06P/vZz9K+ffu8+eabOeecc9ZqG2VlZTnmmGNyww035O9//3seeeSRtd5+586d8/jjj+eoo45KeXl52rRps1Zi79u3b3r16pVDDz00l112WbbZZpu8/fbbuf/++3PooYcu85VcAADAillTAwAAWCMbbbRR7rzzzkycODE9evTId7/73VxxxRVrvZ1jjz02r776ajbffPN89atfXevtX3zxxZk8eXK22mqrbLbZZmst7rKystx///3Za6+9cvLJJ6dbt2456qijMnny5LRr126ttQMAABuSsqIoiroOAgAAAAAAYGXM1AAAAAAAAEqCpAYAAAAAAFASJDUAAAAAAICSIKkBAAAAAACUBEkNAAAAAACgJEhqAAAAAAAAJUFSAwAAAAAAKAmSGgAAAAAAQEmQ1AAAAAAAAEqCpAYAAAAAAFASJDUAAAAAAICSIKkBAAAAAACUhP8PmELSVcGntOEAAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 1600x800 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"network = NetworkParams(\n",
" mixnet_delay_mean=10, # seconds\n",
" mixnet_delay_var=4,\n",
" broadcast_delay_mean=1, # second\n",
" pol_proof_time=2, # seconds\n",
")\n",
"\n",
"\n",
"mixnet_delay_data = np.array([network.sample_mixnet_delay() for _ in range(100000)])\n",
"\n",
"plt.figure(figsize=(16,8))\n",
"ax = plt.subplot(221)\n",
"_ = ax.hist(mixnet_delay_data, bins=100)\n",
"ax.set_title(f\"mixnet delay\")\n",
"_ = ax.set_ylabel(\"frequency\")\n",
"_ = ax.set_xlabel(\"delay (seconds)\")\n",
"\n",
"broadcast_delay_data = network.sample_broadcast_delay(np.zeros(100000))\n",
"ax = plt.subplot(222)\n",
"_ = ax.hist(broadcast_delay_data, bins=100)\n",
"ax.set_title(f\"block broadcast_delay\")\n",
"ax.set_ylabel(\"frequency\")\n",
"ax.set_xlabel(\"delay (seconds)\")\n",
"\n",
"BLOCK_TIME = 0\n",
"block_arrival_slots = np.array([network.block_arrival_time(np.array([BLOCK_TIME])) for _ in range(10000)])\n",
"\n",
"ax = plt.subplot(212)\n",
"_ = ax.hist(block_arrival_slots, bins=100)\n",
"ax.set_title(f\"block arrival slot when sent in slot {BLOCK_TIME}\")\n",
"ax.set_ylabel(\"frequency\")\n",
"ax.set_xlabel(\"arrival time\")\n",
"\n",
"plt.tight_layout()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "a8eff21e-bbd0-432b-84e4-10da319764b5",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"821"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"params.next_block().argmax()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "94cc80de-2c60-495f-a73a-d126717f1007",
"metadata": {},
"outputs": [],
"source": [
"class Sim:\n",
" def __init__(self, params: Params, network: NetworkParams):\n",
" self.params = params\n",
" self.network = network\n",
" self.events = {}\n",
" self.blocks = []\n",
" self.block_heights = np.array([], dtype=np.int64)\n",
" self.block_arrivals = np.zeros(shape=(params.N, 0), dtype=np.int64) # arrival time to each leader for each block\n",
"\n",
" def emit_block(self, t, leader, height, parent):\n",
" assert type(t) in [float, np.float64], type(t)\n",
" assert type(leader) in [int, np.int64]\n",
" assert type(height) in [int, np.int64]\n",
" assert type(parent) in [int, np.int64]\n",
" \n",
" block = Block(\n",
" id=len(self.blocks),\n",
" t=t,\n",
" height=height,\n",
" parent=parent,\n",
" leader=leader\n",
" )\n",
" self.blocks.append(block)\n",
" self.block_heights = np.append(self.block_heights, block.height)\n",
" \n",
" # decide when this block will arrive at each node\n",
" self.block_arrivals = np.append(self.block_arrivals, self.network.block_arrival_time(np.repeat(t, self.params.N).reshape((self.params.N, 1))), axis=1)\n",
" return block.id\n",
"\n",
" def emit_leader_block(self, leader, slot, parent):\n",
" assert type(leader) in [int, np.int64], type(leader)\n",
" assert isinstance(slot, int)\n",
" assert type(parent) in [int, np.int64], type(parent)\n",
" \n",
" refs = self.select_refs(leader, parent, slot)\n",
" return self.emit_block(\n",
" leader,\n",
" slot,\n",
" weight=self.blocks[parent].weight + len(refs) + 1,\n",
" height=self.blocks[parent].height + 1,\n",
" parent=parent,\n",
" refs=refs\n",
" )\n",
"\n",
" def select_refs(self, node: int, parent: int, slot: int) -> list[id]:\n",
" assert type(node) in [int, np.int64], node\n",
" assert type(parent) in [int, np.int64], parent\n",
" assert type(slot) in [int, np.int64], slot\n",
" \n",
" if self.blocks[parent].parent >= 0:\n",
" parents_siblings = self.block_siblings(node, self.blocks[parent].parent, slot)\n",
" # we are uniformly sampling from power_set(forks)\n",
" return np.array(parents_siblings)[np.random.uniform(size=len(parents_siblings)) < 0.5]\n",
" else:\n",
" return []\n",
" \n",
" def block_siblings(self, node, block, slot):\n",
" blocks_seen_by_node = self.block_arrivals[node,:] <= slot\n",
" parent = self.blocks[block].parent\n",
" if parent == -1:\n",
" return [block] if blocks_seen_by_node[block] else []\n",
" successor_blocks = self.block_slots > self.blocks[parent].slot\n",
" candidate_siblings = np.nonzero(blocks_seen_by_node & successor_blocks)[0]\n",
" return [b for b in candidate_siblings if self.blocks[b].parent == parent]\n",
"\n",
" def run(self, seed=None):\n",
" if seed is not None:\n",
" np.random.seed(seed)\n",
" \n",
" t = 0.0\n",
" \n",
" # emit the genesis block\n",
" self.emit_block(\n",
" t,\n",
" leader=0,\n",
" height=1,\n",
" parent=-1,\n",
" )\n",
" self.block_arrivals[:,:] = 0 # all nodes see the genesis block immediately\n",
"\n",
" while t < self.params.TIME:\n",
" next_block_times = self.params.next_block()\n",
" leader = next_block_times.argmin()\n",
" t += next_block_times[leader]\n",
"\n",
" seen_blocks = self.block_arrivals[leader] <= t\n",
" seen_heights = self.block_heights * seen_blocks\n",
" fork_heads = (seen_heights == seen_heights.max()) * (seen_heights > 0)\n",
" block_ids = np.nonzero(fork_heads)[0]\n",
" parent = np.random.choice(block_ids)\n",
" \n",
" self.emit_block(\n",
" t,\n",
" leader=leader,\n",
" height=self.blocks[parent].height + 1,\n",
" parent=parent\n",
" )\n",
"\n",
" def plot_spacetime_diagram(self, MAX_T=1 * 60 * 60):\n",
" alpha_index = sorted(range(self.params.N), key=lambda n: self.params.relative_hash_power[n])\n",
" nodes = [f\"$N_{n}$($\\\\alpha$={self.params.relative_hash_power[n]:.2f})\" for n in alpha_index]\n",
" messages = [(nodes[alpha_index.index(self.blocks[b].leader)], nodes[alpha_index.index(node)], self.blocks[b].t, arrival_t, f\"$B_{{{b}}}$\") for b, arrival_ts in enumerate(self.block_arrivals.T) for node, arrival_t in enumerate(arrival_ts) if arrival_t < MAX_T]\n",
" \n",
" fig, ax = plt.subplots(figsize=(8,8))\n",
" \n",
" # Plot vertical lines for each node\n",
" max_slot = max(s for _,_,start_t, end_t,_ in messages for s in [start_t, end_t])\n",
" for i, node in enumerate(nodes):\n",
" ax.plot([i, i], [0, max_slot], 'k-', linewidth=0.1)\n",
" ax.text(i, max_slot + 30 * (0 if i % 2 == 0 else 1), node, ha='center', va='bottom')\n",
" \n",
" # Plot messages\n",
" colors = plt.cm.rainbow(np.linspace(0, 1, len(messages)))\n",
" for (start, end, start_time, end_time, label), color in zip(messages, colors):\n",
" start_idx = nodes.index(start)\n",
" end_idx = nodes.index(end)\n",
" ax.annotate('', xy=(end_idx, end_time), xytext=(start_idx, start_time),\n",
" arrowprops=dict(arrowstyle='->', color=\"black\", lw=0.5))\n",
" placement = 0\n",
" mid_x = start_idx * (1 - placement) + end_idx * placement\n",
" mid_y = start_time * (1 - placement) + end_time * placement\n",
" ax.text(mid_x, mid_y, label, ha='center', va='center', \n",
" bbox=dict(facecolor='white', edgecolor='none', alpha=0.7))\n",
" \n",
" ax.set_xlim(-1, len(nodes))\n",
" ax.set_ylim(0, max_slot + 70)\n",
" ax.set_xticks(range(len(nodes)))\n",
" ax.set_xticklabels([])\n",
" # ax.set_yticks([])\n",
" ax.set_title('Space-Time Diagram')\n",
" ax.set_ylabel('Time')\n",
" \n",
" plt.tight_layout()\n",
" plt.show()\n",
"\n",
" def honest_chain(self):\n",
" chain_head = max(self.blocks, key=lambda b: b.height)\n",
" honest_chain = {chain_head.id}\n",
" \n",
" curr_block = chain_head\n",
" while curr_block.parent >= 0:\n",
" honest_chain.add(curr_block.parent)\n",
" curr_block = self.blocks[curr_block.parent]\n",
" return sorted(honest_chain, key=lambda b: self.blocks[b].height)\n",
"\n",
" def visualize_chain(self):\n",
" honest_chain = self.honest_chain()\n",
" print(\"Honest chain length\", len(honest_chain))\n",
" honest_chain_set = set(honest_chain)\n",
" \n",
" layout = Layout()\n",
" layout.hierachical = True\n",
" \n",
" G = Network(width=1600, height=800, notebook=True, directed=True, layout=layout, cdn_resources='in_line')\n",
"\n",
" for block in self.blocks:\n",
" # level = slot\n",
" level = block.height\n",
" color = \"lightgrey\"\n",
" if block.id in honest_chain_set:\n",
" color = \"orange\"\n",
"\n",
" G.add_node(int(block.id), level=level, color=color, label=f\"{block.t}\")\n",
" if block.parent >= 0:\n",
" G.add_edge(int(block.id), int(block.parent), width=2, color=color)\n",
" \n",
" return G.show(\"chain.html\")\n",
"\n",
" def adverserial_analysis(self):\n",
" np.random.seed(0)\n",
" adversary = self.params.N - 1\n",
" \n",
" reorg_depths = []\n",
" honest_chain = self.honest_chain()\n",
" print(\"honest_chain length\", len(honest_chain))\n",
" \n",
" for block in self.blocks:\n",
" nearest_honest_block = block\n",
" while nearest_honest_block.height >= len(honest_chain) or honest_chain[nearest_honest_block.height-1] != nearest_honest_block.id:\n",
" nearest_honest_block = self.blocks[nearest_honest_block.parent]\n",
" \n",
" \n",
" adversary_blocks = []\n",
" already_reorged = set()\n",
" t = block.t\n",
" while t < self.params.TIME:\n",
" adversary_block_t = int(self.params.next_block()[adversary])\n",
" t += adversary_block_t\n",
" adversary_blocks.append(t)\n",
" adverserial_height = block.height + len(adversary_blocks)\n",
" honest_chain_up_to_t = [\n",
" b for b in honest_chain\n",
" if self.blocks[b].t <= t\n",
" ]\n",
" last_honest_block = self.blocks[honest_chain_up_to_t[-1]]\n",
" assert last_honest_block.height >= nearest_honest_block.height, (t, last_honest_block, nearest_honest_block)\n",
" if last_honest_block.height < adverserial_height:\n",
" reorg_depths += [last_honest_block.height - nearest_honest_block.height]\n",
" # reorged_blocks = [\n",
" # b for b in honest_chain\n",
" # if b not in already_reorged\n",
" # and self.blocks[b].height > nearest_honest_block.height\n",
" # and self.blocks[b].height < adverserial_height\n",
" # ]\n",
" # already_reorged |= set(reorged_blocks)\n",
" # reorg_depths += [self.blocks[b].height - nearest_honest_block.height for b in reorged_blocks]\n",
" \n",
" \n",
" plt.hist(reorg_depths, bins=max(reorg_depths, default=1))\n",
" plt.xticks(minor=True)\n",
" plt.title(f\"reorg depths with {self.params.adversary_control * 100:.0f}% adversary\")\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "641d8a34-7549-42fd-ad7c-823c1693ec61",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"blocks 184\n",
"blocks time 0.33m\n",
"honest_chain length 120\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGxCAYAAABIjE2TAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAzdklEQVR4nO3deXRV1f3+8ecSkpuBEEgoCQlTYqPIIFJAKlAChQQ1WpTiAKio/CwUsKRSGcSWoJAAtRQrgsVWQBFBKyoKVcJg1IIVGRRDi7hkrjGWIQHBBMjn9wffXL0kTOWG7MD7tdZZi7PPPufss3PDfbLP5DEzEwAAgENqVHUDAAAATkZAAQAAziGgAAAA5xBQAACAcwgoAADAOQQUAADgHAIKAABwDgEFAAA4h4ACAACcQ0ABAuSdd96Rx+PRO++8U6n7mTFjhubMmXPK/f/tb3+r1P3/LzwejzIzM33zmzdvVmZmprZv316ubteuXdWyZcv/eV9du3aVx+MpN1133XXl6h49elTjx49X06ZN5fV61axZMz355JPl6r377rtq06aNIiMj1aVLF23evLlcnaFDhyolJUUX6uHcmZmZ8ng8F2RfQFWoWdUNAHBuZsyYoXr16umee+6p6qactTVr1qhhw4a++c2bN2v8+PHq2rWrmjZtGvD9JSUl6YUXXvArq1OnTrl6Q4YM0fPPP6/HHntM7du319tvv63hw4fr4MGDevjhhyVJBw4c0C233KKf//znmjJliv70pz+pd+/eysvLU1BQkCTpgw8+0LPPPqsNGzYQGoAAIaCgyh0+fFjh4eEX3b7wnR//+McXdH9hYWFn3GdeXp7++te/auLEiXrooYcknRh92bt3ryZMmKDBgwcrOjpaa9asUXFxsZ566ikFBwerRYsWSkhI0NatW9WsWTMdPXpUv/jFLzRq1Cg1a9bsQhxelTpy5IjCwsIqdR9mpm+//bbS9wO3cYoHF1TZsPT69evVp08f1a1bV5dddpmkE/8pzZgxQ1dffbXCwsJUt25d9enTR1988UW57Tz77LNq3bq1QkNDFR0drVtuuUX/+te//Orcc889qlWrljZt2qS0tDRFRkaqe/fukk78VTxw4EBFR0erVq1aSk9P1xdffFHuVMSp/Pvf/9Z1112n8PBw1atXT4MHD9bBgwcrrLt8+XJ1795dtWvXVnh4uDp16qQVK1ZU2C8bNmxQ7969Vbt2bUVFRenOO+/U119/7avXtGlT5eXlKTc313fq4uQRiKNHj2rs2LGKj49X7dq11aNHD23ZssWvzoYNG3TjjTeqfv368nq9io+PV3p6unbv3n3KY37qqadUo0YNFRQU+Mr+8Ic/yOPxaOjQob6y0tJS1a1bVyNGjPCVfb9f58yZo1tvvVWS1K1bN99xnHzaau3atfrJT36i8PBwJSUladKkSSotLT1l+87Va6+9JjPTvffe61d+77336siRI3rrrbckSd9++628Xq+Cg4MlSbVq1fKVS9Ljjz+ukpISjRkz5pz2n5OTo169eqlhw4YKDQ3VD3/4Qw0aNEj//e9/y9VdsmSJrr76anm9XiUmJurxxx8vV6dNmzb6yU9+Uq78+PHjSkhIUO/evX1lJSUlmjBhgpo1ayav16sf/OAHuvfee/0+a9KJz9uNN96oRYsWqU2bNgoNDdX48eMlSS+//LI6dOigqKgo38/ovvvu86377bffasSIEbr66qsVFRWl6OhoXXvttXr99dfLtdHj8WjYsGF6+umndeWVV8rr9WrOnDlKTk5Wz549y9U/dOiQoqKi/D53uAgZcAGNGzfOJFmTJk1s1KhRlpOTY6+99pqZmd1///0WHBxsI0aMsLfeesvmz59vzZo1s9jYWMvPz/dtIysryyRZ3759bcmSJfbcc89ZUlKSRUVF2WeffearN2DAAAsODramTZtadna2rVixwt5++207fvy4de7c2UJDQ23SpEm2bNkyGz9+vCUnJ5skGzdu3GmPIT8/3+rXr28JCQk2e/ZsW7p0qfXv398aN25skmzVqlW+us8//7x5PB67+eabbdGiRfbGG2/YjTfeaEFBQbZ8+fIK++Whhx6yt99+26ZOnWoRERHWpk0bKykpMTOz9evXW1JSkrVp08bWrFlja9assfXr15uZ2apVq0ySNW3a1Pr3729LliyxF1980Ro3bmzJycl27NgxMzM7dOiQxcTEWLt27eyll16y3NxcW7hwoQ0ePNg2b958yuP+97//bZJs/vz5vrLrrrvOwsLCLDk52Vf2z3/+0yTZ0qVLfWXf79eCggLfz/Cpp57yHUdBQYGZmaWkpFhMTIwlJyfb008/bTk5OTZkyBCTZHPnzj3tz6Zs/dDQUKtbt64FBQVZUlKSPfzww3b48GG/enfccYf94Ac/KLf+oUOHTJKNGTPGzMx27txpwcHBNmPGDNu/f7+NGjXKYmJi7PDhw/b5559beHi45ebmnrFdJ5s5c6ZlZ2fb4sWLLTc31+bOnWutW7e2K664wvfzNjNbvny5BQUFWefOnW3RokX28ssvW/v27X2ftzJPPPGESfL7HTAzW7p0qUmyxYsXm5nZ8ePH7brrrrOIiAgbP3685eTk2F/+8hdLSEiw5s2b+/VTkyZNrEGDBpaUlGTPPvusrVq1yj788ENbvXq1eTweu+OOO2zp0qW2cuVKmz17tt11112+dQ8cOGD33HOPPf/887Zy5Up766237De/+Y3VqFGj3M9RkiUkJNhVV11l8+fPt5UrV9qnn35qTzzxhHk8nnLH9NRTT5kky8vLO+d+R/VBQMEFVfZF/Lvf/c6vfM2aNSbJ/vCHP/iV79q1y8LCwmzkyJFmZrZ//34LCwuzG264wa/ezp07zev1Wr9+/XxlAwYMMEn27LPP+tVdsmSJSbKZM2f6lWdnZ59VQBk1apR5PB7buHGjX3lqaqpfQPnmm28sOjrabrrpJr96x48ft9atW9s111xTrl9+/etf+9V94YUXTJLNmzfPV9aiRQtLSUkp166ygHJy37z00ksmydasWWNmZh999JFJ8gXDc9GwYUO77777zMysuLjYIiIibNSoUSbJduzYYWZmEydOtODgYDt06JBvvZP79eWXXy4X5sqkpKSYJPvnP//pV968eXPr2bPnGds4duxYmzFjhq1cudKWLFliw4YNs5o1a1qXLl3s+PHjvnqpqal2xRVXVLiNkJAQ+8UvfuGbnzFjhoWEhJgki4qKstdff93MzHr06GEDBw48Y5vOpLS01I4ePWo7duwwSb7tm5l16NDB4uPj7ciRI76yoqIii46O9gso//3vfy0kJMQefvhhv23fdtttFhsba0ePHjUzsxdffNEk2SuvvOJXb+3atSbJZsyY4Str0qSJBQUF2ZYtW/zqPv744ybJDhw4cNbHeOzYMTt69KgNHDjQ2rRp47esrF/37dvnV15UVGSRkZE2fPhwv/LmzZtbt27dznrfqJ4IKLigyr6IP/74Y7/ysWPHmsfjsa+++sqOHj3qN/34xz/2fZmX/TX40ksvldv29ddfb7Gxsb75soBSWFjoV2/kyJEmyfbu3etXvn379rMKKNdcc421bNmyXPns2bP9vnRzcnJMkv3tb38rd0xlIafsS7ysXz766CO/bR49etRq1qzp9yV4poDy9NNP+5WXjXwsWLDAzE78ZVu3bl274oorbObMmef0V+iAAQOscePGvv2V/czq1atnf/nLX8zMrFu3btalSxe/9c41oMTFxZUrv+OOO6xZs2Zn3dbvK/tCXbRoka8sNTX1lNsLCQmxQYMG+ZUdOnTI/vWvf9m3335rZmbPPfec1a9f3/bt22d79+61fv36Wb169SwpKalc+K3IV199ZYMGDbKGDRtajRo1TJJvmjRpkm+fNWrUsGHDhpVbv+zz/X0///nPLSEhwRfE9u3bZ16v1x566CFfnf79+1udOnWspKSk3OcyLi7ObrvtNl/dJk2alAsTZma5ubkmydLS0mzhwoW2e/fuCo/xpZdeso4dO1pERITf8YWGhvrVk2S33HJLhdv41a9+ZVFRUb7flRUrVlQYsHDx4RoUVIkGDRr4zX/11VcyM8XGxio4ONhv+uCDD3zn5ffu3Vvh+pIUHx/vW14mPDxctWvX9ivbu3evatasqejoaL/y2NjYs2r73r17FRcXV6785LKvvvpKktSnT59yxzR58mSZmfbt23fabdSsWVMxMTHljut0YmJi/Oa9Xq+kExc3SlJUVJRyc3N19dVX6+GHH1aLFi0UHx+vcePG6ejRo6fddo8ePbRz505t3bpVy5cvV5s2bVS/fn399Kc/1fLly3XkyBGtXr1aPXr0OOv2ns0xlB1H2TGcqzvvvFPSibttvr+Pivr1m2++UUlJSbnPR0REhO+ajb1792rEiBGaNm2a6tatq+HDh2vfvn36/PPPtWDBAv3mN7/RqlWrTtme0tJSpaWladGiRRo5cqRWrFihDz/80Ne+suPcv3+/SktLz+rzJkn33Xef9uzZo5ycHEnSiy++qOLiYr87vr766isdOHBAISEh5T6X+fn55a6Bqeh3rUuXLnrttdd07Ngx3X333WrYsKFatmypF1980Vdn0aJFuu2225SQkKB58+ZpzZo1Wrt2re677z7f9Ttn2o8kPfDAAzp48KDvrqzp06erYcOG6tWrV4X1cfHgLh5UiZNvxaxXr548Ho/ee+893xfq95WVlX1xffnll+Xq/Oc//1G9evVOu5+ybRw7dkz79u3z+xLKz88/q7bHxMRUWPfksrK2PPnkk6e8o+TkUJSfn6+EhATf/LFjx7R3794Kv7DPR6tWrbRgwQKZmT755BPNmTNHjz76qMLCwjR69OhTrld2kfHy5cuVk5Oj1NRUX/kjjzyid999V8XFxecdUCpLjRrf/U1W1gf5+fl+X/abNm2SpNM+i2XEiBFq27at+vbtK0n6+9//rtmzZysqKkrt27dXWlqali5dqm7dulW4/qeffqqPP/5Yc+bM0YABA3zln3/+uV+9unXryuPxnNXnTZJ69uyp+Ph4zZ49Wz179tTs2bPVoUMHNW/e3FenXr16iomJ8V0EfLLIyEi/+VPdNt2rVy/16tVLxcXF+uCDD5Sdna1+/fqpadOmuvbaazVv3jwlJiZq4cKFftsoLi6ucHun2s8Pf/hDXX/99Xrqqad0/fXXa/HixRo/frzvFm9cvBhBgRNuvPFGmZn27Nmjdu3alZtatWolSbr22msVFhamefPm+a2/e/durVy50vcFejopKSmSpIULF/qVL1iw4Kza2q1bN+Xl5enjjz/2K58/f77ffKdOnVSnTh1t3ry5wmNq166dQkJC/NY5+dkdL730ko4dO6auXbv6ys5nJOFkHo9HrVu31h//+EfVqVNH69evP239Bg0aqHnz5nrllVe0bt06X0BJTU3V119/ralTp6p27dpq3779abdz8qhOZZs7d64k/9ude/XqJY/H41tWZs6cOQoLC6vwwW6StGrVKr388suaMWOGr8zM9M033/jmDx06dNoHtpV9GZ8cxv/85z/7zUdEROiaa67RokWL/EYdDh48qDfeeKPcdoOCgnTXXXfptdde03vvvaePPvrI784a6cTv2t69e3X8+PEKP5NXXHHFKdtdEa/Xq5SUFE2ePFnSiTvEyo4xJCTEL3jk5+dXeBfPmQwfPlyffPKJBgwYoKCgIN1///3nvA1UP4ygwAmdOnXSL37xC91777366KOP1KVLF0VEROjLL7/U+++/r1atWumXv/yl6tSpo9/+9rd6+OGHdffdd6tv377au3evxo8fr9DQUI0bN+6M+7ruuuvUqVMnjRgxQkVFRWrbtq3WrFmj5557TpL/X9kVycjI0LPPPqv09HRNmDBBsbGxeuGFF/Tvf//br16tWrX05JNPasCAAdq3b5/69Omj+vXr6+uvv9bHH3+sr7/+WjNnzvRbZ9GiRapZs6ZSU1OVl5en3/72t2rdurVuu+02X52yv/wXLlyopKQkhYaG+gLc2XjzzTc1Y8YM3XzzzUpKSpKZadGiRTpw4IAvcJxO9+7d9eSTTyosLEydOnWSJCUmJioxMVHLli3Tz372M9Wsefr/WspGJ2bNmqXIyEiFhoYqMTHxvEeK3nvvPU2cOFG33HKLkpKS9O233+rvf/+7Zs2apZ/+9Ke66aabfHVbtGihgQMHaty4cQoKClL79u21bNkyzZo1SxMmTCh3ikc68df/oEGDlJmZqcTERF95z5499eijj6p27draunWrVqxYoZEjR56ync2aNdNll12m0aNHy8wUHR2tN954w3dq5vsee+wxXXfddUpNTdWIESN0/PhxTZ48WREREeVOEUonTvNMnjxZ/fr1U1hYmG6//Xa/5XfccYdeeOEF3XDDDRo+fLiuueYaBQcHa/fu3Vq1apV69eqlW2655bT9/Lvf/U67d+9W9+7d1bBhQx04cEBPPPGEgoODfX8AlN2ePGTIEPXp00e7du3SY489pgYNGmjr1q2n3f7JUlNT1bx5c61atUp33nmn6tevf07ro5qqustfcCkquxj066+/rnD5s88+ax06dLCIiAgLCwuzyy67zO6+++5yF4/+5S9/sauuuspCQkIsKirKevXqVe5izwEDBlhERESF+9m3b5/de++9VqdOHQsPD7fU1FT74IMPTJI98cQTZzyOzZs3W2pqqoWGhlp0dLQNHDjQXn/99Qov/MzNzbX09HSLjo624OBgS0hIsPT0dHv55ZfL9cu6devspptuslq1allkZKT17dvXvvrqK7/tbd++3dLS0iwyMtJ3a7LZdxfJfn+7Zmbbtm0zSTZ79mwzO3HRbN++fe2yyy6zsLAwi4qKsmuuucbmzJlzxuM2M99xpqam+pXff//9Jsn+9Kc/lVtHFVx8PG3aNEtMTLSgoCC/9qWkpFiLFi3KbWPAgAG+Yz2VrVu32g033GAJCQnm9XotNDTUWrVqZRMnTvRd3Pp9JSUlNm7cOGvcuLGFhITY5ZdfXmH7yzzyyCPWunVr3x0xZQoKCqxPnz4WFRVljRo1smnTpp22nWbffYYiIyOtbt26duutt9rOnTsr7KvFixf7Pu+NGze2SZMm+T4zFenYsaNJsv79+1e4/OjRo/b4449b69atLTQ01GrVqmXNmjWzQYMG2datW331mjRpYunp6eXWf/PN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",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"np.random.seed(0)\n",
"sim = Sim(\n",
" params=Params(\n",
" TIME=1 * 60 * 60, # seconds\n",
" MEAN_BLOCK_TIME=20,\n",
" honest_hash_power = np.random.pareto(10, size=10),\n",
" adversary_control=0.5,\n",
" ),\n",
" network=NetworkParams(\n",
" mixnet_delay_mean=10, # seconds\n",
" mixnet_delay_var=4,\n",
" broadcast_delay_mean=1, # second\n",
" pol_proof_time=2, # seconds\n",
" )\n",
")\n",
"\n",
"sim.run(seed=1)\n",
"print(\"blocks\", len(sim.blocks))\n",
"print(f\"blocks time {sim.params.TIME / len(sim.blocks) / 60:.2f}m\")\n",
"np.random.seed(0)\n",
"sim.adverserial_analysis()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "e7c9c405-155e-4101-ada6-25b29ca854c7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Honest chain length 120\n",
"chain.html\n"
]
},
{
"data": {
"text/html": [
"\n",
" <iframe\n",
" width=\"1600\"\n",
" height=\"800\"\n",
" src=\"chain.html\"\n",
" frameborder=\"0\"\n",
" allowfullscreen\n",
" \n",
" ></iframe>\n",
" "
],
"text/plain": [
"<IPython.lib.display.IFrame at 0x11a835910>"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sim.visualize_chain()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "37a10d0a-b847-434d-b1b3-69dc52c996cc",
"metadata": {},
"outputs": [],
"source": [
"sim = Sim(\n",
" params=Params(\n",
" TIME=25 * 60 * 60, # seconds\n",
" MEAN_BLOCK_TIME=20,\n",
" honest_hash_power = np.random.pareto(10, size=10),\n",
" adversary_control=0.2,\n",
" ),\n",
" network=NetworkParams(\n",
" mixnet_delay_mean=10, # seconds\n",
" mixnet_delay_var=4,\n",
" broadcast_delay_mean=1, # second\n",
" pol_proof_time=2, # seconds\n",
" )\n",
")\n",
"\n",
"sim.run()\n",
"print(\"blocks\", len(sim.blocks))\n",
"print(f\"blocks time {sim.params.TIME / len(sim.blocks) / 60:.2f}m\")\n",
"sim.adverserial_analysis()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e4acf7a9-105d-4dc9-8a42-bcec57d5ff6e",
"metadata": {},
"outputs": [],
"source": [
"sim = Sim(\n",
" params=Params(\n",
" TIME=25 * 60 * 60, # seconds\n",
" MEAN_BLOCK_TIME=20,\n",
" honest_hash_power = np.random.pareto(10, size=10),\n",
" adversary_control=0.1,\n",
" ),\n",
" network=NetworkParams(\n",
" mixnet_delay_mean=10, # seconds\n",
" mixnet_delay_var=4,\n",
" broadcast_delay_mean=1, # second\n",
" pol_proof_time=2, # seconds\n",
" )\n",
")\n",
"\n",
"sim.run()\n",
"print(\"blocks\", len(sim.blocks))\n",
"print(f\"blocks time {sim.params.TIME / len(sim.blocks) / 60:.2f}m\")\n",
"sim.adverserial_analysis()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "89bf9a0f-7f47-4216-80e8-5e6e24998f3b",
"metadata": {},
"outputs": [],
"source": [
"N = 100\n",
"net_params = [NetworkParams(\n",
" mixnet_delay_mean=0.1, # seconds\n",
" mixnet_delay_var=0.1,\n",
" broadcast_delay_mean=0.1, # second\n",
" pol_proof_time=i/N * 5, # seconds\n",
" ) for i in range(N)]\n",
"\n",
"sims = [Sim(\n",
" params=Params(\n",
" TIME=5 * 60 * 60, # seconds\n",
" MEAN_BLOCK_TIME=20,\n",
" honest_hash_power = np.random.pareto(10, size=10),\n",
" adversary_control=0.1,\n",
" ),\n",
" network=net\n",
") for net in net_params]\n",
"\n",
"[sim.run() for sim in sims]\n",
"\n",
"\n",
"plt.scatter([sim.network.pol_proof_time / sim.params.MEAN_BLOCK_TIME for sim in sims], [100 - 100 * len(sim.honest_chain()) / len(sim.blocks) for sim in sims])\n",
"plt.ylabel(\"wasted blocks %\")\n",
"plt.xlabel(\"PoL time as fraction of mean block time\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1c21cfba-68b3-487b-a273-76776defddca",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "776cc7df-7308-45e8-a8ec-6130824623a7",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "eb22240b-a5fb-4470-801d-1598922080b2",
"metadata": {},
"outputs": [],
"source": []
}
],
"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.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}