# \[SOLUTION\] Dice Roller (#61)

**URL:** <https://www.ruby-forum.com/t/solution-dice-roller-61/52063>\
**Category:** Ruby\
**Created:** [January 10, 2006, 4:23pm UTC](https://www.ruby-forum.com/t/solution-dice-roller-61/52063 "2006-01-10T16:23:34Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![davidtl](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/35a633/32.png) [@davidtl](https://www.ruby-forum.com/u/davidtl)\
**Post date:** [January 10, 2006, 4:23pm UTC](https://www.ruby-forum.com/t/solution-dice-roller-61/52063/1 "2006-01-10T16:23:34Z")

</div>

My solution send out 17 hours ago from gmail.  
But it seems the mailing list still not receive it.  
So, I just re-send it from Ruby Forum.  
( If later on, gmail does send out, please ignore it, my appologize )

---------- Forwarded message ----------  
From: David T. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
Date: Jan 9, 2006 3:57 PM  
Subject: [SOLUTION] Dice Roller (#61)  
To: removed\_email\_address@domain.invalid

Personally, I like to use parser tools to solve this quiz instead of  
‘eval’.  
First try is with Rockit ([Rockit download | SourceForge.net](http://sourceforge.net/projects/rockit)),  
unfortunately I cannot get it work with Ruby 1.8.x  
So, I try with ANTLR.  
The ANTLR 3.0 early access version will suport Ruby language.  
You can download and see an example at  
[split-s: ANTLR for Ruby](http://split-s.blogspot.com/2005/12/antlr-for-ruby.html)

Unfortunately, ANTLR 3.0 syntax is kind of different than what I know  
( version 2.6.7)  
I am not successful to use direct return value, neither generate AST  
Tree.  
Anyway, at least, by using ANTLR 3.0, my solution does solve this quiz.

–

> **[乐享彩票-welcome登录](http://www.doublegifts.com)**
>
> 乐享彩票【91682.com】国内最安全、彩种最齐全的每日彩票平台，提供★乐彩彩票\_乐发彩票app下载★网站、登录、下载、网址、注册、投注等功能，彩种有快3、双色球、大乐透、11选5等二十多种。

# File: roll.rb

# Ideally it is better generate an “simplified” AST tree"

# for example expression “3d6+(1+2\*5)” will have “3d6+11” AST Tree,

# and each roll operation will just do the tree walking to calculate the

value.

# 

# Unfortunately, with ANTLR 3ea7, the syntax change from version 2.7.6

# and I am not able to create an AST Tree for Ruby language.

# 

# This forces me to reparse for each roll operation. Really bad :(.

require ‘DiceCalculator’  
require ‘DiceCalculatorLexer’

class Dice  
def initialize(input)  
@sstream = StringStream.new(input)  
end

def roll  
lexer =  
DiceCalculatorLexer.new(ANTLR::CharStream.new(@sstream.rewind))  
parser = DiceCalculator.new(ANTLR::TokenStream.new(lexer))  
parser.parse  
parser.result  
end

# help class, treat String as IO

class StringStream  
def initialize(str)  
@str = str  
@pos = 0  
end

def read(n)  
return nil if @pos \>= @str.size  
s = @str[@pos, n]  
@pos += n  
s  
end

def rewind  
@pos = 0  
self  
end  
end  
end

if $0 == **FILE**  
abort(“Usage: #$0 expression [count]”) if ARGV.size \<= 0  
d = Dice.new(ARGV[0])  
(ARGV[1] || 1).to\_i.times { print "#{d.roll} " }  
end

# File roll.g

/\*  
DiceCalculator Grammar

Date: 2006-01-09

ANTLR 3.0 Early Access (alpha?) will support Ruby language

However I am not successful to create AST tree by using ANTLR 3ea7  
The Syntax changes a lot from version 2.7.6.

Also I am not successful to use direct “returns [value]” syntax to  
allow each expression returns a value; so use a @stack variable  
to do the calculation.  
\*/

grammar DiceCalculator;

options {  
language = Ruby;  
}

@members {  
@stack = []

def result  
@stack.first  
end  
}

parse  
: expr  
;

expr  
: mexpr  
( PLUS mexpr { @stack.push(@stack.pop + @stack.pop) }  
| MINUS mexpr { n = @stack.pop; @stack.push(@stack.pop - n) }  
)\*  
;

mexpr  
: term  
( MULTI term { @stack.push(@stack.pop \* @stack.pop) }  
| DIVIDE term { n = @stack.pop; @stack.push(@stack.pop / n) }  
)\*  
;

term  
: (unit | { @stack.push(1) })  
(DICE (PERCENT { @stack.push(100) } | unit)  
{  
side = @stack.pop  
time = @stack.pop  
result = 0  
time.times { result += rand(side) + 1 }  
@stack.push(result)  
}  
)\*  
;

unit  
: INTEGER { @stack.push($INTEGER.text.to\_i) }  
| LPAREN n=expr RPAREN  
;

LPAREN : ‘(’ ;  
RPAREN : ‘)’ ;  
PLUS : ‘+’ ;  
MINUS : ‘-’ ;  
MULTI : ‘_’ ;  
DIVIDE : ‘/’ ;  
PERCENT : ‘%’ ;  
DICE : ‘d’ ;  
INTEGER : (‘1’…‘9’)(‘0’…‘9’)_ ;  
WS : (’ ’ | ‘\t’ | ‘\n’ | ‘\r’) { channel = 99; };

================================================================  
use java to create parser ruby file by:  
java -cp antlr-3.0ea7.jar;stringtemplate-2.3b4.jar;antlr-2.7.5.jar  
org.antlr.Tool roll.g  
This will generate lexer and parser ruby files, you also need ANTLR  
ruby runtime  
library (antlr.rb).

In case you like to try it and don’t like use java and antlr to  
regenerate:  
Below are a zip file (encoded base 64) contents  
ANTLR runtime library file (antlr.rb) and  
DiceCalculator.rb and DiceCalculatorLexer.rb generated files.

# File antlr\_gen.zip (base 64 encoded)

UEsDBBQAAAAIADN8KTTpvvtvOAYAAEklAAAWAAAARGljZUNhbGN1bGF0b3JM  
ZXhlci5yYuVaW2/aSBR+Dr9iFLbFTh1imwDBG5dEhO5GStnKpOUlEnLMJFgx  
Njs2bbqb/e87N98nrllpJSosATPnfDPnzMx3xnOhCX65nNzeWKDTVqHdB1eu  
A0e252w8OwpQ24PPELUfga6qvWNVO1YHQOsanY7R6TUaCP65cREELduPPNRq  
NBqOZ4dhoY4bUgU4B9SMYdBsA+DnenI7/m1smZpKsx+vJ5+nZpemr66/XF+N  
zT7NfBpbo/Hk1hzQ3Gxqah2asj5dWuOJqek0d8NzGitzg+s6ZfV+vrm9Nnu8  
3tHYPGvQ9AI+ANd3I9f23L+g5PrrTSRTDXnCzRqiojCFf4Sre4hCgWb03fFc  
5+rDJVNCfyEyly2fQOwoQnME7QXuL4OaTov68DmaR8ET9BOb35auB0GENhAs  
gkRKHgSjDfLjHr8lpQxj/McH4D6AC1px2wuCp7m9xMYkTQamGYNHSxtNI+zE  
ipZo5Oq9oA4AE/iul1Pcw0fXz0nIs7IjZ8mcDktK7uNFvk1MEzq4TdwhCzrB  
I+m3wB8/O3BNEsB8D6CgxnWAojlEKEASlEv66e3V2LLA+Tk4dAI/3Kxc/xE4  
uL2g+TcWfIUokkS9I/8DFhtEwAj7gmHf7/zDUu28JKu57FwT2W4IC16TkS+m  
c4TJ9OCcEVxK2xV9X0M8FkyeEjeyUYTF3B/XX8DnROm5Pkx1JJeonMDbrPxU  
yfKpemn7PvSAWWDV1fjDJQ6w+ej3y8lkfJPgL9DGg9PIdp7a6024lFrMzVbq  
fvOVucboG5pqAAm0pBaoDcfglKu026Qh7ivyiFwK1mnoPnAStjGrh2K+Z9vc  
9uE3ifS9EneKwvtMYZ2v5PoeHANNFoVRmw9GbhQy6mRACiOREORVplivMMX6  
OZhi1WbKIGaKXIspDI7BJabIe8oU8pos84RId54lxMk6HNE044xy5F0tjjA4  
Bpc48m5POULXZWWSUPHOs4R6WYsmHTw9EJoc16MJhWNwiSbH+0oTsswW0ISI  
d58mxMtaNOlymhzVowmFY3CJJkd7ShO2syvzhMl3nijMzVpMSZaxJ/Wowtex  
J2WunOzr6oRt/AULFKbYebZwP2vRZUCWHoQub+rRheExukSXN3s7tYyEE8vo  
Z5hWRrUmFT1ezC5qkYTBMbjEkcWecoSfOZZpwhU7zxTuZy2ydPiUgvmitUC7  
DVoDTBuSVdPsUS0ixVVVVdT4j/XUK9YxMoUKfJ4j23+E0lBThgP5V5CjdlWl  
es8oNSIp55CagWR4QbDW5OwR60F68prKDmwv0vC46hlReoyozVWs43zJHi5m  
0DjapHyJ9yYYquDtW5AXn2PxQAYgU5Z8cwe0RkaeBALLOnYIKS4jw63BgZyT  
kJ/KfiOziloeDF40NyaqYEwwDnphtvMODu4RtJ9ykma0RME3wAag0KYkl800  
aWv3cVqbCTbos93fnc9qbc31rkHjVGqB1kvrLiJfPvlCLbl+cUHhdIxI6AmC  
kwbgXUQiUKQlcXjny+DlRag2iRpVavPXIkkvl68QLuithsXvH8wHOxs+uVgi  
zyqE6aB8dENKFbiYwii5QqGcxvSPqSvnKmjymw4J11SgMrvHWGXN06hLACkr  
BgMW97FmX8IxHdXXl+98c8huRcALP5vHCXKuiX/owRX5JScT+JdtPIme7Smo  
aEQEfEWAU7Np5mWKZ3mdvA3UNFzJ5F/1CqIvgqEEjFxvxBUVYLIYphdg78Sw  
TgF2LIadFmBHYli3ADsRw3oF2BsxrF+ALcSwswIMLz50ZdhRhqfKsKsMe8qw  
rwzP8NtPXHxQKH4XKXgywR+EP+ErY5AOJo35HMb/ameifhJ8ce17D156hZg/  
xNQzAA0kbOX/oOCvhwrQFaCKpxY2fxBncwEVp+MVip4rVFiixM8PwovfjJaK  
CW9VsxMVeahJfSuTfSO+Yqs0aVWa7GxjUj812G1NpUF+yyM0d7qVuYHBz/0r  
7cU3BkKD3W0MdroGP0GuNsjPnoUGe9sYPMWxwY8iKy0mx5hCk/2tTOLFdHyg  
VT2MyXGY0OjZNka7PYOdjPyglaNX2zjYxlwPd2u8w660mO7PhUYzE2D8VFnF  
dJ39gKuzmKi52aiRJGgq/98dwT988OdfUEsDBBQAAAAIADN8KTSxQ76mUAcA  
APIkAAARAAAARGljZUNhbGN1bGF0b3IucmLdWt1T2zgQfyYz/A8auCl2Czl/  
xQEXM2VCesNcGphAy8NNJ2MSAR4cO+c4pb3j/vfTZ2zJsnEKfWj10Mir3dVq  
tdrfSnQb/HY8vByMgN02YNAFaRJF7VtgGYa7Z5h7xgEwO55te7bbaqXw72WY  
QrATxFmU7rRarUkULBbgJJzAXhBNllGQJSk4BESj550H6QKmLYDa5dmf/eF4  
ePyhfwF88NfWYRh/CaJwerS1C7YO+2cj2jk5uxrS3sdz8ns++HiBfz+cDlnn  
4+DyFHdOTj+dnvRpr0d+z/ujXn94ibunw8v+H/0R7g7Oj0f9Ie6NVr2riy3w  
mdjFGH3TIJ9kGr9D+nQCv0s+mG7/gHxdXfimTXpUp29a5GvAvkwqg2z3HaoX  
W+27TG+v7+8j55GvKbwBYRxmIfLGP7D3bRKFk5P3xwsyCONpzpUl9zAex8EM  
0kHJraJAkGXpOIXBFKbAC+P5MsPz8UnLE2uER1+N4rZYzmGqGsjFPsDZNUwX  
FaPSanBbGYibygOyxs2C7neLLJjc4/j5vKlkwKpSuFhG2aZgEhVs34TpQhhB  
1my2csfR7jY/EUgozcAcBzEboIfDsw3P9OgA8AD8Ok/B29VSCFnTC56+hrdh  
LNqTLiN4QWyaLxd32g4R2hG9nE9neq4HNDpRLQ/maIkM724QQ/IQxrd0qvdn  
g8HZ1RhzjsN4TCa2XVErHtT0aj3JvFWMJOTxyRLyQz+Ck+QWb2cS979O4Bx3  
gH8EoKAvhfMkzcYwTZNUg9JcaRAiz+YCMF4sU1G+6EFkTs45zfeT6OKbiQhs  
K9XbTFxH6dylDt5l4nQPzMivRs40+3ik2YJ96a8LMcAcuFYIYJnKCOjQCGhi  
Ra2KBgqaxc+MBxD+7RjipLNGASSMrsx06UrrDBQEJ0E2uQOaFyXJ3NTBNBGG  
Nx7uwgiCLF2K4bOxEUSZiTKJLZGRmvtxcIcSpzk20DjJfu2cqpl6mybi7Ntc  
1hneAEnepwsBEiP+lxlgSkMwWqjUUBfU6LFasqJipqWkSYCOFeaX6MhLMC5Z  
QrQXN2bfK2yLrJtwz/BmaJhpF8RhpCNxFi+YtgqXg5ZSulGsuZaunFkRciq1  
pchjTBwiyLy8n8zBG5B/6AJ+MmHiOqvedV3sukIQ1/iOcMnOI0TugK79DO91  
Oz/GeyjDFxz1tsqbeyCmTpSVoKiXD9PGxjWqX+5L1O3sLk0eAD3wipgXKDJh  
m+H8ivjTYBeNGyV0zcrY5ZgYu2YMvDKYzlBOJUUo/Xhk1S39ErCLx8Ja4DWr  
Qy/Hojm9gRm1Gp6Wb4ZdmBkfCGL0wYE4Jx78PuhybLrMOvsEwSJ0WetBl1UL  
XdYzoctimEMWUoE5VgPsYnqYD2oUrQNepVzbALzQ1uAMvNqYugSMmUoJGBNX  
IWMa++ulYCHgTNNUJlpF4L0EgL1+AQBzHOy+QijX+I9yyQ6k1NwHlvUcD1rO  
D/FgQxD7/YVBTPb9LwxisxoUI3FF6Tzs9jGIkdSJ8+oSrQllVB118RMKviTQ  
RxlEJGO6CGQsHNbCMSxTCWMHNL83teQJNY2UVEGNpEPgQjnSJjm1SMyTsr0u  
OKCUroniRz5/NwOvXkmqD332CqZLCR83ZpqYrGXUsClq7Jng8VExM7mCqKbF  
XtSxTEkVfaSrtseS7YGtEmv8JYCIlZ2xYfIpDK4jeBxlqzPWjuGDtiVvzdYu  
sHeBsQvesYe0kmZ62LB+0Qzp1HP8s0sKFADIWzFiuh4xqmwAYaxIvFgCJ158  
MExHqpV4w0yaeqju3Yg3BQLxVrTfdDygNl7I1KYiOdOEWaSsFHcMul81Z1CQ  
KxZtnfWKtk4p1IpFW+eZRVuHF1u9ylKrQ86eNFZTanUkepNSC/mz61V5s7Zu  
6Cmqhl5/FXxu1b23MLNpeapZFVJodY5iO8hY7lNn/T0hGoSNcdhDELNK3psN  
3mUmKd1Lc6QmKq1Kw05dGi5PqPRBOQkWBJvmwsodQUnRqU6KhZkqciM3Uq6U  
KJmHr1OltTqMeROCyvb4AirtxI09flHO0vsXJefR7FYUobwJKc0wlKU771aX  
8LwV14OqeQUQyAJN8KCrfovjTQELdbMwdKjaaOXAZq0PF+EUCjV9PXsWztZh  
p39oQgLG02rb+J8F+JcLvfFRaMdTDVuogzfABP/VKymGA9Wh1wm84PVEBoFf  
+HpCr7bK2wk5MJTOTpJLnthIPsMpjmfiR5Z3QezTvx2x4lPPLybSuaBQwuTx  
Uz7KHKvBmH2vdYvBE1TdYlz2GPekvYI4yqZuzZ3C/Y47hSSdY5myTHefvDYw  
LYOaWr+8hOfV+muHAMI990UvA25JQZPLAI0BZq66pC4FpWJzlYIUBZmgjIKM  
jAEEe80yOorrAGnFfCfa0s7g1wzF1TgsYyJuTW4TLnmqVe6S2h66KiogL4pS  
V2synYo1PfWneCq9r3Yr+88fwFf9cZ63+mcu3uhaRsq1jMS1WEYZkouh+NNk  
dlrvtFp48H9QSwMEFAAAAAgApE0pNNkUQCmzDAAAzzUAAAgAAABhbnRsci5y  
Yu0aa3PaSPK7f8UEqmKoEC7Ze3ywQ2wMcqwqDD5JJJtyXF4hBlsXkLjR4Mc5  
/PfrntFrRhLGcXY3V3VTZSOme3r6Pd0j6uTcuaakdmT3ydz3aODR2sVOnfTC  
5T3zr645aXhN8subN38npy7jfkAc5t5QFoWA1J3PiUCKCKMRZTd02t6pA8Ci  
Uz/izJ+suB8GxA2mZBVRAqujcMU8KmYmfuCyezIL2SJqkVufX5OQic9wxYHI  
Ipz6M99zkUSLuIySJWULn3M6JUsW3vhTeODXLod/FMjM5+GtH1wRLwymPi6K  
gAguW1C+B49v2xpfEQlnCUNeOAXEVcRBEu4Co0jTnYQ3CIpVATRgBCEHPbUA  
wY9AZbACqGR7CtFUhmBPb+76C8pAO+SXIhuwXU4ZCRsg43QFrP0unBApoiQ0  
Db3VggbcTaz1FzBECGBGFi6nzHfnUaZyYSmkmxcCJftrm6AvBe6CIieC7xVY  
E6ncI7dkQtEPgKuQ0GAaMvAJgALlRcgpkQLzKGYK9gWHIjOAShGjcMZv0aCx  
j5BoST10EVjpo+swdI5AukkUSa6AmHNi2sQeHTufupZB4PnMGn00+0afHH0G  
oEG6Y+dkZJHffuvaAN7dJd1hH/4+E+PXM8uwbTKygIx5ejYwYRFQsbpDxzTs  
FjGHvcG4bw4/tMjR2CHDkUMG5qnpAJozagnixWVAa3RMTg2rdwIT3SNzYDqf  
xZ7HpjPE/Y6Bmy4561qO2RsPuhY5G1tnIxs4BQH6pt0bdM1To49KN4ewKzE+  
GkOH2CfdwSAv0ZEB7HSPBoakCBL1TcvoOch4/CRI9EAbwMmgRewzo2fig/Gr  
AYx3rc8tkJ70RkPb+OcYkABI+t3T7gfDJg1VfCClKwA03RtbxilyBzLb4yPb  
MZ2xY5APo1EfFUtsw/po9gx7nwxGqOpjMrYNZKvfdbpicyACigEElGhsm0JF  
5tAxLGt85pijYZOcjD6BCoDPLizuC12OhiiwML8xsj4jYdSFUHaLfDoxYN5C  
9YFsjtVFpdiOZfacPBrs6IwslCyTlQyNDwPzgzHsGQgfIZ1Ppm00wTqmjQim  
2BosDruOheBoE+AMHnV/bAnbEfOYdPsfTWQ+Rgd722bsG0J1vZNY8e2dHYji  
KCI2hF9wtSOjZUao611fetcua7yczEPva1NAcAjI5J5TBQIhuIN/O5BpV3MK  
PDsDS0Ak+R6Qgi2ou0gJGcBJh7x+u5POuJyzS8CBcCV7cz+g5RAvnK8WQTnM  
D6b0LqOIovgB5DB37v+HNvxgueKZKDgOJ6vZDFZ2SK2mAgQyzItPFYS8AeSt  
Oiv5gvk3OzolYArn9zMtoq6SL3U8KFYM0yw59u+C1QKyG7+lkH/eiMz75u4Y  
Bua3TI97e6BARdB5GH69dK9BE41lGKlihrNZRFGamJlXBFDIa00Ef5booz2n  
wRXk5nfJylcaKg70D0kSNNRGEzQybCCu0moW1ie6f/dOkoLtMbPjczvw5wfK  
AkVjOAA73g24VLeq2ulcLrhQCc8jWliQ161Agt3zz4riFy77qlo8gvMPPeQh  
9kjSeR9rviU9W0zgQyvxZzETP64VatI5wKsqGWAUjuRpA/mgTPPvxPkkT+eS  
H1UDiT8nKPhVw0h9O8GRExfVPEHxEEElkCeTc9C3TfB6qFCwfiPoOcnZTGW8  
CV+gHo0iKGXyNHKcxE+aZ2Y+fC5lvyCdDjn4EigCCVKJ2OKzzMHzIZ2bzjsD  
yas4ia2MUkEt0WoSiVTbAEUy3iIRD5elKakdYRGdR4OQEt9wh6ayhbKVzLhO  
+JUGWsotSZUJSGFy25R5rnnJhpQJQR0EdA5AwdjeXt847o4HziUWLkNj8IyU  
KQgq/H9fJlTQbq99OMYST3r99oK8SFnHk+vlyyelSo4rs1wZ0Dt+yRW+kwH+  
KwDtVGOdTHvfviWU8swUaODIJdfyjTQvLibYVFOv3+aS7fvOpmSb4T0vJ/+o  
dPtHptI/LAU+km9SXsRTLhukWObwY3dg9i+dz2eGktxkTSaQwUdv9QWlMC2Q  
1fpHyTjCDy/5/bKqsJNeXlm+ZetbJMZtSV21SHU2zVYBayUsCKQsPSVMKOAN  
iU1m5I5kQAdBzu4InqqdBA+fTuPGna+oxnh8PAnQJifDI6qcQnp8PUKDh5eR  
srJ2Xq8/5DS3bpHd+gOHrLXebREAIW/rPXiQW6zFpFBB8hgu1xe1DVsCrRId  
t7Pz8TA26aFq0wIl8Nl2iQAwvWl/uarUGV5vOL7jQCjbMAZt2jRbXbpxPiwV  
IgoxGdADekeZQvyRQ3uDC09c7ytn8A/vdbRahxzOXDgJpzA/c/VK+ZBBr2dz  
WKkWAyUpm0PXyNzgijYwy7XA5VT+1F5CSZMKHiRFgfpOZks4EMXX90BQwYtx  
Vdnea7IlI5ORsxUtRZEnRwGkH6I4FgzD9tSPhNR0aqHYxp1Hl+J2C3JnqoNW  
LHCxN6oz6oU3lDWAWnO/AGauH1HcSQXp7MTqjA+ihuYSFZatsF5atmrHIky0  
0+sCOIK/Tb6VWqLEslhTTUrVvbXpcGxhPhwVJsRRZkYcC66aUpx+qikn1TbE  
kdmRl9kRR2xLXuS8jK2NJhUIGwI2q4bKjdxAGxYCrsp0iPzTBN3jlkJ2twk4  
TovQchttDrfnBJsb3G9Dudhe3gdLRqeQYNwrvIbXQ1XWKjG1YiGtnQTqd72v  
mdArv2icCHiq2J7RyFtRYoGirwLxYiE1UIEM8BeRmr+A5izyJ3OqXs5VKD4u  
3oWYes+6STLo/vbV3idaeVh5A+aL2HJq7Grm3NfkFF3GYUyk2lqMLkPGLylj  
IWvQRBcq57bTNywL27jaeQ1skJ257X+FftCotWpNmK5d7JEaBt+LHAJdLPn9  
gXZtFtcB6XbtYkUQV55x2OcvYnGkVWXik8p1rMCQhzl4KwQUb5Skj+ZOhZCe  
C3FGK93i9hqaX8Nl83vjzufV7oOjxui/Vz5EA2m02+3mK2y+lqQxpZ6PL3Q6  
9YdMB8nkcLWYULZukqk/FZeRIhwJhCO/Bk/ZJ2EgtVN/SErguAIWQnegSsbP  
9W6tnPWqBFUuwCLFFtT3SEpe52MfdLakHhcvLVH7iBmrP5MyxWluwaBatjyX  
wUxReVbxoqCUUyyQgMl2uxQKxWOlAMPwo+9CwujOH3MP1f6Rx3zxvCbyOiEP  
FjOxb2zrQUFIbgQnxJ3zSl1s5TXHItmcgSvjO+zHjILxr3CPE2sSZ6xlQmWP  
POSR0vn1+uBR37DpY8r9EZ6RZy+FrIvMld7g45Cndnk2gWQskuqXYEPDlrhe  
sSwSeaq0ABKq0t7TgPjwf7cc9+BLAPAvQSWYI5gXwSi3fFviXTNduB39WWsg  
+8ddRVT4HvtAnLDjOxVVbCwZ0sxfqB8qKoL4dk6QK5bA8qa1sgbMrea07bnz  
eVl7m4z46BUXlnIFvkgidfxBAITiYh+TOmbyOA44nbYJWCsMIxmnxavbHFnJ  
S0rajy7dg+SNXZ1Mw2CXJ5d+xOXQnjY4WpDCUdXEZbhBum/pRtUFJA697KGA  
xopoSjGERmpqqx51jjOXRT/B9YIojCxZl9+Xo2Q/SCm+jnjS7YS8XXTE5eLE  
55B/tuqDcnc5eCmfEikYZUNvIMClshajZWPhiaOkZ4I8sVVXpnZkVaT3ddpF  
YbZoxnLajjuy4n5JS3YMxYBGLElRsFOLcHklLF2hqVXyOB7v3v7fhv0cbdhj  
XZgW/NW5I8nYP6IFi2lhDClRqjKjB7A40RSUp7Ry6spyG2svFasvTaXFK9fH  
bxLj6+eChStk2KZT++52R7C2BxWgTK/4O8DoXL4XzRLuhVIxihSsLigpHy9K  
6sfnFLffwehTStsntjRpZwL2qWpv3r//PRqcfamJzmOKqBDxz2zpn8X4n9+a  
VXY/9e9pf5JP8VCX1eCRzyEm5Az+08rB25BN4x9Z1BNSh+KNY1rtpfMCV7wu  
wDcF+O2bipBbDOz942+V0E7y8EoQVfHW2ddEqLqQKl/klh2A5B2xVgH3F9TA  
dJ6q5rsrYCWBar+/yeMhKH5DrtpA8lqMD+R00wmucaxGQ/zaXCtLVkvKSkVT  
F4OM6sQWwpYs0CSsOiqeKGeaTJ8oYpaeO1mq3kIwFbdSJvUy7YkyZW8LnySS  
eEHaEe9JVQAPsTwJt5BOWav9lqNM4LKT6smOmh5VLaJ7be6YerILp2SF/6bf  
vs/TBXKOm+SC4jkBUcApo6gRUcXQrFF1Nj3RInGrhUdVKzujtlW8OPI6YrUK  
SCkBNH0u11z5b1SqxGvUH3C3IRzk6xYcpClxB3+/sj5o1ip+VKjoVmFYgajM  
VoZ8voz8n0ti8PdfUEsBAhQAFAAAAAgAM3wpNOm++284BgAASSUAABYAAAAA  
AAAAAQAgAAAAAAAAAERpY2VDYWxjdWxhdG9yTGV4ZXIucmJQSwECFAAUAAAA  
CAAzfCk0sUO+plAHAADyJAAAEQAAAAAAAAABACAAAABsBgAARGljZUNhbGN1  
bGF0b3IucmJQSwECFAAUAAAACACkTSk02RRAKbMMAADPNQAACAAAAAAAAAAB  
ACAAAADrDQAAYW50bHIucmJQSwUGAAAAAAMAAwC5AAAAxBoAAAAA
