From 72bfbe651be4330466275b7afc943cb4d29cf2e4 Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Thu, 18 Apr 2019 21:51:02 -0700 Subject: [PATCH] Add files via upload --- ipynb/Dice Baseball.ipynb | 139 ++++++++++++++++++++++---------------- 1 file changed, 81 insertions(+), 58 deletions(-) diff --git a/ipynb/Dice Baseball.ipynb b/ipynb/Dice Baseball.ipynb index 0442dca..544b27c 100644 --- a/ipynb/Dice Baseball.ipynb +++ b/ipynb/Dice Baseball.ipynb @@ -8,9 +8,19 @@ "\n", "# Dice Baseball\n", "\n", - "The [March 22, 2019 Riddler](https://fivethirtyeight.com/features/can-you-turn-americas-pastime-into-a-game-of-yahtzee/) asks us to simulate baseball using probabilities from a 19th century dice game. There were some choices to make that were left unspecified in the rules; the following are my current choices (in an early version I made different choices that resulted in slightly more runs):\n", + "The [538 Riddler for March 22, 2019](https://fivethirtyeight.com/features/can-you-turn-americas-pastime-into-a-game-of-yahtzee/) asks us to simulate baseball using probabilities from a 19th century dice game called *Our National Ball Game*.:\n", "\n", - "* On a `b`-base hit, runners advance `b` bases, except that a runner on second scores on a 1-base hit.\n", + " 1,1: double 2,2: strike 3,3: out at 1st 4,4: fly out\n", + " 1,2: single 2,3: strike 3,4: out at 1st 4,5: fly out\n", + " 1,3: single 2,4: strike 3,5: out at 1st 4,6: fly out\n", + " 1,4: single 2,5: strike 3,6: out at 1st 5,5: double play\n", + " 1,5: base on error 2,6: foul out 5,6: triple\n", + " 1,6: base on balls 6,6: home run\n", + "\n", + "\n", + "The rules left some things unspecified; the following are my current choices (in an early version I made different choices that resulted in slightly more runs):\n", + "\n", + "* On a* b*-base hit, runners advance* b* bases, except that a runner on second scores on a 1-base hit.\n", "* On an \"out at first\", all runners advance one base.\n", "* A double play only applies if there is a runner on first; in that case other runners advance.\n", "* On a fly out, a runner on third scores; other runners do not advance.\n", @@ -18,17 +28,19 @@ "* On a base on balls, only forced runners advance.\n", "\n", "I also made some choices about the implementation:\n", - "- I wanted to have one event per batter, so I don't allow \"strike\" as an event. Rather I compute the probability of a strikeout event (i.e. getting three \"strike\" dice rolls in a row before getting another event) as `(7/36)**3`, and check for that.\n", - "- Note that a die roll such as (1, 1) is a 1/36 event, whereas (1, 2) is a 2/36 event, because it also represents (2, 1).\n", + "\n", + "- Exactly one outcome happens to each batter. We call that an *event*.\n", "- I'll represent events with the following one letter codes:\n", " - `K`, `O`, `o`, `f`, `D`: strikeout, foul out, out at first, fly out, double play\n", " - `1`, `2`, `3`, `4`: single, double, triple, home run\n", " - `E`, `B`: error, base on balls\n", + "- Note the \"strike\" dice roll is not an event; it is only part of an event. From the probability of a \"strike\" dice roll, I compute the probability of three strikes in a row, and call that a strikeout event. Sice there are 7 dice rolls giving \"strike\", the probability of a strike is 7/36, and the probability of a strikeout is (7/36)**3.\n", + "- Note that a die roll such as `1,1` is a 1/36 event, whereas `1,2` is a 2/36 event, because it also represents (2, 1).\n", "- I'll keep track of runners with a list of occupied bases; `runners = [1, 2]` means runners on first and second.\n", "- A runner who advances to base 4 or higher has scored a run (unless there are already 3 outs).\n", "- The function `inning` simulates a half inning and returns the number of runs scored.\n", - "- I want to be able to test `inning` by feeding it specific events, and I also want to generate many innings worth of random events. So I'll make the interface be that I pass in an iterator of events.\n", - "- I'll random simulate 1 million innings and store the resulting scores in `innings`.\n", + "- I want to be able to test `inning` by feeding it specific events, and I also want to generate random innings. So I'll make the interface be that I pass in an *iterable* of events. The function `event_stream` generates an endless stream of randomly sampled events.\n", + "- I'll play 1 million innings and store the resulting scores in `innings`.\n", "- To simulate a game I just sample 9 elements of `innings` and sum them." ] }, @@ -49,48 +61,49 @@ "metadata": {}, "outputs": [], "source": [ - "def our_national_ball_game():\n", - " \"An iterator of events sampled from the odds specified in `Our National Ball Game`.\"\n", - " events = '2111111EEBBOOooooooofffffD334'\n", + "def event_stream(events='2111111EEBBOOooooooofffffD334', strike=7/36):\n", + " \"An iterator of random events. Defaults from `Our National Ball Game`.\"\n", " while True:\n", - " yield 'K' if random.random() < (7 / 36) ** 3 else random.choice(events)\n", - "\n", - "def inning(events=our_national_ball_game(), verbose=False) -> int:\n", + " yield 'K' if (random.random() < strike ** 3) else random.choice(events)\n", + " \n", + "def inning(events=event_stream(), verbose=False) -> int:\n", " \"Simulate a half inning based on events, and return number of runs scored.\"\n", " outs = runs = 0 # Inning starts with no outs and no runs,\n", " runners = [] # ... and with nobody on base\n", - " while True:\n", - " x = next(events)\n", - " if verbose: print(f'outs: {outs}, runs: {runs}, runners: {runners}, event: {x}')\n", - " if x in 'KODof': # strikeout, foul out, double play, out at first, fly out, \n", - " outs += 1 # Batter is out\n", - " if x == 'D' and 1 in runners: # double play\n", - " outs += 1\n", - " runners = [r + 1 for r in runners if r != 1]\n", - " elif x == 'o': # out at first (other runners advance)\n", - " runners = [r + 1 for r in runners]\n", - " elif x == 'f' and 3 in runners and outs < 3: # fly out; runner on 3rd scores\n", - " runners.remove(3)\n", - " runs += 1\n", - " else: \n", + " for e in iter(events):\n", + " if verbose: print(f'{outs} outs, {runs} runs, event: {e}, runners: {runners}')\n", + " if e in 'KOofD':\n", + " outs += 1 # Batter is out\n", + " else:\n", " runners.append(0) # Batter becomes a runner\n", - " if x in '1234': # single, double, triple, homer\n", - " runners = [r + int(x) + (r == 2) for r in runners]\n", - " elif x == 'E': # error\n", - " runners = [r + 1 for r in runners]\n", - " elif x == 'B': # base on balls\n", - " runners = [r + all(b in runners for b in range(r)) for r in runners]\n", - " # See if inning is over, and if not, whether anyone scored\n", + " # See what happens to runners:\n", + " if e == 'D' and 1 in runners: # double play: runner on first is out\n", + " outs += 1\n", + " runners = [r + 1 for r in runners if r != 1]\n", + " elif e in 'oE': # out at first or error: runners advance\n", + " runners = [r + 1 for r in runners]\n", + " elif e == 'f' and 3 in runners and outs < 3: # fly out: runner on 3rd scores\n", + " runners.remove(3)\n", + " runs += 1\n", + " elif e in '1234': # single, double, triple, homer\n", + " runners = [r + int(e) + (r == 2) for r in runners]\n", + " elif e == 'B': # base on balls; forced runners advance one base\n", + " runners = [r + forced(runners, r) for r in runners]\n", + " # See if inning is over, and if not, whether anyone scored:\n", " if outs >= 3:\n", " return runs\n", " runs += sum(r >= 4 for r in runners)\n", - " runners = [r for r in runners if r < 4]" + " runners = [r for r in runners if r < 4]\n", + " \n", + "def forced(runners, r) -> bool: return all(b in runners for b in range(r))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "# Testing\n", + "\n", "Let's peek at some random innings:" ] }, @@ -103,18 +116,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "outs: 0, runs: 0, runners: [], event: f\n", - "outs: 1, runs: 0, runners: [], event: B\n", - "outs: 1, runs: 0, runners: [1], event: o\n", - "outs: 2, runs: 0, runners: [2], event: 1\n", - "outs: 2, runs: 1, runners: [1], event: 2\n", - "outs: 2, runs: 1, runners: [3, 2], event: o\n" + "0 outs, 0 runs, event: 2, runners: []\n", + "0 outs, 0 runs, event: 1, runners: [2]\n", + "0 outs, 1 runs, event: B, runners: [1]\n", + "0 outs, 1 runs, event: 4, runners: [2, 1]\n", + "0 outs, 4 runs, event: 3, runners: []\n", + "0 outs, 4 runs, event: E, runners: [3]\n", + "0 outs, 5 runs, event: f, runners: [1]\n", + "1 outs, 5 runs, event: o, runners: [1]\n", + "2 outs, 5 runs, event: 1, runners: [2]\n", + "2 outs, 6 runs, event: o, runners: [1]\n" ] }, { "data": { "text/plain": [ - "1" + "6" ] }, "execution_count": 3, @@ -135,17 +152,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "outs: 0, runs: 0, runners: [], event: 3\n", - "outs: 0, runs: 0, runners: [3], event: o\n", - "outs: 1, runs: 1, runners: [], event: O\n", - "outs: 2, runs: 1, runners: [], event: B\n", - "outs: 2, runs: 1, runners: [1], event: o\n" + "0 outs, 0 runs, event: O, runners: []\n", + "1 outs, 0 runs, event: 1, runners: []\n", + "1 outs, 0 runs, event: 1, runners: [1]\n", + "1 outs, 0 runs, event: f, runners: [2, 1]\n", + "2 outs, 0 runs, event: 4, runners: [2, 1]\n", + "2 outs, 3 runs, event: O, runners: []\n" ] }, { "data": { "text/plain": [ - "1" + "3" ] }, "execution_count": 4, @@ -173,19 +191,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "outs: 0, runs: 0, runners: [], event: 2\n", - "outs: 0, runs: 0, runners: [2], event: E\n", - "outs: 0, runs: 0, runners: [3, 1], event: B\n", - "outs: 0, runs: 0, runners: [3, 2, 1], event: D\n", - "outs: 2, runs: 1, runners: [3], event: 1\n", - "outs: 2, runs: 2, runners: [1], event: 2\n", - "outs: 2, runs: 2, runners: [3, 2], event: f\n" + "0 outs, 0 runs, event: 2, runners: []\n", + "0 outs, 0 runs, event: E, runners: [2]\n", + "0 outs, 0 runs, event: B, runners: [3, 1]\n", + "0 outs, 0 runs, event: B, runners: [3, 2, 1]\n", + "0 outs, 1 runs, event: 1, runners: [3, 2, 1]\n", + "0 outs, 3 runs, event: D, runners: [2, 1]\n", + "2 outs, 3 runs, event: B, runners: [3]\n", + "2 outs, 3 runs, event: 1, runners: [3, 1]\n", + "2 outs, 4 runs, event: 2, runners: [2, 1]\n", + "2 outs, 5 runs, event: f, runners: [3, 2]\n" ] }, { "data": { "text/plain": [ - "2" + "5" ] }, "execution_count": 5, @@ -194,7 +215,7 @@ } ], "source": [ - "inning(iter('2EBD12f'), verbose=True)" + "inning('2EBB1DB12f', verbose=True)" ] }, { @@ -203,6 +224,8 @@ "source": [ "That looks good.\n", "\n", + "# Simulating\n", + "\n", "Now, simulate a million innings, and then sample from them to simulate a million nine-inning games:" ] }, @@ -232,7 +255,7 @@ { "data": { "text/plain": [ - "14.462798" + "14.496243" ] }, "execution_count": 7, @@ -241,7 +264,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAD8CAYAAACcjGjIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAFrVJREFUeJzt3XGMXWd95vHvU0NKRAtxwDWWx66zwgKlaAlklBiBVjQRjpMinJVoFNRtvCiLVyJ0QeqqhGqlqIFIQVqVJloaySLeOBUlZNOysZCpawVQd/9IyBhSQhJQhpRgj+zYxU7SLgI26W//uK+XG59rz50Z23fuzPcjXd1zfue9575Hmfi555z3nJOqQpKkfr8y6g5IkhYfw0GS1GE4SJI6DAdJUofhIEnqMBwkSR2GgySpw3CQJHXMGg5J3pLksb7Xi0k+keTCJPuSPN3eV7b2SXJnkukk303yzr51bWvtn06yra9+aZLH22fuTJKzs7mSpGFkLldIJ1kBzACXAzcBx6rq9iQ3Ayur6pNJrgH+ALimtbujqi5PciEwBUwCBewHLq2q40m+Bfwn4BFgD3BnVX3tdH154xvfWBs2bJjb1krSMrZ///5/rKpVw7R91RzXfSXww6p6NslW4L2tvgv4JvBJYCtwb/VS5+EkFyRZ09ruq6pjAEn2AVuSfBN4XVU93Or3AtcCpw2HDRs2MDU1NcfuS9LyleTZYdvO9ZzD9cCX2vTqqjrUpg8Dq9v0WuBA32cOttrp6gcH1CVJIzJ0OCQ5D/gA8D9OXtb2Es76HfySbE8ylWTq6NGjZ/vrJGnZmsuew9XAt6vquTb/XDtcRHs/0uozwLq+z0202unqEwPqHVW1o6omq2py1aqhDptJkuZhLuHwIX55SAlgN3BixNE24MG++g1t1NIm4IV2+GkvsDnJyjayaTOwty17McmmNkrphr51SZJGYKgT0kleC7wP+I995duB+5PcCDwLXNfqe+iNVJoGfgp8GKCqjiX5NPBoa3friZPTwEeBe4Dz6Z2IPu3JaEnS2TWnoayLyeTkZDlaSZKGl2R/VU0O09YrpCVJHYaDJKnDcJAkdRgOYs3EepK84rVmYv2ouyVphOZ6+wyNuTUT6zk8c6BT/81PfvUV889+9v3nqkuSFiHDYZk5PHPAIJA0Kw8rSZI6DAdJUofhoMFWvLpzktoT1dLy4TkHDfby/+2cmwDPT0jLhXsOkqQOw2EJG3T9giQNw8NKS5jDViXNl3sOkqQOw0GS1GE4SJI6DAdJUofhoLkZcHGcF8ZJS4+jlTQ3Ay6OcwSUtPS45yBJ6jAcJEkdhoMkqWOocEhyQZIHknw/yVNJ3pXkwiT7kjzd3le2tklyZ5LpJN9N8s6+9Wxr7Z9Osq2vfmmSx9tn7oz3eZCkkRp2z+EO4G+q6q3A24GngJuBh6pqI/BQmwe4GtjYXtuBuwCSXAjcAlwOXAbcciJQWpuP9H1uy8I2S5K0ELOGQ5LXA/8GuBugqn5RVc8DW4Fdrdku4No2vRW4t3oeBi5Isga4CthXVceq6jiwD9jSlr2uqh6uqgLu7VuXhjDoBnvufElaiGGGsl4EHAX+e5K3A/uBjwOrq+pQa3MYWN2m1wL9T7A/2Gqnqx8cUNeQBt1gDxxiKmn+hjms9CrgncBdVfUO4P/wy0NIALRf/HXmu/dKSbYnmUoydfTo0bP9dZK0bA0TDgeBg1X1SJt/gF5YPNcOCdHej7TlM8C6vs9PtNrp6hMD6h1VtaOqJqtqctWqVUN0XZI0H7OGQ1UdBg4keUsrXQk8CewGTow42gY82KZ3Aze0UUubgBfa4ae9wOYkK9uJ6M3A3rbsxSSb2iilG/rWJUkagWFvn/EHwBeTnAc8A3yYXrDcn+RG4FngutZ2D3ANMA38tLWlqo4l+TTwaGt3a1Uda9MfBe4Bzge+1l6SpBEZKhyq6jFgcsCiKwe0LeCmU6xnJ7BzQH0KeNswfdEi1G7G1+9Na9dx6OCPR9QhSQvljfe0cN6MT1pyvH2GJKnDcJAkdRgOkqQOw0GS1GE4SJI6DAdJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4TBm1kysJ8krXotSe8ZD/2vNxPpR90rSkHyew5g5PHNgPJ6d4DMepLHmnoMkqcNwkCR1GA6SpA7DQZLUYThIkjqGCockP0ryeJLHkky12oVJ9iV5ur2vbPUkuTPJdJLvJnln33q2tfZPJ9nWV7+0rX+6fXaRjs+UpOVhLnsOv11Vl1TVZJu/GXioqjYCD7V5gKuBje21HbgLemEC3AJcDlwG3HIiUFqbj/R9bsu8t0iStGALOay0FdjVpncB1/bV762eh4ELkqwBrgL2VdWxqjoO7AO2tGWvq6qHq6qAe/vWJUkagWHDoYC/TbI/yfZWW11Vh9r0YWB1m14LHOj77MFWO1394IB6R5LtSaaSTB09enTIrkuS5mrYK6TfU1UzSX4D2Jfk+/0Lq6qS1Jnv3itV1Q5gB8Dk5ORZ/z5JWq6G2nOoqpn2fgT4Cr1zBs+1Q0K09yOt+Qywru/jE612uvrEgLokaURmDYckr03y6yemgc3A94DdwIkRR9uAB9v0buCGNmppE/BCO/y0F9icZGU7Eb0Z2NuWvZhkUxuldEPfuiRJIzDMYaXVwFfa6NJXAX9ZVX+T5FHg/iQ3As8C17X2e4BrgGngp8CHAarqWJJPA4+2drdW1bE2/VHgHuB84GvtpaWm3am135vWruPQwR+PqEOSTmXWcKiqZ4C3D6j/BLhyQL2Am06xrp3AzgH1KeBtQ/RX48w7tUpjwyukJUkdhoMkqcNwkCR1GA6SpA7DQZLUYThIkjoMh0VszcR6krziJUnnwrD3VtIIHJ454HUBkkbCPQdJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcNFrtudInv9ZMrB91z6RlzXsrabQGPFcavIeUNGruOUiSOoYOhyQrknwnyVfb/EVJHkkyneTLSc5r9V9t89Nt+Ya+dXyq1X+Q5Kq++pZWm05y85nbPEnSfMxlz+HjwFN9858FPldVbwaOAze2+o3A8Vb/XGtHkouB64HfArYAf94CZwXweeBq4GLgQ62tJGlEhgqHJBPA7wBfaPMBrgAeaE12Ade26a1tnrb8ytZ+K3BfVf28qv4BmAYua6/pqnqmqn4B3NfaSpJGZNg9hz8D/gj4lzb/BuD5qnqpzR8E1rbptcABgLb8hdb+/9dP+syp6pKkEZk1HJK8HzhSVfvPQX9m68v2JFNJpo4ePTrq7kjSkjXMnsO7gQ8k+RG9Qz5XAHcAFyQ5MRR2Aphp0zPAOoC2/PXAT/rrJ33mVPWOqtpRVZNVNblq1aohui5Jmo9Zw6GqPlVVE1W1gd4J5a9X1e8B3wA+2JptAx5s07vbPG3516uqWv36NprpImAj8C3gUWBjG/10XvuO3Wdk6yRJ87KQi+A+CdyX5DPAd4C7W/1u4C+STAPH6P1jT1U9keR+4EngJeCmqnoZIMnHgL3ACmBnVT2xgH5JkhZoTuFQVd8Evtmmn6E30ujkNj8DfvcUn78NuG1AfQ+wZy59kSSdPV4hLUnqMBwkSR2GgySpw3CQJHUYDovEmon1nWcaSNKo+DyHReLwzIHOcw18poGkUXHPQYvTgCfE+XQ46dxxz0GL04AnxLknJZ077jlIkjoMB0lSh+EgSeowHCRJHYaDJKnDcJAkdRgOkqQOw0GS1GE4SJI6DAdJUofhIEnqMBwkSR2GgySpw3CQJHXMGg5JXpPkW0n+PskTSf6k1S9K8kiS6SRfTnJeq/9qm59uyzf0retTrf6DJFf11be02nSSm8/8ZkqS5mKYPYefA1dU1duBS4AtSTYBnwU+V1VvBo4DN7b2NwLHW/1zrR1JLgauB34L2AL8eZIVSVYAnweuBi4GPtTaSpJGZNZwqJ5/brOvbq8CrgAeaPVdwLVtemubpy2/Mr0HIm8F7quqn1fVPwDTwGXtNV1Vz1TVL4D7WlvplXw6nHTODPUkuPbrfj/wZnq/8n8IPF9VL7UmB4G1bXotcACgql5K8gLwhlZ/uG+1/Z85cFL98jlviZY+nw4nnTNDnZCuqper6hJggt4v/bee1V6dQpLtSaaSTB09enQUXZCkZWFOo5Wq6nngG8C7gAuSnNjzmABm2vQMsA6gLX898JP++kmfOVV90PfvqKrJqppctWrVXLouSZqDYUYrrUpyQZs+H3gf8BS9kPhga7YNeLBN727ztOVfr6pq9evbaKaLgI3At4BHgY1t9NN59E5a7z4TGydJmp9hzjmsAXa18w6/AtxfVV9N8iRwX5LPAN8B7m7t7wb+Isk0cIzeP/ZU1RNJ7geeBF4CbqqqlwGSfAzYC6wAdlbVE2dsCxeZNRPrOTxzYPaGkjRCs4ZDVX0XeMeA+jP0zj+cXP8Z8LunWNdtwG0D6nuAPUP0d+wdnjnQOakKnliVtLh4hbQkqcNwkCR1GA6SpA7DQZLUYThIkjoMB0lSh+EgSeowHDTevFOrdFYMdVdWadHyTq3SWeGegySpw3CQJHUYDpKkDsNBktRhOEiSOgwHSVKH4SBJ6jAcJEkdhoMkqcNwkCR1GA6SpA7DQZLUYTho6Rlwp1bv1irNzax3ZU2yDrgXWA0UsKOq7khyIfBlYAPwI+C6qjqeJMAdwDXAT4F/X1XfbuvaBvyXturPVNWuVr8UuAc4H9gDfLyq6gxto5abAXdqBe/WKs3FMHsOLwF/WFUXA5uAm5JcDNwMPFRVG4GH2jzA1cDG9toO3AXQwuQW4HLgMuCWJCvbZ+4CPtL3uS0L3zRJ0nzNGg5VdejEL/+q+ifgKWAtsBXY1ZrtAq5t01uBe6vnYeCCJGuAq4B9VXWsqo4D+4Atbdnrqurhtrdwb9+6xtqaifWdQxuSNA7m9LCfJBuAdwCPAKur6lBbdJjeYSfoBceBvo8dbLXT1Q8OqA/6/u309kZYv37xHz8+PHPAB9FIGktDn5BO8mvAXwGfqKoX+5e1X/xn/RxBVe2oqsmqmly1atXZ/jpJWraGCockr6YXDF+sqr9u5efaISHa+5FWnwHW9X18otVOV58YUJckjcis4dBGH90NPFVVf9q3aDewrU1vAx7sq9+Qnk3AC+3w015gc5KV7UT0ZmBvW/Zikk3tu27oW5ckaQSGOefwbuD3gceTPNZqfwzcDtyf5EbgWeC6tmwPvWGs0/SGsn4YoKqOJfk08Ghrd2tVHWvTH+WXQ1m/1l6SpBGZNRyq6n8Dpxpmc+WA9gXcdIp17QR2DqhPAW+brS+SpHPDK6QlSR2GgySpw3DQ8jHgnkveb0kabE4XwUljbcA9l7woURrMPQdJUofhIEnqMBwkSR2GgySpw3CQJHUYDpKkDsNBktRhOEiSOgwHLW9eNS0N5BXSWt68aloayD2HM2TNxPrOL1BJGlfuOZwhh2cO+AtU0pLhnoMkqcNwkCR1GA6SpA7DQZLUYThIkjpmDYckO5McSfK9vtqFSfYlebq9r2z1JLkzyXSS7yZ5Z99ntrX2TyfZ1le/NMnj7TN3xjGgGjUvjJOGGsp6D/DfgHv7ajcDD1XV7UlubvOfBK4GNrbX5cBdwOVJLgRuASaBAvYn2V1Vx1ubjwCPAHuALcDXFr5p0jx5YZw0+55DVf0dcOyk8lZgV5veBVzbV7+3eh4GLkiyBrgK2FdVx1og7AO2tGWvq6qHq6roBdC1SJJGar7nHFZX1aE2fRhY3abXAgf62h1stdPVDw6oS5JGaMEnpNsv/joDfZlVku1JppJMHT169Fx8pSQtS/MNh+faISHa+5FWnwHW9bWbaLXT1ScG1Aeqqh1VNVlVk6tWrZpn1yVJs5lvOOwGTow42gY82Fe/oY1a2gS80A4/7QU2J1nZRjZtBva2ZS8m2dRGKd3Qty5J0ojMOlopyZeA9wJvTHKQ3qij24H7k9wIPAtc15rvAa4BpoGfAh8GqKpjST4NPNra3VpVJ05yf5TeiKjz6Y1ScqSSFp82vPVkb1q7jkMHfzyCDkln16zhUFUfOsWiKwe0LeCmU6xnJ7BzQH0KeNts/ZBGasDwVnCIq5Yur5CWJHUYDpKkDsNhHnzqm6SlzifBzYNPfZO01LnnIEnqMBykhfAOrlqiPKwkLYR3cNUS5Z6DJKnDcJAkdRgOkqQOw0E60zxJrSXAE9LSmeZJai0B7jlIkjoMB+lc8FCTxoyHlU5jzcR6Ds8cmL2hNBsPNWnMGA6nMegeSuD/1JKWPg8rSaMy4FCTh5u0WLjnII2KT5fTIuaegySpw3CQFhtHNmkR8LCStNgMGtn0X/9t54mDb1q7jkMHf3wue6ZlxHBoHLaqRc2hsDrHFk04JNkC3AGsAL5QVbefy+/30Z8aO+3wUz/3JnSmLIpwSLIC+DzwPuAg8GiS3VX15Gh7Ji1iQx5+AkNDc7cowgG4DJiuqmcAktwHbAUMB2kuTjU8dkBorDjvNbz8i5+9omaI6ITFEg5rgf4D/geBy8/Wl3l+QcvOKc5ZDLPnMShETlU3cJaOVNWo+0CSDwJbquo/tPnfBy6vqo+d1G47sL3NvgX4wTy/8o3AP87zs4vVUtwmWJrb5TaNj6W2Xb9ZVauGabhY9hxmgHV98xOt9gpVtQPYsdAvSzJVVZMLXc9ishS3CZbmdrlN42OpbtcwFstFcI8CG5NclOQ84Hpg94j7JEnL1qLYc6iql5J8DNhLbyjrzqp6YsTdkqRla1GEA0BV7QH2nKOvW/ChqUVoKW4TLM3tcpvGx1LdrlktihPSkqTFZbGcc5AkLSLLKhySbEnygyTTSW4edX/mK8nOJEeSfK+vdmGSfUmebu8rR9nHuUqyLsk3kjyZ5IkkH2/1sd2uJK9J8q0kf9+26U9a/aIkj7S/wy+3QRhjJ8mKJN9J8tU2P9bbleRHSR5P8liSqVYb27+/hVo24dB3i46rgYuBDyW5eLS9mrd7gC0n1W4GHqqqjcBDbX6cvAT8YVVdDGwCbmr/fcZ5u34OXFFVbwcuAbYk2QR8FvhcVb0ZOA7cOMI+LsTHgaf65pfCdv12VV3SN3x1nP/+FmTZhAN9t+ioql8AJ27RMXaq6u+AYyeVtwK72vQu4Npz2qkFqqpDVfXtNv1P9P7RWcsYb1f1/HObfXV7FXAF8ECrj9U2nZBkAvgd4AttPiyB7RpgbP/+Fmo5hcOgW3SsHVFfzobVVXWoTR8GVo+yMwuRZAPwDuARxny72qGXx4AjwD7gh8DzVfVSazKuf4d/BvwR8C9t/g2M/3YV8LdJ9re7McCY//0txKIZyqozp6oqyVgOQ0vya8BfAZ+oqhf77/MzjttVVS8DlyS5APgK8NYRd2nBkrwfOFJV+5O8d9T9OYPeU1UzSX4D2Jfk+/0Lx/HvbyGW057DULfoGGPPJVkD0N6PjLg/c5bk1fSC4YtV9detPPbbBVBVzwPfAN4FXJDkxA+zcfw7fDfwgSQ/ond49gp6z2IZ6+2qqpn2foRekF/GEvn7m4/lFA5L/RYdu4FtbXob8OAI+zJn7Zj13cBTVfWnfYvGdruSrGp7DCQ5n97zSp6iFxIfbM3GapsAqupTVTVRVRvo/X/09ar6PcZ4u5K8Nsmvn5gGNgPfY4z//hZqWV0El+QaesdKT9yi47YRd2leknwJeC+9O0Y+B9wC/E/gfmA98CxwXVWdfNJ60UryHuB/AY/zy+PYf0zvvMNYbleSf03vJOYKej/E7q+qW5P8K3q/uC8EvgP8u6r6+eh6On/tsNJ/rqr3j/N2tb5/pc2+CvjLqrotyRsY07+/hVpW4SBJGs5yOqwkSRqS4SBJ6jAcJEkdhoMkqcNwkCR1GA6SpA7DQZLUYThIkjr+H0N4dxd+orK6AAAAAElFTkSuQmCC\n", "text/plain": [ "
" ]