<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5537231430235165593</id><updated>2012-02-16T13:06:58.927+01:00</updated><category term='linux'/><category term='mvc'/><category term='MonoRail'/><category term='Intelligence artificielle'/><category term='JavaScript'/><category term='phpdoc'/><category term='python'/><category term='html5'/><category term='phantomjs programming'/><category term='php'/><category term='Validation'/><category term='phpunit'/><title type='text'>Aventures Logicielles</title><subtitle type='html'>Programming and fun</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-5219099054702184934</id><published>2012-01-14T11:16:00.001+01:00</published><updated>2012-01-19T23:13:59.121+01:00</updated><title type='text'>ClassGrapher</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-hWgoZtOq9bg/TxiUlJ1y4TI/AAAAAAAAACk/Js61Aw_mw7s/s1600/ClassDiagram.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="163" src="http://1.bp.blogspot.com/-hWgoZtOq9bg/TxiUlJ1y4TI/AAAAAAAAACk/Js61Aw_mw7s/s320/ClassDiagram.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;I have developped a small utility command line application able to generate simple class diagrams out of any set of namespaced PHP files. The goal being to have a quick way to figure out how some PHP classes were structured without browsing too much code. This is not very accurate but give you a quick big picture of the code structure.&lt;br /&gt;&lt;br /&gt;The code can be found on github:&lt;br /&gt;https://github.com/sixty-nine/ClassGrapher&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The PHP parser&lt;/h3&gt;The class grapher does not use PHP reflection to gather inheritance information about the classes. Indeed, in order to work, PHP reflection needs to be able to instanciate the classes it loads. This is not possible if you have missing dependencies in your project.&lt;br /&gt;The goal of the grapher was to draw the structure of any PHP files.&lt;br /&gt;Thus I implemented a simple recursive parser for only the parts of the PHP language that were needed for the grapher (basically any "use", "namespace", "class" and "interface" statement).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Using composer to download vendors libraries&lt;/h3&gt;Composer is a PHP utility used to manage project dependencies.&lt;br /&gt;See &lt;a href="http://packagist.org/about-composer"&gt;composer website&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The idea is simple. &lt;br /&gt;&lt;br /&gt;You download the composer.phar binary to the root of your project, then create a composer.json file describing the dependencies of the project and finally run the command:&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;./composer.phar install&lt;br /&gt;&lt;/pre&gt;&lt;pre class="php" name="code"&gt;&lt;br /&gt;&lt;/pre&gt;This will automatically download the dependencies as well as create an autoload file for your project.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Example of composer.json file&lt;/h3&gt;The grapher project uses the smyfony/console component from the Symfony2 framework.&amp;nbsp;Additionnaly we would like to autoload the files of the project.&lt;br /&gt;&lt;br /&gt;The corresponding composer.json file looks as follow:&lt;br /&gt;&lt;br /&gt;&lt;pre class="javascript" name="code"&gt;{&lt;br /&gt;    "require": {&lt;br /&gt;     "symfony/console": "2.0.7"&lt;br /&gt;    },&lt;br /&gt;&lt;br /&gt;    "autoload": {&lt;br /&gt;        "psr-0": {&lt;br /&gt;            "LazyGuy\\ClassGrapher": "src"&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This file will indicate to composer that the library "symfony/console" version "2.0.7" must be downloaded to the project directory. Furthermore any class in the namespace "LazyGuy\ClassGrapher" will be searched in the "src" directory.&lt;br /&gt;&lt;br /&gt;Be carefull, this is JSON, you cannot add extra coma at the end of a list if there is no successor in the list (in PHP you can).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Using travis for continuous integration&lt;/h3&gt;&lt;a href="http://travis-ci.org/"&gt;Travis&lt;/a&gt; is a free, lovely continuous integration online service that provides seamless integration with github. &lt;br /&gt;&lt;br /&gt;All you need to do is:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Register to travis&lt;/li&gt;&lt;li&gt;Go to github and enable the travis hook&lt;/li&gt;&lt;li&gt;Create a .travis.yml file to indicate to travis how to run the tests of your app&lt;/li&gt;&lt;/ol&gt;This will cause any github commit in your project to trigger a build on the travis continuous integration server. Travis will run your tests in two PHP environments:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;PHP 5.3&lt;/li&gt;&lt;li&gt;PHP 5.4&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3&gt;The .travis.yml file&lt;/h3&gt;This file must indicate to the travis server how to build your project.&lt;br /&gt;&lt;br /&gt;In our case, travis must download the dependencies with composer and then run the tests&lt;br /&gt;&lt;br /&gt;&lt;pre class="javascript" name="code"&gt;language: php&lt;br /&gt;php:&lt;br /&gt;  - 5.3&lt;br /&gt;  - 5.4&lt;br /&gt;&lt;br /&gt;before_script:&lt;br /&gt;  - wget http://getcomposer.org/composer.phar&lt;br /&gt;  - chmod +x composer.phar&lt;br /&gt;  - ./composer.phar install&lt;br /&gt;&lt;br /&gt;script: phpunit -c src/LazyGuy/ClassGrapher/Tests/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The language section indicates which is the language of the project. The "php" key lists the PHP versions to run the tests against. The section "before_script" tells to travis what to do before running the tests (here it will download composer and then run the install command). Finally the "script" section indicates the command to run the tests.&lt;br /&gt;&lt;br /&gt;Once you configured travis, any commit to github will cause travis to build the project, run the tests, and report success or failure.&lt;br /&gt;&lt;br /&gt;Isn't that lovely?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-5219099054702184934?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/5219099054702184934/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2012/01/classgrapher.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/5219099054702184934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/5219099054702184934'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2012/01/classgrapher.html' title='ClassGrapher'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-hWgoZtOq9bg/TxiUlJ1y4TI/AAAAAAAAACk/Js61Aw_mw7s/s72-c/ClassDiagram.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-6097285334297499294</id><published>2011-12-08T10:31:00.001+01:00</published><updated>2011-12-08T11:16:49.745+01:00</updated><title type='text'>Experiments with the Arduino - Multiplexed leds</title><content type='html'>&lt;h3&gt;Introduction&lt;/h3&gt;&lt;p&gt;In &lt;a href="http://aventures-logicielles.blogspot.com/2011/09/experiments-with-arduino-controlling-5.html"&gt;a previous experiment&lt;/a&gt; I was controlling 5 leds using 5 outputs of the Arduino. With the help of a 4051 multiplexer it is possible to control up to 8 leds using only 3 outputs.&lt;/p&gt;&lt;p&gt;Basically a multiplexer is an electronic device that can send it's single input to one of it's height outputs. The active output is selected by sending a binary number from 000 to 111 to the three control pins of the multiplexer.&lt;/p&gt;&lt;a href="http://en.wikipedia.org/wiki/Multiplexer"&gt;Wikipedia&lt;/a&gt; will tell you everything you ever wanted to know about multiplexers.&lt;br/&gt;&lt;br/&gt;&lt;h3&gt;The schematic&lt;/h3&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-vKeJRrFsgs0/TuCF7hhttbI/AAAAAAAAACc/nZwtorVqlSw/s1600/multiplexed-leds.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="170" width="320" src="http://2.bp.blogspot.com/-vKeJRrFsgs0/TuCF7hhttbI/AAAAAAAAACc/nZwtorVqlSw/s320/multiplexed-leds.png" /&gt;&lt;/a&gt;&lt;/div&gt;This image was generated with the awesome &lt;a href="http://fritzing.org"&gt;Fritzing application&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;&lt;h3&gt;The code&lt;/h3&gt;&lt;pre class="java" name="code"&gt;&lt;br /&gt;int pinA1 = 8;&lt;br /&gt;int pinA2 = 9;&lt;br /&gt;int pinA3 = 10;&lt;br /&gt;&lt;br /&gt;void setup() {                &lt;br /&gt;&lt;br /&gt;  for(int i = 1; i &lt;= 10; i++) {&lt;br /&gt;    pinMode(i, OUTPUT);&lt;br /&gt;    digitalWrite(i, LOW);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  digitalWrite(2, LOW);  // Ground&lt;br /&gt;  digitalWrite(3, HIGH); // Power&lt;br /&gt;  digitalWrite(4, HIGH); // Mux input to send to the leds&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void ledOn(int nr)&lt;br /&gt;{&lt;br /&gt;  int r0 = nr &amp; 0x01;&lt;br /&gt;  int r1 = (nr &gt;&gt; 1) &amp; 0x01;&lt;br /&gt;  int r2 = (nr &gt;&gt; 2) &amp; 0x01;&lt;br /&gt;&lt;br /&gt;  digitalWrite(pinA1, r0);&lt;br /&gt;  digitalWrite(pinA2, r1);&lt;br /&gt;  digitalWrite(pinA3, r2);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void loop() {&lt;br /&gt;  for (int j = 0; j &lt; 8; j++) {&lt;br /&gt;    ledOn(j);&lt;br /&gt;    delay(50);&lt;br /&gt;  }&lt;br /&gt;  for (int j = 7; j &gt;= 0; j--) {&lt;br /&gt;    ledOn(j);&lt;br /&gt;    delay(50);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br/&gt;&lt;br/&gt;&lt;h3&gt;Demo&lt;/h3&gt;&lt;object width="420" height="315"&gt;&lt;param name="movie" value="http://www.youtube.com/v/LX7hpL9yjLI?version=3&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/LX7hpL9yjLI?version=3&amp;amp;hl=en_US" type="application/x-shockwave-flash" width="420" height="315" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-6097285334297499294?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/6097285334297499294/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/12/experiments-with-arduino-multiplexed.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/6097285334297499294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/6097285334297499294'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/12/experiments-with-arduino-multiplexed.html' title='Experiments with the Arduino - Multiplexed leds'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-vKeJRrFsgs0/TuCF7hhttbI/AAAAAAAAACc/nZwtorVqlSw/s72-c/multiplexed-leds.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-8121274999809258100</id><published>2011-11-01T09:48:00.010+01:00</published><updated>2011-11-03T17:36:47.928+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='phantomjs programming'/><title type='text'>Website screenshots with PhantomJS</title><content type='html'>&lt;h3&gt;Introduction&lt;/h3&gt;PhantomJS is an headless (i.e. without frontend) web browser based on Webkit with a Javascript API. It is mainly used for headless testing of webpages. It also allows the capture of a webpage into PNG, JPEG, and PDF files.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Compiling PhantomJS&lt;/h3&gt;Please follow the instructions &lt;a href="http://code.google.com/p/phantomjs/wiki/BuildInstructions"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;sudo apt-get install libqt4-dev libqtwebkit-dev qt4-qmake&lt;br /&gt;  git clone git://github.com/ariya/phantomjs.git &amp;&amp; cd phantomjs&lt;br /&gt;  git checkout 1.3&lt;br /&gt;  qmake-qt4 &amp;&amp; make&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Rasterize a website&lt;/h3&gt;The script to render a website to a file is provided as an example of PhantomJS. The code and explanations can be found &lt;a href="http://code.google.com/p/phantomjs/wiki/QuickStart#Rendering"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;var page = new WebPage(),&lt;br /&gt;    address, output, size;&lt;br /&gt;&lt;br /&gt;if (phantom.args.length &lt; 2 || phantom.args.length &gt; 3) {&lt;br /&gt;    console.log('Usage: rasterize.js URL filename');&lt;br /&gt;    phantom.exit();&lt;br /&gt;} else {&lt;br /&gt;    address = phantom.args[0];&lt;br /&gt;    output = phantom.args[1];&lt;br /&gt;    page.viewportSize = { width: 600, height: 600 };&lt;br /&gt;    page.open(address, function (status) {&lt;br /&gt;        if (status !== 'success') {&lt;br /&gt;            console.log('Unable to load the address!');&lt;br /&gt;        } else {&lt;br /&gt;            window.setTimeout(function () {&lt;br /&gt;                page.render(output);&lt;br /&gt;                phantom.exit();&lt;br /&gt;            }, 200);&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now run:&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;bin/phantomjs rasterize.js http://aventures-logicielles.blogspot.com test.png&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And magically you will see the file test.png appear on your disk.&lt;br /&gt;&lt;br /&gt;This is absolutely awesome, we just created a screen capture of the website. However this will capture the whole page, and that means for a blog, a very long image. The above will produce an image of 1000 pixels per 15'000+ pixels (2.3Mb).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Getting only the head of the page&lt;/h3&gt;With a small modification to the script we can render only the first part of the page. This is possible by setting the clip rectangle before rendering the page. Let's use the page.clipRect function just before calling render as explained on &lt;a href="http://stackoverflow.com/questions/6432302/phantom-js-cliprect-javascript-help/6434812#6434812"&gt;StackOverflow&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;var page = new WebPage(),&lt;br /&gt;    address, output, size;&lt;br /&gt;&lt;br /&gt;if (phantom.args.length &lt; 2 || phantom.args.length &gt; 3) {&lt;br /&gt;    console.log('Usage: rasterize.js URL filename');&lt;br /&gt;    phantom.exit();&lt;br /&gt;} else {&lt;br /&gt;    address = phantom.args[0];&lt;br /&gt;    output = phantom.args[1];&lt;br /&gt;    page.viewportSize = { width: 800, height: 600 };&lt;br /&gt;    page.open(address, function (status) {&lt;br /&gt;        if (status !== 'success') {&lt;br /&gt;            console.log('Unable to load the address!');&lt;br /&gt;        } else {&lt;br /&gt;            window.setTimeout(function () {&lt;br /&gt;  // ----- CHANGE HERE -------------------------------------------&lt;br /&gt;  page.clipRect = { top: 0, left: 0, width: 800, height: 600 };&lt;br /&gt;  // -------------------------------------------------------------&lt;br /&gt;                page.render(output);&lt;br /&gt;                phantom.exit();&lt;br /&gt;            }, 200);&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will produce images of 800 x 600 pixels with an acceptable size (here 360Kb).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Here we go&lt;/h3&gt;Let's try the script on my three blogs.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="float: left; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-pfhljGG7j5A/Tq-yFfwY1uI/AAAAAAAAAB0/ttM_MDR465k/s1600/test1.png" imageanchor="1" style=""&gt;&lt;img border="0" height="240" width="320" src="http://3.bp.blogspot.com/-pfhljGG7j5A/Tq-yFfwY1uI/AAAAAAAAAB0/ttM_MDR465k/s320/test1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="float: right; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-i6GZMjaoQlQ/Tq-2QLqS3UI/AAAAAAAAACA/UUtlUFJwJrs/s1600/test3.png" imageanchor="1" style=""&gt;&lt;img border="0" height="240" width="320" src="http://4.bp.blogspot.com/-i6GZMjaoQlQ/Tq-2QLqS3UI/AAAAAAAAACA/UUtlUFJwJrs/s320/test3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-uLbAc7rvcOM/Tq-2a2U5dZI/AAAAAAAAACM/veXVNHPWSik/s1600/test2.png" imageanchor="1" style=""&gt;&lt;img border="0" height="240" width="320" src="http://2.bp.blogspot.com/-uLbAc7rvcOM/Tq-2a2U5dZI/AAAAAAAAACM/veXVNHPWSik/s320/test2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;First observation, the third screenshot contains a big grey area where there was originally a youtube video. Apparently the rasterize script is not able to grab it.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Create a screenshot from Apache&lt;/h3&gt;&lt;br /&gt;The next step is to try to grab a screenshot from within a php script running in Apache. The following snippet searches for the PhantomJS binary and the rasterize script on the same path as the PHP file.&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;$phantomjs = __DIR__.'/phantomjs';&lt;br /&gt;$script = __DIR__.'/rasterize.js';&lt;br /&gt;$url = 'http://http://aventures-logicielles.blogspot.com';&lt;br /&gt;$outfile = 'webss_' . uniqid() . '.png';&lt;br /&gt;&lt;br /&gt;$command = "$phantomjs $script $url $outfile 2&gt;&amp;1";&lt;br /&gt;$output = shell_exec($command);&lt;br /&gt;header('Content-Type: image/png');&lt;br /&gt;echo file_get_contents($outfile);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you try to run the above script you will not get any result. To understand what's going on you have to var_dump the $output of the shell_exec command. You will get the following error message: "phantomjs: cannot connect to X server".&lt;br /&gt;&lt;br /&gt;Strange, isn't PhantomJS supposed to be a headless web browser? The reply can be easily found by &lt;a href="http://code.google.com/p/phantomjs/issues/detail?id=33"&gt;googleing the error string&lt;/a&gt;. There is an open issue with PhantomJS on *nix systems, Ariya Hidayat, the creator of PhantomJS says: "PhantomJS is not 'pure headless' yet. On Unix, it still needs X Server for font stuff, etc.".&lt;br /&gt;&lt;br /&gt;Fortunately there is a quite simple workaround using XVFB. See &lt;a href="http://code.google.com/p/phantomjs/wiki/XvfbSetup"&gt;here for the full explanation&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Once you have set XVFB up following the above explanations, you can modify the PHP script to use XVFB as X server.&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;$phantomjs = __DIR__.'/phantomjs';&lt;br /&gt;$script = __DIR__.'/rasterize.js';&lt;br /&gt;$url = 'http://http://aventures-logicielles.blogspot.com';&lt;br /&gt;$outfile = 'webss_' . uniqid() . '.png';&lt;br /&gt;&lt;br /&gt;$command = "DISPLAY=:0 $phantomjs $script $url $outfile 2&gt;&amp;1";&lt;br /&gt;$output = shell_exec($command);&lt;br /&gt;header('Content-Type: image/png');&lt;br /&gt;echo file_get_contents($outfile);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The sequel&lt;/h3&gt;&lt;br /&gt;I wanted to play a bit more with PhantomJS and the rasterize script so I created a small Symfony2 bundle using it to create web pages screenshots on the fly. You will find the sources on github.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/sixty-nine/LiipRasterizeBundle"&gt;Take a look here !&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Portability&lt;/h3&gt;&lt;br /&gt;The above code works well on my Ubuntu workstation but a friend of mine just reported the following facts:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The rasterize script causes a segmentation fault on my mac&lt;/li&gt;&lt;li&gt;On Debian 5 (lenny) the QT libraries are too old to use the rasterize script&lt;/li&gt;&lt;li&gt;On Debian 6 (squeeze) everything runs fine&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Links&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.phantomjs.org/"&gt;PhantomJS Homepage&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/phantomjs/wiki/BuildInstructions"&gt;PhantomJS Build Instructions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/phantomjs/wiki/QuickStart"&gt;PhantomJS Quick Start&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/phantomjs/wiki/Interface"&gt;PhantomJS API&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-8121274999809258100?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/8121274999809258100/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/11/website-screenshots-with-phantomjs.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/8121274999809258100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/8121274999809258100'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/11/website-screenshots-with-phantomjs.html' title='Website screenshots with PhantomJS'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-pfhljGG7j5A/Tq-yFfwY1uI/AAAAAAAAAB0/ttM_MDR465k/s72-c/test1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-3544502101783401798</id><published>2011-09-21T13:53:00.000+02:00</published><updated>2011-09-21T13:53:32.610+02:00</updated><title type='text'>Experiments with the Arduino - Led Matrix</title><content type='html'>This one was way harder to implement, I wanted to simulate a led matrix with my Arduino.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The circuit&lt;/h3&gt;&lt;br /&gt;The circuit is not so difficult, basicaly you connect the anode of each row of leds to the same digital output of the Arduino and the cathode of each column of leds to the same digital output. Each led is then connected to a unique pair of digital output.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-0AJG0KvARsM/TnnKOA2jz-I/AAAAAAAAABc/a-e6zebPtrA/s1600/circuit.png" imageanchor="1" style="margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="211" width="320" src="http://3.bp.blogspot.com/-0AJG0KvARsM/TnnKOA2jz-I/AAAAAAAAABc/a-e6zebPtrA/s320/circuit.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To turn a led on you just need to set the digital output for the correct row to HIGH and the output for the correct column to LOW.&lt;br /&gt;&lt;br /&gt;This means that you can turn on several leds of the same row at the same time but you cannot turn on leds of different rows at the same time. Indeed if you set several rows to HIGH and several columns to LOW, all the intersecting pixels will be turned on, and this is not what we want.&lt;br /&gt;&lt;br /&gt;A workaround for this problem is to use a technique named Row Scanning.&lt;br /&gt;&lt;br /&gt;The idea is to turn on the leds of each row alternatively. If this is done fast enough, we will not perceive any flickering and we will have the feeling that all the pixels are on at the same time.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-u3OL6CcYVAo/TnnNjqirUhI/AAAAAAAAABs/-SjFU6iaArg/s1600/IMAG0035.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="192" src="http://1.bp.blogspot.com/-u3OL6CcYVAo/TnnNjqirUhI/AAAAAAAAABs/-SjFU6iaArg/s320/IMAG0035.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7v0V35QowJA/TnnNdU_0NOI/AAAAAAAAABk/YpztNGMmpW4/s1600/IMAG0036.jpg" imageanchor="1" style="margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="192" width="320" src="http://1.bp.blogspot.com/-7v0V35QowJA/TnnNdU_0NOI/AAAAAAAAABk/YpztNGMmpW4/s320/IMAG0036.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;The code&lt;/h3&gt;&lt;br /&gt;The Row Scanning mechanism described above is not so difficult to implement. I decided to use a buffer to store the actual state of the pixels. The demo does not try to change the state of the digital outputs directly. Instead it changes the state of the pixels in the buffer. Then I added a refreshScreen routine that is responsible actually set the state of the digital outputs in a loop.&lt;br /&gt;&lt;br /&gt;The problem is that the refreshScreen routine must be called periodically. This is not a problem if we don't have any delay statement in our code, because the delay statement will stop the screen refresh. Unfortunately to create a led matrix demo we definitely want to use some delay so that the user can actually see something on the led matrix.&lt;br /&gt;&lt;br /&gt;The solution is to use an interrupt library (TimerOne) that allows to set a routine as callback for a periodic interrupt. So we set refreshScreen as the callback and we have no more problems.&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;#include "TimerOne.h"&lt;br /&gt;&lt;br /&gt;const int XMIN = 2;&lt;br /&gt;const int XMAX = 4;&lt;br /&gt;&lt;br /&gt;const int YMIN = 8;&lt;br /&gt;const int YMAX = 10;&lt;br /&gt;&lt;br /&gt;int screen[3][3] = &lt;br /&gt;{&lt;br /&gt;  { 0, 0, 0 },&lt;br /&gt;  { 0, 0, 0 },&lt;br /&gt;  { 0, 0, 0 }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int patterns1[][3][3] = {&lt;br /&gt;&lt;br /&gt;  {&lt;br /&gt;    { 1, 0, 0 },&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 1, 0 },&lt;br /&gt;    { 1, 0, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 1 },&lt;br /&gt;    { 0, 1, 0 },&lt;br /&gt;    { 1, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 1 },&lt;br /&gt;    { 0, 1, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 1 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int patterns2[][3][3] = {&lt;br /&gt;&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 1, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 1, 1, 1 },&lt;br /&gt;    { 1, 0, 1 },&lt;br /&gt;    { 1, 1, 1 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 1, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int patterns3[][3][3] = {&lt;br /&gt;&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 1, 1, 1 },&lt;br /&gt;    { 1, 1, 1 },&lt;br /&gt;    { 1, 1, 1 }&lt;br /&gt;  },&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int patterns4[][3][3] = {&lt;br /&gt;&lt;br /&gt;  {&lt;br /&gt;    { 1, 0, 0 },&lt;br /&gt;    { 1, 0, 0 },&lt;br /&gt;    { 1, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 1, 0 },&lt;br /&gt;    { 0, 1, 0 },&lt;br /&gt;    { 0, 1, 0 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 1 },&lt;br /&gt;    { 0, 0, 1 },&lt;br /&gt;    { 0, 0, 1 }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 },&lt;br /&gt;    { 0, 0, 0 }&lt;br /&gt;  },&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ----- MATRIX MANAGEMENT --------------------&lt;br /&gt;&lt;br /&gt;void refreshScreen()&lt;br /&gt;{&lt;br /&gt;  for(int i = 0; i &lt;= 3; i++) { // For each row&lt;br /&gt;  &lt;br /&gt;    digitalWrite(i + XMIN, HIGH);&lt;br /&gt;&lt;br /&gt;    for(int j = 0; j &lt;= 3; j++) { // For each column&lt;br /&gt;    &lt;br /&gt;      if (screen[i][j] == 1) {&lt;br /&gt;        digitalWrite(j + YMIN, LOW);&lt;br /&gt;      } else {&lt;br /&gt;        digitalWrite(j + YMIN, HIGH);&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      digitalWrite(j + YMIN, HIGH);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    digitalWrite(i + XMIN, LOW);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void setPixel(int i, int j, int value = 1) {&lt;br /&gt;  screen[i][j] = value;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void clearMatrix() {&lt;br /&gt;  for(int i = 0; i &lt; 3; i++) {&lt;br /&gt;    for(int j = 0; j &lt; 3; j++) {&lt;br /&gt;      setPixel(i, j, 0);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void showPattern(int pattern[3][3]) {&lt;br /&gt;  for(int i = 0; i &lt; 3; i++) {&lt;br /&gt;    for(int j = 0; j &lt; 3; j++) {&lt;br /&gt;      setPixel(i, j, pattern[i][j]);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// ----- DEMO FUNCTIONS -----------------------&lt;br /&gt;&lt;br /&gt;void walkMatrixH(int wait, int repeat = 1) {&lt;br /&gt;  for(int k = 0; k &lt; repeat; k++) {&lt;br /&gt;    for(int i = 0; i &lt; 3; i++) {&lt;br /&gt;      for(int j = 0; j &lt; 3; j++) {&lt;br /&gt;        clearMatrix();&lt;br /&gt;        setPixel(i, j);&lt;br /&gt;        delay(wait);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void walkMatrixV(int wait, int repeat = 1) {&lt;br /&gt;  for(int k = 0; k &lt; repeat; k++) {&lt;br /&gt;    for(int i = 0; i &lt; 3; i++) {&lt;br /&gt;      for(int j = 0; j &lt; 3; j++) {&lt;br /&gt;        clearMatrix();&lt;br /&gt;        setPixel(j, i);&lt;br /&gt;        delay(wait);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void animatePattern(int wait, int patterns[][3][3], int steps, int repeat = 1) {&lt;br /&gt;&lt;br /&gt;  for(int k = 0; k &lt; repeat; k++) {&lt;br /&gt;    for(int i = 0; i &lt; steps; i++) {&lt;br /&gt;      showPattern(patterns[i]);&lt;br /&gt;      delay(wait);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// ----- MAIN PROGRAM -------------------------&lt;br /&gt;&lt;br /&gt;void setup() {               &lt;br /&gt;  &lt;br /&gt;  for(int i = XMIN; i &lt;= XMAX; i++) {&lt;br /&gt;    pinMode(i, OUTPUT);&lt;br /&gt;  }&lt;br /&gt;  for(int i = YMIN; i &lt;= YMAX; i++) {&lt;br /&gt;    pinMode(i, OUTPUT);&lt;br /&gt;  }&lt;br /&gt;  clearMatrix();&lt;br /&gt;&lt;br /&gt;  Timer1.initialize(10);&lt;br /&gt;  Timer1.attachInterrupt(refreshScreen);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void loop() {&lt;br /&gt;  &lt;br /&gt;  // -------------------------------------------&lt;br /&gt;  animatePattern(10, patterns3, 2, 3); // Flash&lt;br /&gt;&lt;br /&gt;  walkMatrixH(10, 2);&lt;br /&gt;  walkMatrixV(10, 2);&lt;br /&gt;  &lt;br /&gt;  // -------------------------------------------&lt;br /&gt;  animatePattern(10, patterns3, 2, 3); // Flash&lt;br /&gt;  animatePattern(10, patterns4, 4, 5);&lt;br /&gt;&lt;br /&gt;  // -------------------------------------------&lt;br /&gt;  animatePattern(10, patterns3, 2, 3); // Flash&lt;br /&gt;  animatePattern(10, patterns1, 6, 5);&lt;br /&gt;&lt;br /&gt;  // -------------------------------------------&lt;br /&gt;  animatePattern(10, patterns3, 2, 3); // Flash&lt;br /&gt;  animatePattern(10, patterns2, 4, 5);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;h3&gt;Demo&lt;/h3&gt;&lt;br/&gt;&lt;object width="420" height="315"&gt;&lt;param name="movie" value="http://www.youtube.com/v/6m31-1j6LCI?version=3&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/6m31-1j6LCI?version=3&amp;amp;hl=en_US" type="application/x-shockwave-flash" width="420" height="315" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-3544502101783401798?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/3544502101783401798/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/09/experiments-with-arduino-led-matrix.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/3544502101783401798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/3544502101783401798'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/09/experiments-with-arduino-led-matrix.html' title='Experiments with the Arduino - Led Matrix'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-0AJG0KvARsM/TnnKOA2jz-I/AAAAAAAAABc/a-e6zebPtrA/s72-c/circuit.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-7745190798563574164</id><published>2011-09-18T15:07:00.000+02:00</published><updated>2011-09-18T15:07:32.072+02:00</updated><title type='text'>Experiments with the Arduino - Controlling 5 LEDs</title><content type='html'>&lt;h3&gt;The circuit&lt;/h3&gt;&lt;div class="separator" style="clear: both;"&gt;&lt;a href="http://3.bp.blogspot.com/-HY2GCQScN6Y/TnXsIBi3XpI/AAAAAAAAABQ/YRm1lAAMq2U/s1600/arduino-led-control.jpg" imageanchor="1" style="margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="320" width="192" src="http://3.bp.blogspot.com/-HY2GCQScN6Y/TnXsIBi3XpI/AAAAAAAAABQ/YRm1lAAMq2U/s320/arduino-led-control.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;The code&lt;/h3&gt;&lt;pre class="java" name="code"&gt;void ledOn(int nr, int high = HIGH, int low = LOW) {&lt;br /&gt;&lt;br /&gt;  for(int j = 1; j &lt;= 5; j++) {&lt;br /&gt;    if (nr == j) {&lt;br /&gt;      digitalWrite(j, high);&lt;br /&gt;    } else {&lt;br /&gt;      digitalWrite(j, low);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void ledsOn(&lt;br /&gt;  int wait,&lt;br /&gt;  int l1 = LOW, &lt;br /&gt;  int l2 = LOW, &lt;br /&gt;  int l3 = LOW, &lt;br /&gt;  int l4 = LOW, &lt;br /&gt;  int l5 = LOW) {&lt;br /&gt;&lt;br /&gt;  digitalWrite(1, l1);&lt;br /&gt;  digitalWrite(2, l2);&lt;br /&gt;  digitalWrite(3, l3);&lt;br /&gt;  digitalWrite(4, l4);&lt;br /&gt;  digitalWrite(5, l5);&lt;br /&gt;  delay(wait);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void backAndForth(int nr, int wait, int high = HIGH, int low = LOW) {&lt;br /&gt;&lt;br /&gt;  for(int t = 1; t &lt;= nr; t++) {&lt;br /&gt;    for(int i = 2; i &lt;= 5; i++) {&lt;br /&gt;      ledOn(i, high, low);&lt;br /&gt;      delay(wait);          &lt;br /&gt;    }&lt;br /&gt;    for(int i = 4; i &gt;= 1; i--) {&lt;br /&gt;      ledOn(i, high, low);&lt;br /&gt;      delay(wait);          &lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void fallingBrick(int wait, int high = HIGH, int low = LOW) {&lt;br /&gt;&lt;br /&gt;  for(int i = 1; i &lt;= 5; i++) {&lt;br /&gt;    for(int j= 1; j &lt;= 6 - i; j ++) {&lt;br /&gt;      ledOn(j, high, low);&lt;br /&gt;      for(int k = 7 - i; k &lt;= 5; k++) {&lt;br /&gt;        digitalWrite(k, high);&lt;br /&gt;      }&lt;br /&gt;      delay(wait);&lt;br /&gt;    }&lt;br /&gt;  }  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void boing(int nr, int wait) {&lt;br /&gt;  &lt;br /&gt;  for(int i = 1; i &lt; nr; i++) {&lt;br /&gt;    ledsOn(wait, 0, 0, 1, 0, 0);&lt;br /&gt;    ledsOn(wait, 0, 1, 1, 1, 0);&lt;br /&gt;    ledsOn(wait * 2, 1, 1, 1, 1, 1);&lt;br /&gt;    ledsOn(wait, 0, 1, 1, 1, 0);&lt;br /&gt;    ledsOn(wait, 0, 0, 1, 0, 0);&lt;br /&gt;    ledsOn(wait * 2, 0, 0, 0, 0, 0);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void wave(int nr, int wait) {&lt;br /&gt;  &lt;br /&gt;  for(int i = 1; i &lt; nr; i++) {&lt;br /&gt;    ledsOn(wait, 1, 0, 0, 0, 0);&lt;br /&gt;    ledsOn(wait, 1, 1, 0, 0, 0);&lt;br /&gt;    ledsOn(wait, 1, 1, 1, 0, 0);&lt;br /&gt;    ledsOn(wait, 1, 1, 1, 1, 0);&lt;br /&gt;    ledsOn(wait * 2, 1, 1, 1, 1, 1);&lt;br /&gt;    ledsOn(wait, 1, 1, 1, 1, 0);&lt;br /&gt;    ledsOn(wait, 1, 1, 1, 0, 0);&lt;br /&gt;    ledsOn(wait, 1, 1, 0, 0, 0);&lt;br /&gt;    ledsOn(wait * 2, 1, 0, 0, 0, 0);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void setup() {                &lt;br /&gt;  for(int i = 1; i &lt;= 5; i++) {&lt;br /&gt;    pinMode(i, OUTPUT);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void loop() {&lt;br /&gt;  &lt;br /&gt;  int baf_wait = 50;&lt;br /&gt;  int fb_wait = 100;&lt;br /&gt;&lt;br /&gt;  wave(5, 50);&lt;br /&gt;  delay(1000);&lt;br /&gt;  backAndForth(5, 50);&lt;br /&gt;  delay(1000);&lt;br /&gt;  fallingBrick(100);&lt;br /&gt;  delay(1000);&lt;br /&gt;  backAndForth(5, 50, LOW, HIGH);&lt;br /&gt;  delay(1000);&lt;br /&gt;  fallingBrick(100, LOW, HIGH);&lt;br /&gt;  delay(1000);&lt;br /&gt;  boing(5, 50);&lt;br /&gt;  delay(1000);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;h3&gt;The result&lt;/h3&gt;&lt;object width="420" height="315"&gt;&lt;param name="movie" value="http://www.youtube.com/v/1EtU1OpkaQc?version=3&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/1EtU1OpkaQc?version=3&amp;amp;hl=en_US" type="application/x-shockwave-flash" width="420" height="315" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-7745190798563574164?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/7745190798563574164/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/09/experiments-with-arduino-controlling-5.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7745190798563574164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7745190798563574164'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/09/experiments-with-arduino-controlling-5.html' title='Experiments with the Arduino - Controlling 5 LEDs'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-HY2GCQScN6Y/TnXsIBi3XpI/AAAAAAAAABQ/YRm1lAAMq2U/s72-c/arduino-led-control.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-7878589710935604331</id><published>2011-09-18T12:51:00.003+02:00</published><updated>2011-09-18T13:02:00.676+02:00</updated><title type='text'>Experiments with the Arduino</title><content type='html'>Lately I became the happy owner of an &lt;a href="http://seeedstudio.com/wiki/index.php?title=Arduino_Sidekick_Basic_Kit"&gt;Arduino Sidekick Basic Kit&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I will post here some experiments with this lovely device.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;What is the Arduino&lt;/h3&gt;&lt;br /&gt;&lt;blockquote&gt;Arduino is a tool for making computers that can sense and control more of the physical world than your desktop computer. It's an open-source physical computing platform based on a simple microcontroller board, and a development environment for writing software for the board.&lt;br /&gt;&lt;br /&gt;Arduino can be used to develop interactive objects, taking inputs from a variety of switches or sensors, and controlling a variety of lights, motors, and other physical outputs. Arduino projects can be stand-alone, or they can be communicate with software running on your computer (e.g. Flash, Processing, MaxMSP.) The boards can be assembled by hand or purchased preassembled; the open-source IDE can be downloaded for free.&lt;br /&gt;&lt;br /&gt;The Arduino programming language is an implementation of Wiring, a similar physical computing platform, which is based on the Processing multimedia programming environment. &lt;/blockquote&gt;&lt;br /&gt;&lt;h3&gt;Playing with a tricolor LED&lt;/h3&gt;&lt;br /&gt;&lt;h4&gt;The circuit&lt;/h4&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; "&gt;&lt;a href="http://3.bp.blogspot.com/---Zc1jwYgUg/TnXM3BhnF6I/AAAAAAAAABI/OqWyo02eR-o/s1600/arduino-led-fade.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="192" width="320" src="http://3.bp.blogspot.com/---Zc1jwYgUg/TnXM3BhnF6I/AAAAAAAAABI/OqWyo02eR-o/s320/arduino-led-fade.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h4&gt;The code&lt;/h4&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;void fade(int r1, int g1, int b1, int r2, int g2, int b2, int steps, int wait)&lt;br /&gt;{&lt;br /&gt;  float dr = (float)(r2 - r1) / steps;&lt;br /&gt;  float dg = (float)(g2 - g1) / steps;&lt;br /&gt;  float db = (float)(b2 - b1) / steps;&lt;br /&gt;  &lt;br /&gt;  float r = r1; float g = g1; float b = b1;&lt;br /&gt;  &lt;br /&gt;  for(int i = 0; i &lt; steps; i++) {&lt;br /&gt;    analogWrite(9, r);         &lt;br /&gt;    analogWrite(10, g);         &lt;br /&gt;    analogWrite(11, b);&lt;br /&gt;    &lt;br /&gt;    r = r + dr;&lt;br /&gt;    g = g + dg;&lt;br /&gt;    b = b + db;&lt;br /&gt;    &lt;br /&gt;    delay(wait); &lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void setup()  { &lt;br /&gt;} &lt;br /&gt; &lt;br /&gt;void loop()  {&lt;br /&gt;&lt;br /&gt; fade(255, 0, 0, 0, 255, 0, 100, 50);&lt;br /&gt; delay(1000);&lt;br /&gt; fade(0, 255, 0, 255, 0, 0, 100, 50);&lt;br /&gt; delay(1000);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;h4&gt;The result&lt;/h4&gt;&lt;br/&gt;&lt;object width="420" height="315"&gt;&lt;param name="movie" value="http://www.youtube.com/v/O5mivHc2vXQ?version=3&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/O5mivHc2vXQ?version=3&amp;amp;hl=en_US" type="application/x-shockwave-flash" width="420" height="315" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br/&gt;&lt;br/&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;br /&gt;I'm an absolute beginner in electronics, but I already felt in love with this micro-controller board. Looking forward to experiment and understand some more :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-7878589710935604331?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/7878589710935604331/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/09/experiments-with-arduino.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7878589710935604331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7878589710935604331'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/09/experiments-with-arduino.html' title='Experiments with the Arduino'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/---Zc1jwYgUg/TnXM3BhnF6I/AAAAAAAAABI/OqWyo02eR-o/s72-c/arduino-led-fade.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-1824488719317822519</id><published>2011-05-20T09:07:00.002+02:00</published><updated>2011-05-20T09:07:43.441+02:00</updated><title type='text'>Resign Patterns</title><content type='html'>For all the design patterns lovers...&lt;br /&gt;&lt;br /&gt;http://fuzz-box.blogspot.com/2011/05/resign-patterns.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-1824488719317822519?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/1824488719317822519/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/05/resign-patterns.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/1824488719317822519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/1824488719317822519'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/05/resign-patterns.html' title='Resign Patterns'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-5633030580771053961</id><published>2011-04-25T11:38:00.000+02:00</published><updated>2011-04-25T11:38:10.204+02:00</updated><title type='text'>PhpcrCommandsBundle</title><content type='html'>I started implementing a set of utility commands to manipulate a Jackrabbit content repository from the Symfony2 command console.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The code is on &lt;a href="https://github.com/sixty-nine/PhpcrCommandsBundle"&gt;github&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-5633030580771053961?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/5633030580771053961/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/04/phpcrcommandsbundle.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/5633030580771053961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/5633030580771053961'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/04/phpcrcommandsbundle.html' title='PhpcrCommandsBundle'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-7470185021346566642</id><published>2011-04-25T11:35:00.000+02:00</published><updated>2011-04-25T11:35:38.044+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mvc'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python MiniMVC</title><content type='html'>Going further with the concepts exposed in my last post I coded a basic Micro framework for Python running above mod_python for Apache. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;MiniMVC is a Python micro MVC framework inspired by Symfony2 from the PHP world.&lt;br /&gt;&lt;br /&gt;    MiniMVC is lightweighted&lt;br /&gt;    MiniMVC uses dependency injection&lt;br /&gt;    MiniMVC is configured with YAML&lt;br /&gt;    MiniMVC is tested (partially)&lt;br /&gt;&lt;br /&gt;MiniMVC features:&lt;br /&gt;&lt;br /&gt;    A configurable service container&lt;br /&gt;    A flexible and configurable request router based on pyroutes&lt;br /&gt;    Powerful logging mechanism based on logbook&lt;br /&gt;    Basic SQL Alchemy binding&lt;br /&gt;    Cheetah and Mako templates, new templating engines can be added very easily&lt;br /&gt;&lt;br /&gt;TODO&lt;br /&gt;&lt;br /&gt;    Don't render the content files through the kernel&lt;br /&gt;    Write more tests&lt;br /&gt;    Reorganize imports&lt;br /&gt;    Configure SQLAlchemy with the ServiceContainer&lt;br /&gt;        Generate table classes&lt;br /&gt;        Generate entities&lt;br /&gt;    Read XML configuration in addition to yaml&lt;br /&gt;    More templating engines&lt;br /&gt;    ...&lt;/blockquote&gt;&lt;br /&gt;You can find the code on &lt;a href="https://github.com/sixty-nine/MiniMVC"&gt;github&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-7470185021346566642?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/7470185021346566642/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/04/python-minimvc.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7470185021346566642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7470185021346566642'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/04/python-minimvc.html' title='Python MiniMVC'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-3939281349810741786</id><published>2011-04-10T11:15:00.003+02:00</published><updated>2011-04-10T11:24:13.319+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mvc'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>A very simple HTTP server with basic MVC in Python</title><content type='html'>This is a simple proof of concept of a very simple HTTP server implementing a very basic MVC model (actually only routing and controller) in Python.&lt;br /&gt;&lt;br /&gt;We will use the BaseHTTPServer as the web server.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The controller base class&lt;/h2&gt;&lt;br /&gt;All the controller needs is a reference to the BaseHTTPServer so that it can access the web request and write out the response.&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;class Controller(object):&lt;br /&gt;&lt;br /&gt;    def __init__(self, server):&lt;br /&gt;        self.__server = server&lt;br /&gt;&lt;br /&gt;    @property&lt;br /&gt;    def server(self):&lt;br /&gt;        return self.__server&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;The router&lt;/h2&gt;&lt;br /&gt;The router needs to manipulate the request and response but also instanciate the controllers, that is why it receives an instance of BaseHTTPServer.&lt;br /&gt;&lt;br /&gt;The routes are defined by 3 components:&lt;br /&gt;&lt;br /&gt;- A regular expression that must be matched to trigger the route&lt;br /&gt;- A controller class to be instanciated&lt;br /&gt;- A method name to be called on the controller&lt;br /&gt;&lt;br /&gt;The tricky part here is to instanciate the controller and call a method on it.&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;class Router(object):&lt;br /&gt;&lt;br /&gt;    def __init__(self, server):&lt;br /&gt;        self.__routes = []&lt;br /&gt;        self.__server = server&lt;br /&gt;&lt;br /&gt;    def addRoute(self, regexp, controller, action):&lt;br /&gt;        self.__routes.append({'regexp': regexp, 'controller': controller, 'action': action})&lt;br /&gt;        &lt;br /&gt;    def route(self, path):&lt;br /&gt;        for route in self.__routes:&lt;br /&gt;            if re.search(route['regexp'], path):&lt;br /&gt;                cls = globals()[route['controller']]&lt;br /&gt;                func = cls.__dict__[route['action']]&lt;br /&gt;                obj = cls(self.__server)&lt;br /&gt;                apply(func,(obj, ))&lt;br /&gt;                return&lt;br /&gt;&lt;br /&gt;        # Not found&lt;br /&gt;        self.__server.send_response(404)&lt;br /&gt;        self.__server.end_headers()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;The request handler&lt;/h3&gt;&lt;br /&gt;All the request handler needs to do is to instanciate the Router and add to it the routes it needs to manage.&lt;br /&gt;&lt;br /&gt;This simple request handler is only able to reply to GET requests.&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;class MyRequestHandler(BaseHTTPRequestHandler):&lt;br /&gt;&lt;br /&gt;    def __init__(self, request, client_address, server):&lt;br /&gt;        &lt;br /&gt;        routes = [&lt;br /&gt;            {'regexp': r'^/$', 'controller': 'HomeController', 'action': 'indexAction'},&lt;br /&gt;            {'regexp': r'^/content/', 'controller': 'ContentController', 'action': 'showAction'}&lt;br /&gt;        ]&lt;br /&gt;        &lt;br /&gt;        self.__router = Router(self)&lt;br /&gt;        for route in routes:&lt;br /&gt;            self.__router.addRoute(route['regexp'], route['controller'], route['action'])&lt;br /&gt;&lt;br /&gt;        BaseHTTPRequestHandler.__init__(self, request, client_address, server)&lt;br /&gt;    &lt;br /&gt;    def do_GET(self):&lt;br /&gt;        self.__router.route(self.path)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;A simple Hello World controller&lt;/h2&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;class HomeController(Controller):&lt;br /&gt;&lt;br /&gt;    def __init__(self, server):&lt;br /&gt;        Controller.__init__(self, server)&lt;br /&gt;&lt;br /&gt;    def indexAction(self):&lt;br /&gt;        self.server.send_response(200)&lt;br /&gt;        self.server.send_header('Content-type', 'text/html')&lt;br /&gt;        self.server.end_headers()&lt;br /&gt;        self.server.wfile.write('Hello world')&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By the way, in MVC a controller should not do View stuff, see "Going further with a templating engine" below to make the separation better using a view layer.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;A controller rendering a file&lt;/h2&gt;&lt;br /&gt;This controller will serve a file in the "public/" directory. Be carefull there is not enough error checking in this code...&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;class ContentController(Controller):&lt;br /&gt;    &lt;br /&gt;    CONTENT_BASE_PATH = 'public/'&lt;br /&gt;&lt;br /&gt;    def __init__(self, server):&lt;br /&gt;        Controller.__init__(self, server)&lt;br /&gt;        &lt;br /&gt;    def showAction(self):&lt;br /&gt;        filename = ContentController.CONTENT_BASE_PATH + self.server.path[9:]&lt;br /&gt;        if os.access(filename, os.R_OK) and not os.path.isdir(filename):&lt;br /&gt;            #TODO: is there any possibility to access files outside the root with ..?&lt;br /&gt;            file = open(filename, "r")&lt;br /&gt;            content = file.read()&lt;br /&gt;            file.close()&lt;br /&gt;            &lt;br /&gt;            #TODO: set correct content type&lt;br /&gt;            self.server.send_response(200)&lt;br /&gt;            self.server.send_header('Content-type', 'text/html')&lt;br /&gt;            self.server.end_headers()&lt;br /&gt;            self.server.wfile.write(content)&lt;br /&gt;        else:&lt;br /&gt;            self.server.send_response(404)&lt;br /&gt;            self.server.end_headers()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Running the server&lt;/h2&gt;&lt;br /&gt;What we have defined here is a simple http server that will reply "Hello world" when the URL "/" is accessed, and return the content of a file named myFile (if it exists) when the URL "/content/myFile" is reached.&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;def main():&lt;br /&gt;    try:&lt;br /&gt;        httpd = HTTPServer(('', 8000), MyRequestHandler)&lt;br /&gt;        print 'Server started...'&lt;br /&gt;        httpd.serve_forever()&lt;br /&gt;    except:&lt;br /&gt; print 'Server shutting down'&lt;br /&gt; httpd.socket.close()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Going further with a templating engine&lt;/h2&gt;&lt;br /&gt;It is quite simple to integrate a templating engine like Cheetah in this code.&lt;br /&gt;&lt;br /&gt;Here is a example of controller rendering a template:&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;class TemplateController(Controller):&lt;br /&gt;&lt;br /&gt;    def __init__(self, server):&lt;br /&gt;        Controller.__init__(self, server)&lt;br /&gt;&lt;br /&gt;    def listAction(self):&lt;br /&gt;        self.server.send_response(200)&lt;br /&gt;        self.server.send_header('Content-type', 'text/html')&lt;br /&gt;        self.server.end_headers()&lt;br /&gt;        self.server.wfile.write(Template ( file = 'templates/hello_world.tmpl', searchList = [{ }] ))&lt;br /&gt;        return&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;br /&gt;That's the reason why I love Python so much, in very few lines we could implement basic MVC over a simple web server.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-3939281349810741786?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/3939281349810741786/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/04/very-simple-http-server-with-basic-mvc.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/3939281349810741786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/3939281349810741786'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/04/very-simple-http-server-with-basic-mvc.html' title='A very simple HTTP server with basic MVC in Python'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-4031467688993469236</id><published>2011-03-03T20:58:00.004+01:00</published><updated>2011-03-09T19:03:28.561+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='phpdoc'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Implement your own phpdoc annotations</title><content type='html'>The goal is to be able to implement our own phpdoc annotations for custom use.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Access the doc block of a method&lt;/h2&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;...&lt;br /&gt;  $method = new ReflectionMethod('MyClass', 'myMethod');&lt;br /&gt;  $doc = $method-&gt;getDocComment();&lt;br /&gt;  ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A note from the php.net documentation about getDocComment():&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;In order to retrieve a comment with this method, the comment must be imediately before the class, function or method, start with /** and end with */&lt;/blockquote&gt;&lt;br /&gt;&lt;h2&gt;Does the method have the annotation?&lt;/h2&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;...&lt;br /&gt;  if (preg_match('/@my_annotation/, $doc) {&lt;br /&gt;&lt;br /&gt;    // Do something interesting with $method&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;  ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Putting it all together&lt;/h2&gt;&lt;br /&gt;A function that dumps the public methods of a class unless they are marked with the @ignore annotation:&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;function listMethods($class_name) {&lt;br /&gt;&lt;br /&gt;    foreach(get_class_methods($class_name) as $method_name) {&lt;br /&gt;&lt;br /&gt;      $method = new \ReflectionMethod($class_name, $method_name);&lt;br /&gt;      $doc = $method-&gt;getDocComment();&lt;br /&gt;&lt;br /&gt;      if (! preg_match('/@ignore/', $doc)) {&lt;br /&gt;        echo "$class_name.$method_name";&lt;br /&gt;      }      &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-4031467688993469236?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/4031467688993469236/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/03/implement-your-own-phpdoc-annotations.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/4031467688993469236'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/4031467688993469236'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/03/implement-your-own-phpdoc-annotations.html' title='Implement your own phpdoc annotations'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-6655465170798670357</id><published>2011-03-03T10:37:00.003+01:00</published><updated>2011-03-09T19:04:05.463+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='phpunit'/><title type='text'>PHPUnit - detect failing, skipped and incomplete tests</title><content type='html'>Sometimes when extending PHPUnit it might be useful to test assertions that are supposed to fail or mark the test as incomplete or skipped. However one wouldn't want the whole test case to fail when testing such assertions. Here is a simple way to achieve this.&lt;br /&gt;&lt;br /&gt;Please be warned that this technique should be used carefully in order not to "mask" real failing tests.&lt;br /&gt;&lt;br /&gt;To illustrate the method let's write a simple test case extension.&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;class CustomTestCase extends \PHPUnit_Framework_TestCase {&lt;br /&gt;&lt;br /&gt;  // Failing assertion&lt;br /&gt;  public function assertMeantToFail() {&lt;br /&gt;    $this-&gt;assertTrue(false);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Test incomplete assertion&lt;br /&gt;  public function assertMeantToBeIncomplete() {&lt;br /&gt;    $this-&gt;markTestSkipped();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Test skipped assertion&lt;br /&gt;  public function assertMeantToBeSkipped() {&lt;br /&gt;    $this-&gt;markTestIncomplete();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we would like to write a test case to check if the above assertions actually do what they are expected to do.&lt;br /&gt;&lt;br /&gt;The point is that PHPUnit will use Exceptions to notify the test runner of a failing, skipped or incomplete test. Thus it is possible to intercept the correct Exception to check if the test does what we expect.&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;class CustomTestCaseTestCase extends CustomTestCase {&lt;br /&gt;&lt;br /&gt;  public function testFailingTest() {&lt;br /&gt;    try {&lt;br /&gt;      $this-&gt;assertMeantToFail();&lt;br /&gt;    } catch (\PHPUnit_Framework_ExpectationFailedException $ex) {&lt;br /&gt;      // As expected the assertion failed, silently return&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    // The assertion did not fail, make the test fail&lt;br /&gt;    $this-&gt;fail('This test did not fail as expected');&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public function testSkippedTest() {&lt;br /&gt;    try {&lt;br /&gt;      $this-&gt;assertMeantToBeSkipped();&lt;br /&gt;    } catch (\PHPUnit_Framework_SkippedTestError $ex) {&lt;br /&gt;      // As expected the assertion was skipped, silently return&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    // The assertion did not mark the test skipped, make the test fail&lt;br /&gt;    $this-&gt;fail('This test was not skipped as expected');&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public function testIncompleteTest() {&lt;br /&gt;    try {&lt;br /&gt;      $this-&gt;assertMeantToBeIncomplete();&lt;br /&gt;    } catch (\PHPUnit_Framework_IncompleteTestError $ex) {&lt;br /&gt;      // As expected the assertion was marked as incomplete, silently return&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    // The assertion did not mark the test incomplete, make the test fail&lt;br /&gt;    $this-&gt;fail('This test was not incomplete as expected');&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code is meant to work with PHP 5.3 and namespaces. To make it work on other PHP versions remove the \ at start of the class names.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;I should not tell you, but...&lt;/h3&gt;&lt;br /&gt;Alternately you can use the @expectedException annotation to make a failing test succeed. Although this seems to be a very bad practice, because it can hide problems in your code, I present it here for sake of completeness.&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;/**&lt;br /&gt;     * This test will succeed !!!&lt;br /&gt;     * @expectedException PHPUnit_Framework_ExpectationFailedException&lt;br /&gt;     */&lt;br /&gt;    public function testSucceed()&lt;br /&gt;    {&lt;br /&gt;        $this-&gt;assertTrue(false);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This last method does not work for incomplete or skipped tests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-6655465170798670357?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/6655465170798670357/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/03/phpunit-detect-failing-skipped-and.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/6655465170798670357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/6655465170798670357'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/03/phpunit-detect-failing-skipped-and.html' title='PHPUnit - detect failing, skipped and incomplete tests'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-4671671114683652295</id><published>2011-02-05T17:10:00.001+01:00</published><updated>2011-03-09T19:04:22.852+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Enable CTRL-C / CTRL-V in gnome-terminal</title><content type='html'>gconftool-2 -t str -s /apps/gnome-terminal/keybindings/copy "&lt;control&gt;c"&lt;br /&gt;gconftool-2 -t str -s /apps/gnome-terminal/keybindings/paste "&lt;control&gt;v"&lt;br /&gt;&lt;br /&gt;From http://geekybits.blogspot.com/2007/10/gnome-tip-changing-keyboard-shortcuts.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-4671671114683652295?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/4671671114683652295/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/02/enable-ctrl-c-ctrl-v-in-gnome-terminal.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/4671671114683652295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/4671671114683652295'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2011/02/enable-ctrl-c-ctrl-v-in-gnome-terminal.html' title='Enable CTRL-C / CTRL-V in gnome-terminal'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-830454807585326153</id><published>2010-11-03T17:03:00.010+01:00</published><updated>2011-03-09T19:04:58.855+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='html5'/><title type='text'>Playing with HSV colors and HTML5</title><content type='html'>In a &lt;a href="http://aventures-logicielles.blogspot.com/2010/09/drawing-point-on-canvas.html"&gt;previous post&lt;/a&gt; I explained how to draw points on an HTML5 canvas. This time I will use this technique to draw colored bars.&lt;br /&gt;&lt;br /&gt;The idea behind this post is to implement a color picker with HTML5 and Javascript.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;The HSV color model&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;HSL and HSV are the two most common cylindrical-coordinate representations of points in an RGB color model, which rearrange the geometry of RGB in an attempt to be more perceptually relevant than the cartesian representation.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/HSL_and_HSV"&gt;Citation from wikipedia&lt;/a&gt; &lt;/blockquote&gt;HSV stands for hue, saturation, and value.The hue can variate from 0 to 360°, the saturation and the value must be between 0 and 1. When the full range of these values is walked through, it generates a color cylinder as in the following image:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/HSV_color_solid_cylinder_alpha_lowgamma.png/800px-HSV_color_solid_cylinder_alpha_lowgamma.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/HSV_color_solid_cylinder_alpha_lowgamma.png/800px-HSV_color_solid_cylinder_alpha_lowgamma.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/0/0d/HSV_color_solid_cylinder_alpha_lowgamma.png"&gt;Image from wikipedia&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;If a hue is selected (i.e. the main color is fixed), going through the range of S and L will produce a 2D slice of the cylinder. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Converting from HSV to RGB&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Using the algorithm exposed in the above wikipedia page we can implement a function that will convert a color given in HSV to a RGB color.&lt;br /&gt;&lt;pre class="js" name="code"&gt;function colors.hsv_to_rgb(h, s, v) {&lt;br /&gt;    var c = v * s;&lt;br /&gt;    var h1 = h / 60;&lt;br /&gt;    var x = c * (1 - Math.abs((h1 % 2) - 1));&lt;br /&gt;    var m = v - c;&lt;br /&gt;    var rgb;&lt;br /&gt;    &lt;br /&gt;    if (typeof h == 'undefined') rgb = [0, 0, 0];&lt;br /&gt;    else if (h1 &amp;lt; 1) rgb = [c, x, 0];&lt;br /&gt;    else if (h1 &amp;lt; 2) rgb = [x, c, 0];&lt;br /&gt;    else if (h1 &amp;lt; 3) rgb = [0, c, x];&lt;br /&gt;    else if (h1 &amp;lt; 4) rgb = [0, x, c];&lt;br /&gt;    else if (h1 &amp;lt; 5) rgb = [x, 0, c];&lt;br /&gt;    else if (h1 &amp;lt;= 6) rgb = [c, 0, x];&lt;br /&gt;    &lt;br /&gt;    return [255 * (rgb[0] + m), 255 * (rgb[1] + m), 255 * (rgb[2] + m)];&lt;br /&gt;  }&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Selecting a color&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The idea of the color picker is to show a bar with all the colors (hue).&lt;br /&gt;&lt;br /&gt;&lt;img border="0" height="21" src="http://2.bp.blogspot.com/_UbsnjTsw_eM/TNGMq6m_NtI/AAAAAAAAAAo/GMW5MRG2sM8/s320/hue_bar.png" width="301" /&gt;&lt;br /&gt;&lt;br /&gt;Once the color is choosed, we have to choose a saturation and value to refine the selection.&lt;br /&gt;&lt;br /&gt;&lt;img border="0" height="149" src="http://1.bp.blogspot.com/_UbsnjTsw_eM/TNGNPmBLSsI/AAAAAAAAAAw/mw3zaxLo0sQ/s320/sv_grid.png" width="149" /&gt;   &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Drawing a hue bar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Because we use the HSV color model it is very simple to draw the hue bar. We simply go through the possible values of the hue (0 to 360) using a saturation and a value of 1. &lt;br /&gt;&lt;pre class="js" name="code"&gt;// imgd represents the image data of the canvas object &lt;br /&gt;  function draw_horizontal_hue_bar(imgd, x, y, width, height) {&lt;br /&gt;    var inc = 360 / width;&lt;br /&gt;    for (var h = 0; h &amp;lt; 360; h += inc) {&lt;br /&gt;      for (var i = 0; i &amp;lt;= height; i++) {&lt;br /&gt;        var rgb = hsv_to_rgb(h, 1, 1);&lt;br /&gt;        point(imgd, x + Math.round(h/inc), y + i, rgb[0], rgb[1], rgb[2]);  &lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Drawing a slice of the color cylinder&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that we have fixed hue let's draw a slice of the color cylinder in order to choose the saturation and the value. This is also very simple since we can go through the values of the saturation and value (0 to 1) keeping the hue fixed.&lt;br /&gt;&lt;pre class="js" name="code"&gt;function draw_sv_grid(imgd, hue, x, y, size) {&lt;br /&gt;    var inc = 1 / size;&lt;br /&gt;    for (var v = 0; v &lt;= 1; v += inc) {&lt;br /&gt;      for (var s = 0; s &lt;= 1; s += inc) {&lt;br /&gt;        var rgb = this.colors.hsv_to_rgb(hue, v, s);&lt;br /&gt;        this.point(imgd, x + Math.round(v * size), y + Math.round(s * size), rgb[0], rgb[1], rgb[2]);  &lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;/pre&gt;&lt;span style="font-size: large;"&gt;What's next&lt;/span&gt;&lt;br /&gt;With the above algorithm we should have everything we need to implement an HSV color picker. &lt;br /&gt;&lt;br /&gt;Some interesting problems remain though, as how to get the color of a pixel clicked in the canvas.&lt;br /&gt;&lt;br /&gt;This will be for another post. Meanwhile, happy coding... ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-830454807585326153?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/830454807585326153/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2010/11/playing-with-hsv-colors-and-html5.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/830454807585326153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/830454807585326153'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2010/11/playing-with-hsv-colors-and-html5.html' title='Playing with HSV colors and HTML5'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_UbsnjTsw_eM/TNGMq6m_NtI/AAAAAAAAAAo/GMW5MRG2sM8/s72-c/hue_bar.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-8269783692935873037</id><published>2010-09-14T16:25:00.016+02:00</published><updated>2011-03-16T14:55:40.908+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='html5'/><title type='text'>Drawing a point on an HTML5 canvas</title><content type='html'>Drawing a single point seems to be the easiest task to do on any graphic device. However it's not as easy as expected on a simple HTML5 canvas element. Even if the HTML5 canvas provides advances graphic primitives to draw lines or images, it does not have any function to draw a single point.&lt;br /&gt;&lt;br /&gt;In order to draw a single point you'll have to:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;obtain a portion of the image data&lt;/li&gt;&lt;li&gt;calculate the index of the pixel in the image data&lt;/li&gt;&lt;li&gt;set the RGB and alpha component of the pixel&lt;/li&gt;&lt;li&gt;write back the data to the image&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Getting a portion of the image data that can be processed and written back is a powerful procedure that allows for example to write image filters. However it's quite too complicated when you just want to draw one or more pixels.&lt;br /&gt;&lt;br /&gt;Here is a real example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;// Get a reference to the canvas element &lt;br /&gt;  var elem = document.getElementById('canvas');&lt;br /&gt;  if (!elem || !elem.getContext) return;&lt;br /&gt;&lt;br /&gt;  // Get the 2D graphic context   &lt;br /&gt;  var context = elem.getContext('2d');    &lt;br /&gt;  if (!context) return;     &lt;br /&gt;&lt;br /&gt;  // Obtain a portion of the graphic data&lt;br /&gt;  function get_image_data(context, w, h) {     &lt;br /&gt;&lt;br /&gt;    // Not all browsers implement createImageData. On such browsers&lt;br /&gt;    // we obtain the ImageData object using the getImageData method. &lt;br /&gt;    // The worst-case scenario is to create an object *similar* to&lt;br /&gt;    // the ImageData object and hope for the best luck.     &lt;br /&gt;    if (context.createImageData) {      &lt;br /&gt;      return context.createImageData(w, h);     &lt;br /&gt;    } else if (context.getImageData) {       &lt;br /&gt;      return context.getImageData(0, 0, w, h);     &lt;br /&gt;    } else {       &lt;br /&gt;      return {'width' : w, 'height' : h, 'data' : new Array(w*h*4)};     &lt;br /&gt;    } &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Draw a pixel on the canvas   &lt;br /&gt;  function point(imgd, x, y) {          &lt;br /&gt;&lt;br /&gt;    // Calculate the pixel offset from the coordinates     &lt;br /&gt;    var idx = (x + (y * imgd.width)) * 4;      &lt;br /&gt;&lt;br /&gt;    // Modify the graphic data     &lt;br /&gt;    imgd.data[idx] = 0;     // Red     &lt;br /&gt;    imgd.data[idx+1] = 0;   // Green     &lt;br /&gt;    imgd.data[idx+2] = 0;   // Blue     &lt;br /&gt;    imgd.data[idx+3] = 255; // Alpha channel   &lt;br /&gt;  }      &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  var imgd = get_image_data(context, 400, 300);   &lt;br /&gt;  point(imgd, i, j);  // Replace i and j with real coordinates&lt;br /&gt;  context.putImageData(imgd, 0, 0);  &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-8269783692935873037?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/8269783692935873037/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2010/09/drawing-point-on-canvas.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/8269783692935873037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/8269783692935873037'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2010/09/drawing-point-on-canvas.html' title='Drawing a point on an HTML5 canvas'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-1867980206680314423</id><published>2010-09-14T13:40:00.001+02:00</published><updated>2011-03-09T19:05:44.056+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Wordle-like PHP script</title><content type='html'>&lt;span style="font-size: large;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tag clouds (or word clouds) are very trendy in the fashionable web 2.0.&lt;br /&gt;&lt;br /&gt;But very often those clouds are rendered via HTML / CSS and are quite ugly.&lt;br /&gt;&lt;br /&gt;I discovered recently the website &lt;a href="http://www.wordle.net/"&gt;http://www.wordle.net&lt;/a&gt; and was fascinated by the beautiful images it can generate. However they are rendered through a Java applet and the only way to use them is to take a screenshot and extract the image. I wanted clickable tag clouds, and, unless you manually create an HTML image map, it's not possible with Wordle. So I decided to create my own tag cloud generator using PHP.&lt;br /&gt;&lt;br /&gt;I started crawling the web to get some information, and I found out an &lt;a href="http://stackoverflow.com/questions/342687/algorithm-to-implement-something-like-wordle"&gt;interesting post&lt;/a&gt; on StackOverflow from a guy asking how to implement "something like Wordle". Suprisingly, Jonathan Feinberg, the creator of Wordle, replied to the post explaining the basic idea:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Each word "wants" to be somewhere, such as "at some random x position in the vertical center". In decreasing order of frequency, do this for each word:&lt;br /&gt;&lt;br /&gt;place the word where it wants to be&lt;br /&gt;while it intersects any of the previously placed words&lt;br /&gt;move it one step along an ever-increasing spiral&lt;/blockquote&gt;That was all I needed to start coding a proof of concept, but a lot of problems still needed to be solved...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Bounding Boxes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In his reply Jonathan Feinberg says: "The hard part is in doing the intersection-testing efficiently, for which I use last-hit caching, hierarchical bounding boxes, and a quadtree spatial index".&lt;br /&gt;&lt;br /&gt;Well that was quite too much for me, I needed to find a less efficient but simplier way to test for intersection.&lt;br /&gt;&lt;br /&gt;So I came up with this idea:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Each time a word is drawn, store its bounding box in an array&lt;/li&gt;&lt;li&gt;To test if a new box intersects with the already drawn boxes do this:&lt;/li&gt;&lt;ul&gt;&lt;li&gt; For each bounding box in the array:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;If the new box intersect the bounding box there is an intersection&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ol&gt;This leads to another problem to solve: how to test if two rectangles intersect.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Rectangle Collision Detection&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The scipt I wrote only allows to draw words either horizontaly or verticaly. Thus we need to test the collision of axis-aligned boxes. This is quite simple to do.&lt;br /&gt;&lt;br /&gt;Two axis-aligned boxes do not intersect when their projection on one of the axis are disjoint. This is not the case for rotated boxes!&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;if ($box1-&amp;gt;bottom &amp;gt; $box2-&amp;gt;top) return false;&lt;br /&gt;if ($box1-&amp;gt;top &amp;lt; $box2-&amp;gt;bottom) return false;&lt;br /&gt;if ($box1-&amp;gt;right &amp;lt; $box2-&amp;gt;left) return false;&lt;br /&gt;if ($box1-&amp;gt;left &amp;gt; $box2-&amp;gt;right) return false;&lt;br /&gt;&lt;br /&gt;return true; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For arbitrarily rotated boxes you will need some more 2D geometry to test the collision, but for now let's keep it simple.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Searching a place for the new word&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We have now all the pieces to write down the routine searching for a free space to draw the new words. We start in the center of the image and move the word along a spiral until it does not intersect with the words already drawn.&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;$i = 0;  &lt;br /&gt;$x = &amp;lt;image_center_x&amp;gt;; &lt;br /&gt;$y = &amp;lt;image_center_y&amp;gt;;&lt;br /&gt;while (! $place_found) {&lt;br /&gt;  $x = $x + ($i / 2 * cos($i));&lt;br /&gt;  $y = $y + ($i / 2 * sin($i));&lt;br /&gt;  $new_box = &amp;lt;place the word at x,y &amp;gt;;&lt;br /&gt;  $place_found = &amp;lt;the new word does not overlap with existing words&amp;gt;;&lt;br /&gt;  $i += 1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return array($x, $y);&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Changing the center of the spiral or its equation will lead to another distribution of the words in the image.&lt;br /&gt;&lt;br /&gt;Since the PHP functions to draw text and to get its drawn dimension work with the top left corner as reference point, the above algorithm will tend to place all the vertical words on the left of the image. To prevent this I added a little bit of noize (random numbers) when selecting the center of the spiral for the vertical words.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Clickable images?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As stated at the top of this post, I wanted the generated tag clouds to be clickable. In other words I needed a mechanism to detect which word was clicked.&lt;br /&gt;&lt;br /&gt;Since we store the bounding boxes of all the words we draw to detect the collisions, we can use this data to generate an HTML image map.&lt;br /&gt;&lt;br /&gt;The problem is that each generation of a tag cloud will generate a different image. This is caused by the noize added when searching for the position of the words, but also to some randomness I added in the calculation of the font sizes.&lt;br /&gt;&lt;br /&gt;That means that the tag cloud image and the image map must be rendered and sent to the client in a single call. Unfortunately it is not possible to send back to the client browser an encoded image at the same time as some HTML.&lt;br /&gt;&lt;br /&gt;To solve this issue I used an advanced feature of the HTML img tag that allow to embed a base-64 encoded image in the URL.&lt;br /&gt;&lt;br /&gt;First render the image in a temporary file and encode its content in base 64:&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" name="code"&gt;$file = tempnam(getcwd(), 'img');&lt;br /&gt;imagepng($cloud-&amp;gt;get_image(), $file);&lt;br /&gt;$img64 = base64_encode(file_get_contents($file));&lt;br /&gt;unlink($file);&lt;br /&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then set the data as the image URL&lt;br /&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&amp;lt;img usemap="#mymap" src="data:image/png;base64,&amp;lt;?php echo $img64 ?&amp;gt;"&lt;br /&gt;  border="0" alt="" /&amp;gt;&lt;br /&gt; &lt;br /&gt;&lt;/pre&gt;Unfortunately this does not work in ... well, as usual ... Internet Explorer... This is out of the scope of this article but you can find more information on how to fix this problem here: &lt;a href="http://danielmclaren.net/2008/03/embedding-base64-image-data-into-a-webpage"&gt;Embedding Base64 Image Data into a Webpage&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Since we now return HTML instead of a PNG image, we can as well send back the HTML image map.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;See it in action&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sorry IE users this wil not work... ;-(&lt;br /&gt;&lt;br /&gt;&lt;img usemap="#mymap" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAc4AAAHaCAIAAAAGwz5qAAAgAElEQVR4nOydeVyU1frAvwPDsI+IqISoqLghuaWGiIqIiAtiZrikZmRumZH4s83Ka9Y185qZlZWZW8r1mtctMzQz4uJOaIi4QoSERBMRIiIwvz9mmI1hZoBB0M73w6fPO+d9znOemcZnzvuc5zwHBAKBQCAQCAQCgUAgEAgEAoFAIBAIBAKBQCAQCAQCgUAgEAgEAoFAIBAIBAKBQCAQCAQCgUAgEAgEAoFAIBA0ODYSZkx98MjeqPyrc0vznlc1Ht79WPduHg1rmEAgsBxJQxsgMIWDve3+f48bOqiNpkXi/i9AqYj9q6g0YNi2tIu/N5x1AoHAUmwa2gCBKV5b1H/ooDY/HMvu0Gu9bvu/Pjjt6iJb+nJgQxkmEAhqhHC1jZrJ47sCM2MOXfv5T932VR+cBgYFejeMWQKBoIYIV9uo8WzpDFy6ojBoz//9FiB3tW8AmwQCQc0RrrZRk5VdCPj7Ga6AhQa31dwVCASNH+FqGzWb49KAdauG+bSWq1rkrrLJ47ts/DAc2PafCw1pnEAgsBiRgdCokdpKdn8xdlRY+6q3vkv8ZeRjX5bcLr/7VgkEgppi29AGCExRoWTbzvSMn/90cZbJXWUymW3hX7dP/5i7fPXJ5148cqdM2dAGCgQCgUAgEAgEAoFAIGgUtG/bZP2asOzzszQt2ednrVsV6u3l0oBWCQSCGiGWxRo1fXq2PLI3ytVFRuWWXECpiAV+/+PWoJH/FhtzBQKBoK4kfj1RqYjdtWVM+7ZNNI3eXi47Ph+tVMQe3PloA9omEAgsR8xqGzUluTH2MlvX1muKbt7RbXeT2/+ROe9WSZmT13v1NLSDPSteYlw4ns2xrZJ+LWlXT8MKBPcnYgtDo6bgz9tAG2+5Qbt7Uweg6GZp/Q29/AWefYJWLY34WYFAUFPEPyMrI5eytjtZYZRGACQG0aUOy1e79l0Cdm6KCB/qI3eVAXJXWdiQtru/GAvs/fqqVWw2ysQIgHc/o1lPJO0M/wQCQY0QAQQrc7A/O66zN5ffRiDZw4y2zG1H76O11Obu5pBwYEK3LkaqgJ9Pzw8evSNfcasu1pqgMBVXZxy7UHK7nkYQCASC2lISoX5SUEYCSCWURNRJoZOjdOG8PknfTFJkPFOWv6Agc97xQ5MXze/rYG/RTr9zK3CS1WbcjStRZtClQ236CgQCQf1ysD/+rtiAMhKphO5yDvZvSHsWP8K0gcgda9zRxYkDn3NyD30eRGZXD5YJBH8nRADByrhJWd6NMZ54yCi4w/4bLDpPfm2Xr3ZtGZPz6815i76ttT3K7UYaJZOqEc6wVK0I1woENUK42kZNUfZ8Bwep1GPV3RlOuFqBQPB3JPn7qUpFrO7+hZoid2TNE2StpXQrQOISunhZyzqBQCC4L/BpLT+8+7Hk76eOHNbO3c2hFhoOvsi4vthI1JGEGUNI/qelfRfM4FqC+nrmJIrSUKSw+xPkovqCQFBDRADBOqjyDapDsqe2ahWxpgU0hRGqo2QLLk9QVoFyO5JJSG0o2oTDVPNDz5jAp8sBJO1wciD7OE0r59ZvvM9rdymkIRDcJ0gb2oD7hFo70/rm6Hm6eJF2HUBqg583R89b1DE6CmDOYoCYp2jahH9+yLl0tq9hTKhwtQKBoEFJC2Gsp16L6QlvfePmxLqnyPmI0q3kfcyGWXi4WtSxMBVlBk4O+HhTlIYyg97dkNqizKAorZ6NFggEAtMoI9n7MHF98JBpW+5FSi6izEBqy4IZKDNI2AEgdxGuViCoDaIGgvUZc4IDN0gayORWVtBmI2H2kz0SvpqgyHimNO95RcYzSd9Mmj+rl9S2fuPsmdkAwQGEDQTYsANgyiMAaZfrdWSB4D5ELItZGWWkOm7r5cAnPcguYZZP7SO5Dva2+/89buigNlVv/e/E9dCx/zFxYq7RzQsqqtvCoMuSGF5/Tn198xZe/SgsUifeTn2erbvNaxAIBIL6wiBcMK01V0Jrr23pS4FKRWze5TnTJvp5uDvaSGjh4Thtol/e5TlKRezSlwLraK1pVryEIoWsJMaPULdc+Z4ZE+p1TIFAILjrXEuZoVTEhg42nNWGDGytVMReS5nRIFYJBIKaIpK9GjVeni7A0cRfDNoTj1/X3DVN1TDCXyWcvMLMT7mWZx0jBQKBWYSrtTJpIbycxu5cbYsmelsLcnKL2rVpEhTQ6uj/snXbgwJaqe6a1fDcJvxasXQXuQW0kPPyWDJ/o6CYDbMJXmoorArFquobmK6HIGogCASChsS6yV7LXhmgVMTmXJg9eXwXdzcHGwke7o5TorrmXpyjVMQue2WAWQ15HyPTKWwrsyX/U6Q2FG8yZnyG1sOqrqv7EwgENULMaq3PmBNMa03SQJaks+16nVQtW3k8KKDV4AGtv/hklMGtH45lL1t53KyG1F+YN5wNRykoxs2J6GDO/UxZBcXG6jrqzlXFvFUgEDReNHNYLwf2P8y6HnXdwiC1lcx+ssfRfVH5V+eq8moTvpow96keFubVujvzYbS6slfOR3wYjbszwMY5xowXM1aBQHBPUPdkr09WD1uzPKS+dygYpaqrFc5XILAKYgtDo6M45zlHB6lts39VKNWVvcyW7zJBjTIQdJfFqmsRCAS1QMRqGx0lt8scHaQhg9oc/j6r7tpqlIEgEAjqCTGrtTJGI7M1SvbauSni0YhOFgqbnfDmfYz3XEort+/KbMlZh+csCj/H6QlDYTGrFQjqCTGrtTK6XtXdjsWdSf+rZhrmv3DEw90xoK+Xvcyi48dNU6MMBIFAILgncbAhf4R5sepQKmLNHsRgmjpmIIhlMYHAKogAQv0y1pO4vjjsq2X3ui+L1Ww4cWKuQFA/iACClTGI1d6uYOKp2mu7a05WIBDUK2JWe59jtGqtJfVqBQKBFRGz2kaN1Fay9OUBk8d39XrAxU5q5MgMs9NeXa/q7szicaTnWNdGgUBgHjGrtQ4eMtZ2Z2RLypTsuM6CVIqrPR6hBqx+a8hzs3ubEKhphMHBjuwP8Xi6bmYJBAJBg7CzL1O8sQEnWxb6sq6HddSqTltY9WawZwsnG2v8LI7tQ8kWiyTlLmxcSd4ZUdlLILACIoBgHUKbMy2ZCigu55NM0kKso9bJ0Q5YvCyx+FZZ7TQYxGpvlzHxPYs6rl3K1EdqN6ZAIBDUD2VjTL2sNQd2jFMqYrt0dLeOupqgSEGZwWvzcXK4+4MLBAKBMQxyvOpYOFFDpw5Ns8/PSv5+6qBAbxdnO+sotYyiNJQZONjfzTEFgvsWEUBo1Fw8FQ20esDl+/3GD6o1sSxWNc3rdhkpmcz4mNRsYx302f8tE0bj7cmVny23VyAQGEdkIFgH09PYWp8tZnZXbo0yEKQ2TB/MjBACXjUv7N6Eg5spvcPsl0m7TIXS8nEEAoHg742DnaUZCMC0ceowgshAEAjqiAgg/I1wsWf2MC79apHw0gW8+mw9GyQQCAT3HzkfcXIZvX0sElZlICyeh4tTjQdSFSRT/ZXmPZ99ftaa5SEO9rZUExLRbTTRV0X7tk3STz4JJH8/ddpEPwNV0yb6JX8/1ayFYUPaJnw1oTjnuYLMebu2jKma4zF5fJfErycWZc8vyp6f8NWEsSN9qzNYIBA0AMpIw7+yMeSPYGdfPC1bzbewcGLd6yuapuAcygxktcp6MDDMyVG6YO5Dn6weVvVWVXkTfVXMfrLHmuUhKsnk76c6OUp1hVOTnjD7scx9qsfOTRF+nZtJbSUO9rZBAa0Sv544ZkQHjcDaFUNPfzclbEhbJ0epg71t+FCftOPTl78+sDojBQLB3WZ+e9b1wMsBG/C0Z82DRHnhbsdyPw72t0hDVR9q1KuadbXK7dX+WcLaf6DMwN/S4yAMbTNocZPbF2TOM3oLk65Wt6+KXVvGjB7eXiW5/PWBUWO1Jk4c13nVm8Fm/WDe5TkGmXO+7dyyfpqpug4f6pOSME13Hg3IXWVXkp8KHuBdnZECgeCukheOTCetQyYhKwzAwYbi0RZpsJarrSNSW9a9SfJ+AnrVOLu2qmEuznZ5l+cYvYU5V6vpC9hIyLs8RzWTVSpiA/t57fhc+7Hu3BQRPMBbpcGvc7NziXpH+jjY22b9NNNNbp97cU704/7VGb97a6RBuEDFlKiuOzdFVGekQGAaI8WiBHUh9S+mtMbFFsBNyrz2XLkJEOxB3u2GNa1m3LnCrMn06saxXdxKr1MGgmcLp9cW9d+47XwtzDDoG9DXKy39d8025aSTOX16eao8r5OjNKCPV0KSOmc47eLv+YpbYUPaalRNHt/18Pc/FxTeXvXB6c/eH34tZcb6NWEzpj7YqUNT3REDH/ZKPH69qiWHj/4c2K9VLd6CQIDIQLA640+yrCtLuuBpT34pu39l/EmArQ8x9+xdtUS5HcmkBqtXq5n33Sopu5ZZsG1n+vJ3TxjcqkXf0OC28d/p7ak4kpA1clj7nXsvjR7e/khClm7+74frUxY800cjP39W75kx8cCKNafij2R6tnTu08tz+uRun74XduLMr+Of2JudUwS4N3VU/HGrqlX5v9/yaOZYs09BIBA0WhpJAKEumDCsFgEEXRK/ntinZ0tdyTEjOsR9NhrYuSli/JhOuhpsJGSfn+XftRkQFNAqJWGaUZ39enuuejN4f5y6uE7e5Tke7kZcagsPR00co9F+8oJGiwggCIxz+As2r2poI/SRu8p82zc9nXJDtzH+SGbIoNbubg4hg9oc/FYvtFGh5JNN5xY80weYP6vXus+NP1acTM5d+vax4KDWqpeJx69rrnUJDmp9+scbVdsFAksQrtbKjPWkJMIw36sBmT6Y/E9rk4HQ04/JkbVM9qonQga2OZr4i0Fjye3ypBM5y5cMPH7q16KbdwzufrLx7Pgxnbp0dA8Z1GbrjjRVY86F2QaJtCW3y/Lyi1XXa9Ylv7aovyaHbMfno9csD2nftslri/rv3HPJ+u9K8PdAuForE9eXiaeQ7NH7qwW6mfxVWyx/gH1rIgu3Yvc4V2/QNZaPDvHcJos6jp9DShrr36Z7l8bicMNC2sYfyazavvurK9FTHtz79dWqt3Lzig8cyti5KWLnnksaR7zqg9M7N0WMHNZO7iqzkeDbzu3Df4WufF993ObR/2Xv3Hsp8eAkVV7tqg/OeLdyOX10iouzLG5Xer29OcF9jlgWszK5JcT/1tBG6ODuwrZEyipI+Rmf5ry4nUvvsuag+Y7fbQd4yN94gfAGOZw8LMTnrVUnqrbv/+ZaRYVy79dXjPZa++mPPxyYOGXmAU3LyrWn83+/tXhhgF/nZjKZ7aUrf6z+6MzG7doEiaUrjp088+vCZ/vu+DxCKrWJ/y6zX8gXocFt160aNm3O1yqZqr924nhjgeDuMa8dK7vh1mh+wpL/ybi+AIsfYfEj9PYhbaVFHY1WmbkXy8208XbNPGudw9SmRHW1ih7B3xBRRNHKGI3M1rqIYt0J8GXDbPwW0kLO/kV0acW0D9h9usHsucvI7GyWvjygtLT8tX8mNbQtAoFAYAwXJ5bFknaI4gsUXyDtEMtfQO7S0GbVhKLs+Uf2RhnsshUIBILGgtyF1HgjoYO0Q/eYtxUIBPcPmrhB1cpeDZvsVWtWLUaZwbUEQgfgYI+TA2EDuZaAMoOVLze0cQLBvYaI1d63pCynR1vjtyzZmJuVROsHGPAoScnaxoBeHNvFz9fxCbKOkQKBQHBvMzuUzPeJm8/cYXRvU+PupZdRZiDVD3JKbVFmUHrZWjYKBAJBrTAIF3jILK2dWB84yQjvwYrJnH6L/E858AIvjiGos0V9s5JQZtCvh15jQC+UGWQmmulr+Z40q3CXhxMIakGjyf+8x2khIzWE5vag723vVLC/4fbNF5dy8CwHzwL4tuTFSF57FEeZRQGEXQd57km2rmb2y+oYQlAf1r0FsDu+Po0WCO5HRKzWyigjGzKL1gAnGYO6EtKN0AfxakrSJRLTSUjn9DXzfeUuJO2iW0fD9gtXCHyUgkJTfVVzzLtQqrFBhhMIaoGY1VqZxuNnZ4eyYBTHL5N4kQ1HSc+pWffCIgLHsWgWY4fTvjVAZja7DrLiYwqL6sNegUAgsJjGE6tNWV6ns8UsR+7I2ifJXUfxJrY+g5PMcBSpDYsf4dK7lGzh2nssi0JmC9DFC+V20quUarz0LsrtdPI0012DwXAOdiyL4tp7lGzhymqWPqYnrxJ2sGPDLIo2kvcx655CLkp+C+oZEUCwDrqxWl1UsdpxJxvCpruC1IakpfTVnjbLvmQieoPOE/3+RYzqpdfr0E+EvQVwchl9O9DjBc5lqW/1ac+pNzl2mcDXzHdXoRtAkNoQ/zJDuunJf5tK2FuoDmhQCX96hKdDtAJnMghYTFmFRW/ZdKK0hY81k1uxzcipOmYGNaHcQMCsfI2UC+qOKKJoHfJKaXFQ/WXVLZ8o23cv+dlalJKZOZS+HbiuYOAS7KcwYjmB+ofsThvIqF4cu0yPF7CdTNdYfkhn2INEBwNs/gFg/MNa+YmBABu/t6h7VWaEMKQbN/5k6DLspzDsTX4rZKg/M0L0xKICeHQV9lMYuozfCnmoHTOH1uyN14WUYL7oc/eGEwgEjYuqrtas8z36GsrtRAVoW6KD9Z7o419GuR2f5lqB9i1QbufIYgB3Z0q26MUQstZSvEn7RG+6u9pIneFU9kweoL07bSDK7SS8ric8d5hWYEoQyu0cfQ0LqfsOwFpoMNulLlbdu3sa7yHEspiVkUt5y48xnnjaI9tHYhAzUkhvZOtIVlyyV22OOPyTtuXAj3oCPdsCZKyp0rEtgOImB1OI7IO/N6nZBHWmdTO2J1F4y6LuVfFvDRB/TseeFG27hl06jxqqZLha7PIQCCxHBBCszI6+JBfQ+yh2NgAbf2Hbff2oqJp+FhRrW/L088DcnE11pDJWENUfqkQPLOlutF2h89umunZx0BPTNdKoQN2Z2ZbkYEoiKBxF0kCiK125QbkMDeMe4HAgipGURJA5jNX+OFWpR+Zky+beFI4iZzir/ZFXP1MyUF6dMbq42LKxl1r5sq7CNVgZMau1MsEejDyGZn1lYxZruzekPfVN4S2aOuPuQv5f6hYDJ1hUQlNnmkRrJ6oG7E/m9yKi+rNkJ+Mf5rpCb45strsBKnldezxc1e26yB21Pw8qgYKbFum3kEW+vF25NGdvQ393+rsjl7K6mozmpV14VWcXX1snnuuAmx3T9R8R4voQ4QngKuW5DgS6E/gDZUpMY6ExO/oyoqVa+SudKK1g6UWL3qzAEsRPl5U5mo+fq/pjlUrwc+VofgObVK+kZAKM7q1tCdP/aUn9BWBkz2o1lFUQl0TnB1g+iZZN2PyDOlXAwu4GqOTDdfYTq64N0op1jRzZC7BoW4cupku4xXQAmHAK+70472fOWYD5HUBnoV/33DmV/JyzOO7DdT8vpQGMecBw0KBmjDqO/V6GJHLjNn2bGp+fGmDCGF18nOjxHc77eT0dYIq3JR+DwFKEq7UyE08xrz3Zw7lTQc5wYjow5YwpebkjH0aT9zFFG9kwCwe7mmWJWiJQNe/ViqhSCFZOIaw7UhsGdWHVVD2BuCSANdMJ74HUhhZyVkxGuZ3Dr+goSQD4vwiAjUdr3F2XHcfV9gT7IbUhpBsrpwDsOKYntvoJBnVBakNYd7WAbtSi7rhIAWQ2lCopLmddJpI9tD9Urbz8KyR7WJdJSQVF5ay6AhiJDyxM5cANSpUc/Z0FPwFMtMAhWmjM3LOcK6S4nDVXAbxFrrFVEXm1DYmNhKSlPOyrbfnPcR4LAIuzRC0RMJv3qsHyTC/NMY42EuJfZqi/9tYXiTwepNVvI+HwK4YW3rxN0Ouk/KxtSV9F5wf00mk1+s12N8irPfoaA/RL6hy7TPA/KC3XCv+QzsAuWoF9yYx5x9L3bkkW6ic9eNoH4OsbLL9McTmphZRUxpWq09C7CT2b0F1OSHMelOvJqLo0/5r8UnWLh4zfRvDHHdwPGNGp+9K0MRph5/0Ul9fgPQpqhJjVNiTRwTzsy2+F6iTQUW8T+qCegNksUbMCZvNe60iFktEreGcfvxZwq5R/H2P2ekOBkW/zxi6u3uB2Gb8X8d9TBCzW87NUTmyrTi0t7K6hrIKwt3h7Lz/nc6ecjDze2EXIG2o/q2HsSvYlc7uMn/N5/T+Ms/ZZt/N/4l9X+KuMES35PohTg8kKI6BptfKe9pwcxJlgPuvFcx3UfrYqGj8LKEoBXCw4ysdCY4rLjXUWCO4DVEmjU3TKbM8YUrMsUQsFTOS9/t2o+3u3PAvVyZYoLzb2Imc4ykhOD65Ww44+KCO5NJQVfkxrTRcXQxnVS92TmFvIUEaSG25cZ9UhqjPGqLDItLU6IgPBOiQH06tJtXerexDr3Q70E1H3nuFTHQGzWaJmBczmvQrqj+JyduSwI0f9sO/nWq1keEuAwB/U89Z+bup2mYRSnUXC0BbsrFzfG+0JkPKn9Y0R1AfC1VqHlZdZ1JG9ucRdJ+0v8/Iq3JxAPyk1X7+v2SxRCwVM5L0K6oMroXRwZs5Ztv5CaQWTvQGuVOaT3anAzgY/V+1XRRVYD2/Bjuv0a8rmh9Tt7jJyb2vVrvYn7zbHFYS2YEU3gG3ZdTVGcHcQrtY6bLvOgRtMac22h5BKiLvOjutcMvdtrpqU6q5/GK3ZLFGzAmbzXgW1o7rna9UTzIrLfNyTj3rwkU7amSZNNaeEtk6cD+HXEry+Adiby9TWbHmILQ/paevjplda/ujvfK8Tbvomj82/mDfVtDGCu4NYFrMaBWWszaDnUab/iLcjJweTHMyLHWnvVG2X5AyAUJ3le6NJqSayRM0KmM17/bshmXQ3ioh/8jNPJnOmgFvl3CzjmILIE9pn/9lnuVjEHZ0cgHnn2J7NX2XcKufEHzx2ihfOA8xtp6d2ZgpfZHOrnOu3+Oclxhy3gjECwb2Nh4xd/VBGUjamWpnpg1FuJ/sDdY5nsB85H+mt28wbjnI7ueu0WaJ5H6PcTswISwVUQ+R/qs17zf7gb70sJhA0CCKv1vr0cWNGWya2Ir2IuGx25JBTYlzSRkLC64ZJoEB5BdLHwYIsUbMCZvNeBQKB4F7CTcq8dqQEkxzMIl98LAuJarZylWwh+Z9MHoByOwWfaQWcZCyfROb7lG7l2nssfQwHOz0NZgUc7FgxmZyPKN5E3Hxc7O/erFapiFUqYq2rc/L4LuaFzFEfhlXFKqYKBAItk1txchCLO+FbTSUqC2nTDOV2Ui3eudTIsbpHS0mYZhWFd8HVWstUwf2ByECwDgs70qsJfZvyRlcjd6vLqy38HFcH5nxGXBKFt+j0AGufBP002HsaibuVt2H18G9uXqhxcA+ZKrgLCFdrHXofrU2vzQk8E8ZHT/HRU9rGn/N5a7eVzBIIBI0DkezVkMzfyHObOHWVm7fVG/bf+5o+LzfeXQYO9rbLXhlwLWVGSW7MleSnlr4UKLPTfoVUT+VtvF1PfzelKHv+ri1jqj6nS20lixcGXDodXZIbcy1lxrJXBlTV0KlD0wM7xhVlz8+9OGflG4OlthLNXV0xC3UCclfZ2hVDcy/OKc55buvHI50czcwwTJth4Rsxaqrgb4vIQBBYitRWEv/fx4YE6R0d821CVtgj/1EfRquIVbUMHdSmvEK5bsPZZ2b0RD+MsD/ukVFh7XU1HDr6c9i4naprlYbrvxa1ekC7l+Of7554+Y1EdPyXCo1a0zqltpKk+Ml9e3lq7u47eDUivAPVxzdMm2H5G6lqqkBwl8g5wf4NLF2Af5X6Uv6dWLqA/RvIOaFuqcUBrncBrxbs/IiCcxRf4K3/o6dfQxt0t5j9ZA+lIjb34pyQga1ldjahg9vkXZ6jVMTOfEK9KUI1g0tJmObbzk23RaNh2kQ/pSI26ZtJ3bt52Ejo0tE94asJSkVs9OP+Bhq6dHR3cpSuejNYqYjNPPu0RkPVSaJZnXOf6qFUxGafnxUU0EpmZxM+1Cf/6lzTk02zZpgd1KipAsHdQ+U9lRnEvW94K+597V1d4cbGkW1aOzMTG6OF9cTRfVFKRaxuApPK4yR8NUH1UuVcRg7T7nAycDfxu8YrFbE+rbUlAtu3baJUxB7ZG6UrPyhQXe/as4WTUhFbmvd8dQot0akyO2qs9rc9+nF/S1ytCTPMDmrUVMHfmYZZFruaRWgQNhLt0SY2EsIG8vN12rbSiknaGe3dwAQ+BDBkEgknKK/hKSn3NP5+HkD8EW2l2APxGZp2DcdP/Vqdhp4PNgcydKaHKrp309OQfFa97T/vt2LATmpqRcGszu7+zYHDR7O0Zh+y6H+bCTMsfCMCgYaGWRY7nEgzNwJ6aVsCetG0CYcTq+/TaLCXARw9rncE1t8Buas9oPhDe56i6trFWe8IHUVBNXvjwK2J8WNpVZo1FN28o7qw5BM2q1N1UfCn1iqV6zSLCTMsfCMCgYaGmdXGJzBrMuHBJCWrW0YOATj8P56aoBVTPZtr5rbenixbyMghyF1Iv8rKT9i6Wys2eAK7PiYljdDHAeQuLIlhXDiezcn9jZ0HWLqGwspig0timBiBjzeld0i9yPp/s2GHdlwTfXWDG9XZeb9SdLO0aRMH96aO+Qq1t/Vo5qhqr5GGJm3fL/zL0i5111n4120Ds+vuEOvjjQjubxpmVnvkGOUVaveqYuQQyitMzWpbNOP4bp54lObu2Mvo0ZUt7zJtnFZg1au4ycm5ASB3IWkXzz9F21bYy2jbitinSdiBixPA8hd4/Tk6t8dehqsz/Xvz2dvERKv1mO77dyY1LR8ID/XRtISHtgPSLylqpEE3mGstq0zoTPnpN2D0cG22QFhI2/oeVCAwoGFcbUEhyan09KNFMwDP5nTvSupF8v+otsuiWbRqyalzdAzGuSsfbwNY/KxWIO0yTl2YtkAt3K0jV7PoPw47X7Ry4N0AACAASURBVAY8SsYv9OjKi3MAZk4CGD4NO1+cuzJnMcD86dqBTPTVTF0l7Yxf38fs2H0RWPlGcPAAb6mtJGRg65VvDAZ2/NfS0qdxu9KBNW+HhA/1kdpKWng4rvjHIKUi9vDuxyzUcKesAvDr3MxynZu3nwdWLhscNqSt1FYyKNB71ZtDjOmuAZa8kaqmCv7O3O28Ws2z9rJYXpnHE7Fs3sX08Xz+Du98wqJ/6j2M616nHaKrLw+P5eRZ4zp7jOBcurolNZ5uHRk6mSOVR1KHDuDQVi5cwW8YWUm0foBvEjj4PbvjydSvY2+6r4FVf5PQgQqpreTo/gkDHm6l23jsVE7w6H+X3qmgMplUN4fUoMVGwuE9UQaZuTeL7wSFb09J/c0SDZlnn27bWg78euOmV9d1lui0kRD/38eGDmqjufvFfy48/lhXXbUGo9T9jRg1VfB3psF2ix3+H1SGaMMHA2bWxNq3AUhJq1Yg/aqOcGtAGwgGEk8D+HgDLHyTm7cYPoh3XyU1nhfn4GBvad+/M2XlyrBxO99+7+TPvxTeKavIyPrzjXeOhYzZUapb49okFUpGPvblG+8cu5pZcLu0/Pc/bv33q8sBw77QuCezzF5w6OIVhWrCaKHOCiWjJ+x65/1Tv964eauk7N//vTh7waEavfHavZGqpgoEdw9NqqzMjqI0FClIbVGkUHJR7e+qy6stvoAyA5mdKZ0aVMK6DtTJAWUGxRfUL93kTB9Pwg513/wf1VEFS/o2/sxfgUDQ2GiwWW3pHRJO0LQJsx+naROSzlBy25R8di5Ad8vqf6piAkF9tC1BfbXtQEEhG3cyKIq+Y/jnhzRzY9Wrlva1OtWdUrX3YSsot4oSgUBQRxqy3IwqhvDiXID4H8wIH/gOYPXreHviYM+aJSgzuPSdceHd8QDr3iKgF1JbAnqx7k2A/UcArnyPMoPZj+PiREqaOsJbWmpRXwPKKwB867agXV2JxQhP4+01wipKBALBPYbuE3f3Ltodrn0eNCKge92iGTkntPKqv8mRhmIq5C6kf2sonP4tbnKAmZMMbykz2P2JRX0Nhss9pX55LaEOn0mk+r+vdSYrjJIIxnqijFT/eTtwdAAlERwfpK473saRpIHkhTO/vbZvTHtSgunnxslBlESQHca01qaUCASC+xkDt6hyVYoU4wIGwm282PaeOrB7cg/jhhsXU+HehDVLyEqi9DJZSaxarPWVwPTxnN5H8QWKL3DuIG/9H3IXS/vqDhc1iivfU3pZWyKnFmjc5eJOONgw1pOSCG373oeJ8kImYawnRwcA7OqnllzWVdt3vBcONqQOYdwDSCX0boJipCklAoFA8PdC4y5lEsMWoHCUdnKqcsGFo3CyBXCxNezrIWNxJ3b04UqoGSUCgeBuIk5haESUGtvybwPNvya/VK9FfaGTFa3quz+ATzJJ/J2cEi6GmlIiEAjuJuIUhsZLuRI3KYkKYjpgA9FtSAwCOPwbC33xcWR/gGEXf1dS/iT1LxZ3NqNEIBAIqkX1CFz1ukYdaydglVGq64V+ypfqOr4/xaPxcSQxiJIIUoLxcwXwdiBpIFlhjHtAL6oLzGtH/gjywolpb0aJQCAQVMt96Wprh1RCdBuSg+/ScJaj3I5yu6XCPfXz5GrUVyC4h7iHAwiSPdVmpNZRuJGzsRf5IygazUwfpiebl2+07FrAj8sb2giLuUe3BY4No+QiRWl4Ngd4+Zmadb9H37WgrtTftPGem9U2Wiyfmd5bc9h71OmUXGRsGFJb9cuavot79F03Qhr1rFYuZW13csMpHs3Wh9QZThpUfq2LC8pI0oca9r00FGUknZz1hC3UDEglLO7EpaGURHBtGMu6ajOxLFciEDQ49jJ2x1NW3tB2/O1pvK5WKuFwIM+0o6U9jrY87k1cHyNi6UWc+oPOLnTX2WXQx42OLhxTcOlmLTXv7scbXenogr0N7Zx4pZPhcr+F5tUHJiYa1p2DLBlP+ipKtlD4OUlLiQ7W3pI7smoqme9TsoXM91k5BbmjMXuqzFs1LZp2XRkDeROjqCT9vYl/maKN5H3Mh9E46Zy8Y8J4CxnUj5N7KLnIle+Z/XjluBmMDCbvDCd14lFhA0k5QPEFUuMZqT/Q/OmkxlN8AUUKu9bh7anVMzaM1HhKLpIaT0h/okaR/i1lV9XzUA1zp5KVRMlFjmzTbgGvaobRgVTfB82mG4OXBhgdCIiJJjPRiGGC+4S5PigjyQ4jyB2ZhPAW5I8wviw2rx3KSJbqVKJZ2Q1lJDN1vi66Hc1qVm1pTRpIdzk20MWFhCCUkURrS56aV9IgWNHVLp+kdme6fzEjAOSOpL5jeCtlOS6q2mzVu070Xa3uX1V5S0bJ+1jv7rqnzBtvIT39uJZAUB9kdoQPJv9HrZ/a+i4uTtpD6fv1IOcEYQOR2TGoH1lJ2kPzFsxQuy0bCS2aseIlDm6qfKcZnN6HfydkdiyeR2EqiTvp1A4qo6sqxg0n/Vv8fHGwZ940kvdru+uaYXogvc+/mm+IiYFO7qFLB0PDBPcPRwegjCTKS9sS3ca4q3W3oyRCL4aQFUbxaOQ6+zN0O5rVHN8fZSQ+OtO09k4oIzmis6XVrJIGwYquVrEe5XbCuiO1wUnG7FCU27n2HsCyKJTbubKaAF+kNgR24tp7KLezLAosdrVm71oySuISfFviYMfSx9Se16zxFrLjA6JGaV9GR2ldrUF5uV3rmDJW+zJqFLsq64Bf+V7tpFQ42FNUWXBZmaH1yC5OKDPw156erv3/eHCT4TRZI6BrhumBDDoaxcRAmvokJroLzNJ4AwjdmwAc1qkZfeCGcUnFHQ7eoLML/q4AQe60dmT3rxSW1VJzzyYAGWHazaxXhwF6MQoLzVNmEB1F7ikKzrFhhV7NmhkTKDhH0pfqluUvkP8jRWkk7KBLB9zkZCXpHWjm5EBWEm5yva+7m5z1y1GkkHtK+5Cr4rX55J1BkcLq1/T2lVlIUQnAgpHMG06LJqw7jGQS7Z8DGNsXYOanHL9CWQVJl5j5KcC4fjUexQSWjDLvc67coOQOqw8AuFcWsjBhvIUE9dGrVX9Ap4acbhF6IPAhvapv8T+oj68HfAeTdZ2Q/oQOYFksp/fhrPP7ralzX1QMkHbZiBm9/dWV6auia4bpgSzBxECaw00EdaHxulrVnLTgjrYlr/qCthuzAKJaAUz01rbUTrObsQLkmo41NW/eNALH4R1AYZG6HqOKPt3x6seUGIAFM5DZ4TMAj17sjuet/6OgkL2HmDlZKz9zMvu/paBQT/m6N9kdj2df+kQQHaUjPIlLGXgH4NmXsrIap/gAC7dy8zbDe/DuNFLf4cUxOFR+LO1bACRd0gonXgTwaV7jUUxgySipv6gvFDcBbCu/ziaMtxB3N72POi9fe116x1Dyj7PaInB/nMXDXX0rsDeZ/2PVq0yOJO93Js/X62hQoNnoSexucu0xzwbommF6IEuwcCDBfYhiJMpIPHQWOtyk1W5hkErIH0H6UGwgN5zsMMPfEF1hs5pVAnKT9SHMKlGPm0Fw5XpaGy9tATBlhvpkHRXnDmoXTNybqMV825KVpD54QmpLZqL6IVF3VptzQpvHE9hbe+v0Ppwc1NdeLbjyvan3Uh1uTkwfTMLr6qf1/E+ZORSgeBPK7XrOy0mGcjvFm8BkiEBmW4MAguWjGNVWnfEWkn0cj6Y62uTaAIIBWUl6ZeF0OXeQ8ToBYt0nEtPP9ZqXihTjyg3kaz2QBgsHEgGEWtN4Z7UpfwKMbqltCWtRrXCZkrjrdHZhuR8t7dn8CyZOdDKrObUQYGRLTGC5ecd/VF9k/6qd76B/rINvW/Wx6kBBoVrsys8kp6oPYJ8cSdplw0dXwMNdm8eTfF7b7ufLzQvqedb1E7TxMuxoCQXFbPyeQf+g7yv8cw/NXFg1FSDzN4CgzlrJoC7adl1UpdM1aQP+rQ0FTGD5KDUy3kKOJDFaxzWHDaxW8uhx9RF5Kvw7kXJAfd2pHXsPa2+F1rx8ZUqaNqRrAssHKq/mH4aFAwlqTeN1tZuzAFb6E9YcqYRBzVjlb17+/zqCyeiBJZrjsgHWPEh4C6QSWshY4YcyksOBtTGvrJqQsdEHRhV2lRPqVetZNBsbCQtnsnqDcWFNHFZ3IBsb7HzVp6ZL2iHrWO1Y1XFlNcrtzA7FxZ6UTM79DFBaBrD7NMC6GeoFqwBf9dL//ipb1/IKAWYMwUmGXyvWzdC7qz7DopqfNMtHqZHxFrLsfZbEEDYQqS2D+mnPQ6rKqvWsfIXwwcjs8O/E+rdZUbk6l3SGpQuQu+DiRNQoPlxWAwNUfLiFFS/RvjUO9sycpE0MMMDygQoK8fM18tNr4UCC+xAbOByoXZhSRrL1ITM1ENKHqpO0qqIrbFazDRwZoCegjKRoFD11lsXMKlGPm0Hvbuprj6bkntK262IQQNC9e3IPC58mNV5Pp4a8M+oNl0DvbtpbqfF4Vf8QYAkzhxrJl9odCyB3JH2V4a30Vbg5gf6D/IfRejIHXtC7m7tO/VKTG2CQ7GXhKFX7mjDecvr1IOlLSi5yLUEvA6Eq4YNJOUDJRTITmaszd27jxcFNFF+gMJUj2xgZXOMAAhATTc4Jii8Qv0UbdDKQt3ygmGgKzhnPT7BkIBFAuD9xsGGFHznDKR5NXB91JWwTrvbljobptBoMhE1rVgks7cKVUEoiyB/Brn7q9AbLzVOPm0HCDjyb42DPolnaY3UMvrILZrBqMS5OONizYAZ5Z7S3JkdSmKo90Neg7971LIvFyQHP5hyN095aMIMX5+DkgJMDL84hfouRz8Qs0wdz+i2KN1G8iXMreGuiNhTg7syaJ8haS+lWstayaqraA6Lv8pxkrH2SvI8p3sT+RXg20bsbFcCV1ZRuJecjI30tH8VoiwnjBQLB/YYyg5hodTLWmiV6yV4GrHgJRQqKFHZ+pE2PB6S2pBzQOyxdt697E3WyV94Z5k7Vu/XW/5F3huILHPhcO/MVCASC+xDxwCUQCGjMy2ICgUBw3yBcraC+qO89yqJepUBwj6E6D7xoFJ72psSqnkkjEAgEAkspiWCsJ1JzhQKEqxUIBLVDBBAA7G3YnUtZ9XsKBLpkh2l3JPs4UjgKl8rNwV4OZFWWNDX4ZQp0JyGI4tEoI9W/bXVRXmv9AkGDIFyt9sBazT/d+e1JHULxaBQj2dUPbwcTvf+OnC6gX2V9gMneFJerS/wA/dw4+YfxXut6sCQdt6+Q7GHiKeL6WlO55foFggZBuFr18Y6acx4XdGDsA4w9ict+uhzmShHrxd5wfU7+QT839fVEb2amaIum92tarTecd44j+ZQqAXbnYl/NV692yi3XLxA0COL7aMjcdsw9y5WbVEBeKa+lE+Ruvlejwmgyb9WdmrXO+dVMPP1dKSlnby4OturddAHu1XrD44p6VG65foGgQRCu1hDfw2TdIsSD0OYs68rpwTibrKZYI/y9SX1He31uBcrtlGxhbP2fSyZpZzVVpwvo4wYw2Zu46wA7rjOjLUDPJpwuMN6r1Fgo3KDQRK2VV6dfIGgkWM+L3C8EurO7HzklJP9Jyp9MPsPZIeZ7Wciqaaz9BkBmS+I/mLuBbf9jbB/insOhJiX+GhbFHQrLaO9EVCsGJQLEZXNyMBuzyLlFUU0OZ5XsMWyxonKBoPEgZrWGrOvB3HP0PEr0j6y5RlaxNZUHdGRzAkA/X5o4qY8Y2H0a+zr85KkO1yu9TPJ+gvRnxy/OYVllOSsT4QITGqrj5B8s9CXrFjklAJm3uFbM4s6crH7WaTn1qlwgaBCEqzWkkwt7f9W+DK1bKUIDXB0oLgVo34LrCnWVa7kjN6s/y8csce8TsxSHTix7nw3vaNvnTcOzOYv/VXsNJjhdwEwftulUN4/L5lEvU7FUy6lX5YL6RqScG0W4WkOSFCztilyKiy1RXnzY3ZrKb5cxtg/Bfri7cKTy0IQpQRw3doSfhRw9zuihhARy4CidKmMd08cTNZqYpbXXYJqTf1CmZOd1bUvcdcqV1vGG9apcUN9UDQoJBGp0f4fbOHKwP8WjKRzFkQGMbKlXIddoF8sZ24eSLZR9wdoncapM1E9ZTpdaHUijwqMpR+MouUhhqroMuTKDbe+R/q1esX0TGQhVNQgEAoHACDI7oqPU5z8qM5DaMmUsm1dpBcwme+lqEDQSQpuTNJDi0SQGMe4BbfsYT84NoSSCc0MMD8FTRjLWk9QhlESQOoQQD6K8SB9K2RjDTXQqyfShlESQHEyIh+Ho1e3lMb03z2AWEtaclGCKR5Oqb6qq8IgykoKRHOyPX5XS+wJB4yI1nvEjsJGoV7fQ8aGn99G9i/rahKutqkHQGAh0JzecsZ7IJLR3Imkgs30ABjUjZzghHsgkhDYnZ7he6rcyktOD8XdFJmFxJwpHkRhEJ2eo9G66koqRjHsAmYQQD7LD9I50WtCBIwPwdcYG9fF6B/trO54bojbAqFoN/dzIGU5Yc2QSBjUjK4yAyq2AGgftZMtsH85ZL89HIKgXAnqRGk/ZVUouMjYMdHxo6AAOblJfm3C1VTUIGgM7+zJN54xhP1euDQPY/zCTW2nbp7Vm/8Pal8pIrTtTHcKke1aTQRBsro/25RRvdujkn1wJpYvOceUONhSN0nYc1EzP1Opia7v6McVb+zLKi1391NdlYwjWVyJopDjZsq4HeeGUjTHMhBdroPcBLTz+7meB5QzXVt7RJS9cr91DRv4I7UtlJA42ei91174NfKJu1VAPGTnD9QZysiW8BdNas+ZB0kL0Fi1k+mXwqnO1ueG46SQyuknJDVdfr+1OTHumtSbI3VDbfck9vIVhtT9P+zS0EZZhcOCgUSSTzMs0LL27t5gx7cFBgd4+bZrIZLYFf5akX1Ic/Dbzk43n8hW36qJZqYgFJO7axLQZUx9csXSQe7sP6mp0nS1pwHE9ZChKjQi72em1K0qR6/87LqnQe6n/So88nSxDRSnuOh7cYC/P+p/19vJYuDfP3Y4/Rum1lFd2XHSedT0I8aCVI3/eIfAH0v6ySOc9yj3sasd5AbxxkdVXUdxpaGtM0vjdqFlW/GPQgmf62Npopx/Nmzk17+80sL/3ovl9o+d9s2t/HRLWqvDpeyKQQWEZbnZGvtuFZbjLyK/0th4yCstqOYRcSkFlXw8ZuSXaW6q9PDtz1C/dauUqCstof8i4ecXlTEsGaO9EQFPi+tD9u9oMca9wD+fVqp6S3rrU2P3sfcCMqQ/+37N9S0rKXl+e9OCAjc6t3rNt9q9m7T8YGrnjy32Xmsjtt60f1bt77Td7SNz/dfcnko2fJAVhOh+qr7M6VntcQbhOe3hLU6UhTKOrf2RL4vO0L62yl+fkH3pZBwFNSQk2lLlWzLbrPCg3bBc0FuL7o4zE17mh7aghg7qwfxH5n1K6lfxPOfgiwX7mezUsqUlPKBWxo4e3N3r3w5VDlYrYXVvGWHFEpSJW9Ux9l2lU4wa6kxVGaHNkEvxcSQhifnuA4GbkDCe4GVIJIR7khuutLxksVJh4qYwkZziDmiGVENaca8P0/jUdGcByP+1enrxw4wnmRtVqCG5GbjjhLZBJ6CkndYh2QS8lmNk+6rrvBjkMgsaFvyv5I0gMoqfc/Fk1jYQxD5HzEdMG4uGKjYQWcqYNJHcdI3vW46Bjwyi+gJ+vtkWVe2B5EcXSvOeViliZnfFnIG8vF6UiVpHxzLjRHZWK2IM7HzUQ8G3nVpa/oE/Plgbte7eNVSpiVb00jkZ1bfBn4paBk2rj7bpuVWjm2adLcmNyLsze+vHILh2NFMFU9Wrj7Xr6uylF2fP3bhur264Rk9nZxMzufXRfVP7VuaV5z+ddnrM/7pFBgd7VKQT69Gx5ePdjxTnPKTKe2b01suq77tSh6cYPwnMuzC7Ne/7S6ej5s3pVHVdDeAuSgymJICuMBR207bqZs2P0z5uokatd5Et2GCURHA7Uy/TC4r08RtXqMsaTlGBKIsgcppfw4OfKkQEUjfq7nJpxj7goY5hOM7B8d6Ayw1SBQdN3a0rqO8Rs4nCqXmNIN1Y/QfdFRoauNbo2l1xk4rPsjtfTLGlXg7d2JfmpDj5uTz8Xv37LTybEHOxtcy/OcXCQenT4oOimNqyzdsXQZ2b0/HxbavS8bzSNTo7S/KvPlJVVtOj44a1fY6hcFDLqcUzcAm4W33HxXgMEBbTaH/dIE7neYZy3SsrGTd1z8NtM3UaVqm8TsoYOagNs/zJ98tNfadpVw7k42x3dP+GhHoa+srxCOWbSfw8cyqiq8OHQL47un+DooI1r3i4tDx797+On1Y/i4UN9dm4a4+xkp9v3hSUJby8ZxF1fjlNGik20dw+rxWqjRqHMIEqTeVcHN3GXsaInNYuvJ0fTDBsT0+n0gDFpa6DMwF7Gfz8m5wQ5JwgbaCgwfzqrXzOjZM3HycCn74XF7xo/+8keRueJQMnt8r1fX7WX2YYN8dE0yl1l0yb6ARPHdXF3054dFDbEx9FBuvfrqyW39Qoj6sZtVddVX6r+Xlr6g6p9xvxvADe5/c5NY1xcZP/64HSHXuvtW777cOgXn24+J5PZbls/yrOFU1WDfdu5deu/0bX1moWvHq1698WYfg/1aPlTWv6A8O2OD6xWKfzfieu2NpLFCwOMfgIb1g4/cOiaSn7w6H9fvvaHvcxWI+zh7rht/ShnJ7vtX6Z37rvBrvmqHgM3fXMkszptAoER9m9g8yr2b2hoO6yNdX8zUt8xEpkN6aatF26aedNQZpD+LXOn4t8JFydsJDg50L0LL84h/0fKrvLafMNeygwufUf4YEYGc+k7dYvqv6NDOPC5RUMvf31gWf4CzQN7YdazR/ZGLXtlQPAAb52sBEYOa6dUxG78IFzTsnBeH6UidvfWSKUidtF87YFfG9YO18R/qz4+m42Z9uvtqQprLH9d/evxYkw/pSJ26UuBBpIr3xhctV2lf8bUBw2Edce9ljJDqYjt6d9cV6B92yZKRWxRtuGnrOr44cqhuo3BA7xVoRXVy5cXPKxUxO6Pe0RXxkZC4tcTGyRGLNLP7z1aNCPvDHIX8s7QohlU/mP2asGBzym+QPq39HmQNl4k7KDkIok71ZVQlBlER1FwjrlTmT+dorS7tFtJmUFMNKnxWlMBuQs7P6IoTW2tRnJiBHlnrLOTamwfsj9g8gDcnbGR4OHKlCByPrLoFIbuXSi5SMIOHOyNC3h7kpmoNlgXZQall5HaIrOj9LK6RfXfk3s4vc9S4/06N1vxj0HnEp/Q9blKReyl09FBAerFDqmtJP/q3LzLc1T+10ZC5tmni7Ln+7SWl+UvuJYyQyVmIyHv8hxFxjOq+G9NXa3cVabygwd2jNM4+oSvJigVsVU3PnTp6K5UxB4/NLmq/k4dmhoIm3V5NhKUitiy/AVGO/p11tv/5OQo1RVO+maSUhEbMrC1Qd8xIzo01HKc4K5hnQDClEfYHU9hEXsPMUXnN3vtUs6l49GLxSvZ8A7r3uRwInJ/jh5j3ZtqmZLbBE9k/nQys5kSQ9z7NRjXxZZlXUkLoXg0xaNJC1GvmVqCooDAcXotK15i227cHlRbq6F7F9oEMvHZmtlmlN2nmfw+kweQvoqSLaSvYmIgk99n92nzfZe/gL2MxSspqaa4bXYuC98EiIk2vJWZTWgQIYFkZuu1j44m5wbhgy0yPu3i74teT+getMm93dphj/znlWWJXx/OuFl8p2P7pvG7xquSvcrKlbv2XW7ezCmgrxcwLqJT29byg99mZP5SmHz2Rrs2TVTT2MCHWzVv5rT7qyuld0zk11fLJ6vD2rVpcvnaH5NnfFVRmRLv16UZcOPSXIMVswsnngQ6+Rp6VSAru9CS4Xxay0cPb7/0pcCDOx/NSZ8D6OYX63Llml6dx+JbZbrCXTq5A6dTbhj0On4qB4HAEs4dJDgAIDiAcwehct5UmIqTzsneRWnql3IXitLUYjI79YXqC2n5A7tcSuoQI1ty00LMe1tlBnIX7bWK7ONqYwwk3eSGkg1CURrKDL3PsyouTigzKL6g16jMYGQwuafIOaH2qroZCAG9OBpXe6tcnO0+WT1M97k4ZGBrzXO96tF48vguVEYSVPkJK/4xSKmIDR/qo7awJrPa6Mf9lYrYgsx5BiFjVTyhuj+DeWh1+g3ao8Z2upL8lOmcB8sVqp4Gqnpp1UxZzGrvb6ywW6ynHw925rvtei0qbKqZNFdUUFE5mymtXKmusGyrn4YlXegmJ6OYmSkk/o6NhCB31vWkqyuvdWbheTPdC4sMW1o00xqjS4FFU59GgVQKUKZ/AJdq3c+zr2GL6r/HfyR4oimdhVnPurrIWvmty8m9WfVu0c07Ly9NfHpad00M4WjiL7/euDlmRIe4L9MHPNzqdmn53q+vAjt2X3xn6eDQ4Lbt2zYZM6LD73/cOnz055q+wS4d3de8HVJeoZwy60D6Zb0zcouL7zSR2zdp+37hX8Z2s9acieM6b18/Grhw6fcd/72YmVWYr7iVfklx+cxTtVNY+Nftpk0c5K72BYV6DyYuLsYqHQjuL6wQQJg+nheWI2mn/nthOdPHq28lnGDhTBzsGRtG2iEST7FwJjI7Fs4k8VRdxx3vBTDlDId/o6SC4nLif2Pyae2tmqIoMDKrbTykXgQIDTIlo4omJ6eakqkp587/BoyL6FidQOmdckAqVX+XKpTs3HOpa6dmocFtgfgjmarEr6zsv46dyrG1kYwZ0aGzr/uufZfLymv26yqzs4n7bLSzk92S5Un7v7lmcPdKRgEQ2K8OVdb1USUGzHr+kF/AxiVvH9u4/fz+b64V3ay9H09L/x0I6GuYblKXjXaCe4W6ulqpLeGD+XCLtmXdF4ysLEsx+xVCAik4x9IFTFvA3MWED6YwlZBAZr9Sx5HVRYkMDkFR7VD0WT6A9gAAIABJREFUMvmUXR37vyU4QF22Ne1QXc2rjlrvFtu8C2D1a3hV8w/TvxMrXwFY94WVbAVg47bzwGuL+vt3NV7z7sWYfkDice0BNXG70gFVWHbn3kua9h3/vQhMm9QNiPsy3cSg5caecVa+EdzDv/mX+y4tW3m86t2DhzOAlxc8bPCEPn5Mp6rr/pbg274psDlO7/lowTPqFUwnxxo/Eapm9y/G9DOwcOGzfY13EAgaA1lhKCPp56bXGNAUZSSZw8z01Y26aq7dm3Dgc0oucu6gXgaC0V61oy67xaS2JH2JMoO8M7w4h55+6mQvuQv9erD8BXUwd+dHdTXSAE02UnHOc2uWhwT283JxtrOR4OHuGD7UZ+emCKUitjTv+YA+epO1zLNPl+Y9X5Ib46azocDL01kVr8y9OEfX3VSNVBZkzlNtJLORILWVAKOHt1cqYlMSplXn47w8nRUZzygVsfG7xvfp2VJmZxM8wHvdqtCS3JjSvOe7d9M7Y8CS0Oql09FKRezaFUNbeDjaSPDr3GzjB+GaWK23l0tNFbo422Wfn6VKnOjezUNqK+nUoWncZ6Pzr84Vsdr7nnt4t9hqf57rwOUiZp8lSQEQ1Ix1PejgzHtXibHqQ7S1qNFusarIXdi8ishqfkjKK/hkG/OXGMZq646b3D5uw+jhIT5G7/5ZeLtqZa8V/xj0f8/2/eZIZvj4L3XbE76aMLC/9wfrU+Yt+lbTqLtHS8WRvVFDgrRJURL3f5389vG+vardvKnqa3QvFjAn9vC6z8/qtlQdsWr7jKkPVi0w9o+3kyY+2qWzr/uj0/bqvmVLFAL9ense/PLRpk30Hrumzj6wZd1Io90F9w33cGWv19I5X0hHF74dwK0IbkVwKJAOzlz4iyWmnk0bkjruFissYuxMBjzKB5s5e4G/blJewZ9/8eN53v2MniOY+6r1/SxQUHg7fPyXwx/duWn7+QuXfr9ZfKe8Qvln4e1jp3Jefet/nfp8VrWCoio+sHPPJcP2XenAjt1mztWZPvfrrw9n/FVUeqes4pfrfwG+7d1MdwEOfpvZc+Dmz7b+9PMvhbdLy//4s+SbI5nDHvmPgZ+1kPVbfpo6+8CPP+XdKin7s/D2oaM/j5qwa8nbx1Thi9nRPWqh82RybvcBmz7eePb6r0V3yirOp+dPmrF/644L5nsKBA2IXMqyruqT5lR5tcu6WppX2yDUcbeYQCAQCMxTl91iAoFAUBuC3Dk3hNIIUoforW4FuXNtmPrg5aoHJt/rqDIQ8j5WZyDsX1TjerX9erDuTc4dRJFC2VVt++Ev6GfsoVa53aITd+rC5AF3e8QaodpDca8PYXpEsbAmqJZrwxjdEpmEmW1J1ynTsbsfM9sik7DQV3u+pgrdIxqr7hO7749xdLBnwwqUGXp/GlS1DqJGGfaqb8eXstxQf6NytSkJ0+rbB92FIcyOKFxtI6chl8XaH2L/DUqVbP4Fb50l2d5ubMumVMnmLHo3aTj7Gh/b3uPJx7iaxeMxNO9teHfWy9jYsHGlupTPXaNHW8MWyaRGdJxaD/3SXPfoEA0+oqCONIoMhKBm7NApuNHCnqJygPxSPPU3I0j2aIsZq66r+6sRmlnwuHqrG6seyNhcz7clRRvN9x03nEeG8/N1AsaybQ/5fxgKfLKd9XE4OjB/ep0NbZT068HJPZRcJPs408apy7OlHMDbk6NxlFzk+H/xreL3BYLGQMO7Wjcpc9sRY6q6v3GqCxRcG2Z4nL1ZNK75y34m5eoHBzuLMrRmTARYstqIk9Ww+UuoZvOuiz0b51D4OTkfsSwKgw1LDnYsi+Lae5Rs4cpqlj6GzFZPYOZQkv9JyRYKPydpKdHB6nbNj4du0KDqdSdPDrxA0UZy17FyCtIq3zu5Ix9Gk/cxRRvZMAsHOyNRiA3vsPxDXPwY85S6nHn2rwQ8wofL+HALcn+Wf8T6t/W6aJ6pNc/XSv2jblRHollS72bmE92Tv59akhtTmPVs0jeToh/3NzqEqiLi7q16X82J4zorqxzOpjrvZ8yIDg72tsteGXAtZUZJbsyV5KeWvhSoe7ZQVYOrvikNLs52Gz8IL8x6NufC7GWvDFD9X/br3ExV69LgM/dt56ZUxF5JrmVJB0GNaODEKC8HlvsR85Pe8cV5t3GTUlCGm53eOfWW0MmZdk7crk1lvnpk4xyeGKS+NnAf5RUUFLNkp3klfboDHDB5gHPqJYD2bYzc2hHDiJ4Arg688gilZSzdpb4lteHACwzppn7ZoSWvjiOwE2FvqWsALYrg7cpyr/ZS+nekf0fkjqz+2rzZKo68Sit3AGd7YkdRWsbLOuXEbCTEv8zDlaefPRmMi7Gt1cETmP04E8fQ25+mTQD2Hqb0DsEBRFTG+m9bVqJgw9rwh3q0LK9Q5vxqpIZOVRbN76s6kwawl9n27+vVv6+X3FW2el2ygeSB+Gs3fisOD23n7uagKFAf9j0+shMwZkQHTekGD3fH8NB2v/1e/M23GV/vHK/Zr9HBx+3V/+sf+HCrsEf+o7s52UKDd3weMSK0HeDqInslNqD0TsXSFcfSLv7+XeIvQ4Jahw/10T34JzzUh8rd0oL6piFntWHNievDglSybum1n/yDyd442DClNcn6py73lBsufBmshl0MBcjWV6iL6mxOZSQFIznYHz9XtRLd/2qUv9aZvHAUI1ntr/dJGVVigukfacOXqgvNn/RxPJ62yGepajnmK0zJqErZGi2a49OcHi/g/ASv/wdgis7JNzNCGNKNG38ydBn2Uxj2Jr8VMtSfGSFqgZiRABPew34Kzk8w5zOA+eHqt6PCdHw2/y+6xuL8BO8egCoZC9HBPOzLb4VqA0a9Tajh8QgA+zeQk8eHWxg5Xd2iqsRmY0Pz3upqRw6d9boYHJajafdwd+z40GdSj1W6m9ZMEDPnIWBC9D77lu86t3pvTuxhYP7s3lWHKCtXbt2RZi+znfio2hQnx/9n79zjer7+B/7snkpSSbPcc0tzGy0hLUmSNDOzZlgzM6w1zMzMd1/zMzMzMzMzMzOz5mtmSbOWkNZCWpJLpJIkSZKkmz6/Pz639+f+6eKSfZ6PHh7v93mfc97n5NPrc87rvC6mASM6A0Gj5IkYJ0/sZWZqvP1/Z19+8Ymnh7a/eq1ixLgdFm0/G/nM/65drxjh3WH6lD7CAQgHrGlSQKcOtn2HfW/9+Of/WZEkfou4fP2mNCD8NQUFf+DILqhzMzFwL3iQonZzf4Y5cG20XLqJ/92Qy8LulI1hbldWX1BoklbGr1fUdCXjroirVSxU8ciSETmISccw+o12f7D7CpECg1axGkGm6p3RkXPluPyB8+/UiljUXa9OtNOYw6IScSQd5aSCCnTvDFB0Xc2jWZtJz6OimrX7AFwEsV4neQHM/YH4U1TfJS6D+dsAJksVEeI1prkp1XepqGZDHEYv0OXNegw+fAtnC6ioZmUUQDvFON0TBysMICaNBeoi5rj3IO0UGZksfkOhPPEYEWEYGxE2kUQ99gfAog8PiyOB6YmNtRlgbm5SXVNXcad2w3cnjOw/7dJvk9rKW7ZnAJMnSoz4Akd2sbYyO/x3/mNtrWUJdMUxd7Zsz5g0vgcw970D8YcvVdfUxR3Km//+IQRSsl4DnjUvLv1UccWd2rUbUgGXdpJVwK495y5fKff37eTaWWJWaWlh4jO0fU7ezdT0Iv1/DwYazIMUtS6xygdZ4n/jrtEhFvM9dPqTBBWpMf6otsMx0yic97FTc1T7shqsTAAq7rIhlz6a9+MzOhFVSLWIahGrLzBNsCvXv5MmJOk4oCPpzoxQeU0ljmZJLkorAFoIQqS6tweITZeXxKTJy4HIJIAfZhPzDt49GdgFy3pGm0yVGqUVlQGYKSqCB3QGiPlHXhKlbgoLVxC7jdNxylEiZ76Hz2AqzhI+jRkL9RpP8jGt39gqiP2Jf9gQGLNjvLeXy8B+bS0tTDRVzjhz/fiJq4MHtevSsRVS7cHSlX8DwaO7Au69HPo/4XQi41paxjV3N0cgNl4eujcmNgcQl9d3wEdTC8UX4pC4sty9dSI2fp9uYmw0a7okspGvd4cWlqYG7cF948EfizUYVUsDUyOczAlwIvopja2m/cOPA4l6ihBngrWmnndrye0gyYr78ig6CHJW6d9JEyKOVLnkTY22XBPHMOslgM071Dyt0KzEtG0BUCKIlS6+lilMw7fw6V5uVTK6H4f+w7H/I28dnq4qHWmmXKpzVxsA3s4KpN8BYopvqam2biuO/XF6kjWbJeoCMbn5DJ2AZQ/6BXI6S01DVWRaVD0Jfyf+0y9TbpVXj/brfCj6+WPxk/MyZigFMxOyedtJYMokN3Mz48CRnS/klsYdyrt4qSx4tCswZVJvpOEZbVtaACU35Dov8bWNtUK8cD0HLE6xo5aNW07U1NZNC+0tDo0m1mkYtAf3jWYsaq1M2NKfkkBqgyUCsSaYq6P5fTBjNIu/mKv0PUBUIYFtWdeH0Mc11jQ2wixKvl4239OQTpqQuL/4JpI29iT/SthEHKV7cFsbfDzZuprtazEx5sfdxB6uX8/llQD2gqCAji3l5UBlDfO34fwaz3/O9wlcKaWNLetUMpg1mLI7ygOwt9FU9x4is8oQqwuEVFbdnf/+IeceXz0ftuf7n05duXq7jYPVuk9GKHchZfv/zt6prJ38vFuAX+eWNuYHEy8B8Yfz+rq36dTedvJEt5raum07TgPiWOP2reXf5I4OLWTlTUhhUcXuvedbt7IMndALCPTvnJN3UzXRmYF7RNOIWlWjq/vgr7W8F1M70NoME5VIkP/RGtkrvYxNF5lxguBkftSsZs0qx0lDblr9O2laZi7i4w04OfLtx1yTHn3fPMmBn3jpGUyM+SaSMD2CMSqRcQkgQODUK74+q6iHqahmRzLTvpLEe3Rrui8YsXrBz11e4t9HU917Qk1tHdIFJuAxQP13dcWd2h27z02bva/PkO8Bpfy4QkrLqqL/uNC1k92USW5AfEIeIE7ws3q5z2NtrWPjc4uK7wAZp4uRGgOICfDrDJw9p/UAtEGs+yYNmBbaO2hUl66d7AxL2vtJM17VhjwGMDyRU2UAvfbTO54TNwFOq9t+iknzYWYnbEwAOqmYhd2qxdUaJ3OAzXlMaY+VCVYmLOxG7GB9O7l31IlY+DFufnyykSNpXC+lppabtziZyVfbeDKIGe+qT4+mnR3JAKsm4+OGqTG+vVk1GWDH35IKWWsQ/cRMP2wsMDeR2A9kSddDNXehcZJ3W6JkAN49MTXGx00ygCZBLEa1iEWkqXPnzOhvY23Wp7fjmo98lSqIkznOfLmvjbWZuZlx6HM9gazsUi2v2LwtAwgZ0w1FUfvMmG5Ic1sgjSe56kMfnyEupiZGvsPar/pwOLpssPSZlCoJSfmnzhZ7Dmq347uxGLQH95d7K2rFRlHVY0n1Yag9gJ0pMZ5UBLFvcGOjHYqz2iSXEF8M4NGa07eYngYQ0UVjq9DjTHycwgBE4ySGBEJWZZHqQ/ZIgNUXsDUldyTFo/F2YEqqvp3ca87lsOAjPJ/BsT/m3bDrQ58AZr1Pqq7MlZrY8Cd/ZdK2FQfep+ZH9i+mjS1/n2d9rKTCyj0AX73CrS1UbePzqQBLpSG/C24AnFpFQUMTQGxN4K9MHrfn0H+o+ZED7/OYHcDdpvgOK7hSDpz6e1rBmZma6oij6364aMitS+EnDk9VTVq+8vNjwFef+t26FF519a3PP/JFetKl6RWx8bmXr5SbGBtlZpUUFlUARcV3Tp4uBq7fuBP1u0SpvGHzib+OXG7bxurAnudrrs3d/9vENg5Wfx8rWP9tWiMnpZb136aZGBu1sDS9eKlMdoZm4D5wb0Vt5CAiTmK5h2WZbO4PsKI3C05hu5dNF1nWS1d7rZRLPazSbgK4twRIvwnQT3PkhNO38P0Lm70Y/YblHnYXgsBbbGkmtnux2Su5XXQGp31YRROYTGGVjk4eFOHTyE6g+jwZscx8sSE91Nbhv5yPo7hYTM1dcor4cBe+H1It/Q1v3M/LGziew51qblfx93nGrWLnEcnTmZvIvCJZ2zaMOhGBH/NlLFdvUlXLP7m8uA4EyuLGMHPun5lZJeJloCY+WJH0yRfHrl6ruFNZuzc2e3r4H0oVNn6f/vLsfcdPXL1TWXu7oubvYwXjXtwtS5um9hV1IsnBl3hJKyb2QC6w49fM6hpJ5dq7Iv/xOz/+/OjFS2U1tXU5eTc//ORv3+AdsgoNnpRaZCtZYc43MQVnZkZHPrP0XS/VJHLuvRyWvusVHflMfSX7I0+/+xyMQpOudt9gdnng1wZLqUgvGCWR7saQp9VuSSfxQxCNY0ZHBrRCNI5zI3A0Z7ILonGUqUS3ejSwsWLlu+QmyksiwpQDfa3/8MGNr+no4IDoJ0PE9KZnYkh3Ucm82uK5SsnWkLr5ikrmRX4bpPQo8tsg2dP7NdJmgNBJWidNs6qtqlPoyBiJ+nLycezNiPakaLQkRpejOXfHIRrH3XGSlLcNZn0OwNf9SL3JsRt0s+HaaH54EiD6UdwYmZpw8GfenkFHqVbU0oIPIgD+TqV1X4Y8y7USXp+Mj+cDHGZDKPtOogu2s8LYiJ7t2DwTIK7+kTEMaMLG2szZyWrhW08BKz47kn6qWG21C7mlfj4dhCEyjI3w9+148ZKyUsWAWO2uJ00jatNu0slKfutqLXGoLa7G5y9so4k4SbQnQGmN3IJKaD7VAHYW8GEmt2oBgo/wSwG3arlZww+XmNmQVFL6ohTmpp9tE3eoibCJPOnOxcv0l67ZA4bTqiXA0rWUlpGUysKPAWY8NAEM9WRrAsBXr3DjW+5u58ynjHDnYjHLdz/okT1C3LoUfuXs6/2fcDp+4uoHK5I0VYs7eNGhdQvPQXLjbc9B7Vq3sow7dFFTEwP60DSidtFp1vXB1Rpj6GIlUcgCGU8zoR21IkqqsTcHiLmKW0uMIawDieoCUNWLJWex3QtQWMWEY9juxS6GKakKwWvuKbs8+Ofp+/QusZ/Y/P8jTep2HDAc4PYd4qQqhX0HAYYOuk9DairCt/Dm9xy7wO0qibL4898ZuEjiWmagSbh0+dbtipq9sdnBL/xae1edJwkg9VsLGNFJVhI4sjNS8wkh44O6xe1+riRndmVhRO6JV9csf1qWN16sanDv5RC7a0J5fnjR+dfXrxohzCqvpS3g7GS19avRxRdmVRZGpByYLFZ6CLfqVi1Ml78/NCv1lcrCiOy06cveGyKLhSauaWpitHaFb2nunOILsz54ZzBgb2e5/Zsx5fnhJTmzt3wZIDOd1tKV9oloia+mliZLTh76OCt6086SgkrmZ0jiz3q2ZlM/erakVsSkY+wuxNGc7U/i7cjZW4Qe12aV1SwQr0brGx63YZ0UHOExJxz6UXJTUnLuAN06sT8JP+lpmLERd7OpqcW8HjsbAwZAKjtad1pXnD077WTRwKe3ictTD73Up3cb5+5fXcuahTS8ztJ3vd5/e7BSD9//dGra7H2yrq5dr2jjIN/tfr3lxMy5cTrb2libpR56qVuX1qggfrW5mXHivheUMtX/efCi//idsld/veXEa9PkhuJTZ/0+65V+Tz0p9+77bntG2Jw/tHelfSJK4vVRSyyvPcnN/U940yTv0rOTykyF9DbObSTnYGJ1rRgrS0Q5lGuOtmPAgCZkq7Oj+1+sLZ7r5NgCcHayqi2em5YwBcXlW1neG2JDY0sLE3MzY28vF1HJvJKc2cKuEn+f5NrZztLCZOm7XqKSeUXnX9en7ZIFg0Ul87LTpnsOfMzUxMjbyyXv5AzhqxfNfUpUMi/pjxf69HY0NqJnN/uEvc+LSuaJIwiLa6Yeesm9l4O5mfHi+Z6iknnl+eFJf7zQvWtrSwsTcf/iwWjvSudE6nVO2IxdGBqDqnRTDczo3pLYwZSPoSiA9X0k8WWENZXqi7EyYXkvsvyoHEv2SJb1wlxx5+BkzqZ+FAVQEcS2J+Xd6kQcINFG+uUqO/tKOCKv088NIO+yvn0aMKBKbHyuibGR2GktYERnE2MjsY2aENsOXxjZf3r0+JXQCb1W/nf4upUjELjbiZnz9v6snNLKqrtrvkpF4Hysve34oG7AjDdjk1Ou1N4VJSTlz4iIFXY7MaQHEDp9b/qp4joRZ8+XTJu1D5j8vDwf6qx5cRlnrlfX1K356jhgbWU2IyL23IUblVV3V31xDBCvUvXpSstE6sUDDg1eXxq5Va8X8UNoYwFgbcrrnTE20n3aZm7EwSEMkm59OlvxXnc87PCXul3ZmpLkTVdrye2LLvXw4zh9nsEDGOBOwlEQKGoTU+R1wl8GOJisb58GDKgSd/Die/M8A0d22Rp5WixwVRW1zk5WUT89o7T1NlHM7ZFxRmLkII6VI3uqvW1319ZAQlK+7JE4goQM1y52QM6JV5WGJDRfSzspiQxZflviPXn6rCRIoDAijz5daZlIvWjeq1pjmN6R+CEUj6Z6LECcF30abRIg5lw53eJosYcPM0Fd2jGlQI7AfFcGtebvEvoewOQ3eu3n8HVGOhEmDcA435Wu1pwvZ9AhzKIYdrgeo935O8CKd3BywNtDkhk3er/EE7eTC5tW8HwQVdWs3dLgSRswQNLRgtsVNf6+HU1NjPx9O1ZV3xUKPjFrP/Yd1N/5fPaNT744NnXW772e+k61H02Hb/q0NdYszjQ9Eq6pK6uUPWrUhpTTpys0T6ReNLNVrRBLY6I9GaHorDGiDYnD8ExoggO3Oelk3QZYc4H3e0gsKLQz8XGA0BRy7wCcLWdaKhdGMrk9m/NAGrdhRhoppQCJJcw8we/KJwTqWf8Dk0MYPICr0mXs7Tt8sEZynXMY4E4lYQs4e0F9DwYM6EN1TV1CUv5ov84zw/q2bmV5IPGSquQSx2D08v+puOQOgmgMpiZGOgWT9rZZ2aVPuDl6ebSLPyxZzHp5KIQNzc272au7Q8c+G/PyG/tH3oRd6aQZr2qX9GBEGw5fp+uf8sJPs2hpytKeTdB/hvSXX1IDqIkfpoqrNUCOv/x07sJIQL50FVdIFuRhVI19ronKKnxD+Xo710q4dZs/E/F+Ti5VL17mq230CSCycdbKjwbiFJCyHwP1RawxWBjxFBAbn6taoa5OBAT4dTI3M+7n3mbbxkBxuV0rdVnh6tNWHH9nw2cjPQY4m5oYeQxw3vDZSGHz3XuzgO3fjOnn3sbYCCfHFiv/6y0qmRe3+7n6TrORXd2tEwGyxBbaacar2lAXgBlpZAtCSq++wDxXvOsX8Ej55EpMbf03DZo2PTKFrGqFenmxl5Yx8z1mvqfmUadGGykbeNgQlcwTmxDJLjRVaHLEDguPP2aDOkUtEPX7hZeed/thQ+APGwKF5QP6OsUe0OHsoL3t6i9TQif07NXd4Uic+ogeK9YcDRnjOuSpx/9JmCIrvF1RM3/xQb3m1nRdFV+/07aN1fnjr+Tk3dSU/UhGM17VOlsCnCtXKBRn2LXVlY5FvMWRSUD3JlLv5lYAdIxVTsMj84sTj1b4TeBpr9KLAQPAAzXVTD9VfPVaBXDjZqXa8OFzFuz/6Zezt8qr71TWHjl+5blpe8SJI5UyRapFe9uKO7U+QT9/u+3ktesVVdV3/zlZ9OKMvcCdSslxVtmtai//nz776nhO3s2a2rrrN+78uve858gf0zKu1Xeajewq/J39F3JLa2rrLC2a8ZpVN9kjEY2T7M1l5lbi0DNZfjraFoxCNI65XbEywa0lR73VGHsJ0fRUnADCVWpRsLwXonEkDqWfLcbgZM5KN0TjiPOSVFjUDdE4ckcy1B5TIwbacdq3aYxzTU2wtaFLe0LHEb+9sb01dx4xBYIm481/SfCXDi4tRSXzslJfedADaRTNeFW74zLAhr50klq5dWjB2icAdutKeSeu8Kk7t4M45UtxQ3OLiBue95OEuF1xnjO3GOLAP09zdxxXR/N2N27XMl+adnBVFsdL6WjF4WHUBHNsOKk3NXauSj839n1PaTq1F5QDetVkcfMkFxL4cQ1P63fOZqDxiM3dCzNfL82ds3ndKDtb+cn1iv8MK74wqzw/PGHv8z27STYvIYGulYURopJ5pblz9u18VnYipLZcKEmnTHI7nTytJGf29m/G2Nup14cuWTC46PzrJTmz1yx/ukH2SA8FMgcHO1sLsVvB5nUBQOLfzdtcvBmL2mWZHC9lsD050mCMF/0Z4sCpMpbqSgM6/xRf5nCtijt32VtI2D866msiPJ0Lt6mpk0SJLKvFK4HPLpBTQU0d16v59QqeCaRJffmrRfgk8mUOV6u4c5efLzNTWwBoBTq5kLCDUd60aomJ5v+3zGxeequB0zHQAObM6O/lv92l99dlZdWyA5y5s540Nzfp1Ocbx65f7t6btXyJRJUeuTloUli0kf2n7Xpt2L03SxauUFO5jPDXBgRM+MW5+1dxBy9GvK5mkz5jap9zWSUubl87d/+q9m7donnNLbyblK2Rp4GvPvW7kTvn7vV5Z468PMK7w9VrFVpC5Bi451gas6gbaT6Uj6EiiNO+LOslyUPz6LFmCaIcsg7RpyfGRhQcQZSDkwOWFrh2ZO50StMR5TD9+Qc90IeA+6ZAEJXM8xniIr7u4NJSFjk7PXGqSztJKkp7O0tZedH510MnqLGPUVsuDGgiy84rDhGgVAFIOTBZFrGlnbN1891uGxsR/lr/o/tfLM8Pry56Kztt+vpVI9o5W+tuacBAk5ARiyiHUKlWN2oTohz8h8krDOhNxRnKT9PO6YEM8CHifopaSwvJd7uxEdVFkg1FRcGbsi28sDxwZGdRybyo7SEhga7Bo7vK+lFbLhS1wnBTFQVvKlUQF8qid4tK5sneaOAh4ZE+OHu06OQCEC/dRaWkM3YEQwfJU5GnnmLNZt6dxZypLDLkL7hf1Op/5PjoAAAgAElEQVRhr2dmKhGUMX/m9B32vceAxwL9OweM6GxjbbZ951kt5TLq1Ho7CTA2NjJrs7pJ/JoMGFAgzoutug1LHh3ER2GytVKgD6IcEncq1HFzRZRDavT9H93Dxf1c1Q7oI9lEONq3KMyUxHxSUiCoNRXo595Ge7lwVSvzyreztTiXEiYrl7XKSJpq2GU/zNzbY7HGfMp1tu3XilAX9d4HjyRltwCMpf9jKScBPPpiKtBNn8sB6NIBA/eNNR897exkZWlhEjbZPflYgbhwy/aMubMH2libWVqYTAvtfe26xM0mLWGKOMM50KlDqypppkxN5TLWrvAVvyX0uZ7i6N1KbN6WMWVSb6sWplYtTBdGeMTumnCP5mugYdxbBYLRvcy8MuEoq9zZ1J9VWZy9RfWjvnPKL6R1K9q1Ja8AoOg6N27SuhXuPeR5GSwtAMx1eXA8hHR3JnggQ3vQ/THatcbKgupaSsrJKiQ5i5h/SNRlVaInLvZMG45/H3q2w86ayhoKS8m4RNxJdh5pSN6HXXvOnz7ycnV13Y5fM8Uh+IDV64+v/K93XsYMID4hz/8Zye4j9NW96z4ZserD4dZWZlXVdyeFRWsvlxG5KzP9r6mWFqbxCXlhc5QT+orfuPz9obnpr9pYmx9MvDRlZky9Z2LgXtLEa0KPrqx7mT4dKS5jUSTfz8LoBUQ/8crXrH6JRZGYmrB8EpPXsTsFwMWebXPw7EZaLpPXUV3L9jdwb8/0jfwvQoek1m72fz/DLd4fNn7Eq5N4dSGbfpaURG9mzNO8t4rlX0pKgnzZ8y3nc+kuSMMzfhC/zJXfZl6hp+BWC86tyF+vYFjWez6n9bNuPLuaHoJYaM+uZtcx9TV9e7PkWYbrylR/vpBlu9h6WEc1MUpbIvEHydKM5ZOY5Y8m156au2w5xKJIivWOPXLvXGMNPGI0sQJh80xW/IbNVII/Yc1UeXllDT5LCR9N7jUmryNScoLK+ldYH4vtNFb8xqbXWBfGvhO0e50+hi2wCtt+BVi+AF+ph8KmSICFrxPsh7kZnv1ZtxQg6k+FhjFp3BSEiejxmL6/3oB+yga8AX01VFWkX0cFOXuzguhUNdVsW7D9DfYv1i1ngW7OfD+LxA9waZArc7vWJC3lrUCNchYwM+FVX859xrThDXmFAQNaaGIFgs9SZvoxyYsBnWkt0NHvTKb6Lj0eIzqVOpH84+7jxljp0VZVLdW1TPqcimpWRvH+eB3vevTWrdpJOMq3P/PK8+zfjlFngN2x/BzN80H89o282oU8+SJXTGUNUcd5SWAWNuEp0vN0v1FVsAb0Y7UeG9MJTync7k5BRfdIBwdi31OQyPowpAepHxHwEam59Whlb03ce/R6XHdNoLU1383Ezoo1v9dvbAYMaKGJV7XRCyi4wfo/CVyhUC77S1MyWTE2os0MjF7A6AUsX5KUIDj8MSBk+kJmLyEzW14SGs5bH3LqPHcquVLEN5F4hsjzPMqIVHS0maiH566xEX5PKBcO7YGVPnF7FfuPVHHzcW5Fwgf1lrNi2tgStxg3/eSmZABv6itnZXw2hel65EI2aA8M6EkTr2rd25OWS951Vr+kV/3Es0SMZsn/mDacMB/iTrJwHNsSiXq7acf16LD+B9b/IL+tE7FmM2s262gVm871chwk1kf0eAx3FzKUI+sr4NFVXl9GC3N83IjR6kzcryPdBKlMrpURm65QwdSY3fPp6KjUjr8yifybpEzyrlN6GxtLnGzx7EZAPyY8hZnA0KK1NVFv0+8dyqu0jUTGSMXvjN9S2JpASjaFpdhZ4+bChKcI86GF4rfI6inEppOnd0BhAwa00MSrx4U/Efsep1eRmqO7MjDzW3zcqNhKeAAzvmHOdwztye75TF7XtONSj0i/QTZhwwdFbR27jiqU6FzYBvTTUK5LXavU884jyluZheN4ylWh5MRFnlrM0A9Y9wepuRTforaO0grOFbL1MKFf0P0tflEcf9e2rAjVMRJVLhbz9IeEfMquY+Rdp/ouRWUcPM2c73B/m78UjRxaWrL+4fBuHT/qnr9C00e62X3UH1r+NVapTYooR6ItfSAMfIJpExg6iE4uWFpQVk5WLglH2RRJluagzL692b9YfnvmMm7ztb0l+UNlgSjmfCHdtfp8nvtMYVU7/L8kCPyenGzJXou1IHXTj4lM+0qvEOkLg/lIYJRScxe3eWSpCaaq3ij7fCG+H5JforF/SzOi3lZeAvd9Ry+99j3lPnzeHuxH+t+AQSfanLCxYseXHIti9hT69qJVSyzMaWPP4AG8M5Oz8ax8V2MmiIOnuVIqv+31OO4uGl9kb83ALvLbEwIJ3s2ZLppjLChpDy5dV5CzQHiAgpzdk8rkL/VNRbEiis8Eh3JmJkQEaq6tyJ1qgj/RJmeByhomfa7wWwJm6op9bMCAPjyaojY/GUdphvBOLpRlYGMluW3nRJ70lEa4OQrxpzITUQ6l6ez7HjfFBZ23B0d/ozKTrEPMVEzDEexH+j4qM0nfR6CPwiNRDiH+ZMRSmUlGLL6DmTiGs/upvUBlJiH+1AtjI2K28FwgN27yn894MoiWvTHpQqsnGDyeTzZSXc3bM9i6Wn3zOhE7FZOWT9AcZk/JzGvpL4pPNesQlLQHO1TSpE/xll/frGDGRo1dqWXxz1wVHPqFDsFUv4/w0l84W6C7WsltFvyoUDJ5GJaNdgkR5RA2kcJjlKazeSV2slxzHYnaREma5BMi+0iII7SVppP0i+RTKo5KLL4IHUfWITWfoiXhFB2nJI01SxS+cYW9ofmjHhFGbqJyt7K/EU1TMPCvZvdGudRbNJvCY/LQgiH+7PxKci0UtbKPl5UlM18kfZ/8UT83shMYOhBzMwKGU/yPvKG3BwVH8B2MuRl+Qyg4wtCB8oaiHFL24N4dczMWz6Esg8SddO8sGUZlPd2fZryAKIe8JDq0U1+hT0/J2MR5y1Xx6q4QGeD0Ko3v2jpLXi33C4Dib+QlWg4ts9YovEK4NAYGdlF4uu5lHVNWy6rJCp34uKmpoxQDoXwLti3UVFOLuQlFXys09250VlBxYIou7bGxYs0SIr+QlKfsYeaLWFpgY8XC1yk8Jq+/4f+wsqRLe8mtsKvozZLYQ8JP0YwXmDQWczPMzVi1iMVzFJoIe1P7URflEPejpIKwW6GoVTsFA/9qFs3mgwjJdfo+gv0k3+fA8rdZ8JrkWvgJrr2Aj4ZV3o4vFYRX2ER5w+jN8qiGwJTxRAuMAUQ5ePaXXNtYIcrBvbvC03qR/CuiHCaM1lZn+vOIcjgYqbFC7hcKQkSTyVThBnmdza8B7J4nLyn7DnN1QYEHdFLo/NxnyhXmB+mWkjrxc1foZIk6+2slUbt1Vv1eseEVheYLgxsyToXx5Mg/XR3aUXBETR1zMwW5JpZ6slvhtdpHKXuwkiZnaOdE1iGNTYqOK3xoZXVcO6rpVnihcwoGtPBoKhBS0vHoC+DencoqouKwtJCIOc/+HD2hpsmGbfRzY8p4yepVyNCBxCXKb2MOyK89+hKboPBIJlvFyKITlFcAnD7f0CmBew/lt6uy83eAAe4aK+z4W+FWrR3CgE60bSW/jcsASDgjL2lpyVB1Cz0ljYSqOe0AxYOXjEsax6mFNMWjvz4dNdQTkHSufq9IyVa49epRv+ZqSZZm+si/gqPA4c2zPx9EsP1zMmIV6udqNsXLVvd7c3Pl9hmJnuHyEeWtj7C3afP4cQ1RmwjxJ1igic7WdfqnaQoG9OHRjFebks7APgCh44jcA7BjL9MnEbGUfm6kpKtpsuAjNizH14vH23LzFl7jOZ0leWRvR6kgCklRsfzazpYSwSlKSSm2iraolYqGn7qCjmqjuARrXXb4tbWAQqwvJSKTeHus/HbCU3ywU7mOkplXvFjUKp5uBfQl/pRyw4m6RK1rW4Xba/VU1KpFqU+1HM3SXUeIkqlih3rmuldLba2awunPM3c6qzcRm0BBETmCCA/1/agYG2PmSq2KV55qbzEH6Tsaj74EPk3AcGys2f6bXm9UOwUDeqLXqtbGgrVTyf+Syh849xmLQjQecwt5gJlKS25SVk6X9kwMIjIKIHIPoePo50ZBkWSBqURFJVPm4uJJV29mvU+kwLC3+Ab2dvJb25by67JyhUeO9pQpJktvQnbHgnRtqwm/oQDpZzVWSM0lU5DjsreLGh2C8NTrVD6FNwFSsrlxW1BHxep2QCe6CqTeiYtqAtO4NIXMUsLZTnedwvrkygTl+F72Kq4cDaCPdB9gbyf/el71Ht4T2fQzSany7X/DyLqIk96/3vSzbPqZGe8SPJ0f1+jbSu0UDOiJXqJ202vkl+A2H6speC7G3oZleiSwuqcRFHVyNI35M8i7TEERQG4+2XksfoOjutImZl9i+288IZBo8UkEjZDfCnPMJP9DgCA0ScBw9UvmJmHtd1RVs2i2xu85F2fWLAFYpXW1qKRDUIpXYNsCz27y27iT8mvhMvaJ9rRrjRCdzrjizpscGz0kVOlt3XW01G8SUbvmPzi3wdKCsInynXhpGQHDMTejnxvbNIu8W7dx7ahDkm7ewZRnsbLEypKFrxP7g8aaaTHMfFFik9PJhSq900WrnYIBPdFL1AYNYN0flN2hTkTJbVZGEeZzb4fVeFJOMiNUsjMSE7mHZwPUK2pR/PyF+Ct8/pZ9wQcR+A/D1ARvD1a/L3+0aiMr38XHE1MTfAez8l1WfKXcc1ORfYnFqxg3ksSdTA6hS3tJdForS9y7M3c6qXtxeYz3P5VobDWx/S+FWyUR6d9HwQVWKF6FYhcVky8lkR2pKNDFNN5qqmF9VtQz+bxS/SZJ9L1rH6f/JDcRl8eYJnUeCXubJW9Sfppta1j1jca2qzaSupdsrQEkV2/C1obcvyj+B28PpmiOkxkazsQxFKYgyiHyCya90agpGNATvT5E6SvZdZSlvyhoc859ht8y8q7j7sLJTxj8PslZdHFi37sSbyLRT5KFbchAIt/E2IiMS4RvITFTTUmTM3QgcT/iPEiuZnVuQ34yHsGkSsWH0EPGzZV1S/Hoh3ULqqqZ9IZkwy7Goy9rljDAnYKrLFvHtx/LG4b4s2w+rh3JusiilUTFyVspeeBov1WlMT6R2ntOW0FfwWlSr3lym9NNM3hFGmal5i6Or1J2R3Lr2pbzgpXX/5KZ+LnkekAnjn8kf3QkC0/BF5KMyh+0xTBsGDV3MZ+sXKikvLKeWj9pa9uCmwJLkiultHu9oeMTj6f5+2I9AlN4sOj1wZ+2nh0RhPmw6yjxp8i9Rnoesen492HTAfz7cLsKX3eSswjszz6VHXrkm0R8z8b9hAxk80y6v6WmpMlJTMFSUa1ZeA3Trgolwo/O6Sx8NfvUHz2B17Py28075Ne7YxWEsqb+dd7eTyKTFETtRE+W7pJc+/eRl6fmyOUskHWVi8XyMDF+T2BsJPn2VVoaKy2cZVRUKYja1q9Qqk5v3uRYWdRP1NpZKdzWV/9gwIAqeikQUnNxjWDiGgpLmTWSo/9H6BCJqAX8nmDtPnx7AwT0VQ7jBBw8RdAAfHsTkyaRqqolBlQx6tzwH+0o7e4DpQZqPdvRXqAQVNIYKJW0tpYbb4UMkpffrVNWB8tQOm7S4uDbtCiplXWidHxXcs+OOg38e6iHXW1yFiuiCFiB9wcsn0R8Bt69sDRjYBdWRjGgM7Yt8OquxgZo8pfYWBL9DkUbGdBJfYmB+0nuNY4I7J/E/3eAt2I2BNX/SiXhO7QHQM92CpFnE85oPPHPLVK41ccktknQxyBMiJKTm545frTwCGy9H4EpPFj0ErVFXyscPqTmYNuC8irOFjBnFGkXKa0gLZcFY0nNUbNTK76Fz1JspxHxPdHvqC8xcJ8RWgiYmUg2JUMFKpc71SSqGI3Fn+KuIDSMWDQHP6lQR5P2AJRDZCk1vHd4qAtRpgWv7gq3yY1wPDFgQIxeojY6lblj6OKEqTH21swZJflz2pfGwnESE/e4DCIC2afufD/jEyY8RW0dJeUSuxnVkgeLqcm/LpjkjmQFoSlOuCBc1SadU5OlpqhMwcVLLJqFErOqlp2aXTaFLmdAQF+c6h+1JKg/uV+w8y0WBuPnrqxXVYueKdHE2FsrfwcYRK2BxqOXqF0UiY8bKcup/IGMVbg4MHcrQGw6DjYSx834DKwt1Chqgelf88EEqrcR+SaTPldf0gDCOoCuvLl6knZ4SmOai0rmNcEg7i8FNxQMP/yeoIODQmYEVUWtpDxDft3GFu+eyna4Wk664k9xR7DpaWHOBxPqOW5Y8iwdHXnWg49e4M/32DBdd5O+HSXLdn2Y5a+QjuFicRMoEAwY0MsCofAm/svVlKfmyv0UkrOUfRaEj9wVY0GpljSAb/uzuYliNvfuqZJ95V9AZJI8VW2PxwgdovBUKFIVyk8yTxB8Z/kkhXCLWrQHQEU1O48oJJScMYJ9J4g6ru+Y5wYySNGMRPsbZaydhsd7uu0Q3B5nUYhCyYY/NVRtUkyNqG2E07aBh5/mFG4msC0FoygYhX8byWJW/O/GvlQEEeIM4NeGfH/y/fGyl1TY/iRHvRX6CQl0rSyMqC56K/XQS0M9HxevScX/egxwPrr/xcrCiPxTr02ZJAk8JSqZtyB8UO6JVysLI0ICJWo/ezvLuN3PlebOCXtRc3CXh5udR6gRqAjmCgTojdsaUxYlnKFK4As/RKDevV1FVIqOl67eq6C4MDFm+xv6bvBDBionublYTIx+bku9Xdiq2dFOjIs9u+crLGnvVLNxv179N5I0H21Pm2TrZuDB0pxE7Rp3wv5hehrr+kgyk4v/3V1IaAqRgwDWPsGkFCYcY600bcnmPIYqutlEbg6KePeAZdvPln2SvHndKHHSU/G/m9eNWvHZUdv2axcvS1z9fz6yJk5trNw8v5sUFh25OUhcsuKDYdv/d8ap2/qB/et5vP3QUHxLomcX00agNj14WmPwkYpqjbrLmH9051VMu6i8DrW2IHoBK0O1aV3NTVj8DDvfUvBkA5bs0DeDA/CsB7GLNGqHvXuS+F+FFBLA6r2U3Bej2t5addZGv2l7aqBZ0JxEbScr4q4RV0Qnxb/JmKvsLsTCGMDVmsPD+NubPtLPbtw1qhWlxsHES/6+HX29O8T8md19oEKyWXev73dFn6+uqdu247RDa7nH/rJPkivu1O6OybKQBmoNHNllx+7M6pq6ZatUMg00H9S6z6LOzEuIJjWunnv5iO+5pJiG1sSYt8eS+wXrXiZkIB0csDLH1BjHlvi4sWwiWZ/z4UQFTQVw4BRbtfqqypApiEe4c+4z1k7FqzvOrTA3wcWe8YPYNZdD/1FO4nvsgpqwZ0qIxjGzE0UBFAUQ4ox/G/L9qRwr2WN52HHUm8qx5Pszpb28yQJXckfKqwm3aJqaaGproLnQnIIo5lbg10ZyIcZGJVpg3h2mpJKkNYXU5BkxO78fG/3z+Orquz5BP6emy609He1bzAzr26e344C+CmvV0jLl1ZpTG6vy2zVA4dX7seyReTk3LbuOsmG6GmdZTcJU9vTDicqFNyv03cuX3Cb4ExI+oKVipJhWVsz2Z7Z+eYAuXZe7Betk6S/y/I+trHgjgDcCdDS5cZvJ6/RaMrvb0uVP/J34oCc7LtN9P/5tiByE5R4292fJWaILCXVhtTtbpcYbTha4xcurGf2GaJxk6aqpiQyltgaaC81pVRuRwZYBbO5P+EmAv0soUklJEH6SnYMoDWR5L9UOJBSX3PEZu8O2/dqIdw9E/6wQxD/652eAzdsygp7/VftgCq/etm1pDjg63INwVfeLsjtqHKkvl+hIw3X0AjdVzAx2p6gxDtNE2kX8lnGtTHdNtZwvxGcpxbf0rb9yD7/riugm5Ho5AR9xrlCvyh+cpfwuu6/QtxVrs6m4K99juR9g1xWqRWzLx0GgAl6WqVBNiKYm+rRtKhbNflg6eZRoTqI25irO+2j3B/uKALwOYxUtV2OJL2Ku0u4P7GJYdEZeqERG0tQJwd1ra+tKblTat7YEamrr7O0sAbceDjGx2UlHC8Jf66+mpYCo3y9MmeRmbma88K2ntNd8yFGNdqhdewDUiTh4WrlQT+2BjKMXGPAuB3S9S5Vfj+G1hOwi3TVl1ImY8Bn7NdhUKHHyEl7vc/SCvp0XVwOIl7/lil82juYs7s6OgZwdoVBeqjnGtqYm+rRtKv6vKaJ2NUknjxLNSdQ2FdPDYz9YOLj62tzIzUGTwqKBmNjs/NOvAWFz/ti2MTAr9ZVzWTe0d7Low8OBI7vkZbx2+ux1pUf5X+IoDR/eqQ1l32EjTcfdrjV5gqDjwU+SvpLKH0hfSaBisG3RT8z0k4Rjj12k/PaFwepzajWAqOPcVtSO6BS1qJiCXSvToXNQS34Jvst4djXH9JNrh88y5mPGr67HelZGRTUBH/HhLgXDXiVu3Oa9nxn4rr7rWZ1EewJsziNIb31+A5oYMPAvZfc8udxcFELhBqZLwxKGDGSnNLyOd08KvsK3N+Ym+LlT8JWCX6zoJ5KW0sEBe2tJekQZC8ay9Ln7MI/7Ss92zA9i51tkfELJJqq3UfE9BV+R8Qnb3yBiNN2b6BTIxZ7Fz5DwH4q/oXob5Vs4u5rtbzBlGFbqNuzaEZphqV6XjWFAK2xNWd9H4WhLtUn1WOzNdDfR9Doxs14iL4nKTOK3K+RkDPYjfR+VmaTvk2eSFuUwfhTnDqjJRi77keE/jLQYKs6QESvvob6dGDDQxCwKkTtBpa8k+EmSlkpul09igTS1V/QCBa+BKcOIXiC/Ff2kLHnFzA9imcqRlIEHhXZRO6Edp30pCiCiiw5xuduDiiDdTbSI2vGjOLsfN1csLZgzhdRoSXnAcAqO4DsYczP8hlBwhKEDAUlmZbFEFmYjRyVQskdfCo7gPwxzM7w9yEuS5yrVvxMDBpoe/z7EvAPg7sLRZQCpH+HuAhC/WJ6Ru+hruZ4BcGxJsSAOv+gnhUWWWNTODSS7oX7MBh5t9n2vsN6UkbhTIRX5lPFEbwYQ5dBPkBxeKf+5kF0bmCzwoJs4hl0b5DX17MTAv1FXe69JuSCJwhc6VHLotONvpvsC9OtEilQvaWetEAi1pFw59ZaSF2nEaJzt2HeCacMxYECJAe4kqnPVG9Cb2AT5bcwB+Zo0Q7/sJ15PEh0vv409jJcgHI+enRhoTna1zYWS25TdoYsTEz3x/i9A5N8cXcaWQxTckPtTld3B3kZ+wuPYUiHlgSouDszfhr01if9lZ7JuvywD/yrsbNVnazY3V8huW1KKrTSWnqZM5krY23FDMWKf0LVaz04M3KtVbeODXTWsB6X4BmgIa3AuJaxTe1ugS8dWp5OnaWrbYI5eYH4QecUU3ADIvUZ2EYvHc1QQkDv5vILvf0Bf+YJXLfO3AZTcZkMcS57VVvPhRzRO8qOz8P4woZ2awgc4ngZQflsuQ4WUlmEvyN/uaK9eImuh8BqtnlDI8aGUOMqAPjxqCgSl+AZoCGsQd/BioH9nINC/c8yfOZraNpiUC8wYoWBqGpnEsx4K1pqroln5Ij5umBrj25uVL7IiSq/O1/2B3xP1zixgQC0+DiR7879Bums+5KSdlmsGhCT/Q4BA4xQwnBR1kU6F3FX0kTuYTODT8lv37qTF6B7PXb1jUxioB66d7dITpxZfmBU0SpIqRLwmtbezjN01obIwInbXBLGPAOoWj3a2FjE7xlcUvLlv57NiFyxZD6eTp4mXn66d7WTLTy3s2/lsdOQzfsM7WFooO+2amhjJVsrBo7vG7BgPREc+4zusvc629WVoDyp/UIif4tyK2h+Vs/uEDCTjEyp/IOMT5XDUSjlflW793BXMFZodD8+qVstLm9eqdsJo0mIkKetnvCC3QPDxpOAIPp6YmuA7mMJj+HiCyrGV8Lb4H9xc6SBd6fdzIz+ZgOGYm+HeneRf5eds+ndioGnY+f3YOa/28/Jody4lTFwiFmqb142a/tITpiZGM1/uu2mtxOiusjBi5st9jY0YH9RNXH/Daj/3Xg6mJkYTgruvXeEr7GHFf4bNebUfMOfVfqs+1H0e5Gjf4uCeiZWFEWV5bwzo4yQuWTzfc8d3QVmpr8hErY21WVneG/Z2loWZr8tSMKi2NXCPeHik2MMzksYTEUbBESrOEPsDXdrLy0P8yYilMpOMWIL9JIVapGREGKXplAscAgOGkxZDZSa5icx6SX0rnZ0YaAKKzr9uZ2shLBELtcLM122szQDbluaFma+LH+3b+eyuH4KFi8eCMzPFgUSNjcg7OUPYg+fAx/btfBaI2TFetvzUibmZcdiL7gVnZgLJf4Yuf39o0KguPbvZC/W/CXufX7P86d3blP/IhG0N3CMeHgH38IzEwKNN0+hq7VpZqsa+AuxbW4rDX5XdqpaFZZk8I8bezjL65/FF52dJFp4OLe5enycqmXf3+jznttbCHpJTrvTsZu/k2GJg/7YJSfk6R6Ia38Cth8PO384lJOUrhTWIPXBxZlhfmaJWbdvGoLTlN2DAwL+ZpjH2KrtVZdvSvOyWsnt5yY1KSwuTyqq7ti3NS25ITJnEgbXMzYwnT3SL/nl8u14bSm9Wtuu5ofau+mDUsQdyP1jolXSkQFMFIdPDYzet9Y/cHFRbWyeObyAOa+Do0GL5pwr5BffF5Xy4aMi+/Tla2jaGexHzENj1Q3DBldtzFtQjN4CVCVPaE+xMH1scLTCGoipSb7I9n0iVrFmy9Z3Rb1iZsKQHoS44W1BcTeJ11mSrCVDZgCZqEfajhL0Z0zsS5Iy7LTYmlNWSXsbOy2y6qByMWP/JqmqKld6uZTy2pkzvyIR2uNtiasTZcnZcZl22crgZpU66WLGgG35t6NCCyjrSbrIlT33SpqC2TOuAR2ucLKiuo7CS5BvsLCCqiYIzGLj/NE2m2KjtIbv3ZsAmCm0AACAASURBVJ3OvL51w2hxsG1RyTwj+0+3fBkQ/Uf27r3nwyY/4eXRbtrsfUBG0tQPVvy9a8+54NGukZuDLJ3XbPkyYPX6lIzTxdNC3cMmuw8dHSnrAQgc2XnXD+PC34nf+L2uo9P64OTYInbXc/28t9a3YfpKJq7hbAGubUleRk4RT3SguIxFkWw9jOgn3trKtOH07SiRth0c2DYHD1dSLhD6BRfXyaWwOAptyEAi38TYiIxLhG9RyK6oSnl+uKWlqanjaj1H69manYN4XEOgx9+vEnxEIauVTDRYRBE/hCEOyk0+Ps9CRQVcY5oIpZgm0Tbpcdb3pbWZmvGfuUVQMtnSiI71mqwmpYFOURvWgZW91UQ4vHSHCUc5WqpQKOtk3BG2P4m1ytrm96sEJSM7rjeG7QN5XoOp4e9XCTmi/O1ioFnQNAqEBf9JWPiWR9RPIRHvHhCWz3//0Kzp/couhYeMcZ373kFxoWpgrfnvH1r1oU/FlYjw1wbMiFBOm5eYfNnC3ES4/Gw85mbGs17pF/V7lu6qKmxPZLwHwHgPEPHRbmymEvwJa6ZKKuSX4Pm+vP6G6cSdxHYaB0+rT++6bQ7bE7F8iWW72KxLRXzuwg0TY6MuHVvpM1RnC2IGaxQ9wOi2zOqs/tFyNzVCE3inG/Ndm6yJTiY9zk8D1ctZoFdLYr0kEeIbM1n9mdmJb/urjyTbvgXxQxhop+YREDlQjZwVjypMEBpmvqtEzl6r4pPzvJrGa2l8mcOtWknlhd0bOwUDBtTjM8Ql9dBLuuvVh/L88PioieIju/rSwUES2SD5Q3x6sfgZdrxJ1hqJclb0E+KcODJdbfkWSTQD2xaUb1HQ4Yqv54yi+Bs2vCKJk6CdTu1t43Y/l3ropcCRnWX2c5pY1Vty5pM7kmBn7M0wBltTApxIf1rySCnHpeyYqHosRQGEdcDOFCsTQpzJ8pM8Kh+Dk3nTNFH7ahm2phSPlhTm+zO9I84WGIOzBbM6URIoebSkRwMnq2kkmh45mVM2RlJ4bgTBztiYYKM40yw/zI3UdCIaR+JQfBywNMbOlBkd5V0lDJXXPzdCUuguiI8BeNlLyrNHqhmqgYefZuCYG/XTM1Nm/t60fdq4rG1w27zrVNbg3wdLM1aEsnE/iZkUlJD5maSCpmQEdXUKuRFlQWzX/cHZy3h2Y9c8VkXrSNGac+JV8cXen9UHrBVrXcSMl1o1TkohWRqAt6yWfUXkVnBmBEDPlqil4i7eiZyVehbtLiSphFQfHm+BtSlTOrBKZUvQgCbamdJesn68UolHAgWVkvLCKtbncrac/UMAJruwNLNRk9WTsI60NAW4dAevw5Kg4CjOtKs10zqw8aJy279L8PlLor6orGPjRYyN+KovgLsgh2MHqS12paILQFKJIZlj86YZeIvZdvhid0xDdvr3jsgk/niXLYdwb09aLhmXWKw5UHfiWeYHYW7C/LEknqWqlpCB2FnJA9dmfIKdNct3sz62iQMkdvkTo98w+k0uemSckwpEKw3uGquy5EJTTFE1y85Jrv3bNE0T7QRKPeKWZcrlrIz4Ysnsuu+Hxk1WT/ylxtbLz8nlrJiiapZLZzpend3+yvMKOnFg9xXJhTBF3llpTIzEYSzpgWfr5vAnakAPmsGq9iFkRzKrJrP9L2rriH2PujqW7+alYeorz9rMtjkseoajWUxex4DORL5JeSWLf2ZUX4DpX7PpNSLDqa1jkq4YicJFa31xMsfLHi97PFrLV1ImGk5Gd11RUxgrTTPjri6ZdgOaaEfWZF990tuIqddk9cRNuiiOVTce2SD7qVOkq34BFEnNI80E0nROOjGDaWlKWwv+25P/9uR6NTFX2VVAzFXDgVgzpmksEAw8zAQ7M70jXvbqD3PQYAbQYo/yHhYwN6IqGKCqTp6utTFNtFsgVI6VJCu0iNJXytRrsppGoulR9ViJWFQ7HtlMa+owV5mpWZTyqlbT2zu1YElPJj1OC8U1+M0aIi/zwVkKDUHdmiGGVW3zw9iIsMlPhD7Xq09vR9uWFuZOnwFxu5+b+96B9FPFCjVh65O8KD1tu15NehnJJSSVkHqTy1oj6qjKBfGrxdSpe9qAJtqp1965MZNtEmQzVft7UFuoltw7hP1DeDrBzgQ/hr+TxACjlRmvdSLkMYYeJut2U4zYwH3EIGqbGZYWJtE/jx/h3UGpfIR3h8TfX/Acuf10pjyt5JwuEtGz/xrhJzktyH5oo0tr2c6SPJX4uS5SUyq1C6sGNNFOSQ1tLSQ952oN5kvjJqsnRVUSYzKXFnJjXhkdWsirNZ7yu2y/zPbLAH1s8XdiTmc6WtHWgjXuBB3R1d7AQ4ZB597MWLJg8AjvDof/zu/af5Ow/NMvU1ramC9d5CUsnCE12Jx8XEH0AAME5p9qPwR+6k6xAqTnQqmlap42oIl2MsqkPasL/tOphdy+isZNVk/Sbkou/NWNR1Z4uv4JfbWTXsaqLHylMTl9HJu4fwP3AYOobWaETugFzIj4M/viTWH56i9TAG8vBdPcLtJ4EiWKx+WWxqzsLbhVt+hb1B1bxT2PswWLpPbzu9WdgDWgiXZirkouFnbDTmUDtqCb5GJXATRusuj3lyA791vUHUdFXbCjOQul42nAIZ6M7JGS748BKmdrMj24/roIAw8PBlHbzBCH4zmXpRxWoPj6HcC2pUJ8tdIayYW/E6ZGALamhDhzdDhPtZZXs1PnydHVmmRvAttiZYKNCRPakeTNY5YAFyvYoRI8oWFNtLPpIteqpD0PZ/xj2JliaoR7Szb14/XOADdqWH2h4ZOtksqvSY/r/mPYdokLtwHatyBpGMHOkpkGO5M0TKJbuFHDVnVhDfRE9oW0+ylCH5f4fViZ4OtItKfkUeJ19W3vD9oTpDeALYoRzR/VKGvNQFc7PqjbrujzD3oUDwt5+WXdurR2d3NUOgHz8+kofiosjCrktU4AezzRQicrNVar/9ykfyv2qjSsqWNGmnp7gAY00U5ZLZNSiHoKa1N62PCLh5qep6VKtMANm2xhJR2tAH4cyNYnOVeOW7ymplSLmJRC/BBamtLNht+eUlNn1glKa/WYmwZWZRHWgVZmtG/BjwPVVKip44OHJnNik3hVTO3AtH+auM+HkGawqv1la/CDHsJDxNbI08CG1SPFySkA25bmoRN6blkfAGz/3xlh5cVnOK8ukVRVHW+eJFP6yMteTZ2Jx7ischJ1s4ZJKcReUz+2BjTRSXwxXof556aaR9eqCD4iD3bVsMkKY2WZGNFLlztZSineh9W/6Ho1zx9TEyytXhRUMv4oN2rUP71Vy+TjpNRf623ggdMMRK0BISs+O7I3NnvwoHYyD92bF9/4ceOYNg5WBxIvrVhzVFi5uBqPQ3xynsxy7tzldi2nyvj8An3iWZvNMuniaJK6OFJZt/FM4Md8btRQVcepMj46R/c49X4KDW6iD+llDDjImGR+uMT5cm7VcuQG75zCNU5BK9qwyc7P4KNz5FRQU8ftWvUyXYm0Mvoc4K2THLsheZFkPH+yo6BRMxUTX0zPOP57lr9LuF5NTR23avnnJv93jp776/2K8C5kPE1FECWB7PLARRo2QzSOEGfOjqByLKk++CoetWl/KqymhF8bkoZREUTiUMY/ptcwkEaKUNtnsDPpT1M5lvSn5d6D4mpe9iQMpSII0TgqxxLirPcv5SFn/pyBeSdnVBS8GR810bWzHSpJbWW3opJ50196ojR3TtIfL6jeAksWDC46/3pJzuw1y5+WWSOKSuaFTuiZlfpKbfHcysKIkEBXcaHs535N9J4gasrAZEyZ5Ba7a0LR+deri94qyZmdsPd5cQ6hxtOArASGRAYPLXO7Ej8EV2uMwcmclW7sGyx5JBpHSSDjH8PcCF9H8v3pJ3Dn0/JUi67Wy57CAEKcMTeiixVJw5jZSccwVDsR3no7UDAKX0fMjfBrQ8EohtrLq6U/LXkEhDhTObahv6aHihlT+0yZ5GbVwtTSwmTOq/3Eoba0iNoNq/2sWpiKY/0p3c6Y2mfS+B7mZsbmZsarPhy+eL6nrFV05DPifbE41aNSt82axojajWtGrl3ha9pIr1L9MIjaR4ksP3oKMpZbGlM+RnItGsesTvJHk13YIVANa3mqRdTuHMQUQVIqt5aSOGRahqHaifA2+ilCBbuQKe2JfkpezdtBY8NmzNH9L6rmkdUiaoUBVZVuUw5MtmohOY5r52ydlfqKplZq39JMaYyorSh4U1QyT7xovdcLfIOofcSwMiHAiSntWfsEp30V9unOAnMVR3MKBA51Wp5qEbUFo5Rt4HQOQ7UT4W1RgEKHjuYUj5ZXMzfS2PAhRF9drVsPh8oqDcEB1ZGbd1PTrVsPh9uX3xSLjMunZ3Zwke9blGxFG4mmdJ6iHEL8ObufykxSo/EdrFDNbwip0VRmknWIsIkKrQJ9KDrOUcEJafg0MmKpOENJGrs24CLQFnl7cPQ3ST8zX1R4hf8w0mKoOENGLIE+uidSWVUL+Kp4iBkwoB0ve3JHssINH0eybjMpReGp0KutpBp783o8VYujubJRsz7D0IKdmUKHJdUKhtvNK/iOvsZepqY6hLKSolDJ4V14a2xsZNZmtT6Jwu4dm1cy/R2i4xk6kK2fERRG2mmAfm5s+ZQpb5GYgt8Qtq1RaBU6ji7DcO0kuZ07nSBfQl4lOw9He+bPYNPHBEyV9rOKKXM5egJfL4V+PPqyZRXT5nMwGc/+bFvDxNkk/4MW4hPynh3b/c9fn5OVaFnYNib0l4FHjA19mZXOTulJmpIbiK2p3C7N0ZzCyno8VUtZLXZmlKiYT2gfhvYO7c3l8SodzSlrhCHdg0XfVe25rBvadYVdOmlI9KFCVvYNpzZWuuvdSxZ/yq4/qK4h/m8WrmDRbEn5/FdZuIL4v6muIeYgCz5SaLXya8orJEIZmPUSs94n6yJ1Ioqus2Q1QwdJHi2azcKPSUyhuoZ9hxT6Wfg6Cz4i9jDVNSQcZf7/seA1HaMNfyf+0F+XqjSFHDdgQAPdbYgSmH8o+TcL3YsD2ypHhtT+VC1JJQqtXK0lulrtw9Cy6EoukTt2AwFt/wWGbuGv9R8f1M3SwsTSwmTG1D7iYzGZnYBrZ7vYXRM0aVeVbufOenJhhIdVC1OrFqYLIzxid03Q3qos7w3XznZOjpqTRmlAiwLBWeCt79iaAmnwjvxk7AUOkU4OCq3MVbyqrCzxHYzfEJbNIyNWXrngiEI/zm3kjwqPYSc46rWzpfCYjokIj8UeAWMMA/eN+CGscMPWFBsTJrajKEBBV1swCm8HTI3wb0P2SFyt5Q21PNVugZDnj18bzI1wa0nCUMK76BgGUDwat5bq+/dxoGAUPg6YGuHrSGEAPg5qqmkqaa4sfderMPP1ioI3Y3dNEJ9fie0Eaovnnj36cvDornqKWmD5+0OLzr9eUfBmzI7xzk5W2lstWTC4LO+N8vzw+g5Yi6gVqjuMjaiUGl1Wn1d+JGylhNcAio6TFsPmlYRPo09PeZ3KTI39VJ9HlKPwU3tBx0Tu57GYgUeJDi3YN5iKIMrGED+EwLYKonaBK/n+VI4lzkvB0kv7U+2OuQFOpPpQOZY8f+Z21T0MIKILpYEKphFCQpzJeJrKsWQ8TbDgLORRFrXNDqFwNDdTEJrCdaWTA7mJkuvCYzgK/OUdW2sTten7mDBafmtnK6+Tn6zQj/BRXhK2AsMXfSjJmS0qmec3vAMGUWugidAumAxiq8n5t3iLuSumdPYXJKcJfJrYw5Lr+CT8BOlL/VUSrArp3pmoOPmt3xD5dXwSQSPUv+5gMoFPKwwsLUbH4OMT8oA/f31OuG/Q9KOjLwMGDBhoWiozCfEHcHMl6ReF9WnBEbw9MDXBfxjZCbhKQ526dyc7QfLIx5OCI/LdveqqNn47K97B1gYbKyaOoei4vE73zuQm4j8MUxO8PchPlj/q50Z+MgHDMTfDvTvJvxKqawXRztn64J6JlYURWiSsQdQaqBeGVa2BJiPEn8pMai9w+k8CfRRE7YLXyE+mMpO4H+nnptDKx5OUPVRmkp3AjBcoy5C3UqJDO/Z9T8UZyjKI367wCsCjL0m/SPoR2ucCAcNJi6Eyk9xEZr1Uv0kZ5KmQqE2669Ag/5GmdaQ2YODfiP5/RR3akRp9L4dSfwyiVoie/5UGUWvAwANAy19RwRFmvoidLcZG9OxK3I9Mf/4+jsxAfZCZcLg4/3975x5WVZk9/s/hckBERAREUkQlUiNEQiK8ISISIjCOMWaGRmZqpiSNOWaOOY7jmDmOY2rlhdTUn19zzEwd8xYRkbfIyCsqEREhnpAQEZHz+2Nvzo1z4HATwffz8Pjs/e71rr3OEdZ5z9rrXYtj2ym7QPp/5ViQuyv7NlJ6jvOHCXgM9VXGx3L2c21MSX2V+TPISdOOeLiTsoOyC6TuxMNd/iUxGJRG0j6m4BQzJsoyGfvw7g7g1J6cNMO9PALBg0sNrjaoH4c+ojiT0nNk7GPimHtoVusleVlTaZb+K/esI24kSmtiwzm2HWDXWpa8jp0tY57izAHUV1mQiK2NHFOSJs6brjeybyPzZ6C0ZlES+zbKmg0GJc3SxEVJsszsl5g/A2B6PMvnNdUrNZ/RI4wfc8+X6uKbgaBlY6FgyvN9Uz77k+rqy1IRxbT/PTPjpX73pvRXPWi6PzlJc3GmdoUr+c3iTOxs9cRsbfSm6G5IkUZKzspTHOwpOWt8UFezvZ125Zt5EODkp/jrNDFrLnTf7eZ1dsLV6tJUDW/U21A8Y5akqwMFxUaOBUap3py8Q3vbJ/u7P9nfPS72kbDY/6tTVaDWgYUFLv4U/qY3YkBZtYbh5SY6HVRWUllpclCjWXOQk4eqiLiR2Npw+oe62i54UGj+vNrMt40fC4wyd9YTwwZ7XLteOmHafhev1ZYd3+nkvXrCtP3XrpcOeOKhubOMtbuqjbABpH1M6TlSd2q/cqqvMuVZOU/j4GZ5sOaaZEbrnGlWkbprnLrWNjPF3UocHUg9QWICFgoS4kjdCZDyDa9NluMDZz83S1XqCV6bjNKa1yaTesLk4KFUXpuMZxf2btDO3foJKxew5b/1fyG18tqL5KRReo4jW+V4tNHNkLrvdvV3XvdgXAxZX1BxWRutllg4C1UGBaeYNammZen8GRScQpXBivna8LSjA+uWoMog/4RhNbsaNBtVJTAX9bb6SJo/64HlSsYkzc4xXUIHdVWrkq5kTKqrwmB/8k8QG47Smh5dSftY/iNRXyXtYzzccWovfy8O7EveN4QPQmnN4EBy0gjS6XU6a5LsBSwUuHZk6V848KF8yeAvtmY9deLgZkrP4dmF1J2UXSBjH328ADzc5QdlZw7Ij8U06Lok3RHptZddIGWH9rGYwSDQxY20j8lJY/QI7X5u5w5UXJYFmoLJzxA/GjtbbG2YHi9nxdSw77z6YHWZvRvw7AJoo9XAxDEsnIWdLR7uHP/EpKud/AxjR6G0RmnNsrnMmy6Pb/8PUaEoreniZjjdlGZTqgTmot6GrweZb3PoDZyrWuM52rHvdUo/5MAcHNrIYtKPwbFRYUkm8anmX/yqVUlbPxh5/PCzK5eEFmVPL82bufWDkdL4vh2jS/NmSlV4YiO9Ci9PS343QkrPChvikfvDS7k/vBQcWP8/SmkjQ/WwrNLaQq1K0rSuMJ+da4gfrT3t48WVFAD1VQbqd2zdtZbxsdrTuJHsWqs9zfqCXj21p7Y2cnCTan/wNetpEVhZkhCnTQQM9ift4ya83fFPtLFmDQ10tT26GhlP+1hbiSmon0lXe/JTbSjc3ZWsL+TjvG+wquofEOyvN92UZlOqBOai3sb7L6K0JNyXdZPlwbUv4NMFCwUJIax6XiupO0uDKeH4QVrP21xI60qltRx+6dGtfUXhLGk8YpinplvP+ePPRwzzjBrRQ3K1Z9MnDgx6KCig88mj4+t9a2lVGzKgi8F4vVe1ed/olWvQoL6q92QJM2qS2dkSMYT40axcwNnPTZaPqEdts/uH5GUUfisnlvn2kgeLM/W+gzc6mg8tXRroao2OF2dqv8Lrlg0xoPScXr2k8kvyuOYAsLXRm25KsylVAnNRb8O9A4DSkvyqNUveGvntdm5HziqtpO4sDaaEm93PolN1LGpEjz1bYyX3pzsuHZTlJ1pZKqT1JlBe8Kq0AaG84NV633rRGwPUqqS8c1PGjenl5GhrocDZqc34uN75F6aqVUmL3hhQuwp9DIqZaaj+Z1ZzTbIa6pwZqKpHbbMHHM0XfF1031VTJejq6mpLzxlxiLr/Uxp7rAy7X4H+75KVpd5djGquQVXro6kyEIC83wDK7+JUVcjKuR13t8rHd2p7Tm5KuPhWYxrZQLa8Fxk/db+To+3GdyOqX8268lvoYA9NA4uc3OL4KfvTjjeogfWiZekDgx4aMqDrR++PNLj05de5i5al11VhcQmODqjM6DRUXEKPQRSXGL+6djHT5rFzv3zq6GBcrFY9gupcvIqVJRWm/2R6NFIvpMyLODtRcB3QrtkV3Q3Fsn7EtSN51YqFFxXj6kz+Nb3pNWiuQVXrowkzEKQQra01BVV/xkU3sX4WxTMonkFZ23foOglLqK8y+yWyU7UPVR0d5F1DBz6USxdePCo/DfDrg/qq/ECmR1cuHq3HS2T3Z1nb10f5+rgYvTpv0VfbN0SNHd3rbqUamPH6kZ0fRhdlT1/85kCj8uZQdvtuWOz/TU069MVXP13/7dadisrfbpR9+XXuy38+FBq9ox6ZXmmn9AqYeXWTY7XVOZ6hV5MsqJ9eTbIa6pzd1c+dqlmPoDrrthMdhq0NtjZMfkaOEd8ul3/JvbqxepFW+PebeHXDtaPhsTms/YjJz2Bni7sry980KbZhB/F/xM4WO1vmTNUmqKR/y/R47GxxczGcbkqzKVUCc1FvY9l4lJZEP66NtCZPxddDDr+mLpAHy7don5vpHhsVrjlFQX2VZXOxs9U+VF37d3y8sbJkzFOsXACwYr6813bWJErOyq1upj0nX21cirKn+/m4hA3xyPl+cu3SzUSwPzlphA1AaU0fL1J2MGMiGAsghASRf0KuSebXh8yDejXJaqhzVvgtfby05dNq1iMwysJZ5J+g9BwHN8tPtDTVlM4fJjpM+27Pn0Fxphze1T3GjACCdKPCb8lNZ9pzxgMXEov/TMEpSs+xb6P2eZdTeznZq+AU054zvIspzUZVCcxFvY1IP3JWse91bXTVuR0H51K2mYwl9Klq7747idIPjRwbFa7V1Wq+t0r/zXnfyBEiCwU5aQARQ9jxLsDeDSxK4tBHAHvWERXa0JdcndFRD188mZD7w0ujox5ufO2NR8QQuUlwThqzqp6rGX0kEh1msiZZDXXOEhMoOqP3bKcGPYL7BEcHk99v7lvNgntE9QcCuk9gpIebtjbkpKG0JjcdB3tUGTjYU/it4dN2wYPMA5vfXXCKcTEorXHtyJLXWfqXFqBZ0AxUd7UFp4w839y3kRkT2f0+wJGt2rWtQCDxwLra8EHy95v8EyyfZ6Rv6X2oWdAMVHe1ycvw7aW3ZROYHk/BKXlP1NyXKTnLay/ec1sFzUekH3lryFtDuC+AehtbX+H4ImIDKPyATdNkVxvmQ+675L5LsLeemEDwoFPd1Tp34OBmvS2bgFc37W4ZaeOKQWKKoHVz8V9E9CXSj4v/AlBvI8wHpSUX/0W4L1H9ZFd7dhkDHyHIi5OL9cQEAoFAUDvlW7CyQGlJ+RbQCRdoxqWR8i3yTnEDMYGgHjThFgaB4P4k+xphj8kHumTlE9GXiqos4JxC4leTdvFemycQCAStgUg/8teSt4aIvqCzXI30o+A9kqfKI1JIt2g9i8fqiQkEggcd//bsCKAggrJRZIWxuDd21WKL3m1J7kduOGWjyA1nQz+82hrKqGPk9tSTu3FxGOWjuDiMcQ8BWCmY/wg54ZSN4mwoE7uanDuxK5lDKR9FQQTbA+jTzlBSqSCxB8cGUPiULLb3CQZX29qkURjgyKFgSqNQRbI7kABHrUyAI+oYzg8z8mLVMRQY2TUtENxTWmQl3gQPNuQ0txF1N0Mdg+KTRpbUJb4r6/yw1t9rfaqIwamUVu3XjezEjgDa6seNfq8g9huOFOoZALx5jr/11g7eVRPxNVM8+aN+Dcg/nWBHnuHcP//A2/rdX27dZcwJ9v0qn9pbcmwgjztiwF010d9oxTQKn/iCYwNpo/PJcbuSkFTSq5ovZIXRsy29D3Nep7rCa168/SibfyL+tOGNBIJ7SfN3YagH6+tbSbpxuU/MkPBsw9q+WFuw5irdDmK9h0FfcqmExx2ZXZV64apkewBtrfjvLzx6BOs9PHqE//5COyt29MdVaahzeg8mnKb9Z7js59N8LBVsDyDCledP03YvXf/H/l8BZvQwYs/i3nyt4okvsNlD78N88gttLNnyOM5Vd5njzeOOfF/MgC9p8yk2e3jiC766jqWCed5GFG7ox75fZeEhqVwqwcZCT3LHzwDRbnqzpFNdxy0wIDaAss3aatG6NaMFrZOQjlwZTuFTRHWSFzJUrWhi3SgfRfFI3u8rf52UxiM7kTeCvBGEu8jCS/pQMpLZXmzoR2kUsW4AYS7khpMbTrCTLLb1cY4P1lNrJpopumasfIyiSEqj2Pq4rH9fkPbusW4UPsUmf5PCkjHVJetk3pI+sipd/NujjuHKcPl0UW/UMRwKNpx7KBh1DAt1Mt6klzZGZ/Xay14e1HWs0tfzokg9bZLYmaHY6n+OS3fROMcrw1HH4KdfAKyHHeoYSvRrlkkKV/vqDYZ0RB2DSufWfg6oY0jVqeTjrKQimoponO6zhHlNsc2aSTZWLq4hCo1StpnYgNrFBK2HzKFEuxHsxMVhhq72bCi7A7G31BsELg4jwpXITlwcJo9HdaKX1kX3oQAAIABJREFUPRXRRHUi1o2yUfL0gU4EdeDkEFkszAWlwlCtOehOUesXSelhR0W0PB7hqr37xWGEu+h9fhgIS8ZUl6yTeSeHoI5hoFNNMumDUcfIH0u6hLugjpHfHAnJu+mucy2qBt11djBbKbSfNwZzx1RrNCHdJW1QTRZKd5HeGQOFBqFeO0sjkueHURGtXThP7GrofFsWDXGg5pP9H+yqfaERNDr3UQDB2559v5Kmwvuw4aXob+jVjvwIeVmqwdOOQ9c4VICnnTyy91fOl2CpYO+v7M7HxgLAqy1fDuLrwfhWraEOXaNcbVJtDRidEtWJPU9waACaNjQHCrR397TjSCEHC0wKS8ZUl6yTedKjrTM19hv2tgc4/pvhuDRS/eGYSqenrKYOYn6ZdrBCbfJeKYWGI9JdelV7OObZhqhOLOzFgSfJiwAw2mQ9S7++rRR9NpDc8TOWCm0MIboziOhBbSzby8I4HO1qlxQ0hPvI1WaXEuZCUAcuDuN2JbFuRLjKl7Ju0uswqSp2B8oj0lpPmhLqQnZpTZpzbjHgSxSfoPxUb7y62loxmCKZseVx1v3IwvMmp0S4Elq1lqxB2ECyTubZWwGUVNQoYwlQXE1GGqmeq2DUk1br222cwnLjd9FdpMe5kxXG1XA+DeLNRxjhSqdqHbQ0lJt26xp0w7VKhbx+b0RXq1YlxUZ6ZaZNKMtPzEybEDqoa1ys9/njz1cUzirLT5QaymmY8VK/zLQJpXkzVVdf3rU5uou7va4e3ePgQPeUz/5UmjdT6g4n6dH09dAVNqXTYP1rSqdR/jORpJH8tl7Eah8YIlzJDacggshOxLpRMlKOPwKrfCkeSdkoOfqZNojSKIDITuRHkDdCdsoGYQfNgRTSLYpkcW+9qwZqzUF3isaM5H6URrHcR9Zc/e4FEST3k09NCVeXrJN5RZGoY3CocUuKKhJ1DI7VZJysDUOu1cMC5g9KI9Utke6iybsa+5AseTaUBY8wsStRnfBqa1JhdYyOS+llvezleEVuo7b8UquSTh4d79O7o9LaYt5rQcU5r6TuH+vdswOgaSgnMWva40f2xHl1d7RQ4OrcZulbgw/s/KOuHt3jM6kTQgd1lVrV6eoxcKA16Kzuak3pFAgEDeJ0COqYWkINx03EaiM7GY/VGlAnVxtSLT1WuovmoVzmUNQxTO6mJ+Nm01BXO/8R1DFs8ifWDXUM6/yMTKw3alVSUEBn6di+rbValeTTu6PuVc1x1ukXej2s/c+wtbEsyZ1hVFKtShocrNeU06BDnTk6q7taUzoFzYXYmNtKOHKNfu1J8CBNpR3s044fQrlaSo/PAQ5eo38HZj/MQf0Nqa95yRoakde8OHZde2oBcx4G2FmVgetlD7DpJ71Zs6ram9tZanOB68SOn3mrF7Gd6WEHTRCozfheDqWX3LwDnD1/3aiYl/96uzZWEcM8XV3sAvzcwkI82tqZTINIP2FWu7mm0Cm4ZzRJrFatStL0HdD9iK7hpynMeKBYm82tu7zQjUW9cVVia0FIR7YHACRX7bNYfZUbdxjmwq5AetljpaCXPbsCGerMzQpWmehEXT9GurE9AO+2WCnwacfeIAZ15PJNNvwoC+SUAizzwVWJBfRpR3I//lzVraLe6VnnS/juBu2sGNCRO5XaZ4yNhUH3tkoTEeTgQPfsMy8u+evgkIFds67+NvaFvTXoLL9jVgC8KXQCDm1Y9Tw5q+SqOqkL6FUte0TQcJpqVbto3oA9+7Mq7ur9Jiqc3jEQU6uSqg/WA/VVI309E+LYsKP+OqXpRjXfh2TdZNp3rOvHG968oZPY/9V1llQVTMkrY/wpdvTnD535Q2etzK27jDtFTqO2In76BGv78qeHtCO/lBH7jfbp1tIsPvDj5e68rPP2vnWesV14xJ7ADuT+Us9b7/iZvu0BUlWU1Gtp3HDWLh8+Lenwzj3yW+/oYPp5X7PqBHbMZEc6C3Zy7X2A5C/Y+gr+oktCY9NUGQiHjuXMeMm/iZRXx6g3XP/PBumUprcIPyuR/BMhqXyWz293uF3Jud954yyhX+k9u9/7K/7H2PwTP9/iTiU/3+LDHPyPsSe/kY3ZmUdIKid+404ll2/yz0v4HCHzd63Auh957hTf3uDWXW7c4fMCRqaz4AKLLgBM8az/rTW7hJsxzcvbq8Oe/Vma07CQbjUIm+Ku/pq5UXRWJ+RRkr+gsOq/JvkL+nSpcYLg/kGtSnJytL2SMcnJ0dZUAEEz2Dh3vCr/K7Uijw3XthQLG0BuOrnpBPtrhbf+m+OfsHIBRWcoPcfWfwOEBHElhcJviQrVTpc0Z+zDrw+AXx8y9hnRKdBg6inWPUPaw1YRbSRTuIFUf/pk6vTInrglfx3k0E5p39Y6Lta74NJUg0dhppTojhRentbnkY5e3R1r1VmzYUZHNByYg08XLBSot2Flga8HB+aYkhXcZ0j/r7OmPb5q6bCavWqju9qIIdrO5NLg2c8ZGEBQP05+qhWWenFL9OhKxWWAzINEhxHsz8WjejqBWZO0vc1nTTKiU6Ch2V1tYg/UMezs3/iazXe1Hl3aHdj5x9K8mcU5rxzZExc5vHs9XG3iFP+i7OmaNIMadDbE1TrasfYF8tZQvoWC99jwEs7VtpkI7lOk/1crS8X548/fY1db/VTTNFfqmKsrFhXKnnVcSdFK6vZ81FXi2pH8E9jbkX8CNxcjOgUamtHVOlrh2cZ4dQWBoHlp5FitruusuKueuzDV4Oo9TjmwtyMnjwF/RNEd5cOGV7esYN12Fq6UT7NzCRtIUD95VStNlyi4zsnvef8fZJwl/1pNOgXNyG8juRpOdzs+yiWjxj3KAoGgPhhd1aZ9TOk5IkPI+4aiMyz+s95VIHkZpedYPk8eiRhCbjoFp4gM0U7XCMeNRH2V8bGAEZ0Ntb8VbYVsxlVt9nCKR7Ll8bqVEHrAcW7HusnkraFsM9n/YUU89o2T2iAQ3H+0JlcraFkcm8/kYTi0AXC0Y/oIds1qbpsEggai3sbWVzi+SOtbpQPp3zAfct8l912CvQFiAyjfQvFG3n+xmcwVPAAUrcdWZ8OI0pKi9c1njUDQKKi3EeajbX+Nvqs9u4yBjxDkxcnF8unuJPFtTtC0JITw/ot4dARwdWDZeGaIVmxNQIvsLdZyUW9D8YyRA+nf8i1YWwLcuYtyPF6d2Ps6XZwIXyxaZAuaipqDV9JvqUDQwtD8WktdRiL66q1qs1bIoQNdDsyh4L17aKJAIHgwUTdqGZSmVlvLTatcbWwAJckseUbP1Ub6kbeGovUsHguw6nmKN9a/9ZMo4iMQCOpAa3K19xLhagWC+4f7qOGNQUWC2HAKv2XTcvmqbhWCa6f0JHVLH6BfygAM6xUYqBUIHlic2jJ9BEfmoVpH2WYy32ZRHE6NXThCcJ+iqUhw8Sjhg+TKL1SrQqAraVD6wKCUgUG9AgO1LZTcH15yc5W3snl2dagonKVpM+Xu1jbn+8mAWpWUOMU/+7sXjXa+Ch/aLSMlvjRvZmbahMjh2vJlUq3hiycTzJxlZanI+X6ys1MbjYzS2uJM6gSrqg6LNdwocnj3gktTjx9+thHfmfuf5i0QoaGLE8lTsah6Lm6hwKktwd5seZkuZnc1FbRIjFYkUFobqULw3B/0JA02iRmUMjCoV2CgtoWya3N09FNyx4J5rwUVXp42J1Hu9Rgb6bXzw1GAWpV0/PCzUosUg+5Sgf5ueeemhA/tprS2GBzcJef7yZo+LmpV0rFP46RqUmbOWvrW4Nema8PJ48b0Wva3IebcaMt7kfZtrf18qnXgETQ9O2YS0dfIuK8H22cYGRe0HorOEB3GxDHailxRoUQM0frEvRvY+m8OfGgoaeBqLx4lYoi2lEHWF3qlDqurbYnMnfXEojcGSMdn0yeOifY+mz5ROl385sDZM/oDalVSgF8nzRTd0O2uzdHj43prTuNivXdtjtaI6fo+c2b1etjp/PHnNeOp+8f2eaSjOTfyfdS5Hq9d0CgUbzSesi22MLR+DCoSRIZQcIrkZUaqEBhIGrhag1IGBvUKqqttiYQN8ZC6pfr7up7+4jngTOoEybEe2v10yIAugFqVJDVMldB1mvkXpurW8Hd0sMm/MFUjpvnub/6stP89I93Uz8cl/fNxZt5I17zqqGOIdeP8MMpGcTqEUGftuNRa+PhgrXC0G2eGUjaKM0OJ7KSnJ8yF0yGUjSIrjAQPvX7GBnpm9CBzKKVRqCLZFUgXWz1LModSNorMoYQ6E+fO+WFUROv1MzZfTPc1ju7MxWoyNVveKBStR2msUoTSkuKNjXkjgcAI6m0toxyBo4NN4eVpwPK/h0hf3uckBq5aOgxQXX3Zvq01NdYwLS941aBSe0XhrOpi5s9KeNZn6wcjgfdXDJ/yfF9zptSaIKGOQRXJ6M4oFYQ6kxsu10VUx8gFZTRlEiNcyRtBqDNKBWEu5I1gYFW00c+B3HD5UmQnuT27Rr+unlk9OTIAr7ZYgKuSpX048KRW8uQQfNqhVDDPm+KRpA7Euy1ArBtlo+ospvsajw2QS5jrytRseaOwYya+HkbGfT3YMbMxbyQQGKGluFrg4skE754dcn94yd2tLeDZ1aHg0lTfR50z0yZIAjU4zZzvJzu0UxpVW79Z9m2tCy9Pc3drW3h5mq6M+TcyIhDDNE/t6fgu7AiQx331a9GmDmScThOz+K7sfUI+3vI443Xat0zqpudqdfVkhdHLXntqa0HJSK1kUIeqV2qJOgYfneLZugrNFNMd1K2rq3upBssbBY+ObJpGsDdObeWHYw5tCPJiw0u4d6htsqDu3EfJXoI6cfxU/pxXA7Ou/JaXfxPI/qn4SvaN+bOfPH669jZhx1J/0k0G8OndMSMlviGzSm7e2bP/8uI3Bx088mPx7+UNvJGGXTqdHA8UMFCOAHP+dz0xf0e9duv7fiWoalUb0lGvw9ge/daQunq8DpFzi1BnwlxY1JuTQ2ir0+M044Z8IPWFPKtvQF3FdMk0IVOz5Q0n5zqJHxLlT8oCSpIpSSb1LSL7kbiJvN8a+V4Cmq5jrqCpOX76l+V/Hzot6ZBmZPvH5/+1eOhUnRFTLH/35N7/N7roxu0jKTneXh3WrRyxdOXxBs5at+n7lH1jI/64s+E30lBwW3usKsepan2s25gSUFqgKteTdKj6vXa1oeiO9lJhud5EXT3BTuwOJK+M0zfIuMG4U3w3VHu1TL/Vt6nG32aK6VJhor15zZY3CqqbzN3O3O2Nr1lQHeFqa2J0f6aF498dOxvyi9h9grnbKS2Hqq20Q95iVxIZ2YT9HcCtPUufJbIf9rZk/sTSPfy/mVBVs8NOybzRxAXRpSN5KrZ+xcKPKb9rUlvNnPz214qKyh3/vaAZ2b7r/LJFIcdP1b7+yci8NmnG/5b8dXAvb6f8X28uXXl8687zDZyVdjzvSErOoS9yGn4jDQ5WFFXIx85K8suMixXdwUmpdUbOSoqrZqn0LzkZD2YArO3LtDPsrOq269jcfxnmWy5oETT3L9R9zMKneXO09rSbMzOfwrEtE9doB5fH42gnf+GytyFlAQ9XPUR+vLvsZyWUlhybT385F5burrzxBwK9CF9sXFutpB3Ps3VboTuSX1Bq5azdBqdwekf3qsHpgcPZBw5nV1dbv1kS4aN3Vh8080bGFbpqO41HduJggXGxdBURrmzJlU8jOnGySD4+co0wF7b/XKXQdAqvt73el/Qw11qta1rMt1zQIhCxWpMkRgJMXU+beGzGM+QtgOjH9WTO5mIXT/xqgFkjediNqwU8+SbWzzLkLX66rpV8bRT9e/L1Jfq+juU4eifx5XmGP0ZCiHFtAmDFYwzuiJWCcBfm92JplnGxZVksfZSQjlgpCHVm6aMsqao5ufgii/vISkI6ssyHuya+sKepWNgbByvsLYlzZ7Vvk7wi8zHfckGLQKxqTeLwPIC/J+MG4OtBqA8g9wXRsGyvHAEARgcCTP6A9CyAlPNM/oD9c+SrcUEA4/5D9jWA83lMXMPlfzN+IBuOGdEmAFZcZuvjONuQep3R35B107jYsetM+45VvnjZk1XC5AyOVX3IZf5OwmmW++DjQF4ZC86zzMe4komned+P/Agq1JwsYuK3fBbUJC/KTMy3XNAiEKXB9ZBiplJo1a09e/6s/cqvQVPSG7AZr3WOpR/SRqk3YmvNrU3ylJJk2hrbnHO9BOcXjWi739DUMtc9Ht2fXSea7I4xKD5pZJ0ebdj9BP7HGlntPaDlWi6QEAEEk6ycSP+eXMrn7U+ZsJrextJAq3tGCxPvqIWJDzXdZfJ962cN0Pjcj+/7fn95I5jiiaMVFtDLng39WN1Cdgm2XMsFRhEBBJNE+AEEz6fwd4A+VUnyVhZUGMviyfqVx7oS7M2RH+QR3ZYK2dfo/RDdppNz3chcQRMx+jiLerP0UawUXCxhxWWSf2pum8yj5VouMIpY1ZqkshIgoi9KS/y6sWW6PO5ooqDnjq8B1k4isCdWFgT2ZO0k7dXdJwG2voJfNywUuDqwdBzqbRx6owlfgsSiOAo/IG8N8YMM20dq0Jx6dWLPn7UFTI02gNDtHCHtrzuzVE/A1pqcVTjaNcjsRokepP9GWBoOn2G3F79jLclbtVzLBUYRq1qT7DnFc4PY/DKbX9Yb9+/OwTNG5Jd/xrgB9H6IbxYZubrkE2IDGPAI3y7RDt68zWtbGtXoasyIoOwOXabh3oEdibXLb5/JuiPErcDKgukjWDtJ/pCojqb7JHBkHuG+2rdl3AAOfU9RaSO9BoGg5SNWtSaZvpFtafxexq1yvsni6RX89f8Ak62bS8sJWcj6o1wr5nYF32bz7CqAW+UAxbcIns+/9nG1gDt3uV7Cf08QNI+MH5v2VUwcwvuHKbvDlQKz3HrAXNYeouwOJbdZ/hmd2pt1l9WfMytSezojgrW171kTCASCxsCjI+ptZK2oXbLpKP1Q+0TOTll7AAEI8mLBGLa+wsV/GZevPmihIPddfLoADHyEDJ2Vu0AgQKxqG5Hijai3MSUMRzssFPRyZ8MUgNQLtc1sSip18t7LK4zL6GZHTBrKhinkXmfV//R2stV6l/cPM2skNHhJa6qymqkPiSalpZR5E9z/iFhto7EphZfDWfMCa17QDv56gwVGdqveO87k4OYob/Y1Wp8U6KGzCXXZeLwSDZMuzOH9w1xcwdJPCfUhYW297TWJJsNMIGiJiFVtozEjmZkfcuIyN29z5y5XC1jzOf5/kbeHNRcr9zN9BPY2uLVnuU79wtsVcnaBVydW63w2FJUaSbowxe9leHXC1QEg/wb7vmXnq+xMp+R2LRMFAoGgtTF9BHlryF/LpKHab8SxAZRtpuIjzi8n+nHteOijXPwX5VvIfJtxA2qJ1c4fTfFGSpLl04GPoN6GXzezrFJvIyGE/LUUrWfDS9rMMPU2Ep8i+z9UfETZZm22makAwpJnKPyAkmRS/kovd71Xp95G0XoOzNEuz40K18mYPg81SWabQCBoVTRp8NGjI9n/qYMlp/9BD1fsbVgRr+3SKuUaSzENyWNqxg0OgFmRLH8OextsrZkVya6q3WsaH22nZEqY7BxNCdfVGCmzTUNCCBteMvdVCwSCB4Kmc7VKS5Y8w8Kn62BJSB/52KMjeWu0416d9MRMHQBnltKlquGCU1utkoL3GDfA8I6mhOtqzJgnODBHO5ixhMBqhTIEAgNErFbQOKjWE9iTxbvrMCX9knyQq8JZpw3XFRN1aavj1Ulb3reoVKtk4ho+ms6ePxMboK17aUq4rsbsOo5PV21mG3D8srkGCx5YRAbCg0XTPce3n1jnKRUmyutU1rcwq3VVt+19GfR9ncCeRPYjoi/2tmz9yqRwXY3RZLYlvCc2awjMpZWvan16dzyyJ64kd0ZxzisHdv7Rp3fHWqfs2Rp7DwwToJN85mSPqqQ+GrJ+1bZ3NXgwdSaHdUeZ/AHRb/PR9FqE62rM+4cZE0Qvd0J92PJlfSwXPGi0cle788PoTdt+cOq+yvXh1ds/Pr9rc+39nUdFiMCbjNQ/vNYu4vVmxQTc2mNrTUKI9vt7nUg+xqyR8pOuiUO4ViyPZyxhShj2NgCeLtyuqEm4HsaIzDZBXWnlrta5Y5vTZ34tv1NZdvtu8rYfvAM2ABkp8d49OwBOjrbXLk27nZ9YXvDq6S+eGxj0kK5z6eJuf+zTuLL8xPTPx3l1d1SrkhKe9SnKnj7thb4zXupXkjsjNtJLuktspFeZjpJararrs6nq8q1jF9Ou45x9h+z/0MVJr2Ob+SzfR8Vdct4lbw3Bj2i3t437D3FB5L+HehvbZzL23zUJ18+YVf/j0S4ieiAQADAm2jv/wtQje+LmznoiOFBOpJw9o//82U8C01/0K/81ccOqERYKRkc9fPFkAjqLuD1bY+NivZXWFrGRXsc+jVOrksaN6eXn43L++PPRT/WU3KskWZI7w0BJzdy3rnblktCi7OmleTO3fjAS/VVt5PDueeem5J2bEj60G6C6+nJcrLdalTTxmUdVV+XSZ7GRXuUFrxbnvPL+iuG13uu++rSohzF1ymwTCFo/VpaKgUEPzZ7R//QXz0167jHAo0u7zLQJwMmj4//510GFl6etXR6mCeNqXG1xzitqVZL0U5afqFYlKa0tJAGpaIBGcvqLfgZKquPRkdQFFK1nzBNyjVcNmsKviU/JhVoCe3J8EWWbyX1XLjLr60Hm26QtxNNFO6WLE8fmU7aZ9L/p5SQ1kB7d2lcUzkLf1V48mRAxzDNyeHfps+Tk0fGL3xyoViUt+9uQk0fHSxPPpk/cvSXGvq21OXdp0a62rpltAkErDyAAFXfVqek/L115InTUjqULBwM5ub+rfiuLi/W2tbF8/a0vxybszc0r2bU5ZvIEvS6pFhYKF6/VCqd3FE7vSG3Ay+/I3RcMnkqv+iDDlBKtTAIHvsN9qslCBECuiqA3ATZMYckn2E8g+m1WTAAYG0zgGxw8w6rntfKrX2D1QRwmsuQT1jVGFn3UiB57tsYe+uRpy2r9eTw92h869uOhYz96erQHrmQX+fftBPg+6nIlW24FHv3M7l7eTvkXpmq+QLRW6pHZJnjAaeWuNuv0C9JK08pS4d/X9Ur2DWl8687zK/85bMuOc5lpExzb2y5+J331uoxF8wYAdyvVjg42QGr6z4lT/S0UJDzrk7p/bA13qa6kOiF9WP4ZpeUs3WNSz56TlN0BCFlIny5sfYUdiXRoC7DkE3muJtNe0vn/ZnJ7C/9NIujhurwvJtjyXuS6zd8v/OfX1S9l59wIC+kWOtgjO+cGkJ1T7PeY6/Xfbvk95qJ5V7OuFvUK3Jia/vPuj2p//HhflY+pqzH2EwldJP9nCQTm0Mrzakc/98n82U8GBbg7d2yTmv7z2IS90vjOTy6uenvY1p3njqX+tG5l+PYNURUVldLVIyk5eeen2Ln/e8qrn295P7L0l8TzF1XjXvzsh68nmrrLpBkHDZQYRVopGvR5tNdpo6tp47h3Nu8fJvUCeSou/Auq6h9aKPTSPy0UuEyWq3A1Crs/y9q+Pmrtxu+qX0r8y9Hk1U9VVqoTph8ArmTf6ORi99/PLv1h5MMaV7tq6bD4sX2USssa3gSBQPAAERzonva/e7qs2jWLRXH0cpfrbUub9B3tODBHr1WXREky/p44t2PTNDmwe3AuXp1YFCfv3JeED8xhURwWChJCSF1wL1+NQCAQmEFxziuaVK17g3sHjs3n/HICe6LeJtcuKfyAKWFGXO30ERR+QMF7JD4lu9o+D3F2GSl/lZPwJWFPF1IXULaZjCV1qy3bItA8dXRopyzNm9m8xggEDcTw6YdA0OwkPOuz/j8jdEe++ubngU9tby57BAKBoNXSdLvU6oerkuU+XBxG+SiKIjk5hPmP4FjHhx3utuzsjyqS0ije7wswrtV9HREYRaxqBfcpsZFe2zdE2SjlqjAKp3fqrSoooPPsmf2DAx9y7tjGynl5Rkr8khXfbN9Vh6Zvnm1IG0xnW8Pxq6UM/JK8MnP1HBnAUGf5+D9XGNyRvu1RfGK+IQKBQNColObNbJR4etSIHjnfTx4T7e3oYCOtlIMCOp9Nn2gqA9ooOwJQx5A6EP/2WCmwtWCgEyeHoI5hk38djCkbhTqGMBcswEqBOgZ17XlxAoFA0GQc2PnHiGGeDdeTmTZBU5hCE5Tw93W9kjHJfCXFI1HH0EV/VevVFnUMhU/VwRgD3ypc7YNDK9/C0HIxFak0M4LZvIFOzd11zZg764k6KRkR6rn///6o2Rtdb2O8enRIP5FnMJh5rtDdzd58JdL+wFL9mrZZN1F8gvN+7YitBYt6c2U4ZaPICmNhL5RVITpdryodG5xGu6GOYXeg3i3GPoQ6hij9Xdd7nkAdwz96cygYVSRlo8gezgof7PQr8EpqPdpwcgglI9kVCGClYJ43F4dRNoorw1nUW2uhoEkRrraF0ZCQ5b1H19q/zxtY17m6P/W24fxFVehgw93QYSHdzl9Uma9kTz7A1gB8HUzKWCnY9yRveNPdDhsLerblzUfY96S5f2P7fuXX20R0wkmnhsQYd4BoN+2Is5IIV25WMMebYS50sMbGgm52zOzJamMRkQ39eNwRW0s5oLw7kL/15mF7bCzobscb3uwNMs8+gaBV0sBl6X2yqq11sAYGB3c5eXR8ecGrwOI36+amdYkY5pl3bsr4uN5SrNahnXLcmF75F6ZGjehhvhIna9IHy+vEK8N5vy+jO2Or70SneKKOIT+CUGeUCsJcKIhAHcNknRbCNQcQlj2KOoZpnvKpnSUlI1HHkKeT+ZbYA3UMt0ehjmGKJ7YW2Fsy52HUMagi9eyRlGeE4NVWHonvijqGtEH4OmABvexJGYg6hgTTdTkEglaOWpWUOMU/+7sXKwpnleUnah4QGThvE230AAATxElEQVSssCEep794riw/Mev0CwnP+uh+czc63RRRI3pkpMSX5Sdmpk2IHN7daASg+umMl/plpk0ozZupuvryrs3RXdztDcR0DzQ/vo86G2zV69GtvVRrTZe8c1OCAjpLGoqyp9dsf80E+rvt2hydf2FqecGrhZen7dsxenBwl7oqsYDxXTjwJKVRshdTRTJJx40eG4A6Ri95S3JtKTofEzW7Wp92siuUGOMuT1fHEOAoD54OQR2Dn/7iWqlAHUNFtN6gpDxSJ/hw8EnUMXi20Y70sEMdwxHjdTsEjUkrr4HQohn3dO+IMR+fv6SS0p6k6mK6+Pm4JK9+Kn7KvtT0n8NCum15P7JO0zUEB7qvXT583IufpZ/IixrRc9Nasx70zJr2eFREz9hnP7mSXeTcsc1rr/Rft3JExJiPjQornN5Rq5I0cQCl0tKzq0P2T3IjhMjw7vs+v1p9loWFAhgY9FDJzQZVdjl+On/0c6bL/JhHJWzJZUsuthaEOBPlRnxXPvCjtIKtPwP4OAAcvKadsu9X7bg5ZP7OqSKedKKHHVdK5ejBwgt8Hky0GyeL8GlHv/Z8d4OMYgD/9vi1x9eBUBcAS2NR13SdMIlfe4Cr4YYyNURFBI2FiNVib8fKBeSmU3aBi0eZ+zLVKgg2D9OSDp2/pAJ278vSpJfq8tor/ee8lXLky5/K71Tu+/zq7PkpdZquoyfgtTePpaTllt+p3LX30oIlaWaZN8lvWtKhrKtFlWoKCm/NX/yVOR0oJJK3Zo57urfmNGJY930HrxjITJy2f93K8DsVlRtWjZg0439mar4HlFVyoIDpZ4j4GmB2VU01BysAVblWUjq2r+m9N2TDjwDxXVEqiOzE5ZscusaPpXK4Nr4rwKafcLPh+GBOhbC+HzN78phpX6nS+ZByNFFJ2EGsuJoe4WpZ909yf6FPGHa9CIrFqT2LXmtumwA488O1mgVCBnbdd1C7GNyzP6tO0zUEBz508MiPmtNdn5rV58vLf31ObnHooK5hQzwWvTHg5NHxbe3MKgoObPl/50aPkl2U0trCv2+n1PSfDWQOHv2xT1Cy0vVf3gEbDhzONlNzUyBlerlX279w/DeAXlWdzEvuAjgptQLOSu24mWzN5dZdxnclohPtrDhWCHCkkL7t8WzD+K7cqWTLT6x8jP4duFTC25eYcJreh81SLlnS/jMUn+j9KD+tg4WC+iFcLVHDWPUhxSVUqlHdYOl7JMQ1t02ATiVyU7i62BXd0G5UKrx+q07TNTh1sNXVk//rTXNmBQe6Z595cfnfh457undBYem4Fz8z83ZAUfHtrCtFvo86AyEDu6am51bcNWwCLrUUaniyl0/vjppAsE/vjmdSJ0htNczfH3GyCGCKZzXN7QAKq9o4ZhYDRLhqBSI6AZyvS5XLogr25tOzrbyAPXIN4FABwPLH6GzLwQIKymXNwV8y+yybftIuS2vO3JIsjGy8hh0C8xGulis5zJ6iDRoUXMetP4BTew5upuwCBzfj1F4rr75q5Fh9lcQEMg/Kpw727FxDyVnOHybgMQBHB/ZtpPQcBz7EoQ4JnTWh+q3MqYP2GYfusSl0H09pBguv39Kd69DOxthUpH4/GtYuHz4t6bDf4E0J0/+38r1vc36qW93c5K2Zk57zBcbEeOuuzTVsXTdybMLehid7Lf/70FUfZEj2p+5/ZsmKbxRO74xN2Lt9Q5SZGlZeBpjrzQoffNqhVOBgRVQndgYCbMmVxXb8DLDMh5COWCkIdWbZo9pxo0ifhn3a6Q1uyAGI7QxwpBDg0DWAP3QGSP4JqvJ8I1xRKhjoxPb+8lzdNXV1tucCrHyMCFesFLgqWdoHdQyHgmt/EwQNRLhaJiYx/g9kf8WK+USH4dtLHl/2Bjv2Yt+HDTtY+pfa9aiKCB4tHy/9C1t34/gY85ax4W2AJa8z+x/Y92HHZyz+c+NYfiQlJyxEm6cTHtqtBmEJo5mqB49kRz+lbckeEeZpdK5Pb2fdU2+vDrohi7CQWu5+V79N0MEj2ZHh3QP93eLHPrrvc8NALbB91/nyO3X57m2CoIDOm7b/AAQ+3rm9g03aN3mYEb/WZXc+b1/CUsHMnnwfyu1obozk0yB6tuVrFQurSimszear63Sy4ehA7kRzeAAuNnytYrWRzxEZKdf1h1C9dK6DBfx8C0sFF0rIvw1QUM73xQDXy9nzC1Tl+W5+nNvRfDmI7nbyXE2iglHWZnO0EBcb9j/JnWh+fYo/P8zNCl7LNPOdENQf4Wo5/QNeQ4h7mfxrTHuO458wLgYgcijbP6XiLns+J2pY7Xp2H6S4RD6OGsbeI1TcZed+fCMAosM4e4lKNXs+J7r2frJmsXh5+uL5gwYHd7GyVIQM6LLsbyEG7sxMFi79et6fgyKHd5f0rPhHqObS7fK70hftPo90XPW23ruQdjxv4dwBDu2U9m2t42K9V79Ty3tUdKOszyMdPbrIS7hKNds/Pr/zw+gzP1wrKLxVXf78RdWebX9oeAChnb2y9FYF0MOz/c+/lEhpDw7tlDdL65DVMPssw9P47y/8fIs7ldys4MRvvPo9IanaLWQVasK/5p+X+LGUO5VcLeVvFwj9inLT/ydTvuNCCQaRnkrY9BNURQ8kDhYA7PhZ1jb9DNty+b2CW3f55jeePsHrPwBM617Tq6iEyK/52wUu3+R2JdfL+e8vBKXI+QwCwT0lsC/ZqQDlOg+HKi5rj00FEHQpr/ZgqfwS6qvyT/Wr1TGV0GowHjKgy8mj48vyE69kTJo8wbc455Wap5vCp3fHQ7ufLs2bmfP95NemB2jkpYBpReGss+kTdfNtAY8u7Q7s/GNp3szinFeO7Ikzmo2rK584xb8oe3pJ7gzNiFd3R7UqacHrTxo1qSR3hnfPDjWbbQ5SWDZkQJfEKf6b1sh5bNNe6Htot2h4KxDcQwpOYasTnLSyRJUBkH8CezsAO1sKTmkFNF7Vqb1JV5t/AqX+0/iCU1jVJemnHnh0aXf6i+caRdW92WwmuVpTHd2X/HVQnTZ0mULzabFq6TC7NvLzo4yU+F4POzVcuUAgMJcNS5n7Mj26YmWJU3sSE1j1FkDyMib9CStLxo4ieZlWvuwCseHY2rB8nklXu24J4YOwUBAbztnPZW2+vbBQkBBH6s7GsTzv3JQpz/d1dLCxUNDrYadDu5+e9NxjjaL5HrhaWxvL2TP6r10eVoMNRh/iCQSCFombCwc3o8qg4jJ537Bsrrwgde7Aka2UnmPfRr0MhNhwyi5Q+C2TnzHpap3as28jZRc4c0DOQHDuIOczZOyjTyN1NQsK6Hxo99PFOa+U5s3MSImf+MyjjaP3nrjaktwZR/bE2do08VJfIBAIBALBA8L9sQVVIKjG4OAuy/8e4vuoi9L1X4vfHDj3b6l1mq4puVDDCr1lVaQUCAStENfmLkHSiJW9BIJmR+TVCoyT+XZzW9B4lb0EAoHgPkW9rZkNCB/a7Wz6xPKCVy+eTGhIkzGjAQSv7o66Gb4CgUBQBxzasPNVSpI5v5yAHgDqbSQ+pV2iOtqx73VKP+TAHByqyh4E9uT4Iso2k/su8YPkWdKPhFNbDs6lbDMH5+LUVns7XeWxAZRtpnwLp//BwEfuyas1D6Ou1qd3RxGUEAgE9WTtC4zuj5UFY57gzFIA9TbiB2m96toX8OmChYKEEFY9Lw9mvs3o/igtmTiEwg/kQd1V7YaXmDQUKwvGPsm6ydpxXeVlm5kShoWC0f25+K9GeC2xkV7njz8vNYYIH1p7eQcDkt+NMMjM1fxUFM4qvDwtcUpduooLBAKBhtx3MSiiot6m9bNA3hq5hplzO3JWGU63stB6WF1Xm78WexsAOyX5a40rPzCHvbMJ88HW3KK1taC6+nLEME+ltUXUiB75F6bWW49aleTv61q7nEDQlIjy660K1/aUVyuGVaxTy8W5HXe3yseaslnO7ZgShq8H/iaKlTjZU3IboLQcZ/2Kfxrl499l56vsfZ3yCkLe4nR2A14GALs+vaRUWlZWqsvL76am59Y+wQR3Kiqzc0Q9FUEzI1xtq0JVgtLSiLfVUHQT96lU6JeS2jubIz+wKYX5/8c5Y5mmqhLsbSi5jZ0SVYkRAaDwd0IWorRk/CD2vo57/ZehMi+Mf+yF8dp9xlLItR6ZsIXXb9nbW6uKymoXFQgEAnNYN5lwXywUxAZwdhlUSyRInoqvhxyrTV0gDxZvxN8ThzasTtDKl2/RLmCTp2pjtck6PlRXeebbjHlCvnXZ5iZ4bfVlyvN90/73zMCghzS1ZgQCgaBBOLVl3+uUbebMUm0Ggi7O7eRcgowl9KlqujjmCc4uo+A9Ep/Syu9OovRD7awj8yj9kH2vG2YgaAjyIvNtKj6ibDOxAY3wWiKGeV48mSAle9XjsZjWSBMPx0QJG4FAICDv3JTI4d2V1hYRwzxzf3ipuc0RCBqE2C0muE+xsFBUVqorK9WAlZX4RRUIBIImIHJ4d00AIXJ4jY1cBAKBQNDsBAV03rU5Ov/C1IrCWUBGSvzY0ffThjaBQNAKqF7NwNWBkuRmsMRMGvcRVtSIHjnfTx4T7e3oYCPpCQrofDZ94uQJvo1nskAgeOCp7mq93Sje2BymNAeZaRMGBsnJFhqX7e/reiVjUvMZJXjgEE8bWi2j+2tLxmjKx0g/J//B0j3NbV9tDA7ucvLo+PKCV4HFbw6stx6vHh3ST+QZDGaeK3R3s2+QfQKBQKBLs5dDrB+NVRo8IyVek5arWdVGDu+ekRLfcCMFAjMRq9rWj+KZ5ragvjRKafA5b6Ukr35qfFxvRwcbwKGdctyYXhtWRcz7e9066AgEDUH0Fmv9xAawfSY21Xal3ucuOOv0C/ZtrTt2aHM158aM148cOJxdb1WB/m5zXg0MDnzIqYNt8e+3j5/KX7LieEpa/UvYCAQCgSGNtVP2HuPn47Ji8dD8C1MP7Pzj+LjezW2OQCAQ1EjFR81tQQOwb2u99YORol6BoKUjYrWtn+xreHRsbiPqjp+Py8oloVmnJzm0U/4p4dPmNkcgEAhqZNwA9s6mR0trRJCREj9r2uOuzm1qF60Nf1/XHRuj8s5NKctPzP7uxfdXDPfq7thwtQKBQKDFIKnWoEVjqydsiEfuDy+Nj+vt7NTGQoGrc5uEZ32yv3vRz8eluU0TCASC5qaxtjBkpMSHDupqMBgxzPPQ7qcbZJ9AIBC0AhprC0N5watKa8NnEkpri9K8mQ2yTyCoC+KxWOvHyoL5o8laQfkWeeTgXFwdmtUm82iULQzZOTcih/cwGAwOdC+8fsuovEAgENSHFfEsG497B218dm4se2c3q01mED6029n0iVK92ohhnvXWMzrqYdXVl6e90NfN1c5CgaODzdjRj+Sdm/La9BaYbCxosYjdYq2fovW4Tqb8Lupt8g4xOyUF72M/sZkNuzeYk5Nbj0a8AkGdED1EWz8VlVSq9UYsFJTebiZr7jnCjQruB4Srbf2s+h9jnmDPKQArC1zbM3EI6442t1mmMboOFR5TIBDc70wJ4/Q/KP2Qss1kvs2syOY2yAy2vBcZNaKH1DE3+d2IeuvZvj7K2clwH4SzU5vt66MaZqBAIBC0fAovTwsf2s3KUhE1okfBpan11jMm2jszbcKYaO8aRgQCgaChtLjeYhKxkV5Sx9yz6RMbkoEAODu12bExavv6qF4PO21fH7Vjo5F1rkAgEDSIB7y3mIbkdyPUqqQNq0Y0tyGCBxGxhaHV0tJ7izUi0qrWzs7q0SeT7dtai1WtQCBofB6cyjJGEbFagUAgaHKMrmGldW6z2CMQCAQCgaBJEBtzWz8ttI2jQCAQtCRaaBvHRiQooPOuzdH5F6ZWFM4CMlLix45+pLmNEggErYsW3cax4USN6JHz/eQx0d6ODjbSlt+ggM5n0ydOnuDb3KYJBIJWRNaKFtnGsbHITJswMOgh6VhTXcHf1/VKxqTmM0ogELQ6Wmgbx8aiLD/RylJ+JqFxtUpri7L8xOYzSvDAISp7tX4+mg4wsp/h+APyWOz8RVXoYI+DR3/UHQwL6Xb+oqq5TBIIBILWRsQwz7xzU8bH9ZZitQ7tlOPG9Mq/MDVqhGEXHIGg6RDJXoLWT6C/25xXA4MDH3LqYFv8++3jp/KXrDiekpbb3HYJHiCEq2399HJn8ViCHsa5HdaWANeKCV9Mxo+1zRQIBAKBmaQuYFYkTm2xUGChwKktsyLZITpzCwQCQSNS+iG21nojttao1jWTNQLBA4kootj6KbuDhX6gyMrCsLGjQCBoUiyb2wBBk9PRnlAfzuZSUoZCwUNOzP0DJy5zKLO5LRMIHhjEY7HWj4WCxKeYGIJXJ4CsX9lwlBX7m9ssgUAgEAgEAoFAIBAIBPcHAx/h5GK83bQjkX5sefmBrj4jEAgEjUlgTwo/YOIQrPTTTPo8xNlleLo0k1kCgUDQmjgwh4lDjF+aPoKtr9xbawQCgaBVUrwRBxMduN3aU/jBvbVGIHiwEVsYWi1KK0pvG79U+Dv2tvfWGoHgwUa42lZL5k/4eRq/5OtBVv49NUYgEAhaJ5OHse91wy25Entn81rUPTdIIHiAERtzWy2nrjLqcV4eQWExqpuUV+DcjmE+rHmBW+UkbkJUQRAI7hliY24rZ9wAJg3FzxOHNqhKOHmFTSls/7q5zRIIBAKBQCAQCAQCgUAgEAgEAoFAIBAIBAKBQCAQCAQCgUAgEAgEAoFAIBAIBAKBQCBocfx/BNG53+zWV/wAAAAASUVORK5CYII=" border="0"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Get the code&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The source code of the complete script can be found on &lt;a href="http://github.com/sixty-nine/PHP_Word_Cloud"&gt;GITHub&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-1867980206680314423?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/1867980206680314423/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2010/11/wordle-like-php-script.html#comment-form' title='6 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/1867980206680314423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/1867980206680314423'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2010/11/wordle-like-php-script.html' title='Wordle-like PHP script'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-7431163893275358795</id><published>2008-12-27T02:35:00.001+01:00</published><updated>2008-12-27T02:36:39.002+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Intelligence artificielle'/><title type='text'>AI-Junkie</title><content type='html'>Réseaux neuronaux, algoritmes génétiques, agents, une bonne source d'information sur l'intelligence artificielle.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ai-junkie.com/"&gt;AI-Junkie&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-7431163893275358795?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/7431163893275358795/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2008/12/ai-junkie.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7431163893275358795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/7431163893275358795'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2008/12/ai-junkie.html' title='AI-Junkie'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5537231430235165593.post-1032087613208885985</id><published>2008-12-26T23:13:00.004+01:00</published><updated>2011-03-09T19:05:16.906+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Validation'/><category scheme='http://www.blogger.com/atom/ns#' term='MonoRail'/><title type='text'>fValidate - Type Reference</title><content type='html'>Référence de la syntaxe des validateurs de la bibliothèque JavaScript fValidate.&lt;br /&gt;&lt;br /&gt;A utiliser avec le composant de validation fourni avec MonoRail.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://web.archive.org/web/20070809054342/http://www.phil-taylor.com/fvalidate/"&gt;fValidate - Type Reference&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5537231430235165593-1032087613208885985?l=aventures-logicielles.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://aventures-logicielles.blogspot.com/feeds/1032087613208885985/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://aventures-logicielles.blogspot.com/2008/12/fvalidate-type-reference-high-quality.html#comment-form' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/1032087613208885985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5537231430235165593/posts/default/1032087613208885985'/><link rel='alternate' type='text/html' href='http://aventures-logicielles.blogspot.com/2008/12/fvalidate-type-reference-high-quality.html' title='fValidate - Type Reference'/><author><name>dan</name><uri>http://www.blogger.com/profile/15238025568393454907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
