{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Fast Fibonacci in Python\n", "============================\n", "The Fibonacci sequence is a classic example of a recursive relationship. It has the following definition\n", "$$\n", "\\begin{align*}\n", "F_n &= F_{n-1} + F_{n-2} \\\\\n", "F_0 &= 0 \\\\\n", "F_1 &= 1\n", "\\end{align*}\n", "$$\n", "There are a variety of ways to implement a function to calculate the n'th Fibonacci number.\n", " - **Top-down recursive**: This is one of the worst possible ways to implement Fibonacci, at least in terms of speed. This is because for each call to `fib(n)`, we make a call to `fib(n-1)` and `fib(n-2)`. For the `n-1` call we then call `fib(n-2)` and `fib(n-3)`. We've already had to calculate `fib(n-2)` twice, and this problem just gets worse the further we go down the call tree until we reach the base cases. The time complexity of this approach is $O(2^n)$ which is obviously unacceptable. This problem can be alieviated by caching already-calculated results which will help tremendously, but is still not the ideal solution.\n", " - **Bottom-up looping**: This is a much more reasonable solution. All we do here is start with the base cases and use the recursion rule to calculate the next 2nd Fibonacci number, then the 3rd and so on in a loop until we reach the one we are looking for. The time complexity of this solution is $O(n)$. Much better than the exponential time above, but we can still do better.\n", " - **Binary Matrix Exponentiation(BME)**: We can write the recursive formula as a matrix operation. To see how we do this, first consider a 2-element column vector with the elements $F_{n-1}$ and $F_{n-2}$.\n", " $$\n", " \\begin{pmatrix}\n", " F_{n-1} \\\\\n", " F_{n-2}\n", " \\end{pmatrix}\n", " $$\n", " We can advance this matrix to the next higher pair of Fibonacci numbers ($F_{n}$, and $F_{n-1}$) by multiplying this vector by the following matrix.\n", " $$\n", " \\begin{pmatrix}\n", " 1 & 1 \\\\\n", " 1 & 0 \n", " \\end{pmatrix}\n", " $$\n", " Finally notice that if we multiply the result by this matrix again, it advances our set of number twice, and if we start with our base case and multiply $n-1$ times, we can get the n'th Fibonacci number. Therefore, we have\n", " $$\n", " \\begin{pmatrix}\n", " F_{n} \\\\\n", " F_{n-1}\n", " \\end{pmatrix}\n", " = \n", " \\begin{pmatrix}\n", " 1 & 1 \\\\\n", " 1 & 0\n", " \\end{pmatrix}^{n-1}\n", " \\begin{pmatrix}\n", " 1 \\\\\n", " 0\n", " \\end{pmatrix}\n", " $$\n", " Now if we can find a fast way to find the n'th power of a matrix, we would be golden. Turns out there is a fast way to do this. We just apply the [Binary Exponentiation](http://en.wikipedia.org/wiki/Exponentiation_by_squaring) algorithm which can find the n'th power of something in $O(\\log(n))$ time. Since the remaining operations after we have found the power of the matrix are constant in time, the overall time complexity of our algorithm is now $O(\\log(n))$. \n", "\n", "**NOTE:** While the BME algorithm has the best time complexity for calculating the n'th Fibonacci number, we shouldn't necessarily always use it. For small n, for example, it's likely that the simple loop algorithm will run a bit faster just because it lacks some of the extra machinery required to run BME(We'll actually see this later). Furthermore, if we are interested in calculating the Fibonacci *sequence*, meaning all of first n Fibonacci numbers, then BME has a problem because it necessarily leaps over some numbers in the sequence(hence it's speed). But this means that we would have to make a separate call to BME for each number. Turns out, this leads to $O(n\\log(n))$ time complexity which is *worse* than the simple loop algorithm.\n", "\n", "-------------------------------------------\n", "This is all fine and good, but I wanna see some code! Okay, well since we are so hung up on doing this fast, it's probably worthwhile to make our code as efficient as possible, but we also don't want to do something crazy like not use python for once, so we're going to implement some of thise algorithms in both raw python and also using cython to get some compilation goodness. Then we will compare the speeds of the various implementations.\n", "\n", "Ooohkay! Let's get booted up!" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "27/03/2015 \n", "\n", "CPython 3.4.3\n", "IPython 3.0.0\n", "\n", "cython 0.22\n", "numpy 1.9.2\n", "matplotlib 1.4.0\n", "\n", "compiler : GCC 4.9.2 20150304 (prerelease)\n", "system : Linux\n", "release : 3.19.2-1-ARCH\n", "machine : x86_64\n", "processor : \n", "CPU cores : 4\n", "interpreter: 64bit\n" ] } ], "source": [ "%reload_ext Cython\n", "%load_ext watermark\n", "%watermark -dvm -p cython,numpy,matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following cell contains python implementations of the bottom-up looping and BME algorithms described above. Note I've chosen to just use an ad-hoc 2x2 matrix multiplication function rather than involve numpy. Maybe it's a bit simpler? Eh. A matter of taste." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def fib(n):\n", " a,b = 0., 1.\n", " for _ in range(n):\n", " a,b = a+b,a\n", " return a\n", "\n", "E00,E01,E10,E11=0,1,2,3\n", "\n", "tmp = [0,0,0,0]\n", "def mat_prod(m1, m2): #m1 *= m2\n", " for i in range(4): tmp[i] = m1[i]\n", " m1[E00] = tmp[E00]*m2[E00] + tmp[E01]*m2[E10]\n", " m1[E01] = tmp[E00]*m2[E01] + tmp[E01]*m2[E11]\n", " m1[E10] = tmp[E10]*m2[E00] + tmp[E11]*m2[E10]\n", " m1[E11] = tmp[E10]*m2[E00] + tmp[E01]*m2[E10]\n", "\n", "def mat_square(m1): #m1 = m1**2\n", " for i in range(4): tmp[i] = m1[i]\n", " m1[E00] = tmp[E00]*tmp[E00] + tmp[E01]*tmp[E10]\n", " m1[E01] = tmp[E00]*tmp[E01] + tmp[E01]*tmp[E11]\n", " m1[E10] = tmp[E10]*tmp[E00] + tmp[E11]*tmp[E10]\n", " m1[E11] = tmp[E10]*tmp[E00] + tmp[E01]*tmp[E10]\n", "\n", "def fib_bme(n):\n", " x = [1.,0.,0.,1.]\n", " p = [1.,1.,1.,0.]\n", " N = 1\n", " while N <= n:\n", " if n & N:\n", " mat_prod(x,p)\n", " mat_square(p)\n", " N <<= 1\n", " return x[E10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The cell below contains the cython implementation of the algorithms. I've tried to keep a close correspondence between the two implementations. " ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython --force\n", "\n", "cdef float fib(int n):\n", " cdef int i\n", " cdef float a=0.0, b=1.0\n", " cdef float tmp\n", " for i in range(n):\n", " tmp = a\n", " a += b\n", " b = tmp\n", " return a\n", "\n", "cdef unsigned int E00=0, E01=1, E10=2, E11=3\n", "\n", "cdef float tmp[4];\n", "cdef void mat_prod(float *m1, float *m2): # m1 *= m2\n", " cdef unsigned int i\n", " for i in range(4):\n", " tmp[i] = m1[i]\n", " m1[E00] = tmp[E00]*m2[E00] + tmp[E01]*m2[E10]\n", " m1[E01] = tmp[E00]*m2[E01] + tmp[E01]*m2[E11]\n", " m1[E10] = tmp[E10]*m2[E00] + tmp[E11]*m2[E10]\n", " m1[E11] = tmp[E10]*m2[E01] + tmp[E11]*m2[E11]\n", "\n", "cdef void mat_square(float *m1): # m1*= m1\n", " cdef unsigned int i\n", " for i in range(4):\n", " tmp[i] = m1[i]\n", " m1[E00] = tmp[E00]*tmp[E00] + tmp[E01]*tmp[E10]\n", " m1[E01] = tmp[E00]*tmp[E01] + tmp[E01]*tmp[E11]\n", " m1[E10] = tmp[E10]*tmp[E00] + tmp[E11]*tmp[E10]\n", " m1[E11] = tmp[E10]*tmp[E01] + tmp[E11]*tmp[E11]\n", " \n", "cdef float fib_bme(unsigned int n):\n", " cdef float *x = [1.,0.,0.,1.]\n", " cdef float *p = [1.,1.,1.,0.]\n", " cdef unsigned int N = 0x1\n", " while N <= n:\n", " if n & N:\n", " mat_prod(x,p) #x *= p\n", " mat_square(p) #p *= p\n", " N <<= 1\n", " return x[E10]\n", "\n", "def cfib_bme(n):\n", " return fib_bme(n)\n", "def cfib(n):\n", " return fib(n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we just run the code for a wide range of n values and see how quickly each implementation runs." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import time\n", "import numpy as np\n", "data = {fib : [],\n", " cfib : [],\n", " fib_bme : [],\n", " cfib_bme : []\n", " }\n", "ns = np.logspace(1,6, num=10, dtype=np.int64)[::-1]\n", "N = 40\n", "for n in ns:\n", " for func,times in data.items():\n", " t_start = time.time()\n", " for _ in range(N): \n", " func(n)\n", " t_end = time.time()\n", " times.append(t_end-t_start)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": [ "iVBORw0KGgoAAAANSUhEUgAAAsgAAAHmCAYAAABu5XitAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n", "AAAT/gAAE/4BB5Q5hAAAIABJREFUeJzs3Xm8nHV59/HPlRAghiQnBAIoAglLtaWlCWCfVktVAmj7\n", "aGsTQOtWrUlQcAchbqdTITsgKEISSkutGwFculhMQq36PNUatkdxJQm4sMRATsK+5Xr+uH7DmcyZ\n", "OWfOmXvmnnvm+3695pXMb+bc9+9kTs5c87uv33WZuyMiIiIiImFc3hMQEREREekkCpBFRERERCoo\n", "QBYRERERqaAAWURERESkggJkEREREZEKCpBFRERERCooQBYRERERqaAAWURERESkggLkOsxstZnN\n", "znseIiIiItJeCpCrmNk8M1sGnA6ozaCIiIhIjzG1mq7NzO4C5rv77XnPRURERETaRyvIIiIiIiIV\n", "9sp7Alkzsz5gLbDE3W+r8fh8YCYwABwJrHf3je2dpYiIiIh0qq4JkNOGujOBB4F5wJU1njMXWOju\n", "p1aMbTKzBbWCaRERERHpPV2TYuHut7n7Be6+cpinLQeuqxpbncZFRERERLonQB5JSr2YDWypemgH\n", "MLfel7V0UiIiIiLScXomQAZmpT8fqhofADCzI9KfJ6cyb7OA5WZ2XrsmKCIiIiL565oc5AbsX2e8\n", "HDD3AaQNexuBC9oxKRERERHpLL0UINdTL3AeFTN7nPj33J7F8URERERkiAOAZ9x9YitP0kspFuWV\n", "4uqAuC/9OdDk8fcCxgMz6twmNXn8sWjFOZs55mi/ttHnj/S84R6v99hox/Oi13jkx0fzWnba6wt6\n", "jRt5XK9xtsdsxWvc7HP0Gmd7zDxe49G+vpOI+Okg9oyn9gL2aWA+TemlFeTy5ry+Wg+6+91NHn87\n", "MMPdxzd5nMyY2f3ufkinHHO0X9vo80d63nCP13tstON50Ws88uOjeS077fUFvcaNPK7XONtjtuI1\n", "bvY5eo2zPWYer/FYXt+Kxw6uuH8fESi3VM+sILv7ALABmF710CxgfftnJCIiIiKdqOsC5FTODWqX\n", "aFsOLKoaO4Ps6iCbmd1fcTs3o+OKiIiI9CQzO7ccWxGrxy0vw9s1KRZmNpMIfucADqw2s+upaCXt\n", "7hvNbGkq47aZSLdY4u43ZzQNr7wM0AFWddgxR/u1jT5/pOcN93i9x1rxb9cKeo1HflyvcbbH1Guc\n", "vV54jZt9jl7jbI+Zx2s8ltcXYJW7ryo/p10pFuburT5HTyi/YJ2UgyzZq86Fku6i17f76TXufnqN\n", "u1u74q2uS7HImVIsRERERDKkFIvie0SfWrteUS7nydjo9e1+eo27n17jLlMjxaLlpfyUYpGR9ILR\n", "aaVlRERERLpFu+ItpVhka5JSLERERESyUyPFQivIRdHoJxozu4M27L4UacA2dz8u70mIiIg0SivI\n", "3avcNlEkT+XWnSIiIlJFm/Ty8YBylSVP5U/gIiIiMpRWkLOlHGQRERGRDCkHucBGkYOsaheSO/0c\n", "iohIESkHWUREREQkBwqQRUREREQqKEAWEREREamgADlb2qQnIiIikiFt0iswbdKTItHPoYiIFJE2\n", "6UnPMrP5ZrbZzHab2YI0trDyfhrbbGa785upiIiIdCM1CpGOYmZ9wHXp7npgc9VTqi956BKIiIiI\n", "ZEoBsnSaWenPde5+ZsX4emAhsKH9UxIREZFeohSLbGmTXnZ2VN5x963ufrW7353TfERERCQHeWzS\n", "U4CcrUfd/eCK26q8J1QkZrYe2JTulnOOz02PDclBrvra5Wa2Iz1nk5md1655i4iISOu4+6pybAVs\n", "Ax5t9TmVYiGd5Coi53ghcAuRTlGdUlGdc2wpsD6ZWHXeDMwB5pjZKe5+amunLCIiIt1GK8jSMdz9\n", "BmB1urvJ3Re7++0NfOkrgfnuPt3djwaOBAaAufVWnEVERETq0QpywZhxB5F/k4dt7hzX4nPYGL5m\n", "jbvfWL7j7lvN7HRiY9/5wNqsJiciIiLdTyvIUnTO4Krz4KD7RmArMNPMprR9ViIiIlJYWkEumDas\n", "4BbOMGkYW4CZROm4RlI1REREpKONGwe7W94kTCvI0s0G8p6AiIiINM+MQ8y4BGa0Jc1UAXK2VAc5\n", "B8OkUMwiUjC2tHE6IiIikhEzXmB21rfh4Hvh4PdHlTfVQS4a1UFuPwNOGTIYLatnA7j7rnZPSkRE\n", "RMbOjMPMuALYAle9DO7/Ddx/PvgDtKEOsgJk6QbLhxlb0c6JiIiIyNiZcYQZq4G7gHcRPQ4+CMx0\n", "ZwV4dT+EltAmPSm6AWCWmW0iqlnsAM4E5hHpFUtznJuIiIg0wIwjgQ8DbyHi03uJxa617jze7vko\n", "QJaicIZ20XOic94yYB17lnvbARyv9AoREZHOZcYxwEeANwLjgV8Ri1vXuPNEbvPy9qxUdz0zuw/A\n", "3Q/J4nkyOmY2FTgDmArc6u435zyljqafQxERyZMZLyYC4zcQKb/3AEuAa915sv7Xtef9SyvI0hXc\n", "fSfqmCciItLRzDgW+CixqGVEpamLgM+683Sec6ukAFlEREREWsqM44CPEXuEIDbhXQh8vpMC4zIF\n", "yCIiIiLSEmYcTwTGf56GfkIExl9y55ncJjYCBcgiIiIikikzXgJ8HPizNHQn8AngeneezW1iDVId\n", "5Gypk56IiIj0LDP+0Iz/AL5HBMf/D5gP/J47XxpLcGxm55ZjK2AGbeikpyoWGVEVCykS/RyKiEiW\n", "zPhjYsV4bhq6Dfg74Gvu7M7uPKpiISIiIiIdygwDXk4Exi9Pw98nAuN/cx/Sv6AwFCCLiIiISMNS\n", "YDyXCIxfloa/C5SAm4ocGJcpQBYRERGREaXA+DQiMP7DNPwdIjDe2A2BcZkC5Cpm1gdcQLQwPhJY\n", "7e5b852ViIiISD5SYPxnRGB8Yhr+JpFK8c1uCozLFCAPtQ6Y5+67AMxsE3BCvlMSERERaS8zxgGv\n", "JQLj2Wl4A/AJd76V28TaQGXeKqTV4+PLwXHF+Mk5TUlERESkrcwYZ8Z8ohLFl4ng+D+Al7pzSrcH\n", "x6AAudoJwENVYw8Bs3KYi4iIiEjbmDHejNcTtYvXAb8H/CvwB+682p3/m+sE26jrAmQz6zOzdWY2\n", "u87j883sPDNbYGbLqlaH+2p8yQAwtSWTlaal13Ozme02swVpbGHl/TS22cwyq8MoIiLSLczYy4w3\n", "Ed3uvgD8DvBV4AR3XuPO/+Q6wRx0TQ5yCojPBB4E5gFX1njOXGChu59aMbbJzBa4+21EMFytr864\n", "5CylxFyX7q4nNlZWqt400HWbCERERMbKjL2ANwIfAY5OwzcAF7pze24T6wBdEyCnAPc2ADNbXudp\n", "yxkaOK9O46cCm4D9qx7fH9iS3UwlQ+XUl3XufmbF+HpgIbGRQERERCqYsTfwZuDDxHupA18iAuMf\n", "5jm3TtE1AfJI0mrjbIYGuztIbRHdfcDMvm9mU919Z/kJ7n5z+2YqY7Cj8k4qy3d1TnMRERHpSGbs\n", "A/w1sBg4HNgNfA64yJ0f5zi1jtN1OcjDKK82Vm/CGwAwsyPS/TOA5SlH+SrgHW2Znewh5RGvT7nE\n", "m83sKjObWfH4emLFH6Ccc3xuxdfukYNcdezlZrYjPWeTmZ3X+u9IREQkH2bsa8bZwF3AVcChwLXA\n", "i915k4LjoXpmBZmhqRNl5YC5DyCtHJ+Vxta2elIylJmtI/LIHbiF+HCzkAiEj0/pNFcROccL03M2\n", "MDSlojrn2FJgfTKx6rwZmAPMMbNTKnPTRUREis6MicAC4Hzg+cAzwDXAUnfuynNuna6XVpDrqRc4\n", "Sw7MbD4RHN8CTHP3E919OvGfG9KHFne/gcgfB9jk7ovdvZENBa8E5rv7dHc/muiWOADMrbfiLCIi\n", "UiRmTDLjA8BW4DLgQGANcIw7f6PgeGS9FCCXV4qrA+JyaTdVqugMy4mV39MrG7a4+0oif7yyfJ+N\n", "4fhr3P3GiuNuBU5Pd8+v/SUiIiKdz4z9zPgQERhfDEwDPgMc5c4id7bmOsEC6aUUi/LmvFq1jnH3\n", "uzM4xyQzu7/OY6vcfVWzJ7CS3QHMaPY4Y7TN+/24Vh08baScCdxS5/U4JT0+Vs7gqvPgoPtGM9sK\n", "zDSzKdWdFEVERDqZGVOAc4APANOBJ4DLgRXu/DrPuY1V2ld0bo2HDgQebfX5eyZAThUqNhA/OJVm\n", "EWXBsvCoux+S0bF6UXkjZc2yemm1t6lPv8OkYWwhgu9Z0Nu1H0VEpBjM6APeA7yPWC1+HLgEWOXO\n", "fXnOrVlpUXHIwqKZteX76roAOa1CQu3L78vTrXLz3RnAh1o9r6y0cgW3A5TTX6orjbSDUmxERKQQ\n", "zNgfeG+6TQUeA1YSgfG2POfWLbomQE4lwBYRVQkcWG1m1wPr3X0jPHcpfamZLSMqGPQBSzKsc1yd\n", "YpFJWkUPKXfCa9nGyWFSKMqF0tUURkREOpIZBwDvB94NTAYeAZYCl7rzmzzn1kpV6RZKsRiNdPn9\n", "ggaedwPRRrEVlGLRBHffamaQGrdUqyj/NmuMOeNG5DHv8fpXNJFx5R+LiEinMWMG8EHgbGASsAv4\n", "BPBJ91yuurZVZbpFu1IseqmKhRTD9cC06pJrKYidB+xockNlrTbk5bEVTRxXREQkU2YcasZlwN1E\n", "OujTQD9wuDsf74XgOC9ds4LcIZRi0bzzgflEisyRRPOPPgaD2KVNHHsAmGVmm4hqFjuAMxlsStLM\n", "sUVERDJhxlHE++FbgQnAg8BFwKfd2Znn3PKgFIviU4pFk1KaxfHARuLTcnkDpQOrG/zA4QztoudE\n", "jvMyYB17lnvbARyv9AoREcmTGccCi4HXE1f57yVSC9a4tz4o7FR5pFiYe3UcIWNRfsFGCpAbfZ6A\n", "mZ1M5AZvAW7NqFY1ZjaVqF4yNR03q02ahaGfQxGRzmHGicCHgb9IQ1uJK6f/6M6TuU2sA7Xr/UsB\n", "ckbSCzaJKLVSNiTFQoGJdAL9HIqI5MsMA04CPkJsIAf4MZHu9wV3nslrbp2mVoqFu09p6TkVIGdD\n", "K8hSJPo5FBHJRwqMX0UExi9Nw7cSOcZfcWd3XnMrgna9fykHWURERKTFzBgHvI5IpZiThr9DBMY3\n", "uQ/ZOyM5UoAsIiIi0iJmTADeQGy+e1EavglY4s63cpuYDEt1kLM1yczur7idO/KXiIiISLcxY18z\n", "zgJ+BlxLBMdfBk5051UKjhtnZueWYytgBrHnq7XnVA5yNpSDLEWin0MRkdYwYxKwiNhUdgiwG/gC\n", "sNSdO/OcWzdQDrKIiIhIQZjRB5wDvA+YTnS9Wwssd2dznnOT0VOALCIiIjJGZswA3g+cDUwGHgcu\n", "A1a586s85yZjpxzkbCkHWUREpAeYcagZlwF3AxcQHVuXAIe78z4Fx9lRDnKBKQdZikQ/hyIiY2PG\n", "UcD5wFuBCcCDwKXAFe4M5Dm3XqAcZBEREZEOYcaxRKm21xNX4O8FVgFr3Hk0z7lJ9hQgi4iIiNRh\n", "xolE17s/T0NbgWXAte48mdvEpKUUIIuIiIhUSO2gTyIC41PS8I+JHOMvuvNMXnOT9lCALCIiIsJz\n", "gfGriMD4pWn4VqId9Ffc2Z3X3KS9VMUiW6pikQEzm29mm81st5ktSGMLK++nsc1mltkvq6yPJyIi\n", "xWDGODPmA7cA/04Ex98BXg2c4M6NCo7zk0cVC60gZ+tRVQVojpn1Adelu+thSHH16rIrWZdhUVkX\n", "EZEeYcYE4A3E5rsXpeGbgIvc+XZuE5M9uPsqYkPkc1UsWk0BsnSaWenPde5+ZsX4emAhsKH9UxIR\n", "kW5ixr7AXxPl2o5Iw18GlrizKadpSQdRgCydakflHXffClyd01xERKQLmDEJWAScCxwC7AY+Byx1\n", "58485yadRTnI0jHMbD0898m9nHN8bnpsSA5y1dcuN7Md6TmbzOy8Jucy4vHM7EPp8ZlVedPPPd/M\n", "+sxsXdVjs+ucc256buV55zXzfYiICJjRZ8bHgHuAi4HpwBrgGHfepOBYqmkFWTrJVUTO8UJio8QG\n", "hqZUVOcIWwqsTyZWnTcDc4A5ZnaKu586yjmM5XgXAAuINJC7iJJAc8zsKGAu0Je+j93pWBvNbKa7\n", "76w46cL0/TuwBdienrvOzNa4+1mj/D5ERHqeGTOA9wNnA5OBx4FPAqvc+XWec5POphVk6RjufgOw\n", "Ot3d5O6L3f32Br70lcB8d5/u7kcDRwIDwNx6K84ZH+8dwBx3P83dTwNOT+MLgLvScc5Mx7qVCJjn\n", "lr/YzGYx+OHgSHc/Oj33eCJYXqiVZBGRxplxqBmXAXcTixhO1DA+3J33KziWkWgFOVuTUgmSslVp\n", "52V2zO4gSpzkYRvux7X4HDaGr1nj7jeW77j7VjM7nVjRPR9Y2+LjrakM5N39BjOD+IW8qOq5XyJW\n", "hqdVjC1Pf57u7ndXHOc2M1uUzrsIuGGU34eISE8x4yji9/RbgQnE1biLgCvcGchzbjJ2Kd2yXDr3\n", "QGh9a28FyNlSmbf2cwZXnQcH3Tea2VZgpplNcfddLTze+jrHGqgMeJNyWkVfxdhcYEet1fJ0XoiU\n", "DxERqcGMY4EPA2cSV8fvBVYCa91bH0xJa6nMm4ys9Su4hTNMGsYWYCZROq6RVI2WHK8BUwHUpERE\n", "ZHTMOJHoevfnaWgLcVXuWneezG1iUngKkKWbZX05LfPLc6kxSvnYX8r6+CIi3Sa1gz6JCIxPScM/\n", "ApYCX3TnmbzmJt1DAbIU3jApFLMYrAqR1fEY7fGG4+4DKYXiQXd/Z1bHFRHpNikwfjWRSvHSNHwr\n", "kWP8FbWCliypioUUnTG4gjA4GCuzswFGkX/cyPF8lMdrxFZglplNrXXeVBu5qbrOIiJFZcY4M+YT\n", "5T//jQiOvw28CjjBnRsVHEvWFCBLN1g+zNiKDjjeSK4iAvNa1TaWA/MYWv9ZRKSrmTHBjLcAdwLr\n", "iEWKm4CT3DnJnZvc9btRWkMpFlJ0A8Tq6yai+sQOYhdzOahcmvPxRuTuK81sMTDfzL6RzruTKO02\n", "j6iPvCbr84qIdCIzJgJvA84DjkjDNxLtoDfV+zqRLClAlqJwhq6iOhE8LiNWFyrLs+0Ajq9Mhxim\n", "SoS7+/jRHq/OnMbyfUBUx9hIlHybWzF+C1EfOeu0DhGRjmLGFOCdROe7g4Bngc8BS9z5UZ5zk95j\n", "7ro6kYVyXb6R6iA3+jwZnZS/ewZRMu1Wd7+5k443ivPOJjroAWxp1Xn1cyginSK1g34v0Q56KvAk\n", "cA2w0p2tec5NOk+73r8UIGckvWCTgMcqhod00lNgIp1AP4cikjczDiO6o70DmAg8DFwJXOrO/cN9\n", "rfSWWp303H1KS8+pADkbWkGWItHPoYjkxYwXE+2g30ikem4HPonaQUsD2vX+pRxkERERaTkzTgAW\n", "A68jKvf8kmgffLX7HldfRXKnAFlERERaIjX3eAURGJc3IP+U2Az9eXeeymtuIsNRgCwiIiKZMmMc\n", "8Bqi691L0vAtRKnMr7jzbF5zE2mEAmQRERHJhBkTgNcDFwC/nYb/kwiMN6ixhxSFOunVYWarU8kt\n", "ERERGYYZE814F/Az4J+I4PhrwB+680p31is4liLRCnIVM5sHnAicTpSbERERkRrMmMpgc48ZRHOP\n", "fwaWu/PDPOcm0gwFyFXc/QbgBjObn/dcREREOlGd5h5XouYe0iUUIIuIiEhDzDicweYe+xLNPZYD\n", "n1RzD+kmhQiQzawPWAsscffbajw+H5gJDABHAuvdfWN7ZykiItKd6jT3uBA195Au1dEBctokdybw\n", "IDCPGjnBZjYXWOjup1aMbTKzBeVgOuUVnzLMqTa7+8pMJy8iIlJwZpxI1DD+Cwabe6wE/l7NPaSb\n", "dXSAnALccpC7vM7TljM0cF6dxk9Nx7kBuKFF0xQREekaFc09PgycnIZ/QryvqrmH9ISODpBHklIv\n", "ZgNbqh7awWDHnqZOkcExREREOl5q7vFaYsW43NxjE4PNPXbnNTeRdit6HeRZ6c+HqsYHAMzsiNEe\n", "0MxONrNl6djLzey8ZiYorWVm881ss5ntNrMFaWxh5f00ttnMMvnlnuWxRETyZsYEM94M/AD4MhEc\n", "/ydxFfYl7tyo4Fh6TaFXkIH964yXA+a+0R4wbe7bSHQBkg6WriBcl+6uBzZXPaW6KH2WRepV8F5E\n", "Cs2MicDbgfOAw9PwV4Gl7nwvt4mJdICiB8j11AucpbuUryCsc/czK8bXAwuBDe2fkohIZ1NzD5GR\n", "FT1ALq8UVwfE5ZVjlZ7pDTsq77j7VuDqnOYiItKRUnOP9xHNPaYQzT0+A6xScw+RPRU9B7m8Oa9m\n", "KoW7392+qQAwyczur3M7t81zKbSUR7w+5RJvNrOrzGxmxePric0jAOWc43MrvnaPHOSqYy83sx3p\n", "OZuayTNv5Fhm9qH0+MyqnOnnnm9mfWa2ruqx2XXOOTc9t/K888b6PYhIdzPjcDM+DdxDbMAzoiLF\n", "Ee6creBYOpGZnVsrniKuekxq9fkLvYLs7gNmtgGYXvXQLOIye7s96u6H5HDermJm64i61w7cQrye\n", "C4lA+PhU/u8qIud4YXrOBoamVFTnCVsKrE8mVp03A3OAOWZ2SmUt7camOepjXQAsIH427yJqc88x\n", "s6OIqit96XvYnY610cxmuvvOipMuTN+7Ex8Qt6fnrjOzNe5+1ii+BxHpYmb8NtHc46+I9/vfAJ8A\n", "PqPmHtLp3H0VsKp63Mzua8f5C7GCnDZjQe2ya8uBRVVjZ6TxdqteQdaq8SilrojziKB3mruf6O7T\n", "iV/yEB0Vy7WtV6exTe6+2N1vb+AUrwTmu/t0dz+a6Lw4AMytt+Kc4bHeAcxx99Pc/TTg9DS+ALgr\n", "HefMdKxbiYD5uXKFZjaLwQ8GR7r70em5xxPB8kKtJIuIGS8x48vAncBbgHuBdxMrxksUHEvRVK4m\n", "oxVkSJfUFxErZA6sNrPrqWgl7e4bzWxpKs22mQgqlrj7zTlMueUryPbNb95B/HDkYZu//OXHtfgc\n", "y4nX+nR331UedPeVZraIqHtdNpY61Wvc/caK4241s9OJVd3zSQF4i461pjKId/cbzAzi+63+kPcl\n", "4ud+WsVY+UPf6ZXpQ+5+W/q3WZ+Oo6Y4Ij0mNfd4JZFCUdncYxnR3OPpvOYm0qzK1eR2rSB3dICc\n", "NluNWG5NnfK6Q7pSMBO4pU7++Cnp8bFyBledBwfjQ9ZWYKaZTakMzDM+Vr20n4Ea3285raIyv34u\n", "sKPWSnk6Lwy+MYpIDximuccS4KuqXywyNh0dIBfQpLT8X7YqferJTBtWcPNULttW3RkReO4DU1Ob\n", "SYZJw9hCBN+zgEZSNTI9VoOmAqhJiYiYMQF4A3G16rfT8M1E17uN7qrVLt0jpayW01YPBB5t9TkV\n", "IGdLm/SaUy7XV90ZsR2yzMnLPL+vIg9/gEi/EJEeVKe5x1eAZWruId1KKRbS68qd8FrW6GWYFIpZ\n", "DFaGyOJYjOZYI0kVWwAedPd3ZnVcESmG1NzjXUQd43Jzj88SzT3uzHNuIt2oEFUspDekFAqoqNxQ\n", "KdX+3W1mR4zxFEbkMVcft4+0+a/B/ONGjuWjOFajtgKzzGxqrfOmf58x13QWkc5jxkFmLAV+QeQV\n", "TyGaexzlzlsUHIu0hgLkbKnMW/OuB6ZVl0lLgec8YpPa3U0cv1b5v/LYihyP1YiriMC8VqWN5QzW\n", "jhaRgjPjiNTc424GN6svY7C5x915zU2k3VTmrfiUg9y884H5REm/I4nGGX0MBp5Lmzj2ALECu4mo\n", "QLEDOJPBwHI0x87yWA1Jpe4WA/PN7BvpvDuJ0m7ziBSVNVmfV0Tap6K5xxuB8ai5h4hykEVSLeHj\n", "gY3Ah9INUlm1BquCOENXUp0IIJcB69izRNsO4PjKlIgRKkVsHs2x6sxnLN8DRHWMjUQaSmUqyi1U\n", "1Y4WkeIw43jgI8Dr0tAvgJXANe48ltvERHqUueuKbBbSJ5pJsMcvsiFl3sqffLTSPDIzO5nI590C\n", "3NpkakXlcacS3RanpuOOualMlsca5XlnEx30ALaM9rz6ORTpDGa8jAiMX5WG1NxDpEqtMm/uPqWl\n", "51SAnI1GAw4FJtIJ9HMokp/U9W4u8FHgpDR8O3AR8GV3ns1rbiKdrl3vX0qxEBERaYPU9e41xIrx\n", "iWn4v4ELga+ruYdI51CALCIi0kJmjAdOJwLjY9PwRmLF+JsKjEVGxWhDxSYFyCIiIi1gxt7Am4gy\n", "bUen4X8FLnLnu7lNTKSArGTTgHOYxAwe5YFWn08BcrYmpRp9ZUM26YmISHeraAf9IeAwYrXrOmCJ\n", "O3fkOTeRorGSHczX+Dw/5eWApVIIqoNcMKqDLCLSo8yYDJwFfBA4iGgHfS2wzJ2f5Dk3kaKxkh0B\n", "nAf8Da9lH6Lu/6cosRAYrhRrNudXFYtsqIqFFIl+DkWyY8Y04N3A+4BpwJPANcAKdbwTGR0r2YuJ\n", "tKRys5xtwCXAld7vu1TFQkREpIOZMQP4APAuYDJRB/8S4GJ37s1zbiJFYyU7Hvgw0SzHiGY5K4Br\n", "vN8fb/d8FCCLiIiMghmHEpd+FwATiUu/FwKXubM9z7mJFImVzIA/Jiq8nJqGfwosBT7v/Z5bsxwF\n", "yCIiIg0w40ji0u9bgQnAdiIwvsKdnXnOTaRIUmD8amLF+KVp+Dai9OFXvN9zb5ajADlbqmIhItJl\n", "zPgdYDHwBmAccC+wEljrzqN5zk2kSKxk44F5RGB8XBr+NrAEuMn7a2+Mq9VqusVT1Sa9rIxyk95B\n", "0PoafiLDOAh4QJv0ROoz43ji0u/r0tBWYDnwj+48mdvERArGSlarJvjXgaXe798e1bG0Sa9rbct7\n", "AiLEBzT9LIrUYMbLiMD4VWnoJ8QK1xfceSa3iYkUjJXsecA7iJz9Q4ma4OuIwPi2POc2EgXIbebu\n", "x438LBERaSczDJgLfBQ4KQ3fTuRE3uje+rqrIt3CStZHVHd5P3AA8AzwD8By7/ef5jm3RilAFhGR\n", "nmXGOOA1xIrxiWn4v4nNd193R3mIIg2yks0g6oGfDUwBHgcuBy72fv9FnnMbLQXIIiLSc8wYD5xO\n", "BMbHpuEF+6sIAAAgAElEQVSNxIrxNxUYizTOSnYYsYluAbAvsItIS7rM+72Q6XwKkEVEpGeYUWuz\n", "0L8AS9z5bm4TEykgK9lvAecDbyZiyu3AJ4DPeL8P5Dm3ZilAzpbKvImIdCAzJgJvBz4EHEZsFrqO\n", "CIzvyHNuIkVjJZtNlD6cT3S9+xVR+vBq7/fHMj+fyrwVV7vKjoiISOPMmAycBXyQKG/4LPBZYJk7\n", "hdgsJNIprGQvI2oYvzoN/RxYBvyz9/tTbZmDyryJiIiMjRnTgHcTG4amAU8CVwIr3Lk7x6mJFErq\n", "encaERj/cRq+g8gxvqETut61ggJkERHpGmbMAD5AlJiaDDwGXAxc7M59ec5NpEhS17vXEYHx7DT8\n", "f4mNrF+v1/WuWyhAFhGRwjPjUKIZwQJgIrCTKNV2mTvb85ybSJFYySYAbyQ2sv5WGr6JWDH+drcH\n", "xmUKkEVEpLDMOJJ4I38rMIHYRX8hcIU7O/Ocm0iRWMkmAn9DfNAsb2S9geh6d0uec8uDAmQRESkc\n", "M36H2EX/BmAccC+xi36te+t3uIt0CyvZVOCdRNe7GcRG1muJrnc/znNueVKALCIihWHG8URzj9el\n", "oa3ELvpr3Xkyt4mJFIyV7EDgvcA5wFRiI+sVwCrv97tznFpHUIAsIiIdz4yXEYHxq9LQT4icyC+4\n", "80xuExMpGCvZoURN4YVEvv7DwHLgk97v9w/3tb1EAbKIiHQkMwyYC3wUOCkN307sor/Rnd15zU2k\n", "aKxkRxONcsr5+g8SHzKv8H7fkefcOpEC5Gypk56ISJPMGAe8hlgxPjEND5aXcnpiF71IFqxkxxH5\n", "+qcT+fq/BlYBa73fC5Gvr056BaZOeiIizTFjPPEm/hHg2DS8kahK8V8KjEUaZyX7I6KG8Z+loc1E\n", "vv5nvd8Lm6+vTnoiItITzNgbeBNRru3oNPwvwEXufC+3iYkUTOp6dwoRGP9JGv4BkUpxvfe78vUb\n", "pABZRERyYcZE4O1EXmS57up1wBJ37shzbiJFYiUbB/wFERgfn4a/S6Ql/VuvNPfIkgJkERFpKzOm\n", "AGcRLaEPIuqu/iOwzJ2f5jg1kUJJXe9eT+QYvzgNrydWjP9LgfHYKUAWEZG2MGM68J506yPqrn4G\n", "WOnO3TlOTaRQrGT7Am8jrr4ckYa/THS9+35e8+omCpCrmNkc4Ix0dw5wvrvfluOUREQKzYxDiNXi\n", "dwKTgEeIrneXunNfnnMTKRIr2WTi6ssHGbz68lmi692dec6t2yhAHmqRuy8CMLPZwC1m1ufuu3Ke\n", "l4hIoZhxBLHC9XZgH2AHUV7qcnceynFqIoViJat19eVKYKX3+9Y859atVOatgpnNAu5y93EVYw8B\n", "89395hG+VmXeREQAM15E5ES+ERgPPABcDFzlzsN5zk2kSKxkLyCuvixi8OrLlcCl3u89efVFZd5y\n", "4O5bUooFAGbWR3xS25LfrEREisGM2cQu+nmAAb8AVgDXuPN4nnMTKRIr2ZHE1Ze/BvYGHiLSkj7l\n", "/a6rL21QiAA5BaprgSW18oHNbD4wExgAjgTWu/vGsZzL3W+vuLsYWO3ud4/lWCIivcCMlxLNPV6d\n", "hn4GLAU+587TuU1MpGCsZL9L1AN/PdH17j4iLWmN9/sjec6t13R0gJxygM8k+oXPIy4rVD9nLrDQ\n", "3U+tGNtkZgvKwbSZzSMKZ9ez2d1XVh13PjDF3d/Z/HciItJdzCg3JPgIcFIavoOou3qjO8/mNTeR\n", "orGS/QFx9eW1aWgLsBz4J+/3J3KbWA8rTA6yme0G5lbnApvZLcCV7n51xdgC4PTKoHmU55oLnOzu\n", "i81sKrC/+/BJ8MpBFpFeYMY44k38I8AJafi/icD439UOWqQxqevdK4jA+OQ0fCdx9eVL6npXm3KQ\n", "G5BSL2YzNEd4BzB3jMecQ6yKXJU27c0nim6LiPQsM/YirugtBn4nDW8gAuP/UmAs0pjU9e5/E4Hx\n", "H6Th7xP/l/7F+313XnOTQYUOkIFZ6c/qhPUBADM7YjT5wyng3kBszDsvDbu7r2hyniIihWTGPsBb\n", "gfMZ/J37VaId9P/kNjGRgrGS7UX0WVgMHJuGbya63t2srnedpegB8v51xssBc99oDubuA8McU0Sk\n", "Z5gxCVgAnAu8ANgNfAFY6s4P8pybSJFYyWp9yPwa0fXuu7lNTIZV9AC5HgW5IiJjYEYfcDbwPuAA\n", "4GngamC5O3flOTeRIrGS7QcsJLrePZ/4kPl5YJn3uz5kdriiB8jlleLqgLi8cjzQxrkATDKz++s8\n", "tsrdV7V1NiIiDTLjQCIoPgeYAjwOXA6scueXec5NpEisZNOI/0fvBaYTHzLXAiu83/Uhs0Fmdi5x\n", "BavagcCjrT5/0QPk8ua8mqkUOdQvflRVLESkSMw4lHgTWghMBHYRu+g/6c62POcmUiRWsoOB9wPv\n", "AvYDHgMuBS72fv91nnMrorSoOGRhsVzFotUKHSC7+4CZbSA+oVWahSpPiIjUZcZRDHbqmkDUm18C\n", "fNq97VffRArLSnYEsbH/b4B9iKvXnwAu937fnt/MpBmZBMhmNoUoqzaLwWD1QWKFd4O772ry+OUV\n", "Yqvx8PJ0W1sxdgbxi7/dqlMslFYhIh3FjGOJXfTlTl33Uu7U5a2/bCnSLaxkLya63r0RGA88AFwC\n", "XOX9zcU9sqeqdIu2pFiMuVFIaqCxEFjE4K5MGMz7rUx72AysBtaMJlg2s5np+HOIItpbgeupaiWd\n", "OuWdmM7TR3TGu3G031Mz1ChERDqZGScSzT3+PA1tBZYB17rzZG4TEykYK9kJxIfM1xELd/cAK4B/\n", "8H5/PM+59YJ2xVujDpBTYLycCI5vBb4E3AZsSmXSKp/bR3RamkMUmJ8NLHf3xc1PvbMoQBaRTpPa\n", "Qf8J0ZDglDT8IyLH+IvuqFOXSANS17uTiP9L5S69PyX+L33e+/3pvObWazo5QH4IuI4IdIdtv1zj\n", "a2cRdQDnu3t13nChpRdsEpGUX6YUCxFpuxQYv5pYMf6jNHwL0anrq+6oU5dIA1Jg/GoiMH5pGr6V\n", "yNf/ivf7s3nNrZfUSrFw9yktPecYAuS+6pXiUZ80g2N0Gq0gi0jezBgP/CXxZv77afhbRGC8Xu2g\n", "RRpjJRsPzCP+Lx2Xhr9FBMbfUNe7/LQr3hr1Jr1GA1szOyI9/+6xHkNEREZmxgTgr4i8yN9Kw/8B\n", "XOTOd3KbmEjBWMn2Bt5EbL47Og3/O9H1Tv+XekhWVSzOA+a6+2np/lVEjjJmdgtwcrOVLApCVSxE\n", "pG3M2Bd4O1G153DAgRuAJe7cmufcRIrESvY84B1EubZDif9L1xFd727Lc25SsCoWzx0gguPlwK3u\n", "foKZzSZy3a4HNhG7pLtyY14lpViISLuYMZmo8PNB4GDgWeBzwDJ3fpzn3ESKxErWRzT2eD/RWv0Z\n", "4LPAcu/3n+Y5N6mtYzfpDTmA2V3AgLufkO4vAxa6+/7p/jrg99396GEOU3gKkEWk1czYH3g30cJ2\n", "GvAUcA2wwp1RbZoW6WVWshlEa/WzidbqTxD9FFZ5v/8iz7nJ8Do2B7mGWcQKctlcYEPF/e8Tie4i\n", "IjIGZhwMfAB4J9HC9lHgYuASd+7Nc24iRWIleyFxqX4B1a3V+12t1eU5WQTIW0mNQlKN5DlEU5Cy\n", "WdAzbUuVgywimTHjcCK/uLqF7WXuPJjn3ESKxEp2DFFm9i1E7LMduBD4jPercECnyyMHOYsAeQOw\n", "wMy2MFiI/jp4Lj95IZGP3AseVYqFiDTLjN8idtG/ifg9vY1oYXulO72w4VkkE1ay3yequ5xOdL37\n", "FbASuNr7/bHhvlY6R1psXAWDKRatlkWAfD6xSnx+ur/I3Xea2Rwi9WJLxWMiIlKHGb9P1F2dT7yZ\n", "/5J4M/97d/RmLtIgK9lLif9Lf5qG7iKKBnzW+/2p3CYmhdH0Jr3nDlTV/COlW5zo7huG+bKuoU16\n", "IjJWZvwh0fXuz9LQz4k38392R2/mIg1IXe9OJQLjk9Lw/yOae1yvrnfdoTBVLCSo1bSIjEZqB30y\n", "ERi/PA0Pvpk7ejMXaYCVbBzwOiIwnpOG/5voIPnv6npXfEVpNf0Q8Qt8zWibf5jZFKJ25+JyGbhu\n", "oRVkEWlECoz/FPg48JI0/D3izfxf1Q5apDFWsgnAG4gc4xel4W8QMcq3FBh3p04u8zaLqBW4wszW\n", "A+uADbVaSsNzLadPIRLk5xIb9maO4bwiIoWVAuO5RBWKP0jDNxOB8X8qMBZpjJVsIvA2BjtIAtxI\n", "tIPelNvEpKuMOkBOecanm1l5Y94KoM/MADYTZYgM6COVf0tj1wFHuruK2YtITzHjJCIwLudF3gx8\n", "3J3/k9+sRIrFSjYFOIuoCX4Q0UHys0Q76B/lOTfpPpnkIKeKFXOB6QwGxVuIgHmTu9/a9Ek6nFIs\n", "RKSaGf+LCIznpqHvAB9z55u5TUqkYKxkBwDvIbpI9gFPEh0kV3q/Ft16jTbpFYwCZBEpM2MO8HcM\n", "VqX4PvAx4BtKpRBpjJXsBcAHib1LzwMeAa4ELvV+b0stXOk8nZyDXFfahLd/vXzkHqBOeiI9zIxj\n", "gRLwl2noDiIw1uY7kQZZyY4i8ovfCuwNPET0Vfi09/tDec4tN2b7AuOAx+nBlc08OulllWJxMtFe\n", "ehbgxKa8aUQXvfPd/famT9LhtIIs0rvMOAb4W+D1xB6MHxNVKm50Z3eOUxMpDCvZcUQHyTOIYPA+\n", "4GJgtff7I3nOLRNm44GpwP5EjLR/1d+HG9s3HeVZYiX94UxuXrymKYVZQTaz2cB6Iud4JXBeemgr\n", "ESjPNbPjeyFIFpHeYsZMIhB+C/GGfhcRKH9RdYxFGpO63i1mMCVpM1EA4Frv9ydzm1gtUZFgIqML\n", "bst/n0p8gG7Us8Tq+S+AHcBuYHLF7SBgQpPfz9NkFWzDI7g/09R8OkjTK8hm9g3gBHff38z6iBdz\n", "rrvfnO7fAtzl7qc1P93OpRVkkd5hxguBjwJvJxYa7iFyjv/Jna55gxBplTpd734ALAXWeX+LAy2z\n", "vYgNf6MNcvcn0j5G42EiwH0o3XZU/Vlv7JER0ynM9mHPoHkysF+NsUZv40b5vVV7nOqgeewB96O4\n", "D7kCV5gVZGJ39opaD7j7gJmtJi6ZiIgUmhmHECtdi4g3yV8DFwLXqCW0yMisZOMZ7Ho3Ow3/N9Hc\n", "499G1dwjVnMnMbYgd7Rd2J4mgtctjC7IHcD96VGeq3HuTxJVPbY3fazB1fHRBtW1AvKDgCMzmNOQ\n", "AHsaTNsR/8YtlUWAvBOG3XzSVR3zRKT3mHEgsWnobOIN5AFipWu1O0/kOTeRIrCS7Q28ieifcEwa\n", "von4fxRd78z242/tYKDyNp3hA97RxjG7iMD1LhoPcncQq5ndvTkuvr/H0u2Bpo9nNo74ADPW1ezK\n", "22GkPOx9mp5Yg9PPIMViHfD77n50jRSLqUQu8iZ3P7X56XYupViIdB8zphE7p99L/KJ/kLhidoV7\n", "63dRixTdn77JJm9/Hu8155zpj3HQIY/gc+7jzj/9OXfOHGA8ewbD+zV42KcYfbrCDmI1VylQRRVp\n", "MZP3gp88C7s7vg5y6qh3F5FUv4YoxfIhIhF9MZHj0/Ud9FKAPIn45FWmMm8iBWTGFOB9RMeuqcSV\n", "slXA5e7synNuIrmLS/FTGQxsD2HPQPfgZ4znP7EXM5/3NJMaSGp9nKhYcX+N23aGBr49Weqsl9Uq\n", "8+buo02TGd05MyrzNoso83Zy1UO3Agvc/bamT9LhtIIsUnxmTALOIT7k709sMPkkcIl763PeRHIV\n", "G76qUxwqb5WB8LBXup812DYJHpiEA784bCf/s/8TbKZ2IPywAl5pVGE76aW20wBb3H0g04N3MAXI\n", "IsVlxkTgLGJD8QxiRevTwAr3DDa/iOQl8kCnM3ywW75Na+CIu9gzuL0PuP+HM3jmUy/hT77/Ak69\n", "dz/23j6Jnc+O4wrgMu/3bVl/W9K7ilTFYg/ufmvWxxQRaQUz9gbeAXwEeD6R23g5sNSd+4f7WpFc\n", "mU1i5ID3YKKawEjv9c8QwW69Fd5yIPwA7pVphFjJfpv4YPlXwHhgG3ApcKX3+85mv02RvGQSIJvZ\n", "AqLcW1+953R7HWQRKQ4zJhDNPT4GHE4ECKuBi9z5ZZ5z61pmewOHEv/eh1XdyitBzxLNEJ6tulWP\n", "dfr9Zo7hxIpvvYC3PN7IhraHgJ9SP+At/31HrXqzw7GSvYTYZ/QXaegeolnYNd7vj4/mWCKdKItO\n", "elcRLaUh6gPW6hKj3CIRyZ0Z44E3AP3AUURQ8g/AJ9zp6o3ELRWbtvandvBbvh1M/S5i5feI0XQZ\n", "61VPEMHtD6gf8N4PbEs1cjOTmnu8gqhhXN5z9GNgGfAF729hvV+RNstiBfkMYEO3l3ETkeIyYxww\n", "DygBLyYCss8DJXd+lufcCiE2bx1K/eD3MOB5wxxhO3Ab0TK31u0B3HenQHsccal+fNXfx3I/i2O0\n", "+n712DiinGC9VIdd7d7QZiUbB7yGCIxfkoY3Ec09vur9o1t9FimCrHKQr8voOCIimTHDgNcSbaB/\n", "Lw3fAPS7c2duE+skEZROZ/jgd7jV36eAX1I/+P0l7o3VjI7Ar5xyIDmzku0FvJ7IMf6dNPyfRGC8\n", "cVRd70QKJosAeSNwCnB1BscSEWlaCoxPAz4BnJCG/xX4uDtdX3ZyD8Ov/pZTIiYOc4ThVn/vIS7l\n", "awWxi1jJ9gXeRpQ7PCINfw1Y6v3+3bzmJdJOWTQK6SMutdxCbHJ5qNbz3P32pk7U4VTmTaQzmPEK\n", "4ELgj9LQeuBj7nwvv1m1yPCrv+Xg9+BhjjDc6u89xOrvY/W/XLqJlWwKUe7wA0T1i2eBLwLLvN9/\n", "mOfcRMoKUwfZzGYTq8h1K1gA7u7jmzpRh1MnPZF8mfFSYsX4FWnoW8BH3fl2frNqUu3V3+qNcCOt\n", "/tYKfMt/1+qvYCU7gGinfg7xXv4ksXl1pff7ljznJgIF7aRnZpuAOcD5UP/SpbtvaOpEHU4ryCL5\n", "MONEIsf4VWnou0T5to3uHVxBZ+jqb60KEKNZ/a0MfMu5v1r9lbqsZC8EPggsIDZZPgJcCVzq/X5f\n", "nnMTqadIjULmAOe7+8oMjiUi0hAzjiMC49emoVuJwPjrLQ+MzcYDk4EpNW5T64xX36Yz8urvrdQO\n", "frX6K2NmJTuGWNR6MzCBqJqxDPi097taqouQ3Sa9zl2lEZGuYsaLiXJtp6ehHwIfB74yYmBsNoE9\n", "A9tGg9nq505q4lt4BtgJ/JpYAa4V/Gr1VzJnJZtNNPeYT1Ql+TWwCljr/Q1WGhHpEVmkWMwBNgCv\n", "7PaNeMNRioVIC5nt/df8w3E/4Hc/7NhrJ/PwuIO5/9encdOX38q1Px7P7urV3HqB73ArtiN5EthV\n", "cdtZdb/WrdZznmx3HVvpbVayPyZqGJfTkO4iVoz/2fuzbSYi0mpF2qR3HZFmMYvo4z5Q/RRik96J\n", "TZ2owylAFqkj0hEOBGYQaQWjSktwmGKwTxMzeJzmA9uHs+5KJtJKqevdq4kV45el4TuIGsY3eL+r\n", "1rQUUpFykI8kguLyBr1xGRwzN2Y2F5iZ7p4CrHb3jTlOSaTzmO1NBLwHpVvl36tvBzCGFsK7scd2\n", "MYX7OXifnUzlCfZ9YgbbfnQ0P//BXjxbL8itFdiq/a30DCvZeCKFYjFwXBr+DhEY/4eae4g0pukV\n", "5G5jZncBC9395nIJO3ffv4Gv0wqyFJvZROoHudUB8LQGjrgb+A3wQMXtQYYGsXvcfzeX77OWBWc/\n", "yb5nESvH9wMXAWvd0SquSA1Wsn2ITXfnA0el4a8TzT2KW+pQpEqRVpC7zVx3vzv9fdSrXiIdI8qI\n", "TWb41d3K4HdyA0d9GtgGbGXPwLfyto1yMOyNX8Y1YzpwHvBuouTUduAjwJXuaMOaSA1WsknAQqJc\n", "2wuITfPXEc09eqtrpEiGRh0gm9k3gB3ufmbF/eGWocs5yKeNbYrtVREcAywiugOKdIYIeqfRWGrD\n", "QcC+DRz1cSKg/RFDg9zq20DWG8zM6CM6d72PCNJ3EJ3wPuXOI1meS6RbWMn2Jxp7vIfI7X8a+Htg\n", "hff7z/Kcm0g3GMsK8pHEG1jZdCJAHm61tak31NTOei2wxH3oJ2Izm0/kDQ+k+a1vJm/YzGYSwfER\n", "RC96kdaJTWzTGTmtoTw2oYGj7gJ+xcirvA8Aj+RRVcGMycSb+7lE966HifJtl7qzs93zESkCK9kh\n", "xAfKs4D9iO6tnwQu9n7/VZ5zE+kmHZ2DnHKAzyTyFpcT6Q83Vz1nLvAhdz+1YmwTsKAcTJvZPGLD\n", "XT2bazU6qWijPdPdh33DVg6yAOXNa/sRdXL3S7eRgt8DaWxz60PUD3L3HHd/PLPvKWNmPA94F5Er\n", "eQDxBn85sMqdB/Ocm0inspLNIhZs3gbsTSwIfQq43Pt9e55zE2mnIpV5uxJYXpWaUPn4bOAMd1/c\n", "5Hl2UztAvgW40t2vrhhbAJxeGTQ3eI45wCZ3H1cx9hARgF9d/ysVIBdONIwoB7GVwWzl38fyWCOr\n", "u2W7iTzbequ8lUHwb3B/aqzfbicwYx8iV/LDRAvlJ4HPAMvc2Zbn3EQ6lZXsd4ELgNcTH6QfAC4G\n", "Vnu/78pzbiJ5KNImvUXAOuDuOo+fSWy8aSpAriWlXswGtlQ9tAOYO4ZD7qAi5zgdvw/YNNY5SpMi\n", "/aBekNpMMNtMXd1KTwKPECkN96W/l2+Pplv1yu9gRYdRbGIrKjP2Bd5BvMm/gMiV/AywxJ1f5zk3\n", "kU5lJftfxPtmuZX63cSV1H/0fn8ir3mJ9IoxBchpNbfShtg7VNetYzlPA2alPx+qGh8AMLMj6q1s\n", "1+LuW83sejM7Lw2dCMzv5Q6Bo2I2jkgXmEx2wWwjm8wa8RQRrD5CBKflv1cGs7X+Ptxjj+L+TEbz\n", "6zpmTAQWEKkUzydaLF8NXOjOPXnOTaQTpeYec4nA+BVp+E5gKfAl79fvG5F2GesK8gUVf18GrGHo\n", "Km6ldWM8z0jq1ScuB8x9oz1g2tynxiD1xCeh6cAxVbej062ZVr4AzxKbtR4lcs9HE7DWe+zRoqcn\n", "FEkKjBcSgfEhxIrxGmCpe90rTSI9KzX3eB3x3np8Gv4fornHv3i/Vy9KiUiLjSlAdvcV5b+b2ZlE\n", "t7lOqrc4YmMPGYHZfkTAWx0IH0PtDx67idq4W4jGD2MNZp/Ko6KCNC9tvltEbCQ6mAiMryJyjLVi\n", "LFLFSrY30dzjQ8TvVoANxIrxf6rrnUh+ms5BdvfjR35Wy5RXiqsD4nIAN9DGuRSP2T5EmkqtQLhe\n", "8vu9wO3Az9Lt5+nPLVql7U1mTCJKTp1HVOV4CriSCIx/kefcRDqRlWw/4irLBxhs7nED0dxDe15E\n", "OkDRO+mV0zpqplKMJv84I5PM7P46j61y91VtnQ2UN7m9kD1TIcp/P4La5cUGgO8xGASXA+Gf467G\n", "DQI8Fxi/iwiMDyQC4yuIwFj1WEWqWMkOIDpFvpto+FNu7rHS+/2nec5NpNOY2blEnfxqBxJXnVuq\n", "0AGyuw+Y2QYiJ7bSLGB9DlN6NJcyb5EXPIOhOcHHAEdRu2LD48AP2TMILgfCDyrNQeoxYz/gbOIX\n", "1wFEJY9PActVlUJkKCvZC4lW0AuINuqPApcAl6q5h0htaVFxyMJiucxbqxUiQE7l1qB2t77l6ba2\n", "YuwM8umAV72CnO2qsdlUhqZDlO9PqfEVzxCr7OU0iMrbvbg2fkjjUue7cmA8HXgCuAxY4c69ec5N\n", "pBNZyV5EbFZ9E/F+W256dYX3u5riiDSoajW5LSvInd5Jr9zyeQ5wMrEJ7HqqWkmnTnknApuJdIvN\n", "7n5jm+eaTeFqs32Jdtm1NsfNqPNVv2RoTvDPgLtxf7qp+UjPM2MKcA6xArY/ERhfRQTGbfkkL1Ik\n", "VrITiVJtf0Es7PyKWAm72vu95W/sIt2sMJ30JIzqBTPbCzic2pvjDqP2Svl2aqdD3IX7Y81/ByJ7\n", "MmMqkSv5ASJf8nFi891Kd+rl2ov0pIoaxhcAr0zDPyFWjD/v/drELJKFInXSk0F7pFi8ANb8Cm5m\n", "aCB8JLVbEj/CnhUiBgNh9x0tnrsI8Fxg/F7g/cQVmceI1a9V7jyQ59xEOk2dGsbfJ0q1fVU1jEWa\n", "pxSLAjOz+ybDfrvg3xgMhCfVeOrTwF3Uzgu+X5vjJC9m9AHvS7epxC+gK4CL3dmW59xEOo2VbB8i\n", "t7iyhvF6onmWahiLtIhWkAtoUrRGPpOoaXkP8H8Ymhf8C7Unlk5ixjQiKH4vg4HxciIw/k2ecxPp\n", "NHVqGF8PLFcNY5HuoQA5QwOwA/hjYDPuT+Q9H5HhmLE/kUbxHqIKyiPEZeFL3Nme59xEOo1qGIv0\n", "FqVYZCQt+U8i8jXL8mkOIjIMM6YTq1/vBiYDDwOXA5e6o9JTIhWsZIcR/18qaxivBi7xflfdb5E2\n", "qJWD7O61yttmd04FyNloV06MyFiZcQBRqu0cIh1oF1HH+JPuz7VtFxHASvZiIr+4sobx5cCnvd/1\n", "/0UkJ8pBFpFMmHEgg4HxJGAnUAIuc0fVUUQqWMleQlSkUA1jkR6mAFmkS5kxg7gkdTZxaXgA+Fsi\n", "MB7IcWoiHUU1jEWkmgLkbLW21bRIA8w4CDgPeCcRGO8gSk9d7s7OPOcm0klUw1ikGFQHucCUgyx5\n", "M+NgImfyLGAi8BBwCfApd3blOTeRTqIaxiLFpRxkEWmIGYcA5wOLgH2JzUSfAD7tzsN5zk2kk6iG\n", "sYg0SgGySEGZ8XwiMF7IYGBcAq5QYCwySDWMRWS0FCBnSznI0nJmvIDImVwA7ANsB/qBz7jzSJ5z\n", "E+kkdWoYX4JqGIsUinKQC0w5yNJqZryQCIzfAewNbANWAle6t/6XhUhRqIaxSPdSDrKIAGDGYcBi\n", "4O1EYPwAsAK4yn2Pzo0iPa1GDeNfEjWM/141jEVkNBQgi3QoMw5nMDCeANxP1GVdo8BYJNSpYfxj\n", "4hHrHT0AACAASURBVP/KF1TDWETGQgGySIcx4wjgw8BfE4HxfQwGxo/nNzORzlGnhvH/EDWMv6Ya\n", "xiLSDAXIIh3CjFlEYPxW4v/mvURd1rXuPJHn3EQ6xTA1jJcC31QNYxHJggLkbKmKhYyaGUcCHwHe\n", "AowHfk282f+9AmORYCWbTFSjqK5hvMz7/ZY85yYiraUqFgWmKhYyWmYcRQTGbyYC418SgfE17jyZ\n", "59xEOkWqYfwe4BwGaxj/E7DC+/1nec5NRNpPVSxEupQZRwMfBd5IBMa/AJYA/6jAWCSkGsYfJFaN\n", "JxIrRhcDl6qGsYi0mgJkkTYx47eIwPivgHHAPQwGxtppL8JzNYzPJz5AlmsYLwWuUA1jEWkXBcgi\n", "LWbGi4CPAa8nAuO7gYuAf1JgLBJSDePFRA1jUA1jEcmRAmSRFjBjHPAKYBEwn2hasIUIjD/rztM5\n", "Tk+kI6QaxqcSK8avSMOqYSwiuVOALJIhMw4h6he/A5iVhjcDFwKfU2AsAlayvYkrKucCv5uGVcNY\n", "RDqGAuRsqcxbDzJjPPAqYjPR/yY23j0LfAVYC9zkzrP5zVCkM1jJpgALgfcRpdoA/h1YCfyXahiL\n", "SC0q81ZgKvPWe1Ir6L8hWkGX3+w3A1cD17pzX15zE+kkVrIXAO8lUo6mEKXaPges8n6/M8+5iUix\n", "qMybSAcyY2/gNcRq8alEbvFTwBeJ1eJvuqPLwyKAlexYYtXnr4i26buAFcDlKtUmIp1MAbJIA8w4\n", "hsgrfiswIw3/iAiKP+vOg3nNTaSTpI13LwfOA16dhn8FfBJY6/2+K6epiYg0TAGySB1mTATmEYHx\n", "n6Thx4B/IALj77qjHCURwEq2F/CXRGB8Qhr+AZFf/CVVpBCRIlGALFLFjN8lUijeDPSl4VuI3OIv\n", "uLMzr7mJdBor2STgbcAH+P/t3Wu0nFd93/HvX7Lki3w5+AYNYdWSEyDNDdlxCiZthW9AQkO6bKAN\n", "LbShsiFN25UGOyRvpvMmC8ckgbTUNnZKWmjrOLZDCiHgCygXIAEbErJCFxfZSgLYYGRLAutmSf++\n", "2M94njM652jOOTPzzDPn+1lrlmb2fs7Mtveac36zZ19gc1X8ACUY3+vCO0ltZECWgAhOp2w7tR34\n", "0ap4H3AzcFsmn2uqbdI0im6cD/wc8O+Asyk7t/wfysK7zzbZNklaLQOy1qwIAriEEor/OXB6VfUJ\n", "yhSKuzLHv5WM1CbRjedTRovfCJxC2W7pXcA7s5O7GmyaJI2MAVlrTgTPAl5PCcY/VBXvBt4D/FYm\n", "X2iqbdK0im5cSplf/GrK7i3foByAc3N28okm2yZJo2ZA1ppQjRb/I0oovoYy8gVlruRtwAcyOdRQ\n", "86SpFN1YB/wkJRhfWhV/Efg14H3ZyYNNtU2SxsmAPFqepDdlIjif8lXwvwWeXxU/StmJ4rcyebip\n", "tknTKrpxCvAG4Bfov2/+lLLw7kMeBS1pkjxJr8U8SW96RLAOuIIyWvxqygEFxyhH2t4GfDiTI821\n", "UJpO0Y2zgbcA/4Gy33cCvwfclJ38sybbJkngSXrSskXw3ZTtpt4E/P2q+G+A3wLem8lXm2qbNM2i\n", "GxcAP09572wCDgK3AL+enfxycy2TpGYYkNVqEZwE/ARlCsWPA+uAp4G7KKPF93v0s7Sw6MZFlPnF\n", "rwHWUxar/hrw7uzkN5tsmyQ1yYCsVopgC2W0698Ava9ZvkQJxf8zE/+4SwuojoJ+BWU+32VV8cPA\n", "rwO/nZ10a0NJa54BWa0RwcnAT1HmFl9eFR8E3k8Jxn/i0c/SwqIbG4F/QQnGP1AVPwj8KnBPdvJo\n", "U22TpGljQF5CRNwI3JGZnqLWoAi+jxKK3wCcUxV/nhKK/1cmTzbVNmnaRTfOAq4F/iPw3Kr4w5Qd\n", "Kf7Io6Al6XgG5EVExEWUUPbRptuyFkVwGmVe5HbgpVXxdyih+DbgQUeLpcVFN76bEoqvBc6kzM3/\n", "bcpR0H/dYNMkaeoZkBd3MeXrR01QBFspofj1lD/qAH8O3A78TibfbqptUhtEN36QMo3ipym/4/dR\n", "plH8Znbya022TZLawoC8gIi4PjNviojXNN2WtSCCMylzI7dTPpgA7AH+C3B7Jp9vqm1SG1QL77ZR\n", "dqR4ZVX8VeCdwG3ZyX0NNU2SWqkVATki5ihfq//KQvOBI+IaYDMlVF0I3JeZD6zwtS4H7ltFczWE\n", "6ujnF1NC8euA06qqP6L09T2ZHGioeVIrRDdOAq6mBOPeh8u/oswvviM7+XRTbZOkNpvqgBwRWynh\n", "aTflj8DNC1xzBXBtZl5VK3swIrb3wnREXA1cucRL7axGjOeALQPhOkbwn6JKBOcA/4qyb/H3V8Xf\n", "BP4r5ejnLzXVNqktohubgJ8B/hNwQVX8ACUY3+vCO0landYcNR0Rx4ArMvNjA+UPATdn5u21su3A\n", "a+qhecjXuBrYUiv6JeB3gLtONCLtUdOLq45+3kYJxVcDGylH2N5LGS3+YCaHG2ug1BLRjWcDPwf8\n", "LHA2cBS4k7Lw7rNNtk2SJsGjpodQjfhupWxyX/ckcMVyny8z7x54/usYIhxrYRE8B/jXlGB8YVX8\n", "VeC/U45+3tVMy6R2iW68APgFylaHJwNPAe8C3pmd3NVg0yRpJrU6INMf7X1ioHwPQERckLn8Px4R\n", "cRbwy9Xz3xARGJKHE8F64OWUucX/lHJ87VHgA5SdKD6SiQcSSEOIbryUMr/4JynTvb4B/CZwS3Zy\n", "8PeeJGlE2h6Qz16kvPeHY24lT5qZe4FfrG46gQhOp0yheDnwauB5VdXDlFD825k82kzrpHaJbqyj\n", "vI+uB15SFX8ReAfw/uzkwabaJklrRdsD8mIWC84agWoHih+mBOKXAz8GbKiqDwN3UOYW78jkWCON\n", "lFomunEKZQrFLwDPr4r/lLLw7kPZSd9LkjQhbQ/IvZHiwUDcGzneM8G2AGyKiMcWqXtHZr5joq0Z\n", "oQjOA66iBOKrgGfXqndSThz8KPBxD/OQhhfdOAd4C/DvgfMpC1jvAW7KTv5Zk22TpKZExFsphx4N\n", "Oo+yDmOs2h6Qe4vzFpxKsZL5x6v01KzsYhHBBsrXu71R4otr1d8B/i9VKM5k5+RbKLVbdGMz8PPA\n", "myj7gB8EbgF+PTv55SbbJklNqwYVjxtY7O1iMW6tDsiZuSci7gfOGajagod9LFsEW+gH4suAM2rV\n", "n6U/Svwpt2WTlq868e4yyjZtPwWso+zz/g7g3dnJbzbYPElSpRUBudrODRY+tOPG6nZbrey1wA3j\n", "btcCBqdYTPW0impx3cvoh+LvqVV/g7LzxEeB+zLxD7e0QtGNOeCNlKkUL6iKdwK/Abw3O7m/qbZJ\n", "0rQbmG4xkSkWU31QSERsBq4DLgIuBx4B7mLgKOnqgI9LKH9w5ign490z4bZO/UEh1YEd9cV1L6W/\n", "uO5p4BP0R4n/0gV20upEN7ZSRotfD5xKmV/8IeC/UU688z0mScswqbw11QG5TaY1IEdwPv3FdVcy\n", "f3HdV5i/uO47k2+hNFuq3SheQwnGL66KH6dsefgeD/aQpJXzJL12anyKRQQbKYvrXkEJxVtr1d8G\n", "fp/+4rrBEwglrVB0YwvlG6830V8X8aeU0eJ7spOHmmqbJLWZUyxarMkR5AguZP7iutNr1Q8xf3Hd\n", "05NunzSrohvrKR9GfxZ4JWWdxFPA+4Cbs5Ofb7B5kjRzHEHWoiI4g/mL6y6sVX+Dsodqb3Hd45Nv\n", "oTTbohvnAT8DvBm4oCr+AmW0+H3ZyX0NNU2SNAIG5NEayxSLanHdi+gH4kuZv7ju45RA/BHg85n4\n", "tYA0YtUWbS+hjBa/BtgIHAHupATjP86OX8lJ0qg5xaLFRj3kH8Gzmb+47vxa9ZfpT5vY4eI6aXyi\n", "G6cDP00Jxj9cFX8NuBW4PTs5kU3rJUlOsVhzqsV1L6U/SvyiWvW36e9J/NFMHpl8C6W1JbrxfZR9\n", "i98InFkV308ZLf5gdvJIU22TJI2XAblBEXwP/UD8MvqL6xJ4kP4o8Z+5uE4av+jGBuDVlNHil1XF\n", "e4F3ArdkJ7/YVNskSZNjQB6tJecgV4vrLqMfirfUrn0MuJsSiO93cZ00OdGN5wLbgWuB3td2nwXe\n", "DdzhSXeS1BznILfYQnNiqsV1W5m/uK73oeQwZY/U3iixi+ukCaoW3b2MMlr8U8B64BBwB2UaxWdc\n", "dCdJ08U5yC0VwXOYv7juvFr1l5i/uG7sn4AkzRfdmAPeQJlf/MKq+GHgZuC92cndTbVNkjQdDMgj\n", "de65QH1F+z7g9+gvrtvVRKskQXTjRZTR4tcDp1Hm+n+QMlp8b3byWIPNkyRNEQPySJ10EvAZ+qPE\n", "f+7iOqk50Y1TgGsowfglVfHjwLuA92QndzXUNEnSFHMO8oiUOTGxCeYt5hnJQSGSlie6sRm4DngT\n", "cG5V/AnKaPHd2clDTbVNkrQ8Cy3Sy8wzl/iR1b+mAXk0JjVpXNLCohvrgVdQ5hb/OBCUlc7vB27O\n", "Tv5lg82TJI2Ai/QkaQjRjfOAnwHeDFxQFX+BsujufdnJvQ01TZLUUgZkSa1TbdH2Ysrc4tcCG4Ej\n", "wJ2UaRR/7BZtkqSVMiBLao3oxibgpynBuHcc+9eAW4Hbs5OPLvazkiQNy4A8WkuepCdpZaIbL6TM\n", "LX4jcFZVfD9ltPiD2ckjTbVNkjRenqTXYi7Sk0YrurEB+EnKaPFlVfFe4L3ALdnJLzbVNklSM1yk\n", "J2lNim58F7AduBb4rqr4c8C7gTuyk55AKUkaKwOypMZVi+62UUaL/xmwHjgE/A/KbhSfdtGdJGlS\n", "DMiSGhPdOAt4AyUYv7AqfpgSit+bndzdVNskSWuXAVnSxEU3XkRZdPcvgdOABD5IWXR3b3byWIPN\n", "kyStcQZkSRMR3TgFuJoyWnxpVfw48C7gPdnJXQ01TZKkeQzIo+U2b1JNteDux4GfAK4ENlVVn6CM\n", "Ft+dnTzUUPMkSS3gNm8t5jZvEkQ31gEXA6+ihOKLa9WPAh8Abs1O/mUDzZMktZzbvElqhejGGZTR\n", "4VdRRoufXav+DPCh6vYXzi2WJLWBAVnSskU3LqQE4lcB/wTYUFV9B7iHEoj/MDv52MLPIEnS9DIg\n", "Szqh6lS7l9IPxS+oVe+kP0r8J84pliS1nQFZ0oKiG+cCr6QE4pcDZ1VVR4Ad9EPxlzzEQ5I0SwzI\n", "koBnTrP7QfqjxC8GoqreDbyPEojvzU7uaaSRkiRNgAFZWsOiG6cCl9HfdeJ5terP0x8l/nR28ujk\n", "WyhJ0uQZkKU1JrrxPEoYfhUlHJ9aVR0E/oASiD+cnfzbZlooSVKzDMjSjIturAd+lP7UiR+qVX+V\n", "/ijxx7OT+yffQkmSposBWZpB0Y054CpKIH4lcG5VlcCn6Ifiv3KBnSRJ8xmQR8ujptWIaoHd8+mP\n", "Ev8Y/ff3PuBOSiD+SHby8UYaKUnSCnjUdIt51LQmLbqxEfjH9EPxhbXqL9IfJf5EdvLpybdQkqTR\n", "8qhpSceJbjybcpzzqyhTKE6vqp4G7qcE4j/ITn6lmRZKktR+BmRpikU31gFb6e86cUmt+pvA71JC\n", "8f3ZyX2Tb6EkSbPHgCxNmejG6cAVlFD8E0D9a6TP0p868VB28tjkWyhJ0mwzIA+IiBuAt1cP9wA3\n", "ZObtDTZJa0B0YzP9UeKXARurqv3A79Pfm/jrzbRQkqS1w4B8vATmgLMzc1fDbdGMim6cBFxKPxT/\n", "g1r1LvqjxH+UnTw48QZKkrSE2LFjHbC+up1Uu79+heXDXXvyyady6NCBcf/3GZAXkJn7KFtjSSMT\n", "3TgHeAUlFL8CeFZVdQz4E6oFdsAX3JtYktopduwISr46ubptHNH9DawugK4umB5f3oxNmzAgNyQi\n", "rqdMr7gYuDUzP9dwk9RC1Ql2P0AJw6+ijBivq6qfBP43JRB/JDv5RCONlKSWqoLoqMLnKIPsRiDG\n", "+J8+SseAI8DRgVuv7PAi5ScqW2758Nfu3/+fx/J/YkAr9kGOiDngNuBXFgqrEXENsJkSai8E7svM\n", "B1b4Wpsz85Hq/lnAI5l59hA/5z7Ia1x040zgHwIvpYThFwNn1C75a/qjxJ/KTh6ZeCMlacRix471\n", "wKlD3E4b8rr67RSWHlFt0mHgUHWr3x98vNz7i9U9zWjD5rHctm36Q+AA90EGImIr8DpgN3A1cPMC\n", "11wBXJuZV9XKHoyI7b0wHRFXA1cu8VI7M/Om6v6TvcLM3BsRRMTlKw3cmk3VyXWbKUH4Ukoo/kHm\n", "jxo8BtwL7AA+lB3ntEsav9ix4yRWFkhXev04g2oyPzgeoAyGTSqULhpW2xguNbypDshVwO2F3BsX\n", "uexGjg/Ot1blV1XPczdw94lerxqpfoL+1+DPNGX4VmsWRTdOpuxH3BsdvhR4Tu2SY8DngU8Cn6j+\n", "3eVcYqn9anNKe7f1A48Xu63kug2UUdPVBNhx/m1/mhJSD1D+Xh4YuO1foGyp25LX57ZtngKqRkx1\n", "QD6RKtBuBR4eqHqSso/ssmTmnnoQj4gtwO7M/NiqGqrWiW6cD7yE/ujwj1C+0uv5NmV0uBeIP+1B\n", "HVJRrW4/hX7QO2WRx/X7yw2V4w6q9dvgoMk06Y2qHgAeZxVhdJjrc9u2oxP675Ia1eqADGyp/h1c\n", "4LQHICIuWMFWbXdWi/SgzGdeamqGZkB1Wt33MX90+HsHLnuY/sjwJ4G/zk76h0JTqwqpJ3PiYDps\n", "gF1OXW8f72nVm495ZOD+EeDgwOPFrlvsNorrDjJkiM1t2zwsSBqDtgfkxRbP9QLz3HKfsD6tQ7Op\n", "OqnuR+mPDr8EOKt2yWHgU/RHhz+VnXxs0u1Ue1RfwfdGHzcw/+vyxco2svpgutR19W88xukAJdAd\n", "pKwXqT+u3x98PFg3TKgcRQA96txRSSfS9oC8mBPuOqG1oVpM9zzmjw7/MPP3cHwc+AD90eGHPJxj\n", "9GLHjo3AJhYPj8MGy3FcP4rnalLva/aDlClmKw2py607bNiUNIua/qW+Wr2R4sFA3Bs53jPBtmgK\n", "RDc2AC+iPzp8KfDc2iVJ2W6tPl1ip4vpFleNjp4OnEkZae/dzlzm/UmNaI7SUcqipCMD/x5coOzI\n", "ImVL1fWC5mLBdNiQ6tfskjRCbQ/IvcV5C06laOCo6E0RsdhX8e/IzHdMtDVrQHU6XW8x3aWUqROn\n", "1i55CniA/nSJP89OrpkPTrFjxwaWH2QH75/Jyhcp7aecSrkL2Fs9fprhwuNyw+ZK6xa73q/iJakh\n", "EfFW4K0LVJ1H+ds+Vq0OyNWuE/cD5wxUbQHua6BJT3lQyPhU0yVewPzR4RcOXPY3zB8d/qs2HshR\n", "jdqexspDbe/+qYPPPaRjlGC7F/i76t+9tbKl7vce73OLJknSSlSDiscNLPYOChm3VgTkajs3WPjo\n", "xhur2221stcCN4y7XRqv6MZpwCX0R4cvZf50miPAp5m/mO5rk24nPBNo6zsG1BdNnc7KAu5Kz7o/\n", "SAmoX2X5obZ3/ylHTyVJa9VUHzUdEZuB64CLgMuBR4C7GDhKujop7xJgJ2W6xc7MvGfCbX2UsgBp\n", "f63YaRXLEN14LvNHh7cy/0Pcbvojw58EPpOdPPDMz5fTowYD6kIr/ZdTP+zPjmJ+bVL2Vx421C42\n", "antoBG2RJGkqDEy3OI/yjf2ZY33NaQ7IbTKps8Gn2RKjqMcHymOHT2Pf//teDn79hTy973s5enAL\n", "EXOs2wixEdZthA1n7GXjs55g49l72fCs/aw/7RgRSz3vSkdcl6u+Y8Bit179IeA7DDdq+x0XW0mS\n", "tLhJ5S0D8ohM6whytUhrqaNKl3q8nGt7t4WmwYzaMRZe/b9YSB1lvTsGSJI0QY4gt9iwn2hqR7Cu\n", "Jowu59pxzjNf+OjSzAMc3b+Ow0+ezuHdZ3H4iXM4sm+OY4d55pa5l/Wn7mTDGV/mlL/3Bc78/i+x\n", "/pSnGCKk5rZtrVt0J0mSVm9SI8itWKTXGmecMRc7dvwhSwfXce4Fe5h+UP0W88PrYJBd+PGxIwc5\n", "su8oh/cc4/C34PBuOPhNOPytdRx6fD2HvrWew99az9P7NkBuqv6berdNwPnAiymf8HqOAg/RX0z3\n", "yezk343x/4MkSdKKGZBHacOGU4BX1Ep6UwEOUBZffZPlhNXB+8cOH+LQ40c5+Fhy4OvB/r+FA19b\n", "x8Gvr+fgY+s5dqgXwuuBdaHH5y5RP4oAvwf4MP3FdJ/OTo59z0JJkqRRcIrFiETEo0ScQVAdUZzw\n", "bN7PdfweJw6swz4+ZQxN74Xw/ZSNt/ev8vFe4OHspPN0JUnSqjkHucUi4lFO5zkLnvmycvXwOqoA\n", "W398wCArSZLawjnIbXSUp4GPM5oAa3iVJElqgCPII+I+yJIkSeM1qby1bpxPvgZtiojHarfRTriQ\n", "JElaYyLirb1sRdkta9PYX9MR5NFwBFmSJGm8HEGWJEmSGmBAliRJkmoMyKPlHGRJkqQRcg5yizkH\n", "WZIkabycgyxJkiQ1wIAsSZIk1RiQJUmSpBoDsiRJklRjQB4td7GQJEkaIXexaDF3sZAkSRovd7GQ\n", "JEmSGmBAliRJkmoMyJIkSVKNAVmSJEmqMSBLkiRJNQbk0XKbN0mSpBFym7cWc5s3SZKk8XKbN0mS\n", "JKkBBmRJkiSpxoAsSZIk1RiQJUmSpBoDsiRJklRjQJYkSZJqDMiSJElSjQFZkiRJqjEgj5Yn6UmS\n", "JI2QJ+m1mCfpSZIkjZcn6UmSJEkNMCBLkiRJNQZkSZIkqcaALEmSJNWc1HQDplFEXAFsBfYAz8rM\n", "X224SZIkSZoQA/KAiLgGuCIz31w9fgIwIEuSJK0RBuTjvQe4oPcgM89urimSJEmaNANyTURsAeaA\n", "SyJiDrgEuC8zH2i2ZZIkSZqUVizSi4i5iPjdiNi6SP01EXF9RGyPiLdHxOUrfKkt1b+7M/PuzHwb\n", "cGtEnDXkz4/9ZBc1y9MRZ5v9O/vs49lnH68Ja/skvSoQvw7YDdxImRv8sYFrrgBuyMyramUPAtsz\n", "83PV46uBK5d4qZ2ZeVNEXAQ8mJnPfHConuvWzLztBG19FDg/M9cv6z9SrRIRj2Xmc5puh8bD/p19\n", "9vHss49n26Ty1lRPsagCbi/k3rjIZTcCNw+U3VqVX1U9z93A3UO85MOLlO8e4mclSZI0A1oxxWIx\n", "1TzhrRwfbJ8Erlju82XmHuCugSkac8D9K25kg8bxNdNqnnO5Pzvs9Se6bqn6xera8hWdfXzievt4\n", "tM9pH4/eWujj1V5jH4/2OZvo45X077CvPQ5TPcWiLiKOMTDFojclArgoM/+iVn4FcC+wJTN3LfN1\n", "zqKMPu8EzgHuqD/3Ej83dVMsxvE102qec7k/O+z1J7puqfrF6pZb3hT7+MT1y+nLaetfsI+HqbeP\n", "R/uc4+jj1V5jH4/2OZvo45X070J1TrEYzmJbsD1R/Tu33CfMzL3Am1fcIkmSJLVa2wPyYprYu/hc\n", "YF31yWZanDeG9qzmOZf7s8Nef6LrlqpfrG655U2xj09cv5y+nLb+Bft4mHr7eLTPOY4+Xu019vFo\n", "n7OJPl5J/y5Udz4TmCLc9oDcGykeDMS9keM9E2zLEWA9peMW8lR1m6RxvN5qnnO5Pzvs9Se6bqn6\n", "xeqWW94U+/jE9cvpy2nrX7CPh6m3j0f7nOPo49VeYx+P9jmb6OPl9u+m6hbMz1brgGNDtGdV2h6Q\n", "e4vzFpxKsdz5x6uRmadO6rUkSZI0Pq3exaLadeJ+ymK6ui3AfZNvkSRJktquFQE5ynZuUIbZB90I\n", "XDdQ9tqqXJIkSVqWqd7mLSI2U8LvRcDlwCPAXcB9mflA7bqrgUsoW7PNUU7Gu2fyLV5cRNwK3NI7\n", "3U+zo9pu8LXVw4uAX7SfZ0u1deTm6uGVlNM1H1jiR9RiUQ6musP38WyJiBuAt1cP91BO4b29wSZp\n", "DKrf11spffyszPzVFT3PNAfkWVAL79cClw2zp7LaJSJuzczrqvtbgYeAuczc12zLNCoR8RXg2sz8\n", "WNXHD2RmE7vlaMyqD7z3A9fU991X+0XE9ZSTds+e5BolTU5EXEM5M+PN1eMnVvq7uhVTLNosM+/O\n", "zLfR33FDMyQitgDbe4+rEac9wI801iiNQ/2QooWmeml2XEw5gEozKDP3GY5n2nuAG3oPVjOQ0fZd\n", "LKRGZebD1YgT8Mx8+TmOP/5cLTbwB/U6yiiUZkxEXJ+ZN0XEa5pui8ajGkXeQ/kgdKvTaGZHNWA1\n", "B1xS/S2+hIEpucthQD6B6n/ybcCvLPRGqobzN1PecBeyis5QM1bbxwPTZn6J8kt311gbrWUZxfu4\n", "tibiAmojFJoOq+3jiLgcdz+aaiN4H9+VmY9U195JWdfkVKkpsso+3lL9u7squzsivhIRF1enJC+L\n", "AXkR1TzD1wG7gauBmxe45grKvMSramUPRsR2P5VOv1H3cfXGPTMz3zLelmtYo+zj6g/r26rnfCQi\n", "Nq/kl65GaxR9XP1R3jIQppxKMyVG+D5+sleXmXsjgoi43EGt5o2oj5+A4wat9lAW0d+23DY5B3kR\n", "mfm5zHxbZt60xGU3AncOlN2KW8y1wij7uHrjXpyZb4mIs6rRRjVsFH0cERdFxDOnNtX+2Po1/BQY\n", "0fv4cmAuIq6vvoL/EeCaalRZDRvR+3iOhdcCuVPBFBjR+3ixqY27V9ImR5BXqHqzbeX4DnkSuGKx\n", "HxtrozRSw/ZxNQf5SuCWag7UNfhVbSsM2cdPUptzXJtn7kKuFhimjzPz7oGfuY7ydbwjiy0wZB/v\n", "qbbv6/3MFspX8e5U0gLL6OO7Br4VmKPsSrNsBuSV6811GfxEugcgIi7IzF3VCMSV1fU3RsR9J/iE\n", "pOlxwj6u7t9PeRNeX9XnSvdd1MQN8z5+pPql2+vfSyhbgLllYzsM9bu6un8W8MvVz9wQERiSW2HY\n", "Pr6z9j6+kPK3We0wbB9vp2StiyinLF+z0i1XDcgrt9jE/l7nzQG9X64PAG+bRKM0Uifs4+oNr3ie\n", "WAAAAn1JREFU6SKP9lru+1jtM1QfQ5mXCvxidVN7DPs+/hzg+qB2GraP9wJvHsULOgd59AxLs88+\n", "nn328eyzj2effTz7xtbHBuSV631qGeyc3mjEngm2ReNhH88++3j22cezzz6efRPvYwPyyvUmis8t\n", "VOk+uDPBPp599vHss49nn308+ybexwbkFcrM3uKscwaqtuAOBjPBPp599vHss49nn308+5roYwPy\n", "CVRbi8DCW7TdSDlZq+61uA9yq9jHs88+nn328eyzj2ffNPVxZLpH9kJqx8peRNlE/hHgLo4/nvRq\n", "yrZPOylD/zsz857Jt1jLZR/PPvt49tnHs88+nn3T2McGZEmSJKnGKRaSJElSjQFZkiRJqjEgS5Ik\n", "STUGZEmSJKnGgCxJkiTVGJAlSZKkGgOyJEmSVGNAliRJkmoMyJIkSVKNAVmSJEmqMSBLkiRJNQZk\n", "SZIkqcaALEmSJNUYkCVJkqQaA7IkSZJUY0CWJEmSagzIkrRGRMRDEXFnRGyp7h+LiCci4u1Nt02S\n", "pokBWZLWjgTmgHuBTwPXAg8CN0TE9U02TJKmSWRm022QJE1ARDwEbAWuycx7auVPAJ/JzJc31jhJ\n", "miKOIEvS2vJkPRxXHgLObqIxkjSNDMiStLY83HQDJGnaGZAlSZKkGgOyJEmSVGNAliRJkmoMyJK0\n", "tsQyyyVpzTEgS9LakdVt2HJJWpPcB1mSJEmqcQRZkiRJqjEgS5IkSTUGZEmSJKnGgCxJkiTVGJAl\n", "SZKkGgOyJEmSVGNAliRJkmoMyJIkSVKNAVmSJEmqMSBLkiRJNQZkSZIkqcaALEmSJNUYkCVJkqQa\n", "A7IkSZJUY0CWJEmSav4/UK3zbOg0ab8AAAAASUVORK5CYII=\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import matplotlib\n", "matplotlib.rcParams['savefig.dpi']=130\n", "plt.xscale('log')\n", "plt.yscale('log')\n", "plt.xlabel('n')\n", "plt.ylabel('time(s)')\n", "for func, times in data.items():\n", " plt.plot(ns, times, label=func.__name__.replace('_',r'\\_'))\n", "plt.legend(loc='best')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conclusion\n", "----------\n", "There are two things to see from the graph.\n", " - First lets look at the pure python implementations(`fib` and `fib_bme`). For these guys, `fib` funs significantly faster for n less than a few hundred. However, for larger n, the `fib_bme` algorithm becomes much faster. \n", " - Next, lets look at the cython implementations. For these there is much less drastic difference in run-times between `cfib` and `cfib_bme` than we saw with the pure python implementation. The asymptotic behaviour of the two algorithms in the cython implementation behaves the same as the python ones, as we should expect.\n", " - Finally, it's worth pointing out that for n greater than ~50,000, the pure python BME implementation beats out the cython looping implementation." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.3" } }, "nbformat": 4, "nbformat_minor": 0 }