tag:blogger.com,1999:blog-55266998563899825812024-02-18T20:56:43.972-08:00Go Data InfrasBig Data and related stuff.
Things that took me hours and days to implement, and that
would hopefully take you less.moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.comBlogger24125tag:blogger.com,1999:blog-5526699856389982581.post-61535523576504953692018-05-22T05:33:00.002-07:002018-05-22T05:33:53.698-07:00Spark dataframe json schema misinferring - String typed column instead of struct<div dir="rtl" style="text-align: right;" trbidi="on">
<div dir="ltr" style="text-align: left;">
All you wanted is to load some complex json files into a dataframe, </div>
<div dir="ltr" style="text-align: left;">
and use sql with [lateral view explode] function to parse the json.</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
Sounds like the basics of SparkSql.</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
A problem can arise when one of the inner fields of the json, </div>
<div dir="ltr" style="text-align: left;">
has undesired non-json values in some of the records. </div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
For instance, an inner field might contains HTTP errors, that would be interpreted as a string, rather than as a struct. </div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
As a result, our schema would look like:</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
root</div>
<div dir="ltr" style="text-align: left;">
|-- headers: struct (nullable = true)</div>
<div dir="ltr" style="text-align: left;">
| |-- items: array (nullable = true)</div>
<div dir="ltr" style="text-align: left;">
| | |-- element: struct (containsNull = true)</div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: red;"> |-- requestBody: string (nullable = true)</span></div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
Instead of </div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
root</div>
<div dir="ltr" style="text-align: left;">
|-- headers: struct (nullable = true)</div>
<div dir="ltr" style="text-align: left;">
| |-- items: array (nullable = true)</div>
<div dir="ltr" style="text-align: left;">
| | |-- element: struct (containsNull = true)</div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: lime;"> |-- requestBody: struct (nullable = true)</span></div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: lime;"> | |-- items: array (nullable = true)</span></div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: lime;"> | | |-- element: struct (containsNull = true)</span></div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: lime;"><br /></span></div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
When trying to explode a "string" type, we will get a miss type error:</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: white; color: #212121; font-family: Monaco, Menlo, "Ubuntu Mono", Consolas, source-code-pro, monospace; font-size: 12px; white-space: pre-wrap;">org.apache.spark.sql.AnalysisException: Can't extract value from </span><span style="background-color: lime;">requestBody#10</span></div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: lime;"><br /></span></div>
<div dir="ltr" style="text-align: left;">
<span style="background-color: lime;"><br /></span></div>
<div dir="ltr" style="text-align: left;">
How can we remove the non-json values and still, get the correct schema in the dataframe?</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
Removing the non-jsons values, using string filtering with SparkSql on requestBody column, </div>
<div dir="ltr" style="text-align: left;">
will clean the data, but won't change the type of the column, and we will still not be able to use json functions on the column.</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
In order to clean the data and then, getting the right schema, </div>
<div dir="ltr" style="text-align: left;">
we should load the dataset into a RDD, filtering out bad rows, and creating a dataframe out of the clean RDD:</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
Good solution: RDD -> Cleansing -> Dataframe ( using spark.read.json(cleanRDD)</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
A bad solution would be to load the data as a dataframe. The requestBody column will be set as a String. Now we can filter out bad records, and store the dataframe back to disk.</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
At that point, the value of the string typed <i>requestBody</i> will be encapsulated with quotes, and any future effort to load the "fixed" dataset into a dataframe, would set this column's type to be string.</div>
<div dir="ltr" style="text-align: left;">
With that, we will have to reload the "fixed" data into a RDD, and cleaning out the encapsulated string, using replace command:</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
val fixedRDD=badRDD.map(_.replace("\\\"","\"")).map(_.replace("\"{","{")).map(_.replace("}\"","}"))</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
From that point we can use spark.read.json to get a valid dataframe, and querying it with explode function!</div>
<div dir="ltr" style="text-align: left;">
<br /></div>
<div dir="ltr" style="text-align: left;">
Good luck</div>
<div dir="ltr" style="text-align: left;">
and don't forget that sometimes, RDD is the right way to clean the data, before getting it ready and clean for higher level APIs, such as dataframes.</div>
</div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-68373541933719961992018-01-27T09:58:00.002-08:002018-01-27T09:58:57.905-08:00SolrCloud - suggestions for leaders rebalancing<!-- HTML generated using hilite.me --><br />
This is a useful and simple bash script that helps rebalancing the cluster leaders, in case of having more than one leader of the same collection, hosted on the same node.<br />
<br />
Hosting more than 1 leader of the same collection on the same node (2 different shards leaders) is not a good practice, as it is not helping distributing the writes load for the collection.<br />
<br />
The script would suggest the next action -> move from node A to B, the leader of shard X, of collection Y. What it actually means is -> add node B to shard X, and remove node A from shard X of collection Y. It would ask you to re-run the script afterwards, and get the next required actions, based on the new solr cluster status.<br />
<br />
If it doesn't find any collection that has more than 1 leader hosted on the same node, it won't suggest anything.<br />
<br />
In Solr 7, new rules for cluster auto scaling and management were added, and it is worth checking them as well.<br />
<br />
The script contains many useful commands for playing and parsing the "clusterstatus" output.<br />
You might add suggestion for general rebalancing of the cluster, in case of, for instance, too many leaders that are hosted on few nodes, while other nodes are not hosting any leader.<br />
<br />
It only requires JQ installed on the running machine.<br />
<br />
example for execution: ./solr-suggest.sh "https://solr-node01"<br />
<br />
<br />
<br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;">#!/bin/bash</span>
<span style="color: #996633;">URL</span><span style="color: #333333;">=</span><span style="background-color: #fff0f0;">"$1"</span>; <span style="color: #007020;">shift</span>
<span style="color: #996633;">raw_cluster_status</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span>curl -s -k <span style="background-color: #fff0f0;">"${URL}/solr/admin/collections?action=CLUSTERSTATUS&wt=json"</span> | <span style="background-color: #fff0f0; color: #666666; font-weight: bold;">\</span>
jq <span style="background-color: #fff0f0;">'.cluster.collections[].shards[].replicas[]'</span><span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #996633;">all_nodes</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"${raw_cluster_status}"</span> | jq <span style="background-color: #fff0f0;">'. | "\(.node_name)" '</span> | sort | uniq | tr -d <span style="background-color: #fff0f0;">"\""</span> | awk -F <span style="background-color: #fff0f0;">':'</span> <span style="background-color: #fff0f0;">'{print $1}'</span><span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #996633;">all_leaders_sorted</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"${raw_cluster_status}"</span> | jq <span style="background-color: #fff0f0;">'. | select(.leader=="true") | "\(.node_name)" '</span> | tr -d <span style="background-color: #fff0f0;">'"'</span> | sort| uniq -c | sort | tr -s <span style="background-color: #fff0f0;">" "</span> | tr <span style="background-color: #fff0f0;">" "</span> <span style="background-color: #fff0f0;">"-"</span> | awk -F <span style="background-color: #fff0f0;">':'</span> <span style="background-color: #fff0f0;">'{print $1}'</span><span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #996633;">all_leaders_sorted_reveres</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"${raw_cluster_status}"</span> | jq <span style="background-color: #fff0f0;">'. | select(.leader=="true") | "\(.node_name)" '</span> | tr -d <span style="background-color: #fff0f0;">'"'</span> | sort| uniq -c | sort -r | tr -s <span style="background-color: #fff0f0;">" "</span> | tr <span style="background-color: #fff0f0;">" "</span> <span style="background-color: #fff0f0;">"-"</span> | awk -F <span style="background-color: #fff0f0;">':'</span> <span style="background-color: #fff0f0;">'{print $1}'</span><span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #996633;">no_leader_nodes</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span>diff <<span style="color: #333333;">(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$all_nodes"</span> | sort <span style="color: #008800; font-weight: bold;">)</span> <<span style="color: #333333;">(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$all_leaders_sorted_reveres"</span> | awk -F <span style="background-color: #fff0f0;">'-'</span> <span style="background-color: #fff0f0;">'{print $3}'</span> | sort<span style="color: #333333;">)</span> | tr -d <span style="background-color: #fff0f0;">"<"</span> | tr -d <span style="background-color: #fff0f0;">" "</span> | sed <span style="background-color: #fff0f0;">'s/^...$//g'</span> | grep -v -e <span style="background-color: #fff0f0;">'^$'</span> <span style="color: #333333;">)</span>
<span style="color: #996633;">leader_host_to_shards</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"${raw_cluster_status}"</span> | jq <span style="background-color: #fff0f0;">'. | select(.leader=="true") | "\(.core) | \(.node_name)"'</span> | tr -d <span style="background-color: #fff0f0;">" "</span> | awk -F <span style="background-color: #fff0f0;">'|'</span> <span style="background-color: #fff0f0;">'{gsub("\"","",$1); gsub("\"","",$2); split($1,coll,"_replica"); split($2,coll2,":"); print coll[1]"@"coll2[1]}'</span><span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #996633;">all_collections</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"${raw_cluster_status}"</span> | jq <span style="background-color: #fff0f0;">'. | "\(.core)" '</span> | sed <span style="background-color: #fff0f0;">'s/\"\(.*\)_shard.*/\1/'</span> | sort | uniq <span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #996633;">nodes_sorted_tmp</span><span style="color: #333333;">=</span>/tmp/nodes_sorted.tmp
rm <span style="color: #996633;">$nodes_sorted_tmp</span> 2>/dev/null
<span style="color: #008800; font-weight: bold;">for </span>leader in <span style="color: #996633;">$no_leader_nodes</span>; <span style="color: #008800; font-weight: bold;">do </span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"-0-$leader"</span> >> <span style="color: #996633;">$nodes_sorted_tmp</span>; <span style="color: #008800; font-weight: bold;">done</span>
<span style="color: #008800; font-weight: bold;">for </span>leader in <span style="color: #996633;">$all_leaders_sorted</span>; <span style="color: #008800; font-weight: bold;">do </span><span style="color: #007020;">echo</span> <span style="color: #996633;">$leader</span> >> <span style="color: #996633;">$nodes_sorted_tmp</span>; <span style="color: #008800; font-weight: bold;">done</span>
<span style="color: #996633;">all_nodes_sorted</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span>cat <span style="color: #996633;">$nodes_sorted_tmp</span><span style="color: #008800; font-weight: bold;">)</span>
rm <span style="color: #996633;">$nodes_sorted_tmp</span> 2>/dev/null
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"-----------------------------"</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"amount of leaders per node:"</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"${all_nodes_sorted}"</span> | tr -s <span style="background-color: #fff0f0;">"-"</span> <span style="background-color: #fff0f0;">" "</span>
<span style="color: #007020;">echo</span> -e <span style="background-color: #fff0f0;">"\n---------------------SUGGESTIONS FOR RE-BALANCE NODES THAT HOST MORE THAN ONE LEADER OF THE SAME COLLECTION---------------------"</span>
<span style="color: #008800; font-weight: bold;">for </span>col in <span style="color: #996633;">$all_collections</span>; <span style="color: #008800; font-weight: bold;">do</span>
<span style="color: #008800; font-weight: bold;"> </span><span style="color: #996633;">collection_leaders</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"${leader_host_to_shards}"</span> | grep <span style="color: #996633;">$col</span><span style="background-color: #fff0f0;">"_shard"</span> <span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #996633;">bad_nodes</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$collection_leaders"</span> | awk -F <span style="background-color: #fff0f0;">'@'</span> <span style="background-color: #fff0f0;">'{print $2}'</span> | sort | uniq -c | grep -v <span style="background-color: #fff0f0;">"1 "</span> <span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #008800; font-weight: bold;">for </span>bad_node in <span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$bad_nodes"</span> | awk <span style="background-color: #fff0f0;">'{print $2}'</span><span style="color: #008800; font-weight: bold;">)</span>; <span style="color: #008800; font-weight: bold;">do</span>
<span style="color: #008800; font-weight: bold;"> </span><span style="color: #996633;">related_shards</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$collection_leaders"</span> | grep <span style="color: #996633;">$bad_node</span> | awk -F <span style="background-color: #fff0f0;">'@'</span> <span style="background-color: #fff0f0;">'{split($1,srd ,"shard"); print "shard"srd[2] }'</span> <span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #008800; font-weight: bold;">for </span>shard in <span style="color: #996633;">$related_shards</span>; <span style="color: #008800; font-weight: bold;">do</span>
<span style="color: #008800; font-weight: bold;"> </span><span style="color: #007020;">echo</span> <span style="color: #996633;">$shard</span>
<span style="color: #996633;">shard_nodes</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="color: #996633;">$raw_cluster_status</span> | jq <span style="background-color: #fff0f0;">'. | select(.core | contains("'</span><span style="color: #008800; font-weight: bold;">${</span><span style="color: #996633;">col</span><span style="color: #008800; font-weight: bold;">}</span><span style="background-color: #fff0f0;">'_'</span><span style="color: #008800; font-weight: bold;">${</span><span style="color: #996633;">shard</span><span style="color: #008800; font-weight: bold;">}</span><span style="background-color: #fff0f0;">'")) | .node_name'</span><span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #008800; font-weight: bold;">for </span>inode in <span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$all_nodes_sorted"</span><span style="color: #008800; font-weight: bold;">)</span>; <span style="color: #008800; font-weight: bold;">do</span>
<span style="color: #008800; font-weight: bold;"> </span><span style="color: #996633;">node</span><span style="color: #333333;">=</span><span style="color: #008800; font-weight: bold;">$(</span><span style="color: #007020;">echo</span> <span style="color: #996633;">$inode</span> | awk -F <span style="background-color: #fff0f0;">'-'</span> <span style="background-color: #fff0f0;">'{print $3}'</span><span style="color: #008800; font-weight: bold;">)</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"checking candidate $node"</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$shard_nodes"</span>
<span style="color: #888888;">#check if candidate node is not part of the shard</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$shard_nodes"</span> | grep <span style="color: #996633;">$node</span> > /dev/null
<span style="color: #008800; font-weight: bold;">if</span> <span style="color: #333333;">[</span> <span style="color: #996633;">$?</span> -eq 1 <span style="color: #333333;">]</span>; <span style="color: #008800; font-weight: bold;">then</span>
<span style="color: #888888;">#check if candidate id</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"$collection_leaders"</span> | grep <span style="color: #996633;">$node</span> > /dev/null
<span style="color: #008800; font-weight: bold;">if</span> <span style="color: #333333;">[</span> <span style="color: #996633;">$?</span> -eq 1 <span style="color: #333333;">]</span>; <span style="color: #008800; font-weight: bold;">then</span>
<span style="color: #888888;">#the node with the least number of leaders and is not a member of the problematic shard -> replace the current leader with this node</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"Node $bad_node is hosting more than 1 leader on collecition - $col, shard - $shard "</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"___________________________________________________________________________________________________________"</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"Move the replica that is hosted on $bad_node to $node, which has the least number of leaders on it."</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"___________________________________________________________________________________________________________"</span>
<span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"Re-run this script again after switching nodes for the replica"</span>
<span style="color: #007020;">exit</span>
<span style="color: #007020;"> </span><span style="color: #008800; font-weight: bold;">fi</span>
<span style="color: #008800; font-weight: bold;"> fi</span>
<span style="color: #008800; font-weight: bold;"> done</span>
<span style="color: #008800; font-weight: bold;"> done</span>
<span style="color: #008800; font-weight: bold;"> </span><span style="color: #007020;">echo</span> <span style="background-color: #fff0f0;">"Couldn't find a replacement host for $bad_node on $col - $shard. Remove this replica and add it so that the leader would move to a different host"</span>
<span style="color: #008800; font-weight: bold;">done</span>
<span style="color: #008800; font-weight: bold;">done</span>
<span style="color: #007020;">echo</span> -e <span style="background-color: #fff0f0;">"\nGreat. All nodes are hosting at most one leader of the same collection.\n"</span>
</pre>
</div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-53917230250626109182017-12-29T05:52:00.001-08:002017-12-29T05:52:26.660-08:00Apache Zeppelin as a Web Querying Interface for CassandraHi!<br />
<br />
An important thing that you are missing when starting to use Cassandra is a decent web tool<br />
for executing CQL queries, and browsing the results.<br />
<br />
Googling for a "Cassandra web ui" results in RazorSql, and some other not so promising engines, where most of them are not a web UI at all.<br />
<br />
<br />
As you can tell, by the post's title, Apache Zeppelin turned out to be a great solution for our need.<br />
It has a built in<a href="https://zeppelin.apache.org/docs/0.7.0/interpreter/cassandra.html"> Cassandra Interpreter</a>, which only requires basic connection properties.<br />
<br />
Here is a <a href="https://www.youtube.com/watch?v=eQN10ZkReMw">video explaining all about Zeppelin and Cassandra </a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN960YJKQl01zFIjtgltjuRzSL9uQhTASRj25WdJQFhzn0zjiRfpNU52ieuqRMhX4fAdVAsDI9xdT7LPNI3m4FThYTyKEs8PNtwoaN1p2c-1ATA3Sgf0ajhKF3zf0-yWySgC4ZTZ5nvQE/s1600/zeppelinCassandra.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="397" data-original-width="1347" height="187" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN960YJKQl01zFIjtgltjuRzSL9uQhTASRj25WdJQFhzn0zjiRfpNU52ieuqRMhX4fAdVAsDI9xdT7LPNI3m4FThYTyKEs8PNtwoaN1p2c-1ATA3Sgf0ajhKF3zf0-yWySgC4ZTZ5nvQE/s640/zeppelinCassandra.jpg" width="640" /></a></div>
<br />
<br />
We did had one issue with installing Zeppelin on our VPC, and trying to access it via Nginx and AWS load balancer:<br />
<br />
<ul>
<li>Apache Zeppelin is using a web socket for a client-server connectivity. </li>
<li>It requires special configuration on Nginx: <a href="https://zeppelin.apache.org/docs/0.6.2/security/authentication.html">how to configure Apache Zeppelin on Nginx</a></li>
<li>It enforced us to use Application Load Balancer, instead of Classic Load Balancer, which doesn't support web sockets out of the box. </li>
</ul>
<div>
<br /></div>
<div>
As a result, we no longer have to ssh Cassandra nodes, in order to execute CQL queries.</div>
<div>
<br /></div>
<div>
I hope that everyone who is looking for a CQL web ui, would find his answer here (or a better answer somewhere else :) ).</div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-32260671730885216182017-12-20T08:01:00.000-08:002017-12-20T08:02:52.905-08:00Cassandra as a DockerWe dockerize our in house micro services, as well as 3rd party tools we use.<br />
That includes Apache Solr, Zookeeper, Redis and many more.<br />
<br />
The recent datastore we implemented is Apache Cassandra, version 3.11,<br />
and I would like to share some of the hacks we had until getting our Cassandra cluster dockerized and running.<br />
<br />
The next configurations support a cluster where each node is running on a separate host. For running multiple Cassandra nodes on the same host, such as in development mode, you will need to modify the configurations a bit.<br />
<br />
<div>
<br /></div>
The configurations are located in cassandra.yaml file and will be modified as part of the docker start script (entry point).<br />
<ul>
<li>Set the required addresses:</li>
<ul>
<li>listen_address - The address that Cassandra process will bind to. That should be the docker internal IP.</li>
<li>broadcast_address - The address that Cassandra will publish to other nodes for nodes inter connectivity. That should be the host external IP.</li>
<li>broadcast_rpc_address - The address that nodes will publish to connected clients. That, as well as the broadcast_address, should be the external ip.</li>
</ul>
</ul>
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: "andale mono" , "lucida console" , "monaco" , "fixed" , monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"> <code style="color: black; word-wrap: normal;">
sed -i 's/listen_address: [0-9a-z].*/listen_address: '${INTERNAL_IP}'/' conf/cassandra.yaml
sed -i 's/broadcast_address: [0-9a-z].*/broadcast_address: '${EXTERNAL_IP}'/' conf/cassandra.yaml
sed -i 's/broadcast_rpc_address: [0-9a-z].*/broadcast_rpc_address: '${EXTERNAL_IP}'/' conf/cassandra.yaml
</code></pre>
<br />
<br />
<ul>
<li>Docker running params </li>
<ul>
<li>Data Volumes - map the storage location to a permanent storage:</li>
<ul>
<li>-v /data/cassandra-0:/data</li>
</ul>
</ul>
<ul>
<li>Each node has to map its inner Cassandra ports for external connections. Cassandra nodes / Clients will look for that ports when accessing the broadcast_address of other nodes.</li>
<ul>
<li>I didn't find a way to change the default 7000 and 9042 ports</li>
<li>The ports are:</li>
<ul>
<li>7000 - Cassandra inter-node cluster communication.</li>
<li>9042 - Cassandra client port.</li>
<li>9160 - Cassandra client port (Thrift).</li>
<li>The best would be to play with the ports mapping and see for yourself, which is mandatory and which is not.</li>
</ul>
<li>Bottom line - Add -P -p 7000:7000 -p 9042:9042 to the docker run command.</li>
</ul>
</ul>
</ul>
<ul>
<li>Seed list</li>
<ul>
<li>This is tricky. Seeds hosts are a list of given host that will be used by the node to discover and join the cluster.</li>
<li>From Datastax docs:</li>
<ul>
<li>Do not make all nodes seed nodes</li>
<li>Note: It is a best practice to have more than one seed node per datacenter.</li>
<li>The seed node designation has no purpose other than bootstrapping the gossip process for new nodes joining the cluster.</li>
<li>Please read more about Seeds mechanism: https://docs.datastax.com/en/cassandra/3.0/cassandra/architecture/archGossipAbout.html</li>
</ul>
<li>Docker challenges: </li>
<ul>
<li>Our Cassandra cluster is in some way, a micro services cluster. Any docker can die and another will start on a different host.</li>
<li>How can we avoid cases where a new node is accessing a seed node that is about to disappear, and then, live alone in a separate cluster, while all other nodes were connecting different seeds?</li>
<li>We can't set a final list of seeds ips. </li>
<li><b>Solution</b> - </li>
<ul>
<li>X Cassandra docker will be deployed with a "seed" tag.</li>
<li>The "seed" tag is a hack that we implemented on top of our micro services management system. For example, Lables can be used with Kuberenetes, to set 3 Cassandra pods to start on a "seed" labeled node. All other Cassandra pods will start without a "seed" label. </li>
<li>The Seed list is dynamic. As a docker starts up, and before starting the Cassandra node process on it, it will look for X other nodes that were tagged with a "seed" label. I assume you have a service discovery tool, that will support that step.</li>
<li>The retrieved seed labeled ips, will be set as the "seeds" param for the starting node.</li>
<li>We will keep looking for at least X seed nodes in our service discovery endpoint, and will not start Cassandra process until getting enough seeds.</li>
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: "andale mono" , "lucida console" , "monaco" , "fixed" , monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code style="color: black; word-wrap: normal;">
sed -i 's/seeds: \"[0-9.].*\"/seeds: \"'${seeds_list}'\"/' conf/cassandra.yaml
</code></pre>
<li>Now, we ensure that all nodes will use the same ips as a seeds list. There is a problem with loosing all seeds labeled nodes at the same time, and such a case would require a manual solution. </li>
<li>Other solutions might include selecting seed nodes according to custom rules that will ensure a consistent seed nodes selection by all nodes. For example - picking the 3 hosts that have the highest ip, when sorting all Cassandra service hosts ips (pods in Kuberenetes). </li>
</ul>
</ul>
</ul>
<li>Setting password and username </li>
<ul>
<li>We are willing to avoid any manual intervention. The first node would have to run a CQL script in order to change the default credentials. </li>
<li>Take that in account and execute that script after launching Cassandra process, and once the CQL terminal is ready.</li>
</ul>
<li>Adding new nodes - The docker start script should also take care of new nodes that are joining an existing cluster.</li>
<ul>
<li>They should remove any dead nodes (otherwise the ew node will fail joining) </li>
<li>I am sure that you will find some other scenarios when executing disruptive tests.</li>
</ul>
<li>Concerns and Todos - </li>
<ul>
<li>Depends on you docker version, make sure you are satisfied with performances. It is known that IO can get <a href="https://github.com/moby/moby/issues/21485">slower with docker</a>. </li>
<li>Configure a cross datacenters cluster, where each datacenter has a different service discovery endpoint.</li>
<li>Run a cluster on the same node</li>
<li>Check Docker network to see if it is helpful for that use case. </li>
<li>Publish our docker files.</li>
</ul>
</ul>
<div>
<br /></div>
<div>
To conduct, it is possible to run a Dockerized Cassandra cluster. Pay attention to the required params in the cassandra.yaml file, and add any custom behaviour, especially for the seeds selection, to the docker start script. </div>
<div>
<br /></div>
<div>
In the next article I will present the way our Cassandra java client is accessing the cluster. </div>
<div>
<br /></div>
<div>
Good luck!</div>
<div>
<br /></div>
<div>
<br /></div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-23829162129175166912017-12-20T05:18:00.002-08:002017-12-20T05:18:34.014-08:00Apache Solr Cloud Sanity - 3 simple sanity testsHaving a clear picture of your services status is a fundamental requirement.<div>
Otherwise you are blind to the system. </div>
<div>
<br /></div>
<div>
I am going to present a general concept of sanity checks and some more specific Solr database sanity checks. </div>
<div>
<br /></div>
<div>
There are many tools and methods to monitor a running service, whether it is a micro service, a 3rd party database or any other piece of code.</div>
<div>
<br /></div>
<div>
We use Datadog, and Grafana to have the system fully covered and to dive into relevant metrics when needed.</div>
<div>
<br /></div>
<div>
In addition to these too well known tools, we implement, for each in-house service or a 3rd party tool, a custom sanity test, that in a failure we know we have a problem.</div>
<div>
The sanity tests result are being collected by a centralised monitoring service and they are all being displayed in a monitoring dashboard. </div>
<div>
The dashboard has many features, as have been probably thinking of by yourself, such as deep dive into sanity test logs, get history of sanity tests and so on.</div>
<div>
<br /></div>
<div>
Let's take apache Solr as an example. </div>
<div>
In addition to our Solr docker instance, we deploy a sanity script, for each solr instance. </div>
<div>
<br /></div>
<div>
We defined 3 sanity tests - They are all based on Solr cluster status output:</div>
<div>
<ol>
<li>Replica status - If a replica is down or recovering, we mark the hosting solr instance of that replica as problematic. </li>
<li>Balanced leaders within Collection - If a solr server is hosting more than 1 leader of shards from the same collection, it is also a problem (it might overload that host).</li>
<li>Not too many leaders per host - If a Solr server is hosting more than X leaders, we are marking the host as problematic. </li>
</ol>
<div>
<br /></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMmgjePr1ry6uJRmovCE25BPLXeNNgeMoYCRKWQ0HPCvJNL6vQKr9za1neKetS2FYX9EZo4V-BUUUn9P4E9eLRR5CiDiAPxk4p_9etLq80qs9dgW9NtQpnSjmxh-NZ0f_BU6pj97noixs/s1600/sanitysolr.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="211" data-original-width="877" height="152" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMmgjePr1ry6uJRmovCE25BPLXeNNgeMoYCRKWQ0HPCvJNL6vQKr9za1neKetS2FYX9EZo4V-BUUUn9P4E9eLRR5CiDiAPxk4p_9etLq80qs9dgW9NtQpnSjmxh-NZ0f_BU6pj97noixs/s640/sanitysolr.jpg" width="640" /></a></div>
<div>
<br /></div>
<div>
The test is running every M minutes (15 for Solr test).</div>
<div>
<br /></div>
<div>
To conclude, </div>
<div>
a custom sanity test is a simple and an efficient way to be aware of a service status. Our monitoring procedure starts from the centralised sanity dashboard, and only then goes into Datadog or Grafana. </div>
<div>
<br /></div>
<div>
<br /></div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-50432359176691945822016-05-05T04:26:00.003-07:002016-05-11T22:43:45.241-07:00Spark ClickStream with Talend <div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Ofer Habushi, a friend of mine, that is working as a senior consultant for Talend (an open source Data Integration Software company), </span></div>
<span style="font-family: "arial"; line-height: 1.38; white-space: pre-wrap;">was asked by one of his customers, a major e-commerce company, to present a solution for aggregating huge files of clickstream data on Hadoop (</span><span style="color: black; font-family: "arial"; vertical-align: baseline; white-space: pre-wrap;">Ofer’s </span><a href="https://www.youtube.com/watch?v=9x75hVd2h64" style="line-height: 22.0799999237061px; text-decoration: none;"><span style="color: blue; font-family: "arial"; line-height: 22.08px; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">youtube</span><span style="color: black; font-family: "arial"; line-height: 22.08px; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> video</span></a><span style="font-family: "arial"; line-height: 1.38; white-space: pre-wrap;">). </span><br />
<span id="docs-internal-guid-552fdd3b-8098-06fe-ba01-6355781fb214"></span><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-552fdd3b-8098-d01a-65d1-a8181ae65242"></span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">With a given huge website’s clickstream file (>100 GB or even Teras), Aggregate and create session records for all users (userId) and for all of their clicks which were made within 30 minutes of their previous click on the website</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">If the user was not active in the last 30 minutes -> create a new sessionID.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Else -> use the same sessionID.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><b style="font-weight: normal;"><br /></b></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sample Input:</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><b style="font-weight: normal;"><br /></b></span></div>
<div style="margin-left: 0pt;">
<table style="border-collapse: collapse; border: none;"><colgroup><col width="139"></col><col width="164"></col><col width="206"></col></colgroup><tbody>
<tr style="height: 0px;"><td style="background-color: white; border-bottom: solid #9cc2e5 2px; border-left: solid #000000 1px; border-right: solid #000000 1px; border-top: solid #000000 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">userID</span></div>
</td><td style="background-color: white; border-bottom: solid #9cc2e5 2px; border-left: solid #000000 1px; border-right: solid #000000 1px; border-top: solid #000000 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">visitStartDate</span></div>
</td><td style="background-color: white; border-bottom: solid #9cc2e5 2px; border-left: solid #000000 1px; border-right: solid #000000 1px; border-top: solid #000000 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">visitEndDate</span></div>
</td></tr>
<tr style="height: 0px;"><td style="background-color: #deeaf6; border-bottom: solid #9cc2e5 1px; border-left: solid #000000 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 2px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #deeaf6; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user1</span></div>
</td><td style="background-color: #deeaf6; border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 2px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #deeaf6; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:48</span></div>
</td><td style="background-color: #deeaf6; border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #000000 1px; border-top: solid #9cc2e5 2px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #deeaf6; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:48</span></div>
</td></tr>
<tr style="height: 0px;"><td style="border-bottom: solid #9cc2e5 1px; border-left: solid #000000 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user1</span></div>
</td><td style="border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:57</span></div>
</td><td style="border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #000000 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:57</span></div>
</td></tr>
<tr style="height: 0px;"><td style="background-color: #deeaf6; border-bottom: solid #9cc2e5 1px; border-left: solid #000000 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #deeaf6; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user2</span></div>
</td><td style="background-color: #deeaf6; border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #deeaf6; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:40</span></div>
</td><td style="background-color: #deeaf6; border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #000000 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #deeaf6; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:40</span></div>
</td></tr>
<tr style="height: 0px;"><td style="border-bottom: solid #9cc2e5 1px; border-left: solid #000000 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user1</span></div>
</td><td style="border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #9cc2e5 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 06:57</span></div>
</td><td style="border-bottom: solid #9cc2e5 1px; border-left: solid #9cc2e5 1px; border-right: solid #000000 1px; border-top: solid #9cc2e5 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 06:57</span></div>
</td></tr>
</tbody></table>
<br /><br /><span style="font-family: arial; line-height: 22.08px; white-space: pre-wrap;">sample Output:</span><br /><br /><table style="border-collapse: collapse; border: none;"><colgroup><col width="147"></col><col width="90"></col><col width="157"></col><col width="158"></col></colgroup><tbody>
<tr style="height: 0px;"><td style="background-color: white; border-bottom: solid #8eaadb 2px; border-left: solid #000000 1px; border-right: solid #000000 1px; border-top: solid #000000 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">sessionID</span></div>
</td><td style="background-color: white; border-bottom: solid #8eaadb 2px; border-left: solid #000000 1px; border-right: solid #000000 1px; border-top: solid #000000 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">userID</span></div>
</td><td style="background-color: white; border-bottom: solid #8eaadb 2px; border-left: solid #000000 1px; border-right: solid #000000 1px; border-top: solid #000000 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">visitStartDate</span></div>
</td><td style="background-color: white; border-bottom: solid #8eaadb 2px; border-left: solid #000000 1px; border-right: solid #000000 1px; border-top: solid #000000 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: white; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">visitEndDate</span></div>
</td></tr>
<tr style="height: 0px;"><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #000000 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 2px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user1_04:48</span></div>
</td><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 2px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user1</span></div>
</td><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 2px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:48</span></div>
</td><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #000000 1px; border-top: solid #8eaadb 2px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:57</span></div>
</td></tr>
<tr style="height: 0px;"><td style="border-bottom: solid #8eaadb 1px; border-left: solid #000000 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user2_04:40</span></div>
</td><td style="border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user2</span></div>
</td><td style="border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:40</span></div>
</td><td style="border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #000000 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 04:40</span></div>
</td></tr>
<tr style="height: 0px;"><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #000000 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user1_06:57</span></div>
</td><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">user1</span></div>
</td><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #8eaadb 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 06:57</span></div>
</td><td style="background-color: #d9e2f3; border-bottom: solid #8eaadb 1px; border-left: solid #8eaadb 1px; border-right: solid #000000 1px; border-top: solid #8eaadb 1px; padding: 7px 7px 7px 7px; vertical-align: top;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: #d9e2f3; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">26/01/2016 06:57</span></div>
</td></tr>
</tbody></table>
</div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><b style="font-weight: normal;"><br /></b></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><b style="font-weight: normal;"><br /></b></span></div>
<div dir="ltr" style="margin-left: 0pt;">
<br /></div>
<div dir="ltr" style="margin-left: 0pt;">
<span id="docs-internal-guid-552fdd3b-8099-ade9-172a-6a1aa2b98bfe"></span><br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span id="docs-internal-guid-552fdd3b-8099-ade9-172a-6a1aa2b98bfe"><span style="font-family: "arial"; vertical-align: baseline; white-space: pre-wrap;">The company, that was already using an early version of Talend, had already implemented a solution for this problem using a mixture of three different languages in one process. <b>It was a mixture of Talend, Java , Hive with a Hive UDF in Python.</b></span></span></div>
<span id="docs-internal-guid-552fdd3b-8099-ade9-172a-6a1aa2b98bfe">
</span>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; vertical-align: baseline; white-space: pre-wrap;">With the coming of Talend v6.X, the company was interested in testing the capabilities of the new Talend Spark job feature.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; vertical-align: baseline; white-space: pre-wrap;">The goal was defined as following:</span></div>
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="font-family: arial; list-style-type: decimal; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="vertical-align: baseline; white-space: pre-wrap;">Build the same logic to achieve the same results.</span></div>
</li>
<li dir="ltr" style="font-family: arial; list-style-type: decimal; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="vertical-align: baseline; white-space: pre-wrap;">Reduce the multi language complexity (no udfs)</span></div>
</li>
<li dir="ltr" style="font-family: arial; list-style-type: decimal; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="vertical-align: baseline; white-space: pre-wrap;">Make the job graphical and readable as possible (reduce the code significantly).</span></div>
</li>
<li dir="ltr" style="font-family: arial; list-style-type: decimal; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="vertical-align: baseline; white-space: pre-wrap;">Improve or keep the current performance.</span></div>
</li>
</ol>
<div>
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span id="docs-internal-guid-552fdd3b-809f-6336-d2d9-bef9bd5effa1"><span style="font-family: "arial"; vertical-align: baseline; white-space: pre-wrap;">In order to solve that challenge, Ofer decided to use Talend which allows to design Spark jobs. Talend provides a graphical interface that generates, in this case, code based on Spark’s Java API. </span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b id="docs-internal-guid-552fdd3b-809c-23ca-d8ea-639740508fd5" style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Next, we’ll discuss what's needed in Talend and Spark in order to solve the clickstream use case. A use case that leads us to tackle spark shuffle, partitioning, and spark secondary sort.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">When Ofer introduced me the story, and asked me to review the Talend and Spark solution, I couldn’t tell if the underlying spark talend job would be similar to the solution that I would come with, writing plain spark.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">So</span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><b> I started designing the solution with spark, while asking Ofer, after every step, if Talend supports the method that I am using in the step.</b></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span></div>
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: arial; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Partition the data into userId’s partitions, because after having all the clicks of a user at the same partition we can create the users’ sessions in parallel and with no additional shuffles. </span></div>
</li>
<ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: arial; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: lower-alpha; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #ff9900; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The Spark way </span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">- </span><span style="background-color: #fbfbfb; color: #000088; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">partitionBy</span><span style="background-color: #fbfbfb; color: #555555; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: #fbfbfb; color: #006699; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">new</span><span style="background-color: #fbfbfb; color: #4a3c31; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: #fbfbfb; color: #00aa88; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">HashPartitioner</span><span style="background-color: #fbfbfb; color: #555555; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: #fbfbfb; color: #ff6600; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">100</span><span style="background-color: #fbfbfb; color: #555555; font-family: "verdana"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)) </span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">on a key-value rdd </span></div>
</li>
<li dir="ltr" style="background-color: transparent; color: black; font-family: arial; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: lower-alpha; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: blue; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The Talend way</span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> - Ofer found that Talend has a “tPartition” component, that is translated to a Spark “partitionBy”. Step 1 is checked!</span></div>
</li>
</ol>
</ol>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img alt="note19.png" height="67" src="https://lh6.googleusercontent.com/0qwQBC_3sZFix8mrnERC6hUzY6cqSNCWwUpj-BNadabbahsUoSzSwQrSx-3Llx69rL2pbBLZ4LuJo65JA9mc8M-hXmYYWSK5-pugINGwlMjAzejs1iNsivuI402KL8PpG91VWUoD" style="-webkit-transform: rotate(0.00rad); border: none; transform: rotate(0.00rad);" width="68" /></span></div>
<ol start="2" style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; font-family: arial; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: decimal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sort each partition by userId and by event time (</span><span style="background-color: transparent; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="color: #cc0000;">secondary sort</span></span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">). This is essential for understanding where a session starts and ends - we are going through the clicks of each user, until we find a click that comes at least 30 minutes after the previous click.</span></div>
</li>
</ol>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">An interesting question is how can we keep the data partitioned and sorted? Think about it - that’s not a simple challenge. When we sort the entire data set, we shuffle in order to get a sorted rdds and create new partitions which are different than the partitions we got from step 1. And what if we will do the opposite? First sort by creation time and then partition? We will encounter the same problem. The re-partitioning will cause shuffle and we will loose the sort. So how can we do that?</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Partition → Sort - losing the original partitioning</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sort → Partition - losing the original sort</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<ol style="margin-bottom: 0pt; margin-top: 0pt;"><ol style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: arial; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: lower-alpha; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #ff9900; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The spark way</span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> to partition and sort would be to use repartitionAndSortWithinPartitions. This is not too simple. </span></div>
</li>
</ol>
</ol>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The secondary sort is based on a composite key of the 2 columns and of a custom partitioner that will only partition by the first column. In this way, we get all the values of each </span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">real</span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> key sitting within the same partition. Now we get spark to sort all the partition elements by the combined key. With the next method that does both partitioning and sorting, together with a custom partitioner, we don't need step 1 anymore -</span><span style="background-color: transparent; color: #00aa88; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">repartitionAndSortWithinPartitions(partitioner) </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt; text-indent: 36pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The last thing that we need to do is to supply a custom comparator for the combined key because we can’t expect spark to know by itself how to sort the combination of number and date (the tuple is kind of a new type). repartitionAndSortWithinPartitions is defined by: </span><b><span style="background-color: #f7f7f7; color: #a71d5d; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">def</span><span style="background-color: #f7f7f7; color: #333333; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: #f7f7f7; color: #795da3; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">repartitionAndSortWithinPartitions</span><span style="background-color: #f7f7f7; color: #333333; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: #f7f7f7; color: #ed6a43; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">partitioner</span><span style="background-color: #f7f7f7; color: #333333; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">: </span><span style="background-color: #f7f7f7; color: #795da3; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Partitioner</span><span style="background-color: #f7f7f7; color: #333333; font-family: "verdana"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">So how do we set the comparator? For that we will create a new class that would stand as our combined key, together with an implementation of a custom comparator . You can find here a great </span><a href="http://codingjunkie.net/spark-secondary-sort/" style="text-decoration: none;"><span style="background-color: transparent; color: #1155cc; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">example for all of that</span></a><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">.</span></div>
<ol start="2" style="margin-bottom: 0pt; margin-top: 0pt;"><ol start="2" style="margin-bottom: 0pt; margin-top: 0pt;">
<li dir="ltr" style="background-color: transparent; color: black; font-family: arial; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: lower-alpha; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: blue; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Talend Way</span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> - I thought that there is no chance that Talend would support all of this headache. Ofer made some research and found it all. As we said, Talend has a tPartition component. Fortunately, it supports sorting together with the partitioning with the exact same method that I have used in Spark (repartitionAndSortWithinPartitions). </span></div>
</li>
</ol>
</ol>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<img alt="note21.png" src="https://lh3.googleusercontent.com/Bh_BCyWUK1dcbW89zpoC8abzdnseJrpKmqLo66GGVDEp2ZJfFcZyz22ov4nY1yjStpztjeXM0lK1xh9uSp4Tkc_u5hLRBeh4Jv-bx5Lg1VRW1ooNsilO5puzDvIdZMoxHC-pteGz" style="-webkit-transform: rotate(0rad); border: none; transform: rotate(0rad);" /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="font-family: "arial"; vertical-align: baseline; white-space: pre-wrap;">In the above picture you can see Ofer chose a combined partition key and set a custom partitioner. I find Talend easier than just writing spark in that case, because we don’t need to create a dedicated class with a comparator for that secondary sotr. Talend understands what’s the type of the combined key.</span></div>
<div>
<span style="font-family: "arial"; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">3. Creating the sessions - Foreach partition, in parallel, Foreach userId, run over all clicks and create a session field.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">In spark it’s a simple fold call with an inner logic. It’s quite the same with talend, that offers a tJavaRow for custom java code that will run after the partitioning+sorting. It would be nice to have a tScalaRow component though (perhaps planned for Talend next release v6.2)</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<b style="font-weight: normal;"><br /></b></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">To summarize , Spark is a great platform for this case, having the ability to do distributed aggregation on large amounts of click stream data, create sessions that requires sorting. </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Surprisingly (or not?) Talend-Spark integration supports all that’s required and in addition of course, let you enjoy the benefits of a mature and rich ETL tool.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<br /></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Here is a link to Ofer’s </span><a href="https://www.youtube.com/watch?v=9x75hVd2h64" style="text-decoration: none;"><span style="background-color: transparent; color: blue; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">youtube</span><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> video</span></a><span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, telling exactly how to implement the click stream analysis with spark and Talend.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 18.666666666666664px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
</div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-2113645858148990792016-03-16T05:04:00.001-07:002016-03-16T16:15:45.079-07:00Playing with dbpedia (Sematic Web wikipedia)<span style="font-size: large;">Web 3.0, Semantic web, Dbpedia and IOT are all buzz words that are dealing with the computer's ability to understand the data it has. "Understanding" is the key meaning of the semantic web - a concept and a set of technologies and standards that are "almost there" for more than 20 years, but we are not there yet.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">You can find additional info about semantic web very easily.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">I know that lots of efforts are invested in it, and google are quite semantic. The schema.org project is an effort to create one uniform ontology for the web. But, still, we are not there yet.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">What I do like to do is playing with the semantic representation of wikipedia - dbpedia. You can query dbpedia, using Sparql (the semantic web sql).</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">That is the link for the sparql endpoint - <a href="http://dbpedia.org/sparql">http://dbpedia.org/sparql</a></span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">And an example of a cool query:</span><br />
<span style="font-size: large;"><br /></span>
<pre style="font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;padding:0px;color:#000000;text-align:left;line-height:20px;"><code style="color:#000000;word-wrap:normal;"> SELECT distinct ?p ?ed ?u ?t {
?u a dbo:University.
?p ?ed ?u.
?p dbo:knownFor ?t.
?t dbo:programmingLanguage ?j
} limit 5000
</code></pre>
<span style="font-size: large;">We are looking for people that have any connection to things that are universities (?p ?ed ?u), and that they are "known for" something that has any programmingw language. The results including universities and people that have some kind of contribution to technologies that we are using.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">See how easy it is to generalize, or specify the relations that we are looking for.</span><br />
<span style="font-size: large;">Of course, that tuining and understanding the ontology and also sparql, might bring much better results.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Run by yourself to see the results.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">The results in a graph (I have used ploty for that)</span><br />
<span style="font-size: large;">If a university had 2 different "knownAs" it will count for 2.</span><br />
<span style="font-size: large;">And The winner is Massachusetts_Institute_of_Technology! </span><br />
<span style="font-size: large;">(Berkeley is #2)</span><br />
<span style="font-size: large;"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAAFzCAYAAADi5Xe0AAAgAElEQVR4XuydBViVSRfHz0uDgoUC0ijYXYhiYKMY2IquXeva3a69xtrd3V3Y3d0tdjdIv+d7zvDdS93LvReu6MqZ59lnvffOnJn5zbjvf88574yEiAhcmAATYAJMgAkwASbABPRGQGKBpTeWbIgJMAEmwASYABNgAoIACyzeCEyACTABJsAEmAAT0DMBFlh6BsrmmAATYAJMgAkwASbAAov3ABNgAkyACTABJsAE9EyABZaegbI5JsAEmAATYAJMgAmwwOI9wASYABNgAkyACTABPRNggaVnoGyOCTABJsAEmAATYAIssHgPMAEmwASYABNgAkxAzwRYYOkZKJtjAkyACTABJsAEmAALLN4DTIAJMAEmwASYABPQMwEWWHoGyuaYABNgAkyACTABJsACi/cAE2ACTIAJMAEmwAT0TIAFlp6BsjkmwASYABNgAkyACbDA4j3ABJgAE2ACTIAJMAE9E2CBpWegbI4JMAEmwASYABNgAiyweA8wASbABJgAE2ACTEDPBFhg6Rkom2MCTIAJMAEmwASYAAss3gNMgAkwASbABJgAE9AzARZYegbK5pgAE2ACTIAJMAEmwAKL9wATYAJMgAkwASbABPRMgAWWnoGyOSbABJgAE2ACTIAJsMDiPcAEmAATYAJMgAkwAT0TYIGlZ6BsjgkwASbABJgAE2ACLLB4DzABJsAEmAATYAJMQM8EWGDpGSibYwJMgAkwASbABJgACyzeA0yACTABJsAEmAAT0DMBFlh6BsrmmAATYAJMgAkwASbAAov3ABNgAkyACTABJsAE9EyABZaegbI5JsAEmAATYAJMgAmwwOI9wASYABNgAkyACTABPRNggaVnoGyOCTABJsAEmAATYAIssHgPMAEmwASYABNgAkxAzwRYYOkZKJtjAkyACTABJsAEmAALLN4DTIAJMAEmwASYABPQMwEWWHoGyuaYABNgAkyACTABJsACi/cAE2ACTIAJMAEmwAT0TIAFlp6BsjkmwASYABNgAkyACbDA+o32QOHCheHq1atJzqht27awcOFCvcx66dKl0Lp1ayhUqBBcuXJFZ5svXryAf/75B3bv3g3Pnj0DQ0NDyJUrF3Ts2BHatGkDxsbGOttM7QYzZ86Ev/76K163BgYGkDVr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1Y2bWK3wVlF4jMQXCUISLfQ2WnIElkLsUG4UvelGgi9h0VZgUTsKj1EojMQqzY9O3SfvE3EmEUceOG0FFtkjBiQgyGtHIoq8iiR86TMJYxI5iqMOUiKwSMyROFR4L0mA0/5SFBLF9JIA9Uk5XnS8B60djYc8aEkJLPJS0nEOFKokDx/lglHoll6iUOTIqdpnSa0NhVbpfyKo0AsbZJcLE2ACTCC1CbDASm3iqdSfpnOw6BwoerjRQ5gS3On/+ukVe0r0VuQy0fckuMibRYnv9FCjNwApV4nEQEoEFmEg+yRa6K1GSpimQg9aEiJ0dEDcAzYpfEThTAp/keeHkqAVydMKpPTKP3m5KKGckq9pLoqjAygPieZBRdsQIXnJyCNDx1eQl4gSvlMisEi0Uk4WcaPcJRKVJJBIJFKukbqxJbVlyLtExzwQFxI2dI4WHWVBHiFFCJHap0RgUXtiTWKSiqoDYMlLRMKc3hAkoUXeLhqXIlldnQeLmND86e1QYkKeKTrjjMKSlFdHRdU+S2ptFGdfUY4eedp+5m0AqfTXnbthAkzgFyTAAusXXBQeEhNgAskjQN428rCR8KQ8wlGjRiXPELdiAkyACaSQAAusFALk5kyACfwaBOiFCDrolkKv5L0ib5rihPpfY4Q8CibABNISARZYaWm1ea5M4DcmQEc0UO4WvUBAuW50WCsXJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR536ZABNgAkyACTCB35YAC6zfdml5YkyACTABJsAEmMDPIsAC62eR537TIgFUMWn6TtIShrq62rbXshuu9qsT2HHqA+w8/QHuPvsuhprL0QL8vLJArdJZfvWh8/iYQJohwAIrzSw1TzSVCJAIUgghjIj6JBkYmEBUVBiamWSBsMgo6cv3MPgaGgbB4RHw+N1HjJZRioyOhqhoGejfhZzswN4qK+y78AmMjQzAyFACY0MJDAwAXWzNpPTmRmBpYQhWFoZgZmIgR376YGBkZgYYGQEGVpkU01SMgcVXKi18anTz7Xs09JnzEC7e+6ayu2IeljCpcw6xPzSVunXrwrdv3+DgwYOaqqr8ffny5XD58mX4999/k9Ve20Z79uyBuXPnwrZt2+I16du3Lzg7O0PXrl3Vmrp+/Tp069YNDh8+rG13yaqXP39+2L9/P9jZ2cHGjRuhQYMGybLDjX4vAiywfq/15NmkHgGlkIoMi/EiGJmYQmTodwyP+AqvQndKX0PvYWj4Syk04iUUdp0Au68Z48RdRw00DfHPyqWxlFN+6Dj5nkZx1Kexk1w70z348O9AyShbdjDKZo8mzu6SZbUaaGCeXgLTzADfXyIYZ5TAOB11zcJL0wL8wr93mHQXLt0PTnKEJLLm9fZIss7du3ehf//+YGBgAEOGDIGiRYvqNGtZlmHlypW/vMCKioqCr1+/QubMmXWan6rKNGfipap8+PABMmXKBJIkQalSpeDcuXMp7o8N/PcJsMD6768hzyB1CMgUyouOjAD6x9jMHJ5fOSUHf3hj8P7xHfj09D58fPYQrGwcoOaI+bDtsls8cVTEbTw+/lAee67coVeBNaVLTrnQndXS+2lD4vWX4/AjhG2eAF8fAGTIhWBdVALH6vLb7JWkTGaZpfDocEBEtDSxpHYkujSOK3Uwcy/qCFBYcOSyIK0ADf/DRYQM1ZVx48aBjY0NmJiYAImtUaNGKauuW7cO5s+fD5GRkWBvbw+TJ0+G7Nmzw+LFi0Xdmzdvgo+PD1hbW8OFCxeARAx5ssjerFmzRJs8efLAsWPHIGvWrMKu4nOGDBlgwIABcOnSJSDBUqxYMaCxmJmZqR2rNh6swoULC7uBgYHw9OlTqF+/PnTp0gUUHizyfpHwOXHiBGTJEsOF5mxqagr9+vUT4968eTNERESIuQ0bNgwMDQ2hQ4cOkCtXLvHb2LFj4d69e7BmzRr6uyPmT2ycnJxA4cEaOXKk8GS5u7uLuRHf4cOHi/7IW6gYgz4En1YbgSv9VAIssH4qfu78FyeAEBENICNGfg7Gl8+vGby8cRbe3LkCH57eV+ldMjAwxFYrTsKGU9ni/Z7HoQdGQxtoPmetRq+ULh6sZQNzo+2uf+DzqpnxBdapDwjzSTNh7PdlZsgHLPNIDXcFSLkze2AR68Lo61pN8rLwjLbKYGWIUQjGlsbs4fpFN6U23ivF0Iu6p4f5fXKpnEl0dDRUqFABtm7dCsbGxlCtWjU4evSoEAPkiSlTpgwcOHAAHBwchGgxMjKC0aNHA4UEZ86cCRs2bBChOfo8depUWL9+PeTMmRN69uwpxFWfPn3UCiwSZCRQli1bJkQKiSvqv3jx4ikSWCRmmjVrBr1794Z3796Bt7e3EHEPHz5UhgjbtGkDvr6+yvAd1ZkzZw48evRIzItCe+nSpYM///xTCKHWrVsLkUZMli5dCqGhoVCpUiU4fvw4pE+fXtQPCwuDgIAApcAiwUb8bt++DTdu3ID27dvDqVOnhGdr586dog3Z4pI2CLDAShvrzLPUjoBIQseQSJDSGUP0tbcQvf8xRAd9RtMx5aUlncppFEfUvvm8fXjycXP88O2s0ivkkq0x2lsPhaoTVmu0oYvA2j0yB+Lc/hC8b6PSrnmxcnL2f+ZKsMI2fl/+F3DwvR0w++q8eN+/bhaE++rtwwxuGaRsntlk9xbuBtFh0WCSwYS8djQHjWPWDi/XSgmBCj2uQHBotFYm0psbwpGphVXWpXwkEkmzZ88Wv5MwIpFTvXp18Tk4OFgICCrbt28XooDEFP1D+VokjqjQZ/JSLVy4UHxesmSJ8G5NmjRJrcB68uSJEDxjxowBLy8v4UHSVLTxYJHAovHky5dPmCOP1o4dO+Dz589KgUVzpvFTPheJHxrHoUOHoEePHkIgtWvXTrQlPvPmzYO1a9cKgVWgQAHo3LmzEFOenp5AuV8k1CgkqCgKD1ZcgUW/Va5cGcaPHy8EJHEm8cX5WZpW/Pf5nQXW77OWPJPkESAvlQxRsgFGyRB98jlEkag6+RwAY70/6Q4H4KaRLfHz84caQ2m1Ry/B14ab8fbzacq6thkrYCHXOeD19wqNYkUXgXVsrBt+HtEaQi8eV9rN1KavnNmvggTbSsfr60vAa2y+vzOcfHla+X0h64JyYOXt0rLMy+LV9VnpEy3XtJdsLEwkQwkgvbEyaVrj+JO3DNxKEwF9CSxKCj9y5IjwTFGhEF/p0qVhwYIFwqtE3hwSIuR1ofwlCg+uWLFCCJi4Se1JfVYXIqSQ4e7du4W9W7duCVFHITQLCwu106ew34wZM4QHKG4hweLh4SHEDwmsTZs2gYuLi6ii+ExhOUWSO4kt8tydPXtWiEvy5JG3jTxbV69eVY6BQpcURiRxSQKrXLly0KRJE2GXQo4k0E6ePCmEF4UZqU91Amv69OlC5A0ePFiIq3379oGVlZWmpebffxMCLLB+k4XkaehMAPFbBIX/IGrTHTkq8JGB/PizWvFgOrky3gs5iaeXT9EosMq0G4TpC0l46k5bZd0MFrmxQv69UHTofI0CRReBdebfPPiyjQ9ExglZ2o5fIaezfiLBiS7x+opsF4Gui3NDSOR35feTyo2TfR6Ul/bX3R+vbp1HTeSBT99JS+6+lXyyZ8BOeW2gvmsWCI2KhnQxYkvjPHReEW6QJAF9hAi/fPkivCokECgkqBBY5E0iT9H58+eBRAHlYVlaWorcoy1btugssMiTRN4hyssKDw+HvHnzwpkzZ5Q5WdTvp0+fhPihUB3lOqkrFy9eFN4lGptCFFLdmjVriu/r1aunlcCiNi1bthShv4kTJ4r8KRKCFAalPCv6PmEhgVW+fHlo3LhxvJ8oV4vCo5STtmjRIrUCKygoCJo3bw5TpkwRAlbh7eOtnjYIsMBKG+vMs4whgPg9EiQLY4g6/ASi1t+G6IuvtBIKRv65MKKpA64f2ECjwMpbrRHmqeeL+66XU9Y1McqEfiWuQcFBczT2p4vAOj8jPwbVzAVyyDelXcfVx9Hk+QyAW3Nj+7KvJr+tuFzKtaxQvP4v1zsjPx3yRLqz6E6871tGtUOHVRfhQ1hUvO8b58iCXfLagqeNpfB2mBqKKWqcE2/AlBPQR5L7qlWrhAeHRFTcQp4c8sLQW3KKsB95r8jbFRISIrxDuniwKlasKJLCyTNGAo3CaqdPn4Zdu3YBiTwSVlToTUZKCKdcJXWFPGz+/v5CRJHXijxrlHBPoUsShRTO1MaDRfYp/4u8d5SbRXlmVMhbRzxWr14tcrCoDgm5hg0bCg+WQmCR946S/0lYUSiQ+t+7d68QTQoPFo2F3siM6xGj4zDILtmjP3NJOwRYYKWdtU7LM43G4AjAd98NIlfdgKgdD+KF/7QBIzlaofnSWrC4o7dGMWFfsBR6dxkMu67nj1e3UZm3WHToDAyPjE5SpGkrsKwsDOVDk/JLD71t4vXjtvcGSscCAF4eiv2+7Cz5QLpcUsPdAfHzrwKCcEvhLfD10Vfl93k755WzjC4q5d14Ve1cM5gYYoB7VuiW3w6ymhlhJlMjqquRjTasuY56Atp4sZJKcCehQp4aPz+/eJ1Q6IrepCPhQmKHRBCFBknQdOrUSYgD8kZpGyKk8Nq0adPEuVDkoSK7lBNFb+aRqLpz544QcwULFoQJEyYIAZJUefXqlUiIJ88bCXsSVIMGDQJXV1fRTFuBRQnrlEdFYcVevXopu6SQIYlICg9SyI/ypmi+cQUWhRRpDBSypHlQuJNEJAnEuOdgkZfs2rVrIletUKFCwsNFeWnkgVPktvEeTxsEWGCljXVOi7NEOTwKDEyNpFebbsrWPm5SRDXNb/AlBSrdoea4ZXRr/Pj0fpICKb21Hfr/sxq2XHSOH3YrdQ/bLAjEK09e6UVgFfewlGe1ySwF1cwVr58cR58irM8NEPJc+X14vQvy3/d3SHET3ItlKyrvqrhZWpYlfv5VzQv15OlR36V/rr7USjC5pDeVhxVzkEhwRSOgmaGk0cuXFjekPuZMB432nv1A7VlYJK4md8mp1UGj+hgP29BMgIQliTLKbeOStgiwwEpb650WZitHf4+QMFKGh5NOwJM5Z4RIKH24PZruf4KRa28m++FvOrES3g8/i6eWTtRoo82qM7j5jBNGy2HKutWKnMTJe57gpvM39CKwGpTPKvcsFSw9bxHrVTOwzCC77r0nwQJjjQnuUytMlL1ve0kH6h+I72kLbYOlt9+AO59DtRJYik1lYWSAfxdzhN6Fskth0TKacfjwh/19o3DhjlPv4d7zUNGHh4M5+HlZJ3n21Q8bDBtWS4DeyGzUqBH8/fffSR5FwQh/TwIssH7PdU2Ls8Lo0EgIe/oFH/5zTHq97VY8cWDjlwfzja4C4fU26SQa4oI0quuBkS2ccV1/f40Cq/GM7XjlbV98+XGPsm6F/Dtw51VjnLz7mF4EVq9GjnLdjPekV3/VUc4pnU8d2bb/UAnWxD/olBLcXRblgu9RsaLpqv85+fGAR9LdpXeV7e2r2cvFNleV7FZeSDYnYjayuKM8oLA9/feF87TS4t9GHeZM51BRKE5VcXNzUx4noYPJX6Iq5XjR24N0Plf37t1/iTHxIFKXAAus1OXNvemfAEZ/j4TIz6Fwp38gvN0TKxYSdlXpcT+MaLcT5cdfNAokVcOUHKzQfHktWNxBcx5WjcGz8FvGM3g1aKSyr6JuE/DBe2/svWqnXgTWxM455KL31knvpw5SiiHr7mPkDF6uEuypHiuQHKrLr8ovlvIuLxpPNL1p8QQ3FdhkMQsAACAASURBVNgE34JiE+Rr7Kghb3UxkrqdCkqRwFLw+yu/LY4q7gRxjnrQi139byO2yASYABPQLwEWWPrlydZSjwB+l0Nlww+ydKfvXunNztsaH9zuQ3xkB29XiOi8N1kCi6ZmcaAZbp/QAd8/vp2kjRJN/kSbco549GYjZb28jr0wUv4DAuauS3Ks2ia5LxmQC7PvnQKfV0xT2ss+Y6tsbnBMgvNxrs4pO0vebe4mNd/bWlnP07akvMV7nbQ86/J4Y6n7+Q9scPQBHH8dm/SujyVtnSsbTvJ0kjMhgGQWe6iWPmyzDSbABJjAr0iABdavuCo8piQJhGF4tAyywdg3E7Fv5u7S2ewzNIorMmicyRzL3+gBod7xRYUuuE3/8cEHURfw5OIJSQosj/J+WKhpU9x73VNZzzVbU7TLMhiq/ZP0ae7aCqzdI3IgLBwI3/asV87fedNZNLrzN8D92ANNI+pdlEfc3ybNuRp7Btf0ipPlMjc8pQMNY/Ov0rukl+vdbyJZLD6rFU9duFHdiOZF8NvKg5i5Q00JIyJQMjVJttDVtW+uzwSYABNIbQIssFKbOPeXEgIYgREw/t1kedaH+eK0y2UO8zHf+sx4u39srlNSHRRZ1RgzfYrE8Cmx19joMiCj2u4Y2coV1/Wrl6Q4sPEoiJX7ToDtV2Lf8LPN5IMFXWZCmb9X6sWDdXSsK34Z2Q5CLxxV2nPbfxel/bUA3saKJDrBvdn+TnDqZUzCP5Vr9c/JD/s+lO4tv6f8rtyCcvJNn6xSgwOx3+nCJqm688vlkJu+fyu9HblSAknCbIObgnkBVzDMbEnNfoig09fY2Q4TYAJMIDkEWGAlhxq3SW0CGB0eIgdFPoXqLxobfJVjc4ZsjWzwTI7DcDzbZK0e0pk8nbDwkvoQUTPW66PLZCQ7SzRfXRsWty+bZH9m6TNgk5k7YdN5e2W9jOnyYbl8O6HY0AV6EVinp+TG1x2qQMTj2LyzHMdfIqywBYiIPZVeVYL725ZPcUOeDRD8LFg5ljqvmmOX689h8+OPWrHUhVt0q+L4rPkEiP4c25954RyYbXAzwPAINLLJxN4sXYByXSbABH55AiywfvklStsDlCPDZABJuri4LRRpNQ98ntfHuxF34z2Mp9n9g2XP55FvNd2qvDAvKWrlrnVD+Pc8Rh99mqyHusX+Zrhjcmd89yDp4xZaLTuOe6554vewZ6IfU+MsWKv4ZSg4KM4J6yoGqm2I8PyMfBhUKy/IwV+EIDJ2zik7LT8owRLLWIHk6Cu/9F4o5VsRm+Bexq60vKHMKmlFtvj3IrbHDmi84DSdZaVXgRVYxT261LX7Bh9mblNpN9MfVTBDndIARoZoYG6arDVJ239LePZMgAn8igRYYP2Kq8JjIgIYFfoV3t4+DNdW9xIP5mx5fdCl4TDI87xcvAe1iWSCQblvwUHrcVoJA8dWxTBnh5IYEbA9WQ9z0/EV8RFcxeMLxyTZvv7Edfgg9F989Ga1sl6jMu+w0OCpGBWNattqI7AsTA3kY1MLSg/LZlPO2apeazlr2z8k2FgwloP3bHmXmasUECfBfZbPVLnUleLSwSYHlfWKDS8mh3Z0l7x33NSKoS5bFNuXxkfVBiZ5er6RTWa0GdIUjLJlpLCh3segy3j/C3W/7VoN33avhfD718VwTd0LgGXNpmDp2/S/MHweIxNIEwRYYKWJZf5vTVKODJdlOUq6tLgdfHx4Lt7DtmSX1bjU6hxOeBf/0uWB2frIzYJqwJVqsWImqVlXfT8EQzyXIsjqhY669ka1cmJU2xy4tm/dJAVWpZ4TMNrpKV580FtZr26p+/jHvD147dnrFAmsIjnTy3PbW0tBvh5KPtkGTZct85pIcKCR8jtVCe7XG1yQ7/e8J91fdV9Zz+9eY3nk6w/SvNtv9CpuLtTKI+c4eAE+Lj+glZi1rFYMs/ZpKGF4pCyZGmvV5r+1u1M2WvnbF3g9oAWEXj6p0pB5kTJgO34FGFhm0NgR3Yv37ds3cRefoty6dQv+/PNPOHz4sMb2+qxAV+DQmVd0tU5KSlw7Ce9O1Mbux48foUSJEuKqm7jX99C9g9u2bROXXidV4l6Zo01/XOf3JsAC6/de3//a7OSosGDp9bU9cGP9AJUPepP0WbD84KOQ/UHeRL8/y30PT+abiZFvQzQ+mPNO9kU7xywYNuCQxroJIUq26dF8bV1Y3K5MkmKkcN026Fy9CBy6XlNZr3qR0zhh10PcevFWigSWv7e13Nvru/Q8IDYXzH5BIJqFbAS4MkHZ35eAN9h0Xwc4/So26f3tH09xvcd6CHkRoqzXIrIduq29BK+/R+pNYDmlM5GDGhaSHteMc2SEljvSZlgAmhfJCQbpzRAAdF4jLbv5z1V70cUPwq6cSnLcJLKyz9qeZJ27d++KOwHpPsAhQ4aIC4qppFRg0cGydBmzrqVDhw7ifsAiRYro2jRe/bh2fobAorsOM2XKJLhyYQIssHgP/CoEMDz4A15e0kH6/ORKkv+F9qjeS/5cqiTUeB57xhRNomWmZtgvrAteLr5E43/d0uXMgqUCW0N4lTW6Pw3oPKzAprhrRjd8c+eK2r5cPSthiT86w54bsflPFQvswi0XJZwWeCJFAqt7fQe5ftaH0qs//ZTjd91xCQ0u9gQI2qL8LqpdJDotcofQqDDxXQX7cvKqUkuklbaxbzK6/+EuO0z1lDzWJ81d140SVC+/bLFyv/Rtd3wvpLZ2zAq6oe3IFmCQ3pyaJGudtO3rv1CPwoJvx/yl1VCzDZ4BljWbqa1LlxbTZcYmJiZAYmvUqFFKgdW1a1eoXLky7N69W/w+ZswYKF26tPidLoTevHkzREREgI+PDwwbNkxcfEyXGpNAmj9/PpBnjITW8OHDRRvykpUqVQpOnDghLlRes2aN+N3a2homT54s+qGLoWk8bdu2hTlz5sCpUzEicujQoeJi6A0bNojPdBE1iRe6iDqh/Xbt2sGCBQuEnYEDB8KbN2/gwoULEBUVJS6ppu9p/Pb29mq5aOPBWrJkiWBGDJ4/fw5hYWFizGSXPVhabc80U4kFVppZ6l93opGh3/Dby5twbk5zrR+ilf6+iHXftsELoZfjCZVb7hfxUb0d+Ol0TGJ5UsXzYFs0O/oCI1de11g3oR3TsRXwsckNPDZ3lNq2mZ3csebwObD1Uuy1NcVyTMS7b7yw75pdKRJYEzrlkIs/3CC9nxLr6ctx6CHCjrIAn/6fR6UiwX1e5ZlykQsFpcPNDitZ1zhdR55vFCGNvhR7ObQmdpp+r2KfQd7t5SQ9bTRa6zVVZ9N+Tjc0tsmEBpbmOq+TpnH+l37XxnulmI9ZYS+wn71D5fSio6OhQoUKsHXrVjA2NoZq1arB0aNHhZgiDxYJpLFjx0KDBg2EIJo9e7YII27fvl1cWEzhMgqfUSiRhFPr1q2hWLFioj55xcgGCSESSeTN2rlzp2gzZcoUqFSpEhw/fhzSp08vviNxEhAQANWrVwcSfeTBKlu2LKxfv16IKBoLFfpMY6XwHY2hZ8+eiewvXbo0nh3yYE2dOlW0zZkzp2hDIqhPnz4pElhkd/r06bBnzx7ImjWrEJmWlpbQt29fFlj/pb9QqTBWFlipAJm7UE8gKuwbBh1ZiA8OzNTp4Wmdqyy6Nx0PHs+84j3Aa1nWwAlmw+FS7qSPQqARZfPNjXnHVoVIf93vJzSqkQOjO7rjmj511I7b0NgE/1h8FDacsVGOMa9jHwyPDoCW89QfE6FNkvvi/rnQYd9U+LT8X6XtHCffISwyA5D/H+ZTkeB+s8FF+U73O9KDNQ+U7RqFtMbyu2/BtY/fUyyGFCv9vkEBjJq2BUJO6Sdp3rp7PbQomYuS4PU2xv/a38vHVVxADvmm1bAN0luB677HKutSfhV5hEg4USHhQSKLRA6JI7qcmHKQyFNEXpo8efLA+fPnxYXF5KEhTxEVsjNv3jxYu3atEFjkPVKEGskDNn78eHHBMdkvU6YM1KpVCzw9PYUQ8fX1FaE0RYkrsEgAVaxYEby8vKBTp04iN6t+/fpgZWUl2lIulCr7JPDi2iEhdOzYMVi4cKHohjxPN2/ehEmTJqVYYJE3jrx1Ce2yB0ur7ZlmKrHASjNL/ctNlHJrpDMzG+LnoEvJemgW77AMN2S5gyPexn+b76TbAQzrdRNfrrumUbT5POyLkV32oHz/k8a6cQlKWdOh+cZ6sLht0nlYAfP34/FHDfHjt0vCvqtNc7TJNBhqTFR/2Kg2AmvncDc0WDyE3iQT7MwKl5btpyyRYHnsW4UR/pfk4Xe3SHOvxYrNd62e4Vq3tfD9dYyYsiljI5c5UFPKuvx8stZA1a7qkMdGnuGUTnreLlb86WP3UQJ85tbVwDCLFZnT23j1MbbUsKGTwEpnCa77g1QOi0KAR44cASMjI/E7hdAoBEgCiQRWx44dhZdJUUg0kBeKBNbVq1fBwsJC/CTLMmTJkkV4tkhgkbfLxcVF/EYens+fP4vLjklc7du3Twik69evw9y5c4GS0QsUKCBCk9QmrjAizxaFBUuWLClCeySwKHRHXi8KyZGXTJ39hAKL2v/7779iTNrkZNGYaS5XrlwRXilFIRFJHqtly5YlshPXLgus1Pib8N/pgwXWf2etfpuRypFh0d8/PDU4MalGih6SRmaW6DPiHNjdjz0tnSB5WZTCxdaz4bzzbI32cw6sIDtWygkRHbQ7CT7uIljsbYJ7ZvfCV7cuqhVndcYsw5fSWrzzYpaoY5epMhZwngZlRq1SOzZtBNbRMa74ZXQHCD0XE+rL1LKnnNm/qgRbS6lNcK/kWFFeVmSBtMohtu+qm6rKgbnNpE4nHmtkpe0GDGlSCD8PWw5hN/VzYXTcfk3c7NBhXneQv4ejgUXaOjNLHyHCL1++CO8PCRwKCSoEFnmLSEBQ3lKTJk2EkKLwnsKDdfHiReGRypUrlwgJJiwJBVZQUBA0b95chAVJuCm8SIp2ZJfCd5TLtGjRongC68WLF/DXX38JoUMhSFdXV5gwYYIIS/r7+4O3tzeos59SgUXhUxJJ5OGjfysKicuQkBAxjoRCjQWWtv9lSHv1WGClvTX/qTMODo6MDv4WJl2ZXFgnj5G6Qees3FUOLesDlZ/FPzJhj/MWzDD9Kz6cdCzJfoyszLDCnZ4QWlb3+wlNR5fHIPPbeHTOSLV9eHcYgub5o/D03Q6iTsZ0BbBcvm1QbOjCFAkscYp7x2oQ8SjmkmvbMUvkdLavJTjeUW2C+4Iqs+UCx/NKR9vEXq1T72NLbHrqERx8EXNYaUrLqBKOcm+TaOlVjzl6saduPI6LeqGBuQkaZs2ol32U0nmnRnt9JLmvWrUKzp49KzxAcQuF5UhQkNeodu3aMGPGDKhRowZs2bJFhAH37t0r8rCo3erVq4XYoWR18oI1bNgwkQeLbFP+FNWj3+nP5E2isBoJK1NTU5GDRXZJfFH4kMJ/5cuXF8MiEWhubi48RhRKrFq1qvievGX0vSr79F1cO0kJoaTWi8QUefImTpwoEuMpFNqvXz8gdsRIW4FFIvDRo0fiZQAqNH8qijclDx06JLxzCq9fauwh7iN1CbDASl3eabq3L18icN6Cm/jyVbD095CCcGpM7Nt1KQFTcdhpDPjcG4+FxL6Zl9vUA7c5roNzdjM1PugLr2iImYMRwyee1ulhbVTNDeUuuXF1bz+17fLXaIoe9SrD/qsVY0J5xlnRt/glKDRIvQDRxoN1fno+DKqTH+Svn4Rdx5VH0eTVPICb/5+vYy35pfc8Kd+KYsr53653Rb7Z/Yb0cP1D8Z1pVlO56euWkunC2DsKU7IO1DayWRF81W0WRD57p5F7SvvK2r8RWpTIBYYZ0v3wvlI6Vn2118aLlVSCO3mAyAPl5+cXb0gUwqM37ChkN2DAABHWU5yFpcilogaUt0WhQAoPkjCg30iEJPRgUV3yTFG+E+VvUXiPvEOUyB4YGCjePKQEcUqmd3d3F8KNxBcJvVatWokwIHnNDhw4IMZJbxd+//5diDpFSWifvo9rh3LIdA0Rko3w8HDxViPlepHHj8ZHeWTlypUTXWsrsEhAUoI9/UNF8aYmvRlJhXLd6B/KHePyexJggfV7ruuvNiuMiJChS9dDeOfuFyFGWgTklutVMYFLM6vrJGpUTSxzjpKY549pkPNJbHiM6i2xn4sFt2TFW713J9lHxhIOWHRFIwj3XafTg1qytkDzTf5J5mE5FC6NZTv2h103Yk9Xb1TmPeYbOBkBJZXj0iSwTIwN5FMzCkkPy2RVjtdtzzWUTrYGeL4/5jvvOfIuM2cpYG8bZZ13fzzDNa5rIPRtqPiuzIwy8sNadlKdfbF3GaZk4ywqn1Nu9Oa19DaJ8GdK7Ktqa1WnNGYKqASGGdPrtHb6Hkdq2aODRl/1D1B7FhaJK7sJK7U6aPRHj3nHjh1CTNFbfz+i/Gj7P2LMbDNtEWCBlbbW+2fMNkqWwdCv7k748iU83kNw7GhPdDK5JN/Z0EOrOwSTGnyRVnNwj8Nb7P9qqFK0ZDWyxgs5jsOxbJM0Pny9L3ZFg3mXMepAkE6Cz2JXYwxc2B9fXD+rsp1VNnusM24FbL3kohxDPc9H2HzWdrz58m2yBFbBHOnkBR2zSUE13JU2cxwJQthYACD4ifguYYJ7decq8oICs6XVTquVbfyfN5f/uvNSWvvwvUY+2mwcuVUJfNJ0HMhfYw8w1aZdSuuY5XXG7FM6AhgaRANATOb2b14oXPh11xqI+P9VOSbuBcCKrspJ4uyr1EQSHBwsvDMUbqM3CfVdfrR9fY+X7aVNAiyw0ua6p9as8VtwGJqbm0gVfGIPv4zb+ZqVVfHzqTH44vwGnYRNwgkYGJlilbHXwPZurOigOuNt/saqV4rLNxttSlLEOQQUQfe/SmNE0206jcNkaFl8muUhHpk5TG27tqvP4YaT2RBBFnWqFz2D47bfx+2XbydLYNUpYy339w6TnjWPOaJCMreQ3Q4ESbDASCmUvrZ4g40D28OZVzGHfC6pOk/OfdhDOtb+mLJOG7k9Wi05B6FRcooF1oGqHtHFL98x+DB7R4ptJWdzuuwbjaGRXyGdSWaBJDk2uI1+CFBYj94ebNasGXTv3l0/RuNYSal9CsmRQFNVKKxH4UwuTEAfBFhg6YMi21BFAL+GhEO7EZslG2tLnNCtGlStvl3lg+/IwXp4dFRJjAzR7aiEhJ26Vewog7cveL+IzYkyBEN8kec+7Lceo/GhW/XtYAwptwIhIlprkWVUxQ3xzzy4qk8ttW2azNyJl153w1efYu7jq1hgN246jzhj/8lkCay/6tnLDW0fS6+61BJzSlfOV7YdMlqC1bFesqj2kei40B3C/n+C++16V/DGX9fh0cZHok2hfoVk6JVX8tx2QyMXbbY3digNj6oM0Kaq3uvY7x6MI/YXhI8hQdJEv9doZWbD1+vonTIbZAJMQFcCLLB0Jcb1tSGAH7+EQqfRW5UPb3dna+zT0hv8/HYleqDb2FjgymWV4ejQ3Cl+2Jcfchw7BY/Avd/2K8VL/6y95BbP/OBylZVJCqc8E6tjdrdsGNb3oNYCS8psjhZbG8CiNvEPPI0Lqebwefg53VG89mS0sFvSfQbeeFkE+69VnRumKQdrXAc3uWTQZun95H6CV5auI+WMFfJLsDMmkR5UJLi/b/0cVzmsgrAPMVfm1LneQB7z9Ys048brFDO/5JdXdtl3Dj6t1J6bNptImzo22/vi9FM1IOhj7DleI6vdRlur3NQ8xXPTZgxchwkwASagigALLN4X+iYgv/8cInUZk9hbVaaws9ykaiGpfoO9iR58pT1tcUi/PHB6XIkUPRQzOhfBAu3mgVtQ8Xh2nuS+jecKz8ewF1/ViicL18zoebAthFeOzVPSBo7Fjka4b/lQfH5Z9f2CJZt1w6xlbPDYraai73xO/fB7ZFNoNX+DyrlqElgL++ZCp0PT4dPSyaJ99qkbZXOTsxKcGxhjz3uuvNPUUWoR2FZ89nP1lWflmSqtcYm9d7FZeFvMt/EqPA2OnxenzXzj1vGwMpPv1M0nPfYblqJ107Vfqm+zoReuuvMXXny2PtGajqv5LCqzhQNVSxM5Wcnhx22YABP4sQRYYP1YvmnNOr7//B27jFGfx1S9jIfsUywnBATEhMviltat8sq1ygNcnq0+3KYN0EItZuBhl2Ds+bK/so+AjE1wYORfeKno4iS9U6X2t0Hz068wcqnmU+AVYzEZUgafZQ3CwzOGqLSdq2JdLNCoAQbe8BK/57BtiVms+kPNyaoPG9UksLaN8EDjJYPh286Y9s4bT6PR/XEAd5f+P8H9sjz87iZp7rWYs7aWVVsgux/IIR3veFx8dm3kKudc4C25rb2cYlH01D+/bLY0UPoWeCHFtrRZW0Ud65Vd5J0vJ8DRB7MTMbc0tZEn+j6Xwt6+BfPs2alJqo5Nl3lwXSbABH5fAiywft+1Te2Z4ZfgMGg/UnUye9zBNKpaQM5tbwudOh9N9HCcMK402sEZ+d7mvsl+s1CSDLDaxHtgczv2kmXq/4b7eXzSaC9+PKb+TcGs1T0w/4TqEFFP+/sJjSq5IHbLD6t611T5ILfNUxh9eo6FHVdjQqDZM1fFfE5ToOwo1Z4yTQLryGgX/Da2E3w/e0jYc9t3B6WDdQHenBKfEya43/W/ilc6X4GgLTEnq9c6WEtenFGWhl54liLh4euYSd5W0l562kRzfps+N6P13HbykbAVsOvW34n2TyZzR3ls1QfS/hIxnlDvHTtkCycn+qPWYV99jpVtMQEmkHYJsMBKu2uvz5ljWHgUtByiOuSlqqOuTTxRCjOTBw46lUhIrV9TDd8dGoKvruxI9kPRxbuNbFqpPng+q6G0UcOyKk62GAUXPeYnKSwqPuiDUd33oXz7g1b9SxnN0GJHQ1jUWnUelrlVZmw0fStsPu8g+s2UvhCWzbMFig9TfZq7JoF1enIufN3FFyIexFyknOP4C4RVDgBhH8Tn6PZRaL8gB4RHx4T/3rd5jivtVkL4p5jPDYJbYbV9d+HCu+AUCaxP/vkwbNoW+H4m5jT51CjW45pH38xyyWDphVaJ+rRO5yaPqnRL2l+yZLzfKh0/jkZWafP+wtRYE+6DCTAB1QRYYPHOSCkBjI6W5aYD1unscRrQphw+exAhT5x0KVHbY4f9cf/gAoiRoVqJHFWT8B5wAPuET8PNX2NDlsfd9mFk/7v4YuUVtXZz9isvO1b3gIh2SR9QGrdP8+0N8eCqEfj0ouqreVovP4k7LxfBsIjXBmYmNuhb9DwUGjw3WTlYdIr7k3oFIfrzB8kou7PsvO6kBIssYmw5+ckvys6R8q+IyUGr6+YnT/OYJK11i7kUOnORzHLl03WlTMtSdrlzl3w28pTsFtKLDlNTTVxl7F87+lme9wazT9VJ1KetZV55WPkL0gFPT5XjqXrxYpRkZET7LNXGm9K/WEm2v7sU4N4ygA9XYqplKQyQqxWAxx8/tFs2zgSYgPYEWGBpz4prqiAQER2FEdGRsGD9ZTx99YnOYmh01yp4KPANrlx1J15bB/v0uGh+RTg+Ik+yH4hWDvmwSMel4BIUe11MKYsSuCzrPDjvNEutXcN0JujzoA98L7NM675NBnnhi+zP8eDUgSoZNJi8Hu8GT8Sgt+vE743KvId8A6ao3FNJebCMDEE+M6uo9LCMtRiblV+AnLVTRwk25IsZa4IE95XVF8uue52lE11OiN8rra4knyluJQUcfqD13FQN8nuTwvhxyFIIvx1zsOmPLplaVJI/1DCXJh7xTtSfQ4ZC8sAyJ6WDXqo9iHY1a8rOg4eBuYmxZGJsmCrj/WE8Ij4DBNYDeHVEdRd2FQCqbQEwyajy96ioKHFhs+KiZ7oTsHDhwjBixIj/5J14dJjp69ev4ejRo+JyakWhU9579OghrrWha3+4MIGfQYAF1s+g/pv0GRwRit22T4Orr+5LO1v9gydOv8IN+6/rLLJmDaqNM6bfxEOHnsVrW6Wyk9ytk6t0dkL8K3B0wVew2WS87GIgt3nTXekl2+W8CTPPDsEH4xPngClsF1raALNESBg+/pRW8zGs6ILQMz+s6qU6D6tyr4kY6fAALz2MSbyv5xmEjWZsxnuv3yeyn5TAyutsIS/tZi89ruoqniZZ+/8rWxVML8H++uJzhH/8BPd79a/h5Q6XIWhbTP6V//sW2PJcEOx59jnZQmNcSSe5G4RJr/suSLYNXdbQsnIRWfqzpDR4T/ycOrLhnLG43M/zkHSwbFmVY3Hp2TvasmZdg1oDV0r53WxwZo9aYGVhmirj1mWOWtfdUQHg1dGkq5PI8jucpMA6ceIE2NnZQWhoKIwePRpevnwJS5Ys0XoYv0pFElg0drpAOu6J8R06dIArV67Av//+ywLrV1msNDgOFlhpcNH1MeUvYcE49+w2XH/tkFIgLKjfH4PfGMkzV5/VOVy45O/62K37cbx793M8wdG+XX7Z1ysULs6pr5XQUTW36pMeoP1tD4yCKGHD3SQH7nLaBGftZqh90GYoao9FVzeGCG3vJ7QyxXS7G8OiVqVV2izi3w4dq+SHwzdqi99rFD2Lo7bdwV1X7uoksPy8rOWB5SOkZ81iQmHZ5+1B87BtAJfHis9fW7zFRnvbwtnXMSHAD21f4PKsyyHia4RkaG4otw5pKxkuOJ0igRHZvAi+6jITIl/q54qdpPajWV5nOcM/jaRe22M8dnFLjsxecs8Se6RD3om9WlQv78TJ0W+c8xh0mLhN2bZO2dzRPRp4SRnSmyV7P+nj70+ybFBY8Ghr7ZqWXxITMkxQFB4shcCin48fPy48WAcPHhS1161bJy5ejoyMBHt7e5g8eTJkz54d5s6dC8+fPxeCjErcz+QF6927N9Cl0U+ePIHhw4cLe3fv3gUjIyMh3iwsjR/IlwAAIABJREFULODSpUviNzpJnbxo1G/p0qXh1q1b0LdvX6hSpYq4HJq8UlRPccGyukmTwHJ2dgZzc3NxLY/4O/D1K9SsWRMcHByga9euQmDROIYMGQKfPn0S/dJYK1WqBBEREeJyaxoXXWBNl1bThdRmZmawdetWmDJlChCzUqVKwYQJE0Tb3bt3i0ul6fJqa2trwSNHjhzarQvXSlMEWGClqeXWz2TDoyPlnbdOSmOPrEj00JtQvTPaGdnjyNlHdH6Arf2nCVarsQ1DQ+OfpD55YhnMEnZEfrB9sM7CjWZsX6I+ZqzVEYs/rawc00L72Vh0ux3e7LFT7TjLnv8TDZdex6g9D7Wai/nWBnh4/RgMOhcrOhXE3byqYPEWHXDPjWLClk+BPbj+XDTOOpDYQ5aUB+vPuvZyk+xPpJedfQV71+0X0OBKP4BHG8XnuAnuDdz95UmuY6V1OWMusfac6Cm/beIkVdmd/KT0ZRVzyv4vXkpvx8SeqaWfXZXYioGVuey4YZDUZZNJon3mYV1e7lZkq3SofHmVYrHoshXy5QhzacD8/19+Hcf8oIDy6Fcmd7SpseF/64wsbbxXinnalQfwSxxGTCiwQkJChPDIli0bDBw4ED58+CAECV1HQwKFxAcJJBIRSQksEibt2rWDzp07w5o1a2DUqFGwa9cucHV1hebNm0PTpk2hVq1a4OvrC+Rdqlu3rhAwdBE09UUCyM/PDxYsWADly5cXbUmU0dU1SRUSWNTvsGHDgEQjjXXDhg1CsN25c0cILBJw1atXF2OrV6+e+J7aUVjxzJkzYrzLli0DRBTiqlq1amBrawt16tQRYyRx2bFjR+Ehq127NtSoUUN8T3NbsWIFbNmyBTZv3vyj/hqw3f8wARZY/+HF+0lDlw88uAj99yQ+f0gxnj+9/GUfx5LQb2Lis66SGrO5qTEuGukPqu4t3LKxBj7b2Rvf3tynldhJ2E+ZvntwpLwEV3xeK9pnMcyMl3KeTPIiaPumhdCjV1mMaLxVqz5NBnjhS8eXeODf2PO3FOPI4pILawydCdsu5RCCoKT7LLz2oiAOXLdHJw/WmPZusterXdLbcd2FHbdDD1HaWR7g4zUpYYL76hpLZaddDtLJridF3bpBTeW+j99Iy++9S7YHS25dAp80Gg1ycMyJ8D+y0P2C3bdmhIio+JdH57apIv9ZYK10uML/T65PMIiiO3bLW6+9hjlbVV/A3b1BablekZxgZWNJLbVa2x85T61tL80IEPFFu+qUg9XqU6K6CoFF9+1RzhIJLBcXF+GxIsFAhbxLivv4tm/fLkQO5TJpElirV68W+V2nTp0SninyZlEh4ebu7g5t2rSBsLAwMDY2BkNDQ3jz5o3wUJG4on9I9Fy9elW0uX37thBi5F3TJLD69esHc+bMgYCAAKhYsSK0aNECevbsCRMnThQCi4QiiaXLly8r87QaNmwoRFPGjBmhW7duMGbMGPDy8gLKSaOydu1aOHTokOBChUKpNGYSVvv37xdCkEp4eDjky5dP2La0FPuJCxNQEmCBxZtBFwIiob307E4aH66NC/nIbQr7SV1GJb4aJ6kOk7q38MTR+rC3T/Jc8elt3bFE13Xg9LiwcuxjbEag73VP+Ub9DWo9Y1VeD8Lv1VYjfIvU+CA2LO+MUp9CsLJnjUR8jE3NMWDBAdh41k78lt9pAIZENsFW8xOfQp6UB2tBHw90PjILPi2ZKOzkOPkGYbElQHSYBOXmyztMskstA9uJ3+43uIEX2pyHpzufis+totpjthXn4UtEtMb1U7VGh6vlii5y4ZbBh7k7k9Vel40W937BuO3y2frKHfMul474+KgcQ+nDR3HW7mu44cgNleu1tG89Oct3WdrVb5fUbnc7NDAW1X74fHSZu9q6OgmsDACtPicyldCDRZ/J80MiZefOnZA1a1bhVaLwHgkwCreRB4c8NZoE1rZt24SYIa8QeYLoMxXykFEYr3379rBnzx5YunSpCM1RSI48Tffv3xcCq3Xr1kKcUUn4WR0TEmU09hcvXsCRI0fEJdP0HYkj8pqRwKLQJCW8k8dKUcjrReHIxo0bi5AfzY/GQp4uCk3S5wcPHgiRFrcQg8ePH4twoaIUKFAAKKmehCoXJhCXAAss3g9aEwiJCMNWG8bAo48vtXoglXYugKOrtIcOw1Rf8qyuY3X3Fro4W+GcWeXg5Mi8WvWf0H7+RuPxjnsGufnrjgpBhW/yPIZ91qPV2ss9rhra57XDsJ6xdxuqBZbeBNMFNoFFf6jOw2q56DAeuuuLX0JuGuSwbYWZrPqCn4rT3JMSWFuGe6DZ8iHwdftKyTRvUdlh1joJlmUR46cE92F3Nkrzri+Kyb9q9wKXZV4GkcGRUv6/8svphhWSCm++lix2hhLIUe1LS4+qDEhWe603GV2Bo+J+QWpfKHsdua3HAulI5coqx+B18gz2m38Azt5SfYDqgfEt8em+B3huybkYL6ZbFvSb4gcm6RKHIHUZb6rV/QEhQsXYKWfpzz//BAMDA5FfRHlY5JGh0BeFwEhwkDcnKCgIxo4dK5qR+Pjy5YsIH1KIUJPA8vf3B29vbyCvWM6cOUVyOoUD9SGw8ubNCxUqVBBhwPfv34t8LoXAcnJyEuHHhB6sTp06iTwsRaH8LPJm0RgzZcokxODixYvFzzRP8vaRACTPnMKzpfBgUUK9wuuXavuBO/rlCbDA+uWX6JcYoPw5NFhaeGEHrrmiW9gvZxZ7XNl4GLQfvhW/h2v2Ailmq+7eQt8aLnLn1k5wdqKnRo+SKnLV/rmLee6XwE9yTDJ976zd5TYv/eGSz3KV9sydMqLX0fYQVkm7+wnNtzTAI5vG4eMziTnVHbcCn8kr8N7LuQbZM1fHvI6TwHt0YrtJCawjo5zx24Q/4fvpA1LG5t3kLI1qSrAl5tTyuAnujd0byhNcRknr3GPyr/wu+8uTw0Kkyde0E8cJ2V2rk0922HUaPq05nCzu2u5idfcLFnVoIP+RY6Z0tErVxN7BjBnlcocOS41HrIPHrz4l+t3FNqO8bkgj6eDoA/D0bIw3T1EKNigoF25SWDLLYPbDhaO2DNTW+0FJ7ufOnRPhu02bNsHZs2fh2LFjsHDhQuG9Ig8QCQv6jcJjFDqjfyjUV79+fShSpIjWAovCgZSPRSKFcqUoeZ48QuQ5IuGWEg8W5Ud1794dLly4IEQRhSoVAotCfyQgKSRIocLr169Dy5YthceLxCOJJxJWVPr37y/CmSTIyJu1fv16kcBOv5OnitorcrDIY0V5Ynv37hWClAqxpIR7qkslMDAQ8ufPL14WoBwvske5aOnSpUvxdmADvz4BFli//hr99BGGfguNfBP12aD+yiHJSjK3MrPAA+2mweh5h/Hmw7daP6DV3VvYpVN+rFzsq3xpfhOdx2NX1A9ta3fHgs98lON4lOsmXi61BL8//qRybCV3/YEWV95j5CL1h5MqFsmknye+cn2H+yf3SWSrXKdhaJonFM/c62yQOX1h9MqzCUoMi/E2xS1JCaxTk3Lh2661IPz+dcn274VyOoePEhyLCQlSgnv2BW4QER0hrau5Qs6+1VY61T3m+pwmYW2w2Jbr8OCr7rlT+TJayNdq5ZaC6gz/oSJE3f2CJRybygEuk6Wj1aon6j9L6dJyoVmzJZ/uiyEkLCLR761rFJU7+RaT1rdeDyEf4udyKZhXGlAp2rmMMxiZiYNIf+2ijRdLTYI7TSzhOVgUBnR0dBQhNBIOlOROoTwSHRQapFwm8vRQzhJ5uCgvinK06O05Dw8PUY/yl7TxYJHdPn36CBFH7enP06ZNE8KDvGApFVgU1iSvGgkeKgqBRUn75CWjUOXHjx/FG4IkpMqWLSvmS3+mxHfy3hUsWFCE/0gAUeiQ5kbMyAaFPSlHi0QTjZvCnHTUBdUhLxkV8pxRgjy9pUiFhB0Jv6pVqyrZU6iSQqlcfn8CLLB+/zVO6QwxMjQS7p2+D+nzZkL/DaovNNamk/3tpuLWwHu49+Q9rUWWunsLp04pi1Zf98mPdo3Q+aHo1WsHTjTciPM/LhHjaJqxIQ6N7okXCy9SOa6sVdwx/xRfiKgT86ZeUsXQ2wml/kVgZY/EYqBAzeaYs055OHC1smRuYofVi56GwoMTX9uTlMA6Ny0PPm1QDKI/vpUclh5B0w+LAK5Pk8Cpjvy87CypwP9PcH/Q8Aae/+M8PN39VHKs5SgXWOMjOa6+pHH8qub2rH4B2WTRbil4f/Laa2JGv6u7X7CUc0u5qeM46Vj1xHltLi1ayNbtu0hVesdccp2wzOnmF+1qamqwtetWjfNuu7MtGpqKraSxrjbz+WF1xEGjddWfhUXiqtpWtQeN/rBxsWEmwAQSEWCBxZsiSQLfv3zHOQHz4O6Ju1LFjhXlekPrSL32z8Kzz25pLZLidrCm6Qh8dC9EXrzlstbCSN29hdu31MRHGzvi+3sndBqLhbUzevbYCo6PCykfptdynsUXAQfw/cFHKm1VvNcbo/odRPnau6T7sjDGdAeawaKWia9scSpaFku36wW7bxaWJJCwYZl3dJq79h4sCeQLc4tJD71icq7cdl1B6UxHgGd7EiW4f2z/EpdYLYGo0CjJd4+vvNHeQOp5WvdT1+s4ZZI3FM8uPWsac87Wjyjq7hf0cm0jN7QbKR33TXx4q/vwkdERxcsYNPtb9f2Xe0cHyO/OP5dOTo85wV5Tsc1vi9VHV//v5GOJq3KWxr8qx6OVyrOvNM2df2cCTODHEGCB9WO4/hZWI8Mi5QtbL0pLOsd6CDI7ZsZBBwbAuQ935P4H5mktkuICme7XA03DMsn/LDqudXtV9xYaGEh49JA/BPaNOfpAl5KnzjB8UtBJbviyjRhDtfSVcZLlKLiSU/UFzC5dS6Nb/QIY3naXRjFnvrk+Ht82GR+cjH8EQwZbR6wzdhlsuegixuvv+QT9p23Ah28/xrOpzoOVy9FCXtHTQXpcJaZ9jiOPETYXAfj6SIqof0Ueenu9NP/6Yikgd1N5lP0waX2u9aJevS9/YN3D9+HUm286c/pcPz+GTtkE38/d0bmtNuuh7n5Bb7eOsn+2QdLxWn6J+i00d170fQtbg+4zdif6zdLCVA4cGyCdm3sW7wTGv35J03i8e3ijRxUPNDQx1LjGmmzx7/ohQHlflKelqtBZWhS+5MIEflUCLLB+1ZX5+ePC8OBw+Msx5rylhKX13FboVtEdfNf1S9aDd0SlNpgrQw4cMlX7pGlV9xbmzJkRZ/zrBSf/zq/zOKqOu4nFgyrhi6iX4oF6xHUvwpCH+GzppUQPWENzY/R53Be+e2m+n9Ckjye+dv+A+yb2SmSn7epzsP6ktcDpW+w8Dt98E/deix8yVSewfEtllodUipaeNS0lgZGRnOPYawnmG4h5U4J7w71t4NzrC9KGWqvkrGuySGf7n5WsPKxkv5uNpPSLz+rMp3t+O3mCjan0otM0ndtqs33V3S9YIWdXuXaWPtIJv5hT7+OWghu2yufehEojlx1J9Fv98vnkQc3KSRvabYDPz5N3FVDAugC0yGwhA4DW4l+buXIdJsAE0h4BFlhpb821mnHo11Cc0WgmPDj7UO3DtYhvYeywrD2MO7oSt9w6pvP/9bcu7iv75/KReozbq/UDXNW9hbX93OS2ze3g/OQyOo3BtmB1tPfvD/mfxZwGXty8KK6yWQTnHGeqHE+hxfUxCxph+JikQ5KGZRxRGlQMVnavlshO01m78fyLTvjmyxGDSgUDcfXpcJx76IxWHqxOtbPLzR2fSS871ZAsvKrKdiMmSrDaUfQRN8H9of9NPPPHGXge+FyqsKSCfMU7i9T44D2tGSs2SGjTwvhh4GIIv6v62AOtNpKaSuruF6zk3kP2zdhNOlmnbqLxlgg8gCtOPMBley8nWufJHapHF7bNbLChbYzXLrnFf2Xj6GhLI7CxSMcCK7kQuR0TYAKCAAss3giJCFBo8OyGc9LybomvwklY2cTCBIccGQQvTL7IHbb/o/NDqVZuL7mbVyOp8wjtD69UdW9h756FsXTut/KVhQE6jcGz+2acZboPp3+IOZl+s9MazL5AxvujE193Y1XYDoutawoRNWKOPlBbTI0w3ZEAWNQi8SXVfiMX4juzvXjz6T8GpTzm4KWneXHIhkCtBNbfbVxl7w+B0tvRXaUsnYbIGauUkGCHtwQu9eTnpadLBVbGHNfwscNLXGSxCOQIWar7JgA7XHkG24I+6iQ8Jnq6yF2iQqTX/VWHTFPy10bd/YJVcvWVq1t2lE7W9U801lLHTuKkDWdw95nEL0hsH95EDnvwUTo09pBOc4w7h2ItismFWhSRxlw9gy7prKQmOfKgqQGHClOyztyWCaR1Aiyw0voOSDx/+c3DN9LQ4rq9ku8/oh56tigNddYPwuDIUJ08SQXtcuKs2r2g1eDNWj8gVd1bOHN6OUz/di0+CJyqdf9mmeywbN9AsH8YE2J0M3HFfc7b4LSt6rBY2bNd0HDVLYzaeT/JPszX++OJvVPx/rH4OVulAnpgFs/MePx2gEF+p0H4LaIhtlmwQSuBNa+3B7odnwMfF02Q7KaslS3Mr0hwtp8E5RbI201spT8C20t/5A2Qh1kPlDbmj3njsQN2AGn+aZ13eVRAUXzRcRpEvdZNmGnqSN39gnVyDIsua97Y4HSTpon2QLlzF7Dj5O1w49GbRL+dmNIGryy/hDe2qT65XdN46Hf/lY3lZ0ZhUstju+F7VKTo44Z/GzSPuapQ6z2pTV9chwkwgbRDgAVW2llrrWYa8ikEF7VfDDcO3tT5wZLTMyf22dkLFl/ahXPOaHd/n2JQtpaZcWerf6D3xN34/O1XjQJJ3b2FO7fVwrur/sBPj89rtKHoO1etAfimWF65zvMY79f87DOwxB4nvNF1eyIb2RsVxNz9ymF4oy1J2jf5qzi+KRiMgRO6x6uXu5I/FmhUD/deK2OQ064NZkjfC2pPiX/YqLocrM3DPNB81TD4unW55LT+JBo/mgRwZ1G8BPfNfuvkjCsspfODzkslRpWQv7ZxkyrsvKXTWq7ycZf9nj6X3o1bq1M7bTaYqvsF67mPjvaUahmcbdkyXn+WuXPLXuvWGVTvsxzffg6O91vlom7yP12qS5s7b4b3D94na5ye7Tzl/I0LSH3PHYGtT+7Hs1HRzgn/LeUDlsb/kVPetYHPdZgAE0hVAiywUhX3L98ZPrrwGMZXmZCsB5Zidn129kIDZzNsvGWk1iKH2hoaGODh9jNg4YZLePLKE41tVd1baGpqiPv21IH9/XPqNIfKY66hz/P6eDfirkFGwwx4zf0sHM0ac99fwlLl5UD8XnsdwsdwtWM0LO2ABoOKw4qe8fOw7PIVwwrdRsLOa3kl+8w1MLfjP1Bu9Jp4/agTWIdHOWPwP3/B91P7JLd9t1A61BDg9XEpboL7o/q38HTAaXi+/7lU+0FjeeiL99LCO291YiG3LolPGvwN8vdwndpp2t2q7hds4DE+unh0FYNzrVvH68u+dm3ZeeAQqXy3xAexjmrpE10+t4PBmoA1dEilzmOUDCW5zqrG0oOob9D6xB6IkmWVNlaUr4le2ezZi6VpYfl3JsAEVBJggfU/9q4DrMnjjb9fEjIIYQ8BWSLgwj1xj7r3av/WKq5aq7XWrW1d1Vatq9ZRrbbuat177z3BgXugIEO2kACB3P2f92yQkUCYQvzuefIIyX137/3uyP38ve/dyy+MDARSklLorKazITq4YIpAZijbfduWdBjXgRt+aAG9/yY4T7KU+dkDA+fTizdCyb+H7+cZT6Urb2HlStZ0wbwGcHm2r8Gbr32VVtS9zzSoHNqMPfOT/Y+0y4Mm5G73f3PY4DP7E+rs60xSvzuu3z6xkMrPfpEjDsvUyo72Xrgd9tx0A2ebTrRWhaUwadsZzsJUCmKhkJpJxVwTbw9a3sIedp6L4hKTNVSjoVxcUjr88oUDjf3pK1CeP8x5ngsB+KcCQHIkR4ZpaLnV7pBG0ri44eF0jWQNkHTCfZE2lLptuQlRKekG43Cug4/G98o9QezqnFcgFOZPRVd+wb4+CzS11M0F14cMyWKf+7gJGrMOXQRdpmzOYff27/sQYYSKOzbtqMFjymx341GNSdVuVblRl0/A4dDnubahMBGTE+0/5WylsgL1VRi88np2y8Nt8M+jf+Fu9D1W1de2GvSr9Cn8z6dvXo/yn/MI8AiUEAI8wSohoEt7NxjYfm3ndW79qA1Ftpk4eDnQKccnw6ngm2T6ub/zJEuZMfqz5ySqjBKR3zdfzfM5XXkLe/bwJF/0sYObi5sZTO7qf72FrjO/RudFLWLPRFZ+QY/az6EcoVnakDqZ08aXv4KUljkJQOYxyLZ2p5dOrqAhty5wNhWrUHvPapyDd3WwcqlAxWZyIETNpaZRotaIBBITEZiKTaiOmB+qSiWcOo2AhKqJREgEAomU0NRYARcTAJCeSKMcmkOzf9tAJ8+OMMVyPLez+k6u0tBKxGF+Pa7S9kCD51MuFJCkQfW55+2mGPyMIetaV37Bzyov0VRXNRLcGDYsS19VFy7ShJf3EQxfkDNB+Nlf/emDnfdo4La8UxZlt0usEJPOa3txQSnxMPjCYYPHN6V6Q/qFV9VSE/CekJoA/Y8Mhgthl3RC38TJDza1/wssJBY6P8+eKgdTv9SsWRNmzJgBmFuvuAr2gcmTMbWMvvLnn3/Cs2fPYO7cufk2A1Pg1KtXD+7cuZMlz9+OHTtYEmpMVp1bGTp0KMsRiHdrFVfJPD7MvYhJqTGHIl+MFwGeYBnv3OZnZLixw5dWXxm88eSn8RGbv6IOtZ1ol+1TDCY72P689iOok6g8nbEi77uydOUtnDShNq3jFkLurB+UJ0nD/sRmNrT592fB6WkVhsN3tqPIsDd94Wbz9TnsrrfvC2r2IJ6qV+W8MwvEQir0Kw/iQdUp8TSjHHCC9NRkKlFYavFFvPOFhQ688a4mLDQmOQZSNKmcldSSKgOVXMDcAKg6uRZdTpMFvwS8NnhOH3WpQmwPXoL4bWcLa1uGubryC/arukxT9W0dwc3hw7PYVmfjJnIzWcJN+fNE1ve9Hcmqcd24gxMOQvjdcIPHozWi+bjmpGI7b27EpWNwMiz/t9k/7j2MCjnWbb77zs/fiSF1O+/tCRfDcj+0gCRrf7eduRKsCxcuMLKTnJzM8gCGhYWxxMWZCyGE5ecrimIIwUJbkAAqFIp8d1lYgoWJrZFs4qswJTfMMo8PE1JjLkP+otTCoF36n+UJVumfo2K3MCUxhR749SA99vvxovk21WFx4y/8SL/5/+OmnVlLjz0xPAD960Y9SBu3hjBhft626cpbuHJ5Cyp9tY4+O/XuGoa8inf7sSS+QX3oENqX1X/mc5febrKJKh9HZ3nepqUn9f2tM6R1/y8/oVhITTp7UVE3L07gbQM0OQ04M7G2u5LamJF0cWmJaUQpoPBYqRYsDQqHzU/ydvnWspaTG+29uOAeM4vMVl35Bb/wXampFFtdcHPEiCz91D5wmOwODIM/9l7LgvOYPn6kTwMfbuuArZCW/O6En6FFbi8n7VZ24wISo+GrS8fy9WzmPvp5VqGTfOuD2QcOeEe34MjTYwwa/vKWS5jLMHvRKlhagoWfnz9/nilYmCw5KCgIJk+eDF5eXhAZGQmbN2+Gbdu2werVqyEtLQ2cnZ1h4cKFLBE0ErJHjx4xohAaGgopKSmwcuVKVufcuXOsTSRo3bp1g7Vr12YoWNgPtqFSqVii6SVLloCNjQ1kVnjw53/+YTF2LDE01tcmVNYFgCEEKzd7tQrWunXrGOlp37496+bYsWPsJvldu3YxfHTZjWQJcUDsWrVqBX379mXJnlGh0mg08Omnn8KIESMyxte1a1cYNWoUiEQi6NKlCxsnzgFigAUVPMT6xx9/NGiu+UqlFwGeYJXeuSkpy2hSnJKOrTDOIAJSGKPk1nL6/akp8DgtjHxz+DeDVCXsr2/1VmRI7a7c17PyvitLV97Cwwe60Ltr+tC3rw07yt961k3a/c1geiM5QNDXohedCRPojep/5sCnxcOxFDbd0wjqOApFfuUBUtIpyE1wIy/wZl4YfHU8S5PSNGBmIuR2v4ilf9yPoMdeJ+ic59e9qxPR6gNc0smAIrFdV37BgdX/1HhFVRLcGjkySx+NTp+jvx8MpDvPBmWxbf3EHsQqiXCHJh3Mt03Np7fWuDdyF3x58ShciAzN9/PZsbzVbaDGQixB+wrdVkHn2BD1Stt2Y6dGcKDbrjwJllKphB9++AHs7e1hypQpjCj06tULfvnlF7b5x8TEQOPGjeHEiRNQvnx5Rr6QGKDqtWHDBli6dCkjTnZ2djBt2jSmPo0dOxaaNGnC2mjRogUjadOnT2ckAhWeDh06wL///guVKlWCNWvWwI0bNxiJ0RKsiRMnQuvWrVl9MzMzQDcfkrf+/fvrhc4QgqXP3gkTJoCWYCGpfPLkCSxYsID1NX78eKhcuTIjXPrsxnaXLVsG27dvBzc3NzZWxANJVGJiIkyaNAnmzZsHW7duzXCBYh1UEJHMDRs2DFq2bAn9+vVjfX7yyScwf/58qFWrVkGXCv9cKUGAJ1ilZCI+lBnKiAgiMjXl4l7H02VfrIE3z94UO9H6fHE/Wq2TL7TdOt7gzaqhazX6c9svYdi0nLE52bHLnrfQTG5C9+/tDCeneBnUn61PE+r1v7ngHeLH6gdUvEQj/c/SqKNPM7BxHVKPek5pDiKZCeEk7EJKg9r+UPOMrsQEdTqkaChMvvYS1j2KyrC3t7s12VLDngvpX7jTo9qx6covOKjmX5oK4Z6CgG++yYKT36WrdPwfx+D6g6wk6OS8ATT4yBN6fZ3haif2b+1hTVov6sidi3kNY6+dLvSceJtbktWNmoODFF1HFMRiq2L/+9C3Rtx8Efj3AAAgAElEQVTWesNbdaJBS8hCbA7BQx7pJVhIXDiOAyRYGHuFCpWHhwcjWD169IB79+5luAeTkpIY0cGyb98+RniQVOALlTB8FgsqRKjioFqDJC0wMJC9n5qaClWqVGF1z5w5w5QgJFZYsP/atWuz/lA9whgsVL4aNmwISHw6duwIVlZWeY7ZUIKly14kU1qChbb07NkTrl69ytSzBg0awP79++Hs2bN67UYFCse0fv16ZieSLexn6tSpUK1atQwcMyt0mQkWto+EE+PEXr58yYgkkku+lH0EeIJV9uewMCOgsY8fw1/Vq3PVBw+mLebPh8inUXRBt6WQ/DZ/l4Xm14gqLSrTMbu/5X67tJ1suHnEoE3L08aJbv50Onw5cw9VJqfl+kz2vIW+1WzovDn14NLs6gZtunW/XE+32zykM97MEbQxa0mXmP8CAZ5/chUnNSeOn1YHEyspJ1KweA2D2ssvPsVYn6la6RTgh+uvYHlQBJfQ25cq5/8LyTfzn04nu5268gsOqbVe4xbqKgj89n1eSxNbW9Ls2HGu7/RtEBwRl4Ghp5M1+ef73tyJWSfg1bVX+cK29ex2mnJ1nARDLxyBa1H5j9XKPBZLEwn5p0V78JCbcrdvT4UXLzZzHTrcoGZmFT7YnOeHYJmLFfByyGO9BEvrIkSXIf6MqtGBAwcgLi4O/P394fLld3FeSDKQMCCBQEKGsUroHkQygAQrICAAFi9ezOpqf0eCMGbMmCwkoWrVqkwFQ4L2+++/ZyFNqPKgKw6D0bVB7nfv3mWq1sWLF8HX1xd++umnXIPw4+PjoU6dOozUZY7hQtUIFTYkP/rsRfszB7mjcodqHLrp8DNUplatWqXX7iNHjmTBAZ9DlyiOBxXAIUOGwPDhw7O4QDMTLIzNatSoEcMY8UEVDZVCvpR9BHiCVfbnsMAjSAoPJ4cGD4ZXp98HkTecOJE0nj6de3b1CZ3fyfAb0QtqxNTTU6jSVkMH7J5jEMlSSEzpiaFL4Oc/z9J7TyNzfSZ73sK+fbzIp10tIWBpyzz7EkkVtNWMa+D4xIdt8hc8ThAvcQUgag0nkIqKIki9oJAV1XP0rVoDMpGAal69gbBhi/PEJK+OdeUXHFZ7s8blZTlB4HffZZAl2yZNiO+SpVyrMX+BKvV9XNWQTnXIsHa1uH8H/QuqWJXB5Mq+ij1pObc9dyw8GCbfPGfwc/rGs7NlR1LDphx3795s+vDh0gxcypVrSerVW06l0nIGu7fzwiw/nxeHi1Dbf6dOnWDkyJHg6ekJgwYNgkuX3p1SRPKAbkCMw0LigrFIu3fvzpVgYTu9e/fOULBQpapevTojcqgMIeFBwpK96DpFiPFdGKOFyhqSFn0FY51QLUIyhP9qy6xZs5hKhi46QwnWihUrICEhgcWWYdwX4rFnzx69dmdvN7ONSBg///xzpvLh2LUEMjPBwvrfffcdU+2QlKGrFkklX8o+AjzBKvtzWNAR0IQXL+DPypV1bkhNZ84kDSZO5O6fuE2W9P2jWDeUzpM6kZZfteS+2DeHhiQY5qI8PmQx3Xv8KT184VGuxCB73sLpP9allW2fkXubhuU5poptRhGuWWeaKtRw5U2cwVygKE3xVQWd9+zPUaJKpelR8VzkjE2QFvredZifDnTlFxxed5vG6ZmV4Pa4965gj4EDifXg4Vzb8euzrLs/vu2icTeRCPZ8sydfBOmT+R011lXtBIPOH4bbsfm7UDX7+DY1a6tp5OAuePBgAb1372ed66pz53tUJnPCR/NlZ36w1Fe3uILcr127BoMHD4adO9+dPMxMsJA8YMA6uvRQvcK4IiQsWFcfYcH4IT8/P/j1119ZDBYSJ3wP2zExMWFuPyRs6JLEaxWQtKFbUEuwMCgcCQkSKzzVhy5JJHpat6I+fJBM3b9/n/Xr4OAAp0+fZsocxoAh6TKUYL148QK++eYbFj+FrjtsCwPW9dmdvd1vv/2WuRmbN2/OYsdQEVu0aBFcuXIlg2BhDJtYLGb2YUFbUbFDxQuVPr4YBwI8wTKOecz3KNJTUsjhwYO5R7v05//jBALmNqz55ZcQcOAuXT0kZ6B3vjvW84CrrwudeGQC7Hp6jv569h+D1JQtn82gwU9VZO2uW7mSpex5C/9a04qSByto8Lm/cu2nxhe/U0uP2hqZeTm0miWmM+JCabIa3h66BjF/5H2YIDMO7/ILTuG+3inJIB0j6u3QODw2E9yZMDHjPe+ZP2lSajUUfP7Tfycv/2vk6Jz+5M3VEO7i7xcNJi3l65Unjae14g6EPodpARcMfk7X/P3eqA1p7+TCPX26Bm7f/j5X8uTq2ovWrfs7EQqleRL04lgrhqhY+gLc0Z7s92Ch2w9P8qFLD4O4USnKTLBww8cgbFR00DWISgsGZvfp04cRD10uQnSrHT9+nLn10MWIhAldikjKMFAeyQSSICQfqIrhaTm8D0pLsObMmcMC5I8ePQpCoZAFjP/888/sZGNuBWO9fvvtN6YCob1YH+1t1qwZe8xQgoV1kUyhbUgEtUWf3dnbvX37NhsTui3xFCXGtCHpyqzQoZr39ddfMwKKCiG6FVHB+t///scC6/liHAjwBMs45jG/oyCahHDgJGbc432H6YEBA3IlGhJzc9pq8WLw7t4Druy4RTeO2WQQAcqvUVh/9I5vqMLHivbc/aNBffzW5VsqS7Um89ac17vh6cpbeOxwN3pzWUeqfPMsRz/21drQWgNWANWkUYGJ1CA7CjLW0vgMVacT0Gi4iFlbIPnGI4OIS/b8giPr79bYPZAI7kyanPF8jVWrNY+l9oIxy95f8mmlkJJDs/tzV1Zcpo+O5a5EZsaqw+IuROxpzg25cAQexMcYZKMurH+p25z0dHXlQkP2QEDAJEhPz5rvMPszDRuuIfa23bhUlYaa27ArOEp8beBFo58fGaT3LiwkV5vb/633otHSuOZ4m94hgKcH0T2ZF5Hk8So7CPAEq+zMVZFZStNSqPLMr1R1/S9O0e4nkNX8FF5fPE92dO/NpalUejcNMycn2mrRInBt0RLObrhKd07fVSwbTIthLUiv6T248SdW0Euv8r5aYXrrwbSShSf9YYn+C0mz5y20sJDQ3Ts6wKmp3lk26Jpf/EZsfZpzIim77LDAm3eRTdaHaYiSRBUorzyiUfO35TrH2fMLftNgn8b6nkBwd8rU9+Rqx15yLiSRm7vlfMZ7fVpUI5M+a8JtH7IdEsISDMLZvak78Zvcgtv0/D78eveaQc/ogm9KjUZkgIcbFxl5GgIDJkNKSqTetkQic9Ko4TZqZ19fcP1oLOxZHga1W1lCj2+cwUQsKLANhZ1WdBduebQta6ocn0913n1V2L7454sfAVT3MM4rrxvni98SvoeiRIAnWEWJZllpS6OmETMcgSbHsg2CEyuoWdtpYNb8O4i+d5ceGDwMou/e1buxWnt50da/LQX7mrXg8LIz9Mhiw04B5gceK2dLOvXkFLgZ94RMOL4yT3eMf50OpHel1ty3vxzRu+llz1tYq5YdnT2tFlz+uSZnIrOgjccfAvxXKJYVC3HMz/hLQ12amqZJC4sRhH65RCemdnvH0mWXO0Nw7HX2+ZhGBzXmgRrBve9/yKhf/+hJuu7cE7rxWEAGpouGt9dUt7cS7Bi63WCC0un3boS6mHIYa/U8Md7g5zLjOLJybTLC25t7G3cLbt0aD0lJ+nMRWlrWJHVqrQBLq0rc8U0RcODPcFCnvE8KPf9IdWJha/JBVKzSsDY+hA0YOI9XRugqGKelvUriQ9hWmD4/++wzFu+F6hXeo8UX40GAJ1jGM5eGjoSmPjgEsWs76dyk5E2/pYq2M0D5JpqenPQDPNmzRy/ZcKhVi7b5fRmYlXeFPfOO0fPrzhc5MfFfMZBWbOMNHba+j+XRN9BOPo3It40/5UbM0B9DlD1vYb/PfMin3Wyolb2NQCBibp8Cbd6Ggl8G61FI10DIkMWQFvb+Rvjs+QXHNjqikd9KEQT9OC0Dv4bnL9H52y7Rw1efZKyL/TM+I6onsdzpn08ZhLNPOx9S71s/bu3Tu3TJvRsFWl+fe1YlE6tU5lJVL+DWzbEQH39Xb9/ly3clVSr9AiYiO27/qnA4tVV34Hy/ya60eW87fr2UwQXNm8wjUFII8ASrpJAuJf2Q5Dgat6EPqJ+czHWDk9XpTxUdfoZ0NYWLv/5Ob/3+u97NzaVpU9p6yRIQmVnB1ukH6K29OvLzFWL8NTrUoCM2Dod55zbTHffO5LrJ+pbzpCu6jwP/qfqD9zPnLezVs4Lmi88rga2tDPEo0AZeiKGVlUfxpCHELN9HE4/dFGTPLzjO77jG9HqiIGj6jIw11fzaDTp0wV64/+I9QbmwaDANWH+TBu3LemO7PhC6rupBUuzE3MDzhyBUmWgQIcvcVieXCmRm9eoclxYNAbfGQ3T0Fb1t+PiMJG7lJ3AkTQK7lobBtaPv1F19xdZZQn/8pwqRmgryVFfLyiTzdvII8AgULQI8wSpaPEt7a5QkvYHIGQ4Gb1aSSh2oosMc4MxdIXDtRnpm8mS9JKRC+/ZM0UrXiOhfozbD44vvlYvCAmMiFdGpp6ZApCyJDts/P1ci5KCwpgf9f4XxCw/SkIi3Ouv2/sSXNKvpQU2EQrCx+TAnwgqLSUk/T5KSqUodR06Gr+QO3Z/NcJ3Q5LRGcjlGcH/mLLamzCtXJo22bhW0HbeeRico2Xtt63mSucPbCXZ9tYtGP8s7L2LVrlVJrRH1uRUPA+nKB+9di4aO18/eiSyo24gz45KZYhURkTV5dOZ2atSYTco7DuaiX2tgz7IwCLryNs+/jU/6O5C2/1OAXJ4OQjOWP44n5oZODl+PR+AjQoAnWB/RZNP0FJp4+AdQnl2Y5yaSHRYTl3rMdWji0QQe7TlED/r7691UKvXuzU4dJsYm0+UD/oKIxxFFtgH1mN6d+g1sDD22f08TUpV62xVwHD3z5e/w145Aej4gOEe9MZ/70dqVnYlUIuIViHz8DaSkJ2muvdoMm29+JZzY5BwRXQjnHs6ew9aTc7duxG3y91zz0Wsz1tcc/9aaJl7Ogn/6bzFozXX5qxdJUAA36MJheJNs+GWj2L+PuTVZ3agZ2Io5LiBgIoSG7tXbZ8OGfxJ7m27c41tK2L8qDF4+yL0vhbWIDJzmDlXrcJwm5ikknZ4HnEgMFj1XstO4+YCQr8ojwCPwkSDAE6yPZKLZMEkajfjBGqg69+PouUEitKlA2cnDGr0h9Pw5sr1rT06jVuskOr7+/rTlr7/Cm+cxdGGP30EZp58Q5WcaPOt70vEHxsL6O0fo8ku5n2TcP3AevXIrjGw9GJRBpHq2rqrp2boqiFG+4ku+EVBrVOnP7x2jsjtvhQ8XLGRz7zF+osa0XWdB16mbM8jG9h/6EMFrJXd8xrE8CUjNT2uSGoPqcAuDrtO1j+7ki5DbSGRkc7O24C6XcYGBUyE4WDeZEwpNiV/D7dTOvqHgxrFY2LMyDGLD1bna1vJTe9KhvwIs7E055eVVoLywFDTRTzOecVyAl/rzhUeAR4BHICcCPMH6eFYFTbm3F+LWdc9zszMEEk5iTs3aTgezZmMg6k4gPTBoKMTcv69zY2wwfjxpPGMG9+L6MzqvY+FTsmjtG7f/OyryMKV9d83IdUNe1WMiTY0Rk982XRGO/aIxrV3FmYpFLEEzXwqIAFGr0yNPnhTcmTxZUHXRYk2Yk7fgq4XvE3GfXeBPH+y4RwO3BeaJc/cNfUmkJI3FWiWoU/O1Pne36kSqWdlzQUFzsqS1yTwsC4uqpE7t1WBtXZk7vvndicBU1fsTgdkhkMkFxH+GB/g2AI7EvwLlmXmQfEs3abPy302l1bpjE/myu4Cw84/xCPAIlCEEeIJVhiarMKbSlAQau7YzqF8U7tZrXTbIm33H3IdJEZH0xPgp8OzAAZ2bapPp00nDKVO4ByfvkMW9VxSJetT2m09Ip4kduRGHFtG7kc/1bua/tPuKukhcNJ5O9pzYhA9MLsxa0j5LUlM1IUGP6YM0iXDqn+/inOpVciYrx3ThDkw4ABH3InIlHXUH1iXVP6/JzQ68TDc+NSzwXdv35ubtNA3t3YQPHswn9+7N1Tnvzs6dSdVK80Astuf2rw6Dk1tyT6XTpIct6TzQHKycFJzyyhpQXVwK6ZEPch2DxKcdtRqwHTgJS6NUYuXRukfweP1jiAmMYX3a1LQBH38f8B7oXWI28B3xCPAI5I4AT7A+jhVC0kKuQ/Rv9fNUEwoDh6zuAKpoPwfS1AQuzl1CA1as0NUfS79Te8QICDh0l67yX11om+w97emU45PgbOht8uOZtTqJ28A6HcigOh1BITEtdH+FwcjYnk1UpZK/DgXA+iO3BGP7Nia96nlzWwdshbSU90mcdY251+bPyEuBihtw/hAkp6cbTE5W+LUlbZ3Kc48fr4I7d37U+ZyX11fEw2USR9KlsHtZGFw9rP9EoFAEZMhPFaCGn4CjyghQnvoZkm9sMNgeHFu5n5MoJ5aXiIqljlfD0R5HIfxMuM6l5NjCEdrtbgdiS3blSI6SPVUO5vqrWbMmywXo7u6eo/7du3dh9OjRLL1NbqVOnTosFY6uNoprzd+7dw/69+/P0vJgOh1twTQ8z58/h5UrVxZX13y7PAIGIcATLINgKvOVaNLZxZC4f2y+No6CjlpSuSO74oEzc4aANRvo2alTc5AasZkZbbVkCVTq2Quu7rxF14/eWGji89XG4dSxnjPt/O+ULG01dK1K53f4GuRiaYmMv6C4ldXnkpLV5O6jcOKiEQoPTT6UK8aNhjci1Xr7cmOvnoZ9r54YPB/z6rUgPVxcuVevdkFg4ERIT88ZlF6jxixS3nEIFxNO2YnAe5f03xDfsLMN6TrYHGxcrTjVjY2gPLsA0sP134+V29yY91hO5Y2/LhGCtb/Ffgg/q5tcaW1EktXldJdcCRbmwnN0dITk5GTAxMNhYWHw999/Z3mGEAL4wiTP1tbWpY5goUHTpk0DlUoFCxYsYPa9fPkSunfvDgcPHmS5E/nCI/AhEeAJ1odEv4T6Lk73YG5DMHFtwBQtE5e68HD3QXpo8OAcJEperhxLv+PeujWc3XiN7vhxZ6GIVr0edan/yoEw8/Tf9PCTqwJrmYLs6D+Hs5DKDd7MS2hajKobtVJNt/TbAmqV7qBxTsiR7ps/5R6lvwW8jZ1QatB8/FDTj3zu7sZFRpyEwMBJkJISleO5Bg1WE3ub7tzT20rY/0cYBN/XfyJw8CwPUqeZgIPkaFCemQuqq+9PPBZ0Qkyca1GbEaeBk1oYNKaC9oNuwbODzhr0ePO/mzOXYfaiVbC0BAs/P3/+PFOwTp48CUFBQTB58mSWDy8yMpL9rFWwMJnyhAkT4Nq1a1ChQgWoX78+vH79miVuRgULExpv3rwZ4uLiwN/fnyUzxoIJk1evXs0SGjs7O8PChQsZ+UFC9+DBA1Cr1fD48WP2Wb9+/WDTpk3w7Nkzlqi5W7duuY4XyV+bNm1g1apVUKtWLZaYGm3BhNRYMHn1Dz/8wGwSi8Uwbtw4aN26NUtSjWM5ceIEq5f5d7QLn0O7QkNDWVJqVMPQPhzviBEjWCLnBg0aMIKKCZvxlnm+8AhkR4AnWMa/Jiikp0L45A+n3ghtK1JFu1kgq94LQs+dJds6deMoIVmIlJWnJ229dCk41K4LR1ecoYcWHC4w0TK1NKXfn54Cj9JekyrlKwisZeY4y8W6+Rn/MspzhFQVq4JNn27KgXPTb5qSSl0rcyMvH4ejoS8MmofRVeuQ4RW9udiYy3A7cDIkJQVneY7jxKRxo53U3t5PcPNkLOxdGQ7Rr3UHyNf9xIr0+NICbD1sBMm3/qHKcwsgLfSWQXbkOer/KtiODSAmTjXxtwKv27z6MkS90rbh2NwRupzJqWJlJ1hKpZIREHt7e5gyZQojFr169YJffvkFunTpApldhBs2bIADBw7Ali1bICoqipGKpk2bwty5cxmp6dy5M0yfPh2Cg4OhY8eOjLSgutS4cWNGZMqXL88Im0gkYqoZtrd8+XI4duwYS3PTrFmzjPaQxH3//ffM/ZdXwTQ5SMrGjBkDc+bMYeoVkilU39q3b88IUY8ePeDhw4fQt29fOHv2LLNRH8FCu5YuXQqHDx9mrkdUyRQKBauPpBHT2UyaNIkR0y+//JJhhaoZX3gEeIL18a0BmhK0D+L+7lakG0pBYOSkltTsE8x5OAaiAm/S/f5DIfbhwywbkn2NGvSTZctA4eoBe389Ts+uPVvgDWvS0QnEuXZ5KhVJiiSgviBj/pie0aRoNA+OPoBLyy4xvCUWEtJ5TS/uTnIsDL2gP0dkZowGeFUj4ytV5pKVzyDg1ncQHx+UZd2am1cidWuvAWvbKtyJzZHsRGCKUqNzbftPdyd1m3Mcl5bATgKqLq8qtr8B816rqLzRl8VK5NdZrgN1gtqgJYUxWP5x/jnqagkWEhqO4wAJFsZNocLk4eHBCBaSEYxvEggEWQjWN998A7Vr14ZBgwaxdmfOnMkUHC3BWrduHfj6+rLPMK5r//794OLiwvIHavME7tu3D5AQIYnB15UrV1gOPiyffvopDBgwADp16gQRERHQrl07uH37dp7jpZQy4oQECpUsPz8/9gy6C1EBQ6KHY8XSp08fGD58ONjY2ORKsFDhQ0ywoKKFyh66IVG1W79+PVSuXJl9hkQSSRZPsPKcpo+yAq9gGfm009QkmrBrBCTfzKksfMihm7X+gZi1msAlhYXT42Mnw/PDWRWr8o0b09a//QYmFjbw7/QD9Mbum/kiWtXb+lL/lf5gZs27BktyntUqNT318ynwbOZJPT7x5L66eAxOh7/Kk9h0dfUk032rc6COhICACRAdfTXLM05OHUnVyvNBKnHgjqyLpIf/Dte5Hmq2sCQ9vrKFcp4KLvn2DlCeWwhpr67l2X9hMZJU7kStPt8CnNS82PrKF8GyEIN/vH6CpXURIuHCnydOnMjUKa177/LlywySzAoWkh9Ut7Ruuz/++IMpQVqClTnIXRv0jmrPsmXLmPsRSQ669NA9uHHjRkawsH10MWL53//+x1x8rVq1YgpZy5YtGdEzpBw6dIj1g/9qCxIrVLVQsdKWoUOHwieffALe3t65Eix8dvHixewxtFP7u4+PD2uvXLly7DMkm4gHT7AMmaWPrw5PsIx9ztNTacQMR6ApccX2xV8YCGX1/N+dPEzRwIW5i2jgypVZNk6Ptm2ZopVOxbBu9Fb68FxWxUtf36tiVlJOwIZcKsddGMxK+7OxiUrNjYQ3ghGXj+eJfZNyzuTX2g05U5oIAQHjISIiaxJoL69hpILbFI6kmcKe5WFw+WCMzjb7T3UjDdqIOCFRstgq5aVVeLFunv0XGZaciDrOTwX4b9EVWbuZGioOF6G2eVSNRo4cCZ6enow0XLp0KQfBQvcYuvs+//xz9tlPP/3EFLDcCBaqSuhuwzgsdLPt2rULdu/eXeQEC92M6G7cu3dvBmIhISHMzZldwcL4LHT9YYwXEj8s586dg1mzZjFXZmZClZ1gIXH8999/GU5YEDckhTzBKo4VX/bb5AlW2Z/D3EZA06MeQ9Q8n5LbaAqIp6RKZ0a0QO4IAavX0XM//piFaPn07MkSSifGp9I/Bv0Nr++H6VW0RqwfTmp1rYWW5Ev1KqDp/GM5ESBHQl9gzJXedVfV0pqsbNgMbEyAQ8UqNHR/lrrVq88g5Z2GcXER704E3r2Y80RgVT8L0nuULThVNOOSg/azk4BpwZc+2Fq3/e4mNXGuXWykvriC3DHeafDgweyaBSz6CBa63zDuCAnImzdvmLsNCVduBAuJC77WrFnD1KtRo0YxUoZ9FaWCpYtgoesQCRC6BFFlQrUMVbgzZ86w2DBUsq5evQpyuRymTp3KgvfzIliIU40aNVhA/8WLFwEVMUNisLBtmUyW4UI9evQoVKtWjQXOo51I2jCGDW3hi/EgwBMs45lLXSOhiUd+hKQTsz/YppNfeE3cGlFF+9lgUr42PNixnx4eNiwLSao2YABtuWABRL+Mo4t6LYPEqMQsn1duVokOX/8lmFqalpkx5xejslD/bVoqHXHpOFx5E5ZlHhykpmRT01bgIpdzgYFTIDh4a5bPGzRYSextenHBQUrYszwcXgS9SxiduXw20ZX4tRVyIoH6XWzVpVVA05M/+HybtZxIzdrOBM6k+A6UGKJi6QtwRwyz34OFbjuMk0JXWocOHVgMlj6ClZiYyOo9efKEKThVqlSB2NhYRjCy34Ol/R1VK1R4EhISmGsQVSNUkJCcOTg4FJmLUBfBwvGirRjEj3ZKpVIWnN6kSRO2nDDQHgkjuvvwJCAGyqOilZuChfjgGPBEJcZ6hYeHM5Usr9OOGCCP/eApRq3yhSStbdu2GXOCrkc8CMAX40GAJ1jGM5c5RoLXM8SsbAlprwM++OaTX5iFtl5U0f4nwDQkoWfP4MnDLIHq9ceOJU1mzOSCA17Que0XZZCsefd+oVbOVsWmIuR3HB9xffJK+RZaHtrK5kbAcWRXy05Q1cqWu3dvNn30aFnGnHGcgPg13EMdHBoLbp2Kg70rwyAqNOuJQO+6CtJ3tB24+JhxKQ8Pg/LcIlA/O1uq1rXIsTq1HXm+WOOw2EWj3Y/qvQsLyVW7PfovGi3sesSTeRj8juW3335jShCePvyYSmYMMG4Mg9wxXowvPALZEeAJlvGuCUpTEiDiB8tStQnlF25OZk3NPvkRcx5yb25dJ/sGDIL4p08zNufGP/5IGn3/veDhqbuaG/vvCnr/1BOkZsWnIOTX/o+5flKams69cxW6uriS+vZugvv359KgoPkZc6dQeJO6ddaArV017sSWSDiwOhySk7KeCOw9pjxp2knEiU00kHRmAagu/QFUnVhq13S52fGUk1oUO8FnqQf2pvMAACAASURBVHLWZU2V4+3vrfPuq6Jag+g+W7JkCWzfvp1dgYAqFKoweNrvYyl4DQS6OPFf7aWmqJ7hNRd84RHgCdbHswZIqkrDpSmTQBx/jabc2wfKi+9Vg7IIg1mbacSs5QQu8fVremzMBAg+dixjs24xbx6t0q8/MbWzwc2Xj70qHRNMUtSx9OWLzYI7d6ZnkCJHx7akWuUFIJU6ckfXR9JDf2U9EehZXU4+HWsPrpXMOPWTk5B0biGon5wstaRK3vgbIq3WFTQKH04ot6RihYKJdqVjCorOCo1Gw+65wrQ5qGIhscK7qrRXIBRdT+9b2rNnD+BpRV0FA8u1F4oWR9+62sTTjWPHjmWpeDDNEF5dgTFZ2ktVsz+DF7Jqr6EoKRv5fkoPAryCVXrmoqgtoTt+C6VhT5M5r9oKqN7EHJy9TDlVnJKKEm5D6sPDVHVpJRBVTJnbCGT1BlFFhzmgVqXBhZ8X0NurVwvqT5ig8fv+eyqSSkVFDSTfXsER0GhSNEFBc7lHj5YKPD0Hk4oe33MkXQ57l4fBpQNZTwR2H+lMWnSTcBIJAdXF5TTp/G8cTdafR7DgVhX8SYHcjpg2+goklTpwRFEBpDYOXNSdO/TZ4cMQeuECWFWsSFvMncsJJZJSSwgLPnr+SR4BHoH8IMATrPygVYbqqt5q6KpJz+Dh9azuFK+aZrRibTOo2cwCXCrJIVWZCtzbZ5Q8P4xpQyD9jWHXIJQGKKRVu1Kz9rMBZA4gUthQTijETY3f2ErD5Ly3gVKaTlWJai7+DbATgXcuvD8R6OJjSj6fYA/uVeWc+uVlUJ6ZD6kPDbuUtCSGKbKvQkwbDAGhRyvgFC6ciVwBkYGB8HT/fgi9eBFeX8p6atG2ShXa7+xZECsU/DosiQni++ARKMUI8ASrFE9OYUxLSyV0cue7kBSXnusXvauPKfWqbQa+TcyhQnUFkHQN0MTXFEKOQ/L19aAOvljqFS7zjr8Q08ajKCcx429sL8yiKaZnU1VEc2rbG27P8tcZa6nLcCfSqoeEk8oFoLq0kirPLeGI8s0HJyVij6ZEVncAcM6NQahw5DihCMKuXoVnBw8yQvXm9u08bRwRHEwxx6YxugmLaYnwzfIIGCUCPMEyymkFmppMYHTT/J8edHCTUq9aZlDNzxzQtSiWcKBJfEO5sLOgCtgKqUF7Sx3hsv8+mAqt3HAm89z8jHO6S/2oSMgjFaz54QUMmFyOVvCVCdQhN0B59ldIvX/gg86ZtGoPIqv9KXDl6oFQ4cClqZIh5Px5eHH0KHP5xT19mm/7Bl6/Tu3epYzJ97OlfiZ5A3kEeAQMRoAnWAZDVaYq0lcPVTCn/4NCf8Fb2ptQ71oKqFRfAVUamIPC2gRSE2JBFH2ZJt/ZBcnX//6ghAvvzbIecgAEptaFHmuZmuEyZqwqSZMuM0kWKC+t5JQXfgOS8PqDzJes3mCCSceJdTVObOkAyshIeHnyJLw8dYopVElhWe/tyg/MckdH4t2jB9QZNYpaVqiAChYfD5gfAPm6PAJGhgBPsIxsQv8bDj27Iwq2zM07B1x+hy9TCN8RrgYKqNbIHOzKS0EVlwgm8TchJWg/VV5cDqBJLTHSZTVwJ5X69uTVgvxOZMnXJyn3D3Bxf3UpOWJlIiPyRl+DtGoXTmPuBVKbchD/7Cm8OHackSokVKkJOW+INwSacnXrkoqdOoFz48Zg5laRk1lbgUhiAuGPw0EVn0zda7mBRC4psb8DQ2zm6/AI8AiULAI8wSpZvEuktxSVhm5fFAoX9kQX+2YmFHHMpYjuxBrNzKG8txxS3qoA4h5TzZP9oLzyJ5CEkOLZaIRS6jg3CYBjwe18Ke0IUELDJ0mLLUegwNKVyBsMA1HFtsBZVuDEFtYQdec2PD1wkLn78EXSc49J1AWhV/fuxL1NG3CoU4czLecKMmtzUKvUjEw9PPsIQu6Ewqu7ryDm1btTkW413Oi4A2NBalZ8JwnXPYqC9Y/fQGCMkplc00YO/j72MNDbrrSvAt4+HoGPBgGeYBnhVKcoNXTJ1090phkpieFWqCZnJxWrN7EA92pmkJ6qBu7tC6p5cQxU19ZBelhAkRAuWb1BxLzrIhDILIukvZLA5mPug6Ym0rd7x4Dq2l9FQohFznWIvP5AELq1AFCU54QSKYRfvw7PD727MiH8+vV89SN3cCBePXuCW9OmYFW1Bpja23OmVgqICYmBV4Gv4OmVZxBy9x2ZUsWr9LYtFArIL3d/BkvHol+X8WoN9Dj6EM6Ev9W5lFo4msPudpXAUqz7vEf2VDl4l1PNmjVhxowZ4O7uXiqW5+HDh9ndV5kTN6NhmG7Gzc2N5TPUVzDf4OjRo9ldXcVZMI/g8ePHwdHREXbs2AG9e/cuzu74tssoAjzBKqMTl5vZRAN0dLMASEsl+dpgigsKZ08ZO6lYvZkFeFQzA4GAAk0MpxB6ElQ3t4D6yfECESSbr89RcYWmaHapGGdx4WdE7ZKUe3sgbl2PAs232LsdMa39P+CcGoJA4cQRjQbCr+EJv0MQev48RN+/b/A6yHDx+fmB3K0iZ2pjzVx8EY8j4PmNF/DiRjCE3A1hZApo/tfXoqcLqJmNWZGvzRb7g+CsHnKlXSdIsk53qapz2WgJ1oULFxg5SE5OZjn5wsLC4O+//87yTOaUMCW5BgtDsHB8mFTa2tq6UCZjAubcLlCNiYkBKysrVqdBgwYsUTRfeASyI8ATLONbEzQxLh3Gf5L3cfIPNXRbZwn1rmUGVRubg3dtBcjMhJAaFw2iqPM0OXA7pNzeZtAG7LgA44j5UqYQoBoaPtkMQJOSJxmS1fwfkdbsDcS6JiexdoKUhHgIOff+hF9CcHCebSA2Fbt2JR6ffMJcfDJHVzC1sgB18jsX36NzjyHkzjsiFf0y68WnhcF1yonJ1KMOU4QMstGQvtAtOOjsU0Oqwt/NK4K/T053YXaChY1hwmNUsDDRcVBQEEyePBm8vLwgMjISNm/eDIcOHYKlS5cC3uRua2vLCJlIJALMw3fp0iVmz48//ggPHz5kaXSwYIJnVHUwuTK2d/ToUXj16hX06tVL763n2oEZQrBQddPVrlbBQvULiQ8SSRsbG9b0Tz/9xG5fnzhxIixfvhx27doFarUaWrVqBdOmTQOhUMhuZR8xYgSsXr2aPYvj/+effwAJF4594cKF4OrqCloFa+bMmUzJQrwwwbVYLGa33WPB5NhaGwpL+AyadL5SqUOAJ1ilbkoKbRB9GpgEvw59VGRf7IW2KI8GFNaijMB5PKlo5SCG1IR4EEVfo8n39oLq8sochEtSuSO1+vyfYk2sW9zj/hjbxwTk8VsHQsq9vTnWp7zR10Tq2w2IVVVObOUAiSGvIPjkqXcB6RcugCoqKtc1LbO3Z6f43Jo3B6sq1UHu4MCZWiogNjQGXt4OgWfMxfeOTCnj9Lv48jMv5bzLEa9GFcGtuis4eJfjzN0sqcxSxoklJtRULMWmDPrPgiF9GqJeadtp7mgOZ3SoWNkJFubV++GHH1guPUza/OjRI0aCfvnlF+jSpQtTtjp06ACYssbDwwM2btwIu3fvZuSkSZMm8O+//4KTkxNg2hos+LuJiQnUq1ePETYkL/369YNx48YBpplp2rQp3Lp1C0xNTfUO2RCChWRGV7vPnj3LcBEOHjwYOnbsmOG+w75XrlzJ0twsW7aMufbkcjmMHDmSEaFBgwYxkqQlhvHx8dC6dWtGQM3MzFj9lJQU6N+/fwbBQsLWuHFjePDgAdy7d48RSySdqGwdOHCAPbNu3TpDppevY4QI8ATL+CaVHlwTDvv+KPhx8w8NiVgmoD61351UrFJfAU4VTUEZlwQm8YGQ+uAQVV3+AxSd5nGmDYehqWWGSH5oXEtJ/1R1dQ0kHpxMTRt+BZLKmHKmIkhtHbjoe/fepZw5f54RqjSVfhLkUKcOqdixIzg3agRmFXw4mbU1iGVipkqhiy+YufjexUtRQgu1RjwbehLPehXApZoL2HvacabOCpCbm4JUKgG1Jh3C30ZD2NtoeBD1EkISoiA04Q3Ud6lMRzToXqSZBSzXXYMEtcagacQYrDj/+jnqagkWEgYkAUiwMPYKFRskUEiwevTowcgC5htEwoQKzZ9//snaSk1NhapVq0JAQACgetOyZUvw8/NjOQEx7x6SM3NzcxYvhSoSEpYNGzawZ7Cg8rR//35wcXEpNMHS1S6SIm0MFqppSPIwngvHg++fOnUKxowZwwjS0KFDmQ0Yr7Vq1SrYunUrsxfHWrt2bUamGjZsyMaCRA1dgtqiVbAyEyz8rE2bNjB37lyoW7cufPfdd4x88fFZBi1Zo6zEEywjm1ZVooZumv0Sbp6MK9SmUspgoehKxDguPKno4iMHZaySKOzMcIxFphCUsjEbqzkk+U0YkVjbCd+wlDMH2HUJoRcu6F2vnp07kwro4qtbl5M5uTEXX1qKGsIfRcDD8+9P8UUHF+zUrMxcRrybeIN7bXcoX9kZrD2tQWJrysnNTEEukUJ8chKEv42Bp7Gh8CIuHF7Fv4HQhEgITYiC5LRUnXa39KxDv2/5BVjJii5lTn4IloVYCPG5ECxtDBYSLvwZ3WaouMTFxYG/vz9cvnyZrT8kJy9evIB58+ZlrEdfX19Gkm7cuMHcgvXr12eECwlWbGwsU3tCQ0OZexAJy86dOzMC6LP/rmuRozvx999/Z/ZkLkhYvL29mQtPX7voltMSLCRbLVq0gKtXr7KEy+jiHD9+PKCydfv27QwVDWPN0I24b9++HO2iyxExuHjxIuC40c2IhFQfwUJXKvaLSbCRXB07dowRTr58nAjwBMvI5j0xLp0sGPYIIoJTjJZ4yC1EdP7h6iASc8ZEIo1sJeofTnpKCv2zUiVQRkRkmT+ZrS3xxlN86OKrWgPk9vaczEoBca/j4NXt/07x/RcvlV8Xn62bLfHyqwiuNdzAubITmLtZgMzKlJPKJCAWmUBUUjxEJMbA/TfBEJLw5j8lCknUG8B45/xOjretC1nRfRwSrCL7OywOF6F2XJ06dWKuMk9PT+Yq08ZWoYsLSQIqXJkVrMDAQEhISIBvvvmGkRJ0saEChkQM3W49e/Zk7sCCEKybN28yden69ess1iuzjfg+KmyGECx8bsCAAWw8v/76K4ufqly5Movd8vHxYe9nL/oIIMZqLVmyhCl8a9eu1UuwgoOD4fPPP4dFixYxJWzNmjX5XTp8fSNCgCdYRjSZOBR1CqEz+gZBTJg635tCWYHCp46CfrXAE0wV/P1XZWXOMtuZHBdHrvz8M5FYWAjKa118Nujik0DEk3en+N65+N7FSxGNYS4+jzrupEK9CuDq6wr2Fe05uauCmipMOalUjCQpmyvvPYmKVhbsstHcsLeVW9A9X/wCMpOiuwuruILc8QQcqjqoNGHJTLDCw8MzYrBQucGThkeOHIFt27axuugSk8lksH79euZCa9u2LXsf1SB8vyAEC1U1JGj4LKpW6Mr866+/WDwTxmehQmYowcIA9TNnzgDGZp04cYLZhm5DVJq2bNnCyCDWQSLXp0+fLO2iKofEEokVugKxfxw7kiatgoW2oDsxsyKG8WjYLranjU0ri3+nvM2FR4AnWIXHsFS1gARrcqe7oEzI/4WKpWoguRjT6jN72nO0M5iIBUZLIsvKXBTQTpKcmAxhD8K4R+e1p/hCIOpF7kHsIrGQ+DT1ASRR5auUB1t3W07sKAN05ZlKpJCUqoLwxBh4GRcBT2JCsyhRSanJxb9WKEc8bRzB3coRnC3t4Fu/PghPkSlY2JghKpa+AHd8Pvs9WEheMB4K45IwmB0VmswEC59Bl91vv/3GTtzh1Q5z5sxhJ+mwoBsQFScteRkyZAioVCpGWrAUhGDhc0jsMNAeXXN4gg/bmTp1KlPJcms3s4sQ6+F1ChhHhW7FsWPHZixXdBkioUT3IBJHjJtycHDIYi+6FNEGHD+eMLSzs4Off/6ZnRjMfA8WqmR37txhJBNPIaLCtWDBAqbAIQHjy8eLAE+wjGzu8Q6sb5regnS1Yf/rL4vD95/hTht1Zkevi3/TLIsAlX6byf2T92FJ76U5yIeVs9W7U3m13MDZxwks3a1AYiPjZKZSkIklmVx5L5n77p07D2Oi3oCGFP29b7amFgQJk6ulAzhZ2ICDmQ3YyS3BUirnTAVyKhNLOKmJCQgFQpCYCCE5NR3eJqVAUnIqlLNVgFwqxrtEioxk4UWj3Y8+1HsXFpKrPblcNFr6l0bZtxDj05CU4UlFvnzcCPAEy7jmH/+zB1/Vu2nUxGPGv1WpYwV2BN6ox2lcSzPLaGhSnJLePhAI9hUdOLmbOZWboytPwtxBGAsVlhAND6ORREVlkKjIxNhCz7eIExIPGydws3SA8pZ24KiwA3u5BVjJzDlzEwWVCiWcqVgMJkIhiE1EQCiFJJUa3ipTQJmshsiYJIhNSIaEpBT2ik/Ef1PhbWIKJKqyBrwvn9qV2lnJi2WdortwXfZUOd72Ou++MuJ1VOqGlpSUBH379oVZs2axk4R8+bgR4AmWcc0/TVdTGOl3q9AbUWmGZcWV2lQoYkM06nGW5jkorG2JqSpy9dV9TuvKQwUKXwkpynzPqYPcirhbO4KLhT04WthAOYUN2JlagoVUzsk4UzAVS0BqIgahQAAmJkJISUmDBCRMKjXEJ6VAeFQivE1KzUaaUuBtYiqkaTT5tkeLzS+j2xJPF6a0FpmCVVjcS8PzeA/V119/rdMUPImI7ruyWNBNiqcH8X6ub7/9tiwOgbe5iBHgCVYRA/qBm6MpSg182zywwJvCB7Y/z+5tnMRk/J8+YO0g5jetPNEqvRUik+LI4O0/Q0RSbI55FAtNiKe1E3PLOVvYQTlzW3CQW7IrDxRMZRJzMqYyiUBsIgSNBlWmVFSQmNoUGZ0EMW9VzFWH6hKSqARUmhKZClVifxs/jWxDfdzZbeol1mfpnXHeMh6Bjw8BnmAZ15zT2Ag1TOl812i/0F19TOm4P31AasoHuJflpatSpxCMnRIJhExlwvgqjGUSCQUgEmIsUxpTlNAth8QoLCqR/Z6ZNOHP8W9TiiX2qiiw/fHLVtTXy4EnWEUBJt8Gj0AZRIAnWGVw0nIxmUYEp8D03kFGS7C8ayvoiIX8FQ1lfdkmKlPJnScR8OB5FMeI0n9qE8YyKVNKTmXKjqNQyBGFqRjMZBIwMxWDqVQMcpkYZFITMJWagExiAjKZCUjEIpCJRexfsYmQE4uEIBYLwEQkBE0aR4UCjhOLhUQhF0NKSjpT6ZjSpkzL6NLVpeguIS3r64G3n0fAGBHgCZZxzSp99VAFc/o/MFqC5dvYgvrPcgczi3dBWHwpmwikpRPNim1XuIuBL/N09RYl6TERCUAoQpVMAEIhB4L/7qrF9/AzLOnphJGh1FQNJKekg1pNIC1N8+5ftQZS1QRiY1MYWcI6qSka9m8K/sx+T2c/d+roDi4uZjQkJEnnWm3s58iv4bK5fHmreQQMQoAnWAbBVGYqlblEz/lFtu4nVvSzCS6gsDbhN6f8gle66qdHx6g4dRoRZCc9QqEA8weCCIlQNtKjVmtAlayb9CQmqiEuPjVX0pOSgwz9R4rUGlSakFgV2bqaOL42rVnTlidYpWvd8dbwCJQYAjzBKjGoS6Qj+uDqW1gy8kmRbRIlYnU+OmnS3Zb+b5IriEz4NDn5gK00ViWHj7yE8xfCOH2kJyU1nZGloiQ9JQnEpAm1aY0aPMEqScz5vngEShMCPMEqTbNReFvouZ1RsPmXV0ZLsFr3s6d9x7ogUkY7xsIvg9LfQloaoQsWBcDBQ8FGO4/FSbASj96ExOM3Qf00jE22uKITKNrWYS++8AjwCJQOBHiCVTrmoaisoJf3x9DT29+ASMSBUIwuFg7wzih8CTL9LDLhWAwKxp4IRcA+Ewi5jPoCIYDgvzgVoRDetyF4145AgJ//1ybWxffxdyEAxwHHCbB9AA7fE3DAYX2sIwDAz7CuUBxL8WJJ/B0JE2pSnEDAfsj8etfgu5dQKKRiCbu8Mc/YnaIClW+n6BGIi0+lixYHwukzoUZLsCZPrEOrV7cpUhchSUqGiBkbIeX2c52TIq1RAcrN+AIEZjK9k4a3EWNuP0xnExoaCtbW1tC+fXsYP348mJqa5jrZNWvWZPkAo6OjYfTo0XD69GlISUmBzz77DMLCwuDgwYMspUxhS+ZUNAVpC3MYYuLqUaNGFeRxlqIH7+TC1ECYtBlzGWI6nczv59YwJn1GjPjLRgsEv9E8xBMso5lKNhCi0aRwhKTBu5cagBKgQIBSDVD8OeOlyfRZ5s/f12PP/vfSkGRIS4v9rx1tHfxXA4SkZ2r/3XvvX9rPtO+96+v9M1nbojS3tjTg6tqLVqw4HDdlo92YjWtJ6h5Nwls1/Wn2dbhyNcJo57E4CFbYuFWQcudFrksESZbTgi/11kGicOjQIZg/fz5LVIxkAHPsYV6+DRs2GESwkES9ffuWkTPMX+jv7w+XLl16978kAwr2JcD/bekpmEMQk0fnVie3bgpLsL788kuWv7BWrVqQnJzMcjgqFArI/H5u/SOBxdyNX331lQFo8FWMFQGeYBnXzNKnT1dDQMBkw77lyuDYPTz609q1F4BAIDbaMZbBacm3yUlJaXTSlEtw+0600c7jlEl1qK9v0SlY6BaMWrDdIKztxvcBRbuc7sLY2Fjw8/ODbdu2scTE2qJUKmHPnj0szQsmNp4xYwacO3cOMOExJkvGpMcikQiyK1h79+6Fbt26seTMmDR506ZNcO3aNVi6dCl71tbWFmbPns3UJCQdSMaCgoKgVatWjEDh70hEUElDJWzlypXg7OycJZky2rp69WpIS0tjny1cuBCcnJxyxSEzwfr777/19oPqFCp5qOqhrdg2kk9Mbo3Jn6dMmQKvXr1iChaOL/P7L168YHbj+LD88ccf7PeOHTsy5Qzx6tOnD0yYMMGgOeMrGR8CPMEyrjmlYWFH4OLFfka7aZUv35URLInE1mjHaFxLUvdoVMnpdNQ3Z+Hxk3ijncepk+vSatWsi8xFaIh6pUVbWt0DnBYOzwE+pnOZOXMmnD9/Xu8yw0TFixcvhn379jHigQRq5MiR0KVLlxwEC12E9+/fZ5/jz+gm7NChAyNrHh4esHHjRti9ezfs2rWLqWOYAHn79u3g5ubGfkcihi5HVMSmTZvGVCIkJFoXoVgshsaNGwPaXb58eZg8eTIjLlpSo28QmQmWvn6GDBkCrVu3ZliYmZnBjh07GMnr378/c5kiqUQFK7OLMPP7WkKVnWDh79OnT2fuRV7B+hi+zfSPkSdYxjX/NDr6Kpw+3cFoNy0Hh+a0YcO/QSy2NNoxGteS1D2a1FQN9R9yEkJCEo12HouaYAV3nwFEmWLQ8hDIpeC+Z0aOukgitmzZwgiPvoKkSqVSgVzOYh1h6tSpjNxg/kBdMViZCda///4Lx48fZ6QES2pqKlStWhUCAgIY0Tp58iSsX7+efYbE58KFC0ydwoJKE6pbCxYsyKJgYQJlJEBYkPThGPJyZWYnWLr6QSKE6hwSOlSdUFHTFp5gGbTM+Ep5IMATLONaIjQh4R4cO9bMaDctK6uatEWLfSASmRntGI1rSeoeTXJyOv1f/2MQHZ1stPM47rtapE4dO1SwcgQbmZqKoFZNu3yNvSgI1qlTp1hC4suXL+tdZgkJCSwm68GDB0wtQrfXgAEDmNsrL4KFqg66zubNm5fRvq+vL+zfv5+5HJFooTqmJVj6ftcqWOXKlWOqFxIzjO/CuC90D6IyllvJTrD09XP37l3m2sPgdbTzp59+Yq5AnmB9DN9CxT9GnmAVP8Yl2QNVKoPh0KHa+friLkkDC9uXqakLadXqEMhkzvwpwsKC+QGfV6sJ7dr9AN6GbrRrddnS5lQo5OjbtzlT/5ibi8G3mk2+xl4ULsL4+Him2mA8FMZiaQsqTUguUK3C4HcM7J4zZw4jWJMmTQIXFxeDCBaqS8eOHctQpbQKVmBgIFPN8kuwbt++zdyIGIeF7kNsA5WwoiJY2vFjHNiSJUtYrNbatWsNIliovGkPCGA7v/76KyA55V2EH/CLpZR1zROsUjYhhTSHqtVxsHevZ76+uAvZZ4k/3rt3FOU4IfZr1OMscWBLsEOlMo127LqPatKN97qN9X+3oSpVepERrKIIcscpxkBudOUhkWrUqBG8fv2akSupVAorVqxgrkA8XTh06FB48uQJDB48GLp27cpcaXkpWBjsro3BQiUI3X5HjhxhBAndevklWOhuROVrzZo1TL1CFQ0D8nfu3FloBQtVOSRJSKwkEglzPaKt2Ffnzp3ZeJs3b54lBivz+xhntnXrVvbC2K1evXqxmC0kWPjC+LGJEycyOzHwXyaTMZUMC8a5oUqHQfvoksX5wLa1btkS/FPkuypGBHiCVYzgfoCmKV7NsHNnOaMmHh06XKdmZp48wfoAC6yIuqTx8anQudsBo16ne3Z2ouERyiIjWIi9ISqWvgB37dzhho7EZ/PmzYxcYYA5BrLjvVZICm7evMnuxELChYQAA8ExuBwJGZKO7PdgZY7B0pIHPG2HqhAGeqMS5urqWiCChfYMGzaMKUPoGkTXHwaO53U6zxAXIcZ6YSA7kh08OYk4oGvUy8uLqWZIvhAHPL2ovQcr8/t44hKvbcAYMTyB6O3tzezE8WLMFxLVFi1asLYQN3R3jhs3jk1Dp06d4Ntvv4W2bduyKyB8fHzg7NmzLNaNL8aDAE+wjGcuM74/t2+3Nmry4ee3kTo7dzLqMRrfsswyInr+QhhM+f6yUROs0yd60GvXI6kupdXFxQxcXRT5Hj+7aHT6Br13YSG5KjdzQK4XjRr52uKHxyNQahDgCVapmYqiMYSQFLp/f2VQqxPy/eVdNBYUfyuVK4+l1ar9wBOs4oe6hhTJtgAAIABJREFUWHpQpxG6fPkd2Ln7mdGuUZFIQE8d7w6Xr0ToxLCgBEvbGEuVc+wGqJ+Fs7fEno6gaFtX591XxTKJfKM8AjwCeSLAE6w8ISpbFdLTlfTYscagVBpfPkKBQEo8PPqBm9v/qEJRkYrFFqKyNTu8tYgAXjI6esw5o74Dy85WRjesa0Pv3I3RSSILS7A+9pWE8U94+k9X6d69O3//1Me+QErJ+HmCVUomoqjMSE2NJmfOdIO3bx+U+VN2Tk4dibNzB7CwbgBCqRNnJpbBk4RYuBsXQzu7VOCkmGCRL2UNAUoIhWYtdxn13Hl7WdI5sxvS58/f6hxntarWYGEhMWoMytrC5O3lEShqBHiCVdSIfuD21OoEeuvWdxASsqdMfXlbWvoSF5ceYGfXmBOYeoNCqoA3ySoIjImES2/CIDD2DTyIf68GHGnXh3iZs4sByzyR/MBLpqS7p0+fJoD/kBNlan3mF6QGDcrR70ZXpy9fJfEEK7/g8fV5BIwEAZ5gGclEZhoGvX9/PgQFzS21G5iJiSVxd/8UypVrBSKz6pxcag0aSuFuXBRciHzNyNTt2DeQnJ6eYwyfe1YlnVw8OF+ZDTE1YwoAT7DK1hqmm7Y8gj9W3Su167Mo4OzQ3o327+dDQ1/rJlg1a9iCXG5i1BgUBY58GzwCZRkBnmCV5dnTbTuNiDgF58/3LjVf3uXLd2WuPpllA5BKHThTEyk8ToiB85GvISDmDdyJfQMRyUqd9rZ1dic93H2gjqkNZ20uh7iQeHhx7jlo0jWk1me1OBOZCU+wytAaxvuvZs2+DhcvhZea9Vkc8PXo7kn69PbUeYs79tfYz9Gox18cmPJt8giUNQR4glXWZixve6lKFQIHD9b4IF/g1tZ1iItLd7C19eMEpp6gkCggXJUEATGRcPFNGFOmHifE6rWtmqUt6VexCjS1dAJ7uZxLjk+GkKuv4NWVV/D61mskVuxZsamYDtg5gApEAp5g5b0mSk0NzEHYs89hSEhI/SDrs6SAmDm9PvXwMKfh4Sqd4+QJVknNBN8Pj8CHQ4AnWB8O++LqmQJQ2L49f2k4CmKMRGJH3N0/g3LlWoBQXo2TS60gVaPJ4uoLjHkDaeQdKdJVrMQSMsSnBrSxdwFXqYIDQuF1wGt4efElhN4MheT4rLnqylUrR2p+VhMUPrYc4YDaWJhiszzJKsgElvwz5OHDOBg6/JTRz9fmDW3p20S1zktGC5KHsOSniu+RR4BHoLAI8ASrsAiWwufxqobjx5tDUtLzIlUJXF17EyendmBqWQ8kUntOIhTDI+bqC4XAmCgWOxWdovt/7Jlh8veqRjqUr8BVklqCqVQMEUER8OLCC3h98zXEh8TnsNm9sTvx7eELZp5WXDqlcPT6Uzhy7Qm42FnAlP7NwFTCx7KUwmWYwySlMp2u+vMe7DLi+6+0gz57qie9cjVC5yWj9vam4FXRolB/m5eC18Hl4PUQEh/IunSxrAl+7v7QyH1gWVgKvI08Ah8FAjzBMsJpTk2NpTdujIKwsCMF/hK3tW1EXFy6gYWNH4hN3ThziQJCkhLgFjvVFw6BsZHw7G1OMqQLzo4uHqS7mw/UNrXhrMzlEP08BoLPv2AK1ZuHb3Ta6NPeh3h3qgwKV0suMTkVDlx6BCduPocnodFZ6t9a8zVuYlgKPFYjXAKlcUhsnpo032n08+TsbEb/WN6C3n+g2xVemDuwVGnxsPJiD3gcdUbnHHvbtYARjXeDqYml3jWAqXIw2fM///wDoaGhYG1tzZIbY1oYU1OmCOst2lyEmAKnOEph209MTGSpajDtDKbqsbS0hC+++AL8/f2ZuZicOTo6GurWrZur+YbWKw4M+DaNBwGeYBnPXGYeCb17dxY8fLjEoM1MJnNmp/ocHJqDUF6FM5VYgCo9De7FRbFAdHTz3Y6NAg0lBrVX29qefFaxKjS2KAd2clNOFaOCl1dfQsiVEBZHRYjudqr3qk4qtPUC8/IWXGRsEuy98ABO3noOIW/030o/d3hb2rZeRZ5glf51TA8dDoaf5940aA2V/uHot7BZUyc66mtf+iI4scivaFhwpgU8iTqbKzxIssa1OK23zty5c+HQoUMstyAmdUYygTn4CCEsX2BREixsU5CPMMnCEqzvv/8eUlNTYdasWYwsPnr0iBEszDvYrFkzRiyReGE+w9yKofXK8jrlbS9+BHiCVfwYf4geaGjofrh8eaDOL3h3937E0fETMLWsAxKJHScSiOBRQjScy3SqLzY1xeCNsJxMTvy9faGVnQuUl8g5mk4YkQq+FMzcfilv9bdVd2Bd4tKiAliWM+ceh0TDwcvvlKroBN2nCrODWcvbiS75pgMoZPyljR9ioRnap0qVTgcPPQn6ri0wtJ2yUG/woCq0TevyeIJQ599Q3Tr2IJEIDf770o4Z3YLrrw8yCIKB9f5mLsPsJTY2Fvz8/GDbtm1Qo0aNjI+VSiXg7eiYwBgTHy9evJgldeY4jiV8njlzJigUCshMgJCkYSJjjUbDkh3Pnj0bPD09GYlBYhMUFAStWrWCMWPGwIwZM+DcuXOsbsOGDVmSZZFIxN7Dz5CEYcLptWvXsn5RIdPXfm4AoP39+/eHrl27ZlQLCwsDKysrCAgIgFGjRrF+MVk0Jl7WZde1a9ey1MNEzcuXL4ddu3YxcoZjmjZtGsOJLzwCuSHAEyzjXB80MfEpHDlSn3NwaEEwMbKljR+YyFw4c4kZF5wYT69EhcO16Ah2qi84Mf95C4f61CDtnd05bxMLkEpNIPxeBARfeOf2exum+/ZqhFooEZIGgxuAc2M3sLRXcAFPwmDfpYdw8sZzUKao873pYJtbpvUhlVzt8EejD54uo8uVPngYB8OGnyrQ/OY2Zi8vS5g9syFcuRYBi5e8i0fSlk/auMCE8bXh519uwJmzr9nbHh7mMGK4L6CbbMOmhxAUFMvel8tF8PtvzWHUN2dBlZxeKJiXLGpK5XITGhub8z8WIqEAGjRwKBAOhqhXWsO97JrD+BY53YgnTpxgZOn8+fN6x7h//35YuXIlbN++nalAo0ePBhcXF5g4cWIGwUL8OnTowEiZh4cHbNy4EXbv3s1ICKpgy5YtY8+7ubnB0aNHGWHbt28fwx2J1MiRI6Fjx47QpEkTRrZatGgBmzdvhv+3dx5QUR1dHL+zSO/NiijYFbF3jb2X2LvR2EsSayyJ0agpGqNRk9i7n72XxN7FClYERQVRBBFBKVJ333znDtmFlV1YcOl3zuEob+dN+c3A+3Pnvnvnzp0rxpZe++ktzvr162HdunXCQoUWKxR8qQu2j+INP9c2rq5du4pxKOvhuHE++/btA3NzczH2Bg0awJdf6iZ2P2kz0c35mgAJrHy9fFoHL72Lj+bmRmayqMQE8Vbf5deqAJ5Z+uXevUx56fMyFcHd1I7ZWJmxsCdhPOBSsmN62JOwdNs0L2ou1fuiHjjUKcls7M3hyv1A+Oe6H5zz8geFluPCzCxL92ZV+LR+TcHMhJzdM8Mtp+pi7Kt5C27B1Wv6jX3lVs0epk6uCf4BURDzIUlNYPXrWwEwmKe9vQns2OmnElg/zqkP23f6QeT7BJj+bR2YNv2KwDDpmxrg/TACzpx9+clYDu3vjAFG+YcPSWl+LqysjKC6W9be8J10yAbikiJ1Gp+poQ0s6/4uTV0UCTt27BBCSFuZMmUKVKtWDUaMGCGqXLlyBRYtWgQovJQWLBRBp0+fFmIGCx7L4T1oJUKhdfbsWdiyZYv4DMVSbGysECdYvvvuO3BycoL27dtDr1694O7dZGGMbVStWlX0l177aElLr6BwwjFcv35dCMQBAwbA+PHjhcUptXDSNi6sm7oeWuDc3Nxg5MiRotvz58/DmjVrYNeuXTqtBVUqvARIYBXQtY+Vy6UhF4/C3YiwLFl1GhUtIfV1qQINrIoxB3MziAn7AC+uBybHo7rzKkORZlfOTqo9qDbYuhVjZhYmcPFugBBVl+89z/DerCzJxRUjuKWZMd6aLe1nZUx0T/LzNTIyETp3O6r3dSlVyhwiIuKhb+8KYGdvoiawype3BkzJs2xpMzh02F8lsDaubw0TJ12C6Jgk2LW9PfQfdBLKuVrDV+PdYfI07VYdXdeySBEZP3e6O1y7/lrjLSVKmIGrS9beIMycwLKGZd3fpxnDuXPnAP2Url27pnVK6BCOR2w9e/YUdR48eABjxoyBq1evqgTW4cOHISAgQAgvZcGjRBRheOyHQgutVlgiIyOFj5evr684nkPH+i+++AIaNWokjg9TW9NQpKGVLb32y5Ytq9NyoP+Xl5cXzJw5UxwJotUqtXDSNi48Rkxdb/jw4XDv3j3VCwDYrr29vbDIUSEC6REggVVA98eHpCS+4N412BvwSKcHW1lzK2lIBTdoaVcKSphZMEWCHIJuB6niUSV+yPj4rlTtUlKNPjXAoqI9Mygig9OeGE7hKXg+yliQfeoy9GpejU/q0xjMyYr1qSj1en9iooJv3faYb96afcnHhw6pnEZgKSfxscBav6aVsFqhwNq2pR0MHHwSVvzxGazf5AM9PncFjFG1YaMP+D1JK050AVOxoo00f259zEGo8Q8bl7JWULKkuU4/kx/3p48jwvfv3wsfKPSTQl8sZUHr0YIFC4R1Cf2L0JKEwgILWpTQIR4FhdKC5eHhAadOnYK1a9eKOkoLFlqj0DqWWmChn1NcXJx4uw8F1owZM8SRI7652Lt3b5UFC/3A3N3dRX/ptW9hYaFxKbAPtKql9r/CiqtXrwYfHx/hL5ZaOGkb18cCCwVapUqV6EhQlx8AqqNGgARWwd0Q0tZn3nze7asaPTFljEmjK9WETsXLgoupJTMqUgRCvEMg4HLysV90qOY3oD7G5drcVarcvRrYuNixBLlCxKfCr4cBmsMvZCfuy3+N5OYmRthFlh5g2Tm2Qto2fxMWy3v1Oc44z741yYzAmjKpJly9/ho+xCTB591cwdMrFFxdrSExQQGvgj+Ap9cbmPtDffjqm/Tf1NO2nkOHVOadO5XlAc81+yG6VbMDa+usvZChDyd3HPeSJUtgz549QjShFenVq1dCXJmYmMDKlSuFk7nSh8rY2Bi+/vprqFChAkyePFklsLAdpQ8WWpQ2bdoEJ06cEM7z6IOVWmDhkRu+rYhHbE+ePBHCDUUQWq9Q5C1evFj4YOFxI44JLWDpta+NPTrQt27dGnr06AGjRo0SFqfAwEDhM9WvXz/xNiE64hsZGQl/Mm3jQqf21PXwuBPFGR6t4jEnhrdQOsqn93ONb2f6+/sLp3gsyARLrVq1xL9oTXR1dQVdLXKF9HdIvp42Cax8vXzpDp5HJMRDvSNbVWKjV9lK0udlykN1Y1tmZW0Gbx6/AfSjCvIMgnD/cJ1FSdUuVaXyHSuBVWlr9i4mXjipoz/Vs2DtKXByAvOeH3rJS9tbcGMLc8Oc6I/6SJ9AdHQiX7zkDpw7H6Tz3soK08wILHs7E5g6pRYYGDBYt+EhzJpRF7765gLMm9sQVq15AAEBUbB3V0fo0/94VoYCmze04UlySaODOzb4qSlydLFiaXNwV04IfY9QEKFTOYorR0dH4XiOzuwoPvBzFBTHjh0T/8eYUWjVQsGS+i1C9HVavny5eLMOHcLRQuXs7JxGYOExHcbYQgGHx4gogtAqhGIKC4o77AdFEDrL79+/X/hoaWs/vYVBQYXHlrdu3QK5XC6O8tDPC48H8Y1ItI6hsEJBN3ToUK3jMjU1VdVDFig8cVx4PIiCCENdFCtWLN09gv5uKGTxSzlP/PeHH34Q3+Mbj/iFVjwqBZMACayCua5iVnEKueQR+gpqmNoxe0tziHodrfKjCr4XnKmHXs1+NaUybcqDTSlrFhwWBYc8fMWbf8Hh2t8YzCm03/ZvKvVtWpEFHTwIjs2acTMnJ+w6S75nOTXmQtAP9/YOh7ETLmRqn2WFS2YEVur2J36Nju3hcPZcEPy+qAn8tfI+PA+Mhv17O0GvPv9meigGBoxfONsTnfk13vspDu7KBjHQ6EqP7lpjYaG4Gt/kULqBRjM9MbqBCBCBLBEggZUlbPnjpoSEJPlbnzeyh4cfMoxLlRSX9q2m9GZSf0R9yekzF7ApZsl8nofBsWuP4KyXP7yLVs8PmFs0ejd3kyZ3q8mi794Fn/nzIOHtW2bfsCGv9ccf3MDMjARWbi0MAMjlnHfscgTi4uR5UmB97Ng+YXx1ePAgHO7eewtLFjfFkBKZptewQXE+dXIN7h+g+Xj9U/yvPh4MHhfiV9B/qXKc/kuVoyn2VaYnQjcQASKgFwIksPSCMc82wgOvB8LJH07q9JAzsjSSGgxrAI4NSjGHYlZw69ErOOKRfPwXl5g5cZadRNxdi0pLhjdnRtGR8PDHuRDp7S3mZ+HqKrltWsFiWTQvYV2VrFjZuQjptI1BRddv9OF79j7JVpH7zVfu0P3zcuK4D4tCweHosQD4Y/ld2LCuNaCgKVJEBpLExdeCn2/C+QvJ8bCW//EZLF12BwIDo8X3xYqawvff1YMiBgw2bfWFW7feZJre1Mk1eY0aDjwkRHM+TgwbYW5OoUQyDTbVDRh3C53WNZXu3btnGKH9U/r++F6MFo9vVmoqeKSa+iUCffZLbeUfAiSw8s9aZWWkXJ4gh41dNmoVWFZOVlLdgXXBvnZxZmljBpdVMaqeYX86CbOsDCwr96Bj/p5Z3aC0vQV7OG8ehJ49qxpfrbV/Kazr1JKtu94PHoT8yxZ2CeK2pqUw/122PuSzMo8Cfg9/9iwShg4/k6f2Tk4w37+nIw95Hasx/tWnBBjNibFTH0SACOifAAks/TPNUy0mRCXwk3NOwuuHr1UPvKJVikq1+tcCyyoOzNTMWMSoOnbtMVz1fpFnH4orv+qgaFjTVfZk+XLuv3GjSjQ59eghlZs1mXkEboI99yarxr+o7QuFpdyKGzhYF8lTC1LABxMfr+AY+uBNWN44Rs4p3Ha2Jnzn9nb87j31ZOTK/osWNYMK5bMW/yqn5kD9EAEioF8CJLD0yzMvtsa9D3rDS6+X3L2XO1iUt2dolzp16ymcuPUE7vjpN7q2vgGkdmB/tHChmgBscHSvFGctZ6uu9YLQ6OR4Xw3KDJaGVV/P3m85zY0qOjHzJtU4MzYkK5a+F0ZDewmJCr59hx/fuMmn0PHu0N6ZfzG4Mn8ZpDn/YIXyNlC0qGme/QMmB7YHdUEECh0BElgFf8mlN+HRwAxk7PgNPzh58yn4Bqaf2iYvIFFzYF8wHxLCUsZcddYsyalfH7bn3lR+7sly1cN8RtMrUmleiQVPWQuK/95udFo7STJyKY5TKnQP/RxeRyk68oPUsduJQmkxnPFtHUXFilb87dsEjXHnsprgOYfXkLojAkRAjwRIYOkRZl5tKjouQRq39CjzyYXgn5llos2BHduxrlZNqrb6dxb44S6svNoDFFJydHkXu4bStAanWczRWzxi40k1ISWzMONl9nwPzPA/T+jMDojq60QgKfY99z+3VlGq6UiDDp+fL3SWmssXevHbt28DBzOemGgJnMtUDDA6fK2ajoWOiU4bhyoRgQJMgARWAV7cVFPj/1x7DD9sSHEKz2vTTs+BHcdae+NqhUX1KrI11/uAb2iKA/WE+ocU1WzbykK+XQeJ/pqPO01qlpNKLBjKmYmRRutCXmOR38YjyRMUnuuGs4hn12V25Rvy+mO2wYJfb/OTJwMLhdWwXZvSfMTwqvx54HswNQnDgJZMkkx5QqKFEFqfkn8wv+0FGi8RIAIpBEhgFY7dwOUKCeqPWZ0n/4r+e0J7RaNa5dI4sOPSOA8YILlMHc8u+a+F/fe/VY3f3NBe+qXFY5Z48zm8Xbw33XlZb58lnYxKgralrJmjKb0mr88tHy6P4Bc+XObNwA1uL2gnBBWTGfKm00/CvUcgTf/es8CL2tUrW3ADA9l/0dsV3NzsLStVqhQEvXoFksKUu7g4gZ2duV7F5qvDhyH4yBGIevxYLKdVpUpQ6vPPoWS3bvpcXmqLCBCBTyBAAusT4OWnW+MS5HzuprNwxvNZnhFZSgf2V4cOge+vv6YZV8N/D0jR5jFs1dWe8PaDv+rz/rX/lFqWHc9CZm6EuLtP052P7eH5fLrnS77+0RvZ6qaufGSVYgoDBoXST0jf+1UBCvmWiB0Gs0LnsKG2g6SZdpPYze8aqNbDre8iblqmBXze92Ke2XP6ZqDp7UHGJG5mGsZq1qzJwsLCOKajwZQtJUuWBANl0K4sDkQeHQ13Jk2CCE9PjS3Y1a0LtZYtgyKWllp7wLQ0mOwZc+oFBQWBnZ2dSLyM6WwwHU5uFUxt888//4i0PfoqmIpm4MCBgDGydC2YRujx48ciHQ4VIvApBEhgfQq9/HUvv/MkBEYsOpjrD7tezatJU7rVSo7A/pEDOyJ1+/FHqVSPHrItt4bzq883qY13ceuXksGzGBb63eZ052FUtqhUbM1k1viwN3iGJb/Z9ahtVUlxKphXGlqRGZgY6NWikL+2wqePVoqXS0GH7vPE5uasyfsOgmUd05r8UJndcH1RJx77NkBcK+7egVfvvwS+mXqD37v/tsAxHzKoEm/f3pm/evVBbT8ykLip6RtWq1Ytcf3Nmzc8ODj4k4XWzeHD4Z2XV7oLiCKr3oYNWuugcPj3339FLkBMwoxJiX/55ReRZw8TNedWefjwoUj8vHnzZp2HgGOWybRvq9wQWBmNSefJUcV8T4AEVr5fQt0nkJik4B2+3QrvY3InRpHKgT0mEh7OTYnArpyBbe3aUtU/FzK/d5dhzfU+ag+sdpWnS72q/creLNwNMefvpSuurLo1kozGdGGld3hBVKJC1H3RpboUstwH7v52V1b/5/pS5dGVmYmDSa6LTd1XL+/UlBLk0utDPsx7whHmOqmxZDqmPNR+21w85axlVvxOhavw/MCv/OX1neKasZUjbzLlXzhzIUz6ZbF3gToyPHygM4Zm0BhclDHOzUzfCEuWcvVQaCkTLKNFSyZLcYbPaIXxWNB7zpyMqonP3ebPF0eGH5eIiAgRYXz37t1Qo0YN1ccfPnwAjJKOgsTAwAD++OMPOH78uEiQjAma582bB5aWliLZ89SpU+HUqVOAiZXnzp0LZ8+eFRafIkWKiCTSaAWrVq0aTJw4Ec6fPy+sZMOHD4cvv/wS7ty5A99++y2cOXNG9K38HvtGK1pkZKToA5M+Y7tLliyB2NhYKF26NCxbtkwIVLS+YX8oyFq1agVTpkzRyiQ9gYUM1q5dC0lJSYBHutgXrgnOAV9YiImJAV9fX5HAGpNaY0Lr9+/fi2TNPj4+glPXrl3h66+/FmPBBNYVKlSA0NBQkUibChEggVWI9kBikpyvOnwLtpy4k7PCgnFp36zuGiOwK/HX2bpBMqlchq2+1huehF1SG9/85g8lm0grFjxpNfDE9HPbWc4aoHhdo7zMff99VRuhPWpwvzm3uc/qlPhM7pPdpdpzajMjG6OcZZHP95sUL1eEHPCWPfzmmIqb8/A6ksPM2qxaaEPVtSuup7nBwzuS346ZKkFVe/h6nmReiff74lqBsGRVqWzHf/25EX/s907jHsLkzlWr2MD9+/dBaclSLn9oaKgUHBzMHB0dxUNdF6Gli/VK9cdKnTpQf+PGNLsNhQ2KpcuXL2vdiUePHoVVq1bB3r17hVj65ptvhMCZPn061KlTB0aOHAnjxo0TR4wLFiwQx3ouLi4waNAgGDBgAHTp0kWIpD59+sD3338PYWFhQgidPn0aQkJCNAosHBcKOhQ9aMFCEdqxY0fYs2cPVK5cGdavXw+enp4iTQ5a2f766y8xvjJlyqT7E6VNYIWHh0OTJk2E0HNychLiCAXiTz/9JATW0qVL4cCBA0Iw4bxRTP36668we/ZswKNM/D8KMDzORJFZvHhx6NWrl7iOoosKEUACJLAK1z7goREx0HH61hwTFek5sCN6l6FDpbLfjGFnni7nhx98r/bgrVu6nzSi5hb2futZ/n7vpQwfylarvpGuGBizHqf9VPN737c29xx/hT/b+SzN/RW+qCA1WdGEGVmTyNLlxyDpXRwP2ugpPfn1YhorVMk+bpLzouaswqvaKvZbSq3l1aPt+IM/+qrYl202XHJp8zXrO+QSDw9PyHBNdRlXbtWZ8W1tXq2andbcg8rgopIk8Xv37qURWTju1EILrSj4BqK2+Zxt0gTkMTE6TRd9sFpfuZKm7r59+2DHjh1CPGgraBFCC9SIESNElStXrsCiRYsAhRcKLLy/UqVKIg/fjz/+KKxZWGbNmiUECVqrUGBhvapVRU5Q6NevHwwdOlRYgTRZsD4WWCje0IKFwgoLWtjwONPb21sIO/xsy5YtGbJIz4KFAsnCwkK0ceTIEUA2KN5QYF26dEn8i8XDw0MIyRMnTghRtmbNGnBzcxOf/f777xAfHy/EZI8ePcT40juyzHDAVKFAESCBVaCWM+PJRMcm8HF/HIXsjomVkQM7MzKSGh3dC++MwtnfV7vD+7ggtQfL9KaXJWdWlYVMWQvysPcZCkLb3bP58oD3fJ7XS9VDO6ZXLX6u12kIuag9Wn2Jz0rw9kfaA4ms9PdO0vt4/vSnc/zl5ttaRZFdMxdebUM3KBvqrlqv6Y6TpaHG3ZnnvJaqa5YlKvJGEw/Dhs1P+JZtj/KtyDp8oJPkHxDNExMVGufQoH4xTDYt5o0i6+7duygSNO7l169fC4tWsWLFhEVLk9DKlMCysIDWHh5pFvXcuXPCqnTt2jWtCz5s2DDo1q0b9OzZU9R58OABjBkzRggqFFiHDx8WVp/r168Liw1+jwWtO2hRGjVqlBBYaNlC0YgFrV6tW7cW1ihdBBaKmD///BNsbW1V44yOjhZiDoUOHi3iMWZGRZvAQkd/tIKhUENNGxUVJbjj0SQKKzzyQ/GknP/o0aMFsyqsOtodAAAgAElEQVRVqoh7sC4WFIBYd+zYsYDc0uOa0Vjp84JHgARWwVvTjGbED172gQVbLmQoWjJqSNPnwoG9ay0WfU+zAzveU3Xxzwqndp1km258ATdebFcbRxmbutK3Dc+xD/94QcSGEzqN0e7oAj7B4znf8SzFiTphYF1+tNkRCL8bnmEbNlVseKcTnbiFswXWzbB+Vrjk43vk8rBY8Bq82yDS61WGbKxqlOB1Dg0Cp6Aqqrp9rHtKPxWdza7PrKt2f4MJe3jIh1J89ASPfCeyunV14f36VOBBrzSnxtGUe5BzzlEYaBNZuEfSE1r6OCJEH6KGDRsKPyb0xVKWhIQEYaX57rvvYM6cOcLyhJYoLGjBQod4tPJkRmChWEH/LSy9e/cWFjEUXJMnTxYiBQtaiubPny+O6lIfEaJPFn6PQuvjglamTxVYKNJWrFghjiTRtwwtegcPHlQJLLRaKa1neJyKQhJfDGjWrJk4pkQLHxYUYcgO54c+ZihCqRABJQESWIVvL0jhkbHQdupmvT7UMnJgR8wOjRtLlX9fwB6+PQUbbg5K87AeX2+/orpjR1nwtPWQ+DTjh7mJu4tUYskYVm3PXfB9n+K4Lx/WgO+ptBuin0dnKAiUy2/saCz1uNaDmZcyBwMTivouuMQrFFJojEx6/p6Hh0bxe+MP6+SgbuZixxtfGQ0l/Cuo+FcxrsTPuf4Lt/7sx98FpljBKrSfLBWrN4h1+PyczmuVF35k/znSlb94Gc2jopKzCXxcKleyBXv7tC9RoMhCB+o6deqkO18UWq9evWJ4pPaftYTpw8kdx4nO3OjbhKKpUaNGwt8JxZWJiQmsXLlSCBulj5OxsbFw4sajPxRGmRFY6JOEDuH4lmKnTp3g4sWLwn+pbdu2cOPGDTA3NxeC7ubNm0JgoY8Wihc8qkMfKbwHBRD6d6EfG4ogPJLUh8DCNlDcoYhC69VXX30ljiH3798vLFhoHUNBhZa6GTNmCDbou4b+VugUj29dolhFHywUX+h8/7HAwnn7+/sL/zMsKAqx1KpVS/yL1kRXV1coW7ZsXtjSNIZsIEACKxug5vUmE5LkfOvJe3zVoRufLrLQgX1mdyjtYMEezpsHoWc1R4uvt3OrJHNxYGuu9QH/8GtqDxdjA0tpUctnTO71EsIW7dHpQWszsJWk6N+SlfifJyh4stWphKmRFDykDttitwUS3iXo1M7Ha1X3x7qS+zR3VsRchMrKUht5ff11GB+XJ8TzxMAwiQ8+I0CYHe7D7806zt8ce6zTnjGyN+MtHk2Bin41eKQUJe4xZIb8foUb8PbEOu5/fo2qHbtyDXj9sf+Dn3+9zY/ng+jvbdqU5mNHVePP/KM07g9jYwOoW6eo1r2jq8hCZiEhIeLo8D+hxXSxYtlqcXBXrjsej6GIwDfdlG80olBAZ3YjIyPAz9G6c+zYMfH/unXrCqsWOrxnRmChIzxavV6/fi3eKPziiy/EENCRHK1C6BjeokUL+N///icsWugMj8eSiYmJQoDhG4gYtgF9nNDKhGINx5JZgYWCNrVrGx53ohjCo0x8axEFLIpHPOZDXypra2vw8/MTnz158kT4jSEPfCEBxRiyUPpa4REkHn/iW40fCywUiihk8QsLilgsOA8seC9+ofWLSsEkQAKrYK5rRrPiEv7iHLXqkwSEyoF9xQruv2GDxgdvudGjJecxX7KTfr/xYw/npanTt+YfUutyE1nIdxshzuuJTuOxmT9M8cilpKzJkYeq+nUdLKTrnaqyjeYbgUtcp3a0QbIqb8U7n+4MZsXN8pw1KzIyUhEaGsqio6N1EjoZbQT6PNcIcBQNFStW1GmvotCKjo7Gddepfq7NqpB1bGFhIRUvXpxbW1vrZN0tZHgK/XRJYBXSLYAhGzb+e4evPXor0w/qqf2aSv2bVWTaIrALa4WNjVRn3xYWIXsN6MQeHR+a5sGwuNULqUhAHHs9a6PODw2zjdOk8/GcDTyfEsH9c2dbaXcDF7Rc6dyOLste76d6kttEN2ZoYcgBINOcdOkjs3UeP34sVapUKU+MJbNjp/pEoCASePTokYRCWZdQGwVx/jQn7QRIYBXe3ZHp/IRKB/aY+3fh4fz5kBAWplHQVP9jkaJYy9ayDdcHgVdQ2jyBbStNlXpXXcTeLN4LMefu6iyK7PfP4Qt8wviS+8EqgTGmSjHpd9dibIeTurO8vpbVprINb3SlsWRjaQOWRhbYr87j1dcYUrfj5eUljmmoEAEikDcI4M+ks7Oz5ODggC9/5urvh7xBhEahJEACqxDvBbRirTt2m2/4xzNdi4ibS1HpjxHNmZGWCOxKhEVbt5Yq//wDuxd6FDbdGqrxF828zx5IttG2LHjyauDxSTr9MpKZGkuO++ew7qcew5lXkap7FrmVUox2tJbtqbhbp3aystQtNrWQfJo+YjdCb0kLGs1lEpe4kYFRrgktElhZWUW6hwhkHwH8mXR0dJQcHR2Zqalptv0uyr4ZUMvZRYAEVnaRzR/t8kS5AhqOXaP5l4KODuw41br7tkmspDVbfa0XBL7zStNerVI9pdG1trP328/z97sv6nzEZd60muQwZwhz3XkbXsSkOK6vr1dW0ekDkx1tdiRbf6ENVwznzhsqQJw8nj0dfJPbvzjMoVx/BkXMAYqY4tSztf+PtxEJrPzxg0WjLDwElAILLVhmZmY5+vug8FDOnzMlgZU/101vo46JS+SrD9+EHWdSUstg47o4sGO9Cl9/LTkPH8KO+Szgx31/1iicpje9oHCWuctCpq4FeajmtCKaJmT2VTeFvHUdWekdt9V+ae1vVl5R93m87ESn49n6y6zzzc7S/9guWHjrd1l3167SxiazGdtRJrlP504cas8BsK8BYGCisgjrbWG0NEQCK7sJU/tEIHMESGBljldhqk0CqzCttua58rgEOTSZsFYIB+HA3rQie3X4EPj++qtWAWNSooRUa/s6FqJ4Cquu9YK4xLTCycm6hjSz4SX24cRdiFj3b6bEkN1voxVejnayNv/6qt13tm0VhbNnhOxc3zOZai+zy2xXy05qe60tK7uxsujHf4gnt/UYDxD4kcXM0oVDzVkAlYcDyGMBDC3RIR7vyZbxkcDK7EpSfSKQvQRIYGUv3/zcOgms/Lx6ehr7h7hEfuzaY6lb7TKyjBzYsUv3v/9QODRqJFt/fQDcDT6sUUiMrbtXUaNYF1nItPWQ8EQ9DU5Gw7bcNlPaGx7HJng8V2v7dtsqksn51+zyCPVk0Bm1l5XPuwd359PufwdH/f9hfSv0ktbU+5rB7mSxpbVUHsGhwlCAEs0AkmIADJPznOlTbJHAyspq0j1EIPsIkMDKPrb5vWUSWPl9BfUzfikhPBzufPMNi/T21ioiSnTsKFX4cQbzCj4A27xGaqxnKDOVfmsTyBSeQRC2MPPO59YH5/Ef7gbzVT6v1Y4bn7SvJkVtfgqeP6bvkK8PHG5fu0lWc2xYs72txRxfDr7BLTzGAbzIhBWuZAsOZXoAlO0KYFkWIOEdgLEdNqe0cGVJeKUnsDBKtqGhoQjo+HEiXAyiiGlHMAp1kSIidmiuFQcHB5HHrnz58tk6BowOjrnvMBo3ssGo4IMGDcpSn0+fPhVtYTtZKan79/T0hP79+wO2qeu4dK2XlbHhPRgUFANibtiwAbp3766xmfbt26tS3CgUCjAwSAn9hJHXMUBnZgoG6+zQoQMEBQVl5jatdTHXIwbtRK45WUhg5STt/NUXCaz8tV7ZNlopIYEHbtvG/f78U6MfVb1DOyWFozFbdbUHvIp8oFFc9aqxWGpXYSp7/f1miL31OFNHZEVK2EnFNk1jLY/5wLVQ9WCKQV3dpZe/P4D7S+/r7Bz/KaD6fejHOx/rAQ/DfdngygOkFTVHMra3Wqbmo9a/zJBDyVYApVoBONTiYF2ZgalDsu+WIhFASgRQJODxIoCBcWoBltwMlwPI4wFkBuB114dpC9OAD2F8yBUtWhR8fX1Feg8sKKowt9zLly8hJiam0AgsjE4+a9YskXsP05Tg/zEHXVbKpwqs1P3jOmGaFRSauo5L13pZmRveg6leMFFxxYoVdWoCRStGKke2WS0ksLJKju7LLwRIYOWXlcqBcSaEh/NLXbpwKTZWJWQqT5smOQ0ewA49+I6ferxYq8D5rWWAZPgiib2esSHTQsSyQz3JbGIPVmaHF4THy9XuD+tZk/vMvMUfbXiUI+Kq5eaWkncTXzb89BgxjldDbnGzSyMAgk5lel4ZLhkKL7MSAGbFQfxrWhzfTOTAZAySogESo0H8q/xKjAavCrvSFVgWFhbCAoF54DDtBxbMqYbpRdCCo7RgYQ42zEWH35cpU0YkuS1durTIG4cWMExvglYKTAOCwkTbdUmSRK46FC4oHFq2bCnyu6GVDEUBJvjFttBatXnzZpF7DYUF5pRbtWoVvH37VqRRwZx0aNUaNmwYPHr0SIw79ffa+sd6P//8s5gfJt3t0qULLF++XCThxbxxmFwYkwnjHN69ewcNGjQQPHBcmJgXx49Jj3HMpqbirVCNJbXAQksJjhMFHKZ8wXQzmEoFLTyaxol57jA5sLL/hQsXCgsW8kl9HVPIaJr/rVu31OqhZe7o0aPw/fffi/x5mKtv586dIpVLeiUiIkJYqrBfXJ8BAwaItC09evQQKW0w3yByQ4YZFU0CC1PL4F7AdcO9gGuK+wcLpsPBvnC/YXoctJRhGhrsC5NKY+oeTMuDVlbkuGzZMpGOBtc0ICAA4uLiRC5C3Kva5pHagoXriiltUARiaCpMsfP333+DlZUVBAYGipQ82E7z5s0hNjZW5D18+PCh6A/XEgvOB9PkvHjxQuxZbYUsWBntlsL7OQmswrv2mmbO3169Cl7jxjELV1fJbdMK9ireF1Zd6wmJ8g8aBUarChOlvtWWsPC/D/Oof25mWgRZfNtX8b5+FVmlPWkDjkb2rsVvjL3MA/YGZLrdrC4rhmUovaECxMvj2fBqX0i/uw1mbJ+7/sVVFgfoVcdTa6BRfKih1UqZsPbQoUOiFxRMStGFDzh80OMDEvOnYaJZfAjiAxcT7aIYwBxxs2fPFnnX8OGHD0N8mGu6jjnk8EGGDxll3jq8FwUEWkMwES6KPRRzmNwWhRg+rPBzfJDhQ9bd3V2MCRP6ahNY2sb1zz//iNx2KJYw9QweEeEDHEWbm5ubEE5oZcEHLf4f+8f/r127Fk6ePCnG/O2334oHbpMmTXQSWPjgx4S9KHLwiAuP1lAQ4Bi0jRMTGSv7T31EmHpc6QnM1PVQIFSvXh2uXLki2GHyZvz/wYMH091VeEyMe2TdunVibVF0/Pnnn2IONjY2QtDgvtClaBJYo0ePFmIFj6fRQoft45iwbfw/CkVnZ2exF5E1iiu8jmuBexTHhSLcw8NDJJvG5MoPHjwQ+278+PGiHUyyrG0eWE95RLhr1y6x97AtTCqN+w2FKIpbrFOuXDlYtGgRnDp1Crp16ybWBq28Xbt2FUeWKMrwDxIUfhlZPUlg6bJjCmcdEliFc921zjopKoqHP7ytsK1X12Dd9X7wIES739GPn92X7GLsWciUNSDFZj65sv2fE6TLpuas68m0x4mxfWrz011PwmuP1zkmbrrc6iJthR2w6NYSIeiCv/DipueHAASfy7ExZLQddRFY+JDDv/R9fHyEVQYtEyhk8P9KC1Z0dLQQJFh27NghHmz4sEGxgmIALRl4FCmTJWtbbddRoKAVBS1nWPAhi6INrWf16tUTwgkfVtgvWgewHgosFDfKo05bW1vAhLyhoaFaBZa2/gcOHCjamTp1qugfBReKuYsXL2oVWChG8IGL1pLWrVurjlLTY5/agoVCBAUCWjiw3Lt3TzykUfhoG2dqgfSpAgsFCYo7/MKCx752dnZiHdAHT1tBC+Xhw4ehdu3aogqKZrQMLV26VC8CC5Mm45iU64rCFRNEoxjDxNHYNxbcn+i/hXsSrYco9j7miAIL96HyHrRK4h5B8aZtHijSlAJryJAhQgRPmTJFtI1tzZgxQ7RRrFgxsddr1KghPkOROn36dBg8eLDws0Ox1bRpU/E97g9M4pxeQYFlY2Mj7dq1i507d46hiEVB2K9fv4x+nOnzAk6ABFYBX+AsTE96FxcEM4+V1mo1qlHyc2lM7V0saucF/m7nhSxZl6x3fsdXvozic269THN/4qB6/FCDg/Duoe4xs7IwT7VbPg7LMNZ9pPRL5V6M7a+VZ8QVDlgXgYW/4EeNGgX169cXD158uOFf4kqhgw83FAL4MMRrKILQsoBHTyiE0CKyfft2CAsLEw8ofPhou473orhBkYEPdzwiQytOu3btoG/fvuL7j8vHTu7K7/G4UJsFS1v/eLRz8+ZNlcDDY030QcOHnjYLFo5n79694sgIj8vQwoaWHLR0aCsfC6zUztmpfYm0jVOfAgsFJB57pj62QrGHghqPtLQVtG76+fmJtcaC64zzx+M7fViwUEzhflO+RIGCGvcAHtPi2FDEpy4f+2Cl/h4FFlr0cGxYUn+vbR7Tpk1TCSxcHxTfKLrEz42XlzjSReuUkZGROHbEcWHp2LGjePkBBRVazfDoEEUnCjkcN7LJSGA9e/ZM8vPzY7NmzWIoGLEvtAijVYxK4SVAAqvwrr3WmSdJ8dKFJ3+zffenpREX05qcUbgUqSMLnrYO5CHhWRIfNkcW8CnXA/mWJ2Fq4sqAgZQ4rAHb6boTPgRpPpLMruXqHtKdT703C475J1vsQobe5SZn+wKEZH9IiMzMSVeBdf78eXEcgg8H9DdCwaMUWGgVwAcJ+g+hUzz6L+EXCqzUBX2hWrVqJawIaI1SltTX8T60SKA1CB+seKSIflb4cEMLAT740QqWmJgI/v7+wkKgTWChWMMHHYoALGjlwqM+pU+Wpv6xXxRSkyZNSoMxPYGlrIxvv6GlAf1+0OKiregqsLTxQ+tWRkeEeISmbf6pBRqKDvxeeQSs6/5Bqybeg5YdLGjBio+PF0JLHwIL/exwXDVr1lQb0saNG8V19H3DguuMVjfcG9qEanoCS9s80GqltGANHTpUjGPy5MmiT7RgzZw5Uwgt3H9oxcS9iAXroThDgYVWNdzzyBituEorYUYC6+LFi1KbNm2Yu3uyOwEeT6LFWNO+1HW9qF7+J0ACK/+vYbbMIDIuWFp8vjmEfXgqRFBxy6rS902vsdgT9yBizT9ZElbGlUtLxVdMYLX334P7EbFqbZS1MJYCBtRmm6w3QVK0bjkK9TVxt4luktVsa9Zsbxsxpq9rjpPmle/M2MG6WZqnvsalqR1dBRY6+eIDBMUNOu+i1UopsPAvaxQv+PBAXxm0MuCRIb5Fho7P+HDCBx8eH+FxEjrA40NY03UUcY0aNRJWLPxrHy1K2AY+YNCBG0ULWqXwiOf48eMqH6zUYRqUggstEzhmdIrHo0Q8brx06ZIQWNrGFRISIsTihQsXxD14fKYUeigkcBw4FxSJOFb0k0JrFVop0AkeC4pCHCs+ZD9VYGkbJzrCK/vHh7wyTEPqcWEdbfNPXe/NmzfiWAsFMvq5oTBDoYvzSq+gZRGFLjLC+aP/E4o+FBT6EFi4B/CYEl9eQCsqHskhDzw6RL7oD4Xzw7njMSL6YGVFYGmbB1rPlAILBR1aabFP3Fe4x3EMaPnDPYrWXXzRAv+oQL8r9P9CgYUF/5jA43PcF8prGQms06dPS3fu3GHBwcHidwZy7ty5s/BPpFJ4CZDAKrxrn9HMeUTsS5j1jzMbXWenolaJHrKQ6esh4dHLLIkOmz6fSTC0HSv1Py+IV0hqbTQpZildaFuZbTTfmKW2M5pIRp+nDsuAdV8Pu8+NT3UHCL2aK+NJ95e5Dk7uylhN+MDDYxq0BmBRCiy0IOBDRXk0iA8dfJMMHyjogIxvmuEDGMUZWgXwQYTHcJquo2DBYxj8ax0f2NguOs3jkSRaNNAig8KhSpUqwn8GHY21WbCwPloc0D8G/Xbw6AYf1uiMr61/nBc6PuPxE4pK9DdDp3x8qOO88DgNP8cHLL5BiA8+dKZHvxr8F4UnPlDRyqL0I9PEX1cLlrZxomhU9o/CVimwUl9Hsaht/h/XQ18zfDMSRTBaIdHJHv2G0isoptE3CP2QcG3Rsqn0XdOHwEKRjuIHrUNYUDyhsEbBi0eyeNyMe7NNmzZC0CDTrAgsbfNI/RYh+gai8EZHdfw/ssGx4DEwHkXivkTrHYpLPDbE9VDGSMPjQbTuoZBV+ilmJLA8PDykVq1aMTc3tzz3OyOj33/0efYRIIGVfWzzfctJUrwi9k0IyHwjZWG/7MryLw7rOYMVgZXLyuocTBs/q5+rg7S5Zmm21XFrltv/FNAtt7SUHjTyYSPOjBX9T6k9UZrt0oqxQw1yZTwZzSU9C1ZG99LnRIAIJBNAMa58gQPfOkVLK1qcsOAbiBgSAt8O1aWgRTIwMFAKCAhgU6ZMEU7u+IYivviAlkb0Q8TjcRRzWNDvDYvyqBbfrsVjdXw5hErBIkACq2Ctp95nI8UmSCHfrmMJfplLd6MciOn6qdKVJMb6nnuSRrBMdCshLXByYDvL7Mg1MZM6LAOOOfRLb250vCNA2K1cG1O6fy2nY8HS++JTg0SgABJAqx1a29B/D61oaMHEwLz4ggBeR2sXvgCRkUVQiUb5FuHu3bvZqVPJ8fI+++wzcTyI1js8rkSxphRsCxYsELdiXDAsaF3FLzzepFKwCJDAKljrmR2z4TxRDgGdZ2dacNjt/YEv8gvnC+++SvOm4NIaToovzC1k+6vvzXS7+ppkF88u0lZpOyzyXCrGN6vet9K3Tg0ZO9Ik18aU0dzIgpURoax9jseQ+DahpoJ+Q/iQzOsFHbPRz0tTwaNe9InKqOBLBRjbTFPBuGVKK0xG7eTlz/G4FX2rcM3RPwuFEPLBYKtjxowRX3gsrmuhOFi6kip89UhgFb41z8qMedztpxAyY71uwsOASY6HF7B+557A8Zfv09yzuYGLou17LjvW4qhu7WVlxBnc41DHQWrl0Zq5bExJ4PxmuA83/Kc1wNs7uTaujKZKAisjQvQ5EchZAiSwcpZ3fuqNBFZ+Wq1cHKsUm8DfbTnFIw94pBv3yrR+Ran4z8NZhV134FlUfBqhcqRFRYW7X4zsZNeTuSpiPg7LMKfhd9LE4jWZ7OhnuTqujJaYBFZGhOhzIpCzBEhg5Szv/NQbCaz8tFq5PFZ56HspeOoakIe+0yiyTMZ0UbBO9WUl/+elUaRcbF9FUeLqW9n5gbkbGb36pOqSxXdW7LN9yWEZsISNeMSLHGkGEKE5kXUuo1d1TwIrr6wEjYMIJBMggUU7QRsBEli0NzJDgPMkOQR0SuuPZbtwpOJ+CUdZi2M+GsWVd6vKEpx7zTzGXM51C1G/D/15p2Ofg0/4IzGWBY3nSl/ZOjM40TnXx5bRYpDAyogQfU4EcpYACayc5Z2feiOBlZ9WK2+MlSc+C4GgsctVYsRiy3Tp0PtENuZKgEaB4t/RTQpf9xhuL7idpbQ6+px2q62tpHsNvdnIM+NUYw0f/pDLDjcFeO9b4AQWxrW6ceOGiFCNKUIwvg8GDcVgi5hCR1mUeQAxse3ixYtVQTcxrhM6AGO0dowrhLGysKCDMMZ1wthD+Ho53oORsjHPnvL1c32uG7VFBPIqARJYeXVlcn9cJLByfw3y3Qh4YpIUc/YuC1u6n9ke+JH/+OA1X+EdolE8BXdz5wG/3uPeK7xzXVwh6BHSCF5qXTlIUCQnp17Y9CdptFVRxk52y/PiShxHZCJMAwqrhg0bilfOMcAkvnaOb1C9ePFCpM5RCqzg4GCRLgQDgOLr6qkFFr41hl94L+ZXw/sxqCje36xZM9Wr5hjQFIOIYjvKlCj5bmPTgIlAFgiQwMoCtEJyCwmsQrLQ+p6mIvKDFGZQhPU7/xQuhURpFCfhvWrye1Ov8ydbnuQJcfVxWAZkEj7Cl8sO1geITBunS9/M9NFeZgQWRlTfsWOHEEVWVlYwe/ZsEf27WLFigMEVlQLr2bNnIoUNpg/Br9QCy97eXiStxbg+xYsXhwcPHgjrFVqrMCK2sbGxaloYBgDzt6GAUybS1cecqQ0ikJcJkMDKy6uTu2MjgZW7/PN17+8T5FL7477s5puYNAIrqntNfm3sZf784PM8Ia4c6jpIra60Yi4bq6jGuqT5IulLU0vGTvfMF9arzFqwSpcuLVLHYKTo1AVTwqQWWMrP8HgPgy6mFlho1Vq5cqUQWBiI8d69eyIdDgZF3L9/PyQlJYnP0bqF8ZMwDQqmxMH0OVSIQGEgQAKrMKxy1uZIAitr3Oiu/wgkKDh32u4Jb+PlKpES168OP9HuX3hz802eES7dQ3rwKXdnwD8Bx1VjihjxmLP9NQGiNfuO5cVF1tWChfnabG1tAQNHYp66rAqstm3bCp+qxo0bC/8r/EKLF1qxMJ8fJthF3yy0Wr19+xYcHR1Ffju0ZFEhAoWBAAmswrDKWZsjCayscaO7UghwBQcosu6aEC5JQ+rxA7UOwPvHaQOM5hY09ynuktlMC9Z8X1uVuFrRcok02MiIsTN98owI1IWPrgIrICBA5DfDiNRz587NssDCh8eAAQMgMjJSJHvGhLno+F6xYkVhsUILV40aNSAsLEyILUwNMnToUJHsmQoRKAwE8GfkzJkz0oABA5izszPDqP+U9qYwrHzGcySBlTEjqpExAf4+Uc6tDQzYdqftEPs6Nk+Jlv6x/XmHI93AN+JxivVq5BPO9lYFiHmZp8aaEeqcFlipx9OhQwfh7I6+VvhGIgosFFVo4ULrFVrMSGBltIL0eUEjgALLyspKKlGiBDM3N2f4di3+EUKFCJDAoj2gLwLyhHcJsi12W/KEz5VyUprCMqxsvULqb6Bg7OyAfCWucE66CixNR4TKfHoYdqFatWrCUb1cuYEBBf4AABvPSURBVHLg5OQEFy9eBH9/fyGe0H8K/azwDUT8DMuBAwfEZ0rH9urVq4vwDZaWlsJnC++lI0J9/ShRO/mJQGoL1i+//MJOnz4tfB/r1Kkj/hBRWpAxkTSKrytXrog/TKgUfAIksAr+GufkDDmeF26x3wKJkYl5QrxgWIaS61whUZEynohRzzjbVR4gNiRPjDEzC6SrwMI2P3ZyZyztdDGxbf/+/aFly5ZphrFz507xGcbAqlKlCqxZswbQioUFrVfDhg0DhUIB69atg86dO5OTe2YWkuoWGAKpBZaFhQVr0qSJCHeCf4yMGjUKrl69Cvizh2/t4h85mzdvLjBzp4mkT4AEFu0QfRPgijgF7HXfC1FPNYdv0HeH2trr6tVV2qzYBr95/qGyqq1ru1LqxWMZOz8434mrzFiwsC46pO/atUsVpiG7uSvDNAQGBqosX9ndJ7VPBHKbgDaBhePCt2oXLlwIdevWhcmTJwOKL/LPyu0Vy7n+SWDlHOtC1VPcmzh+qscpCL0amitCRlNYBlyAd6MDOGx3BogPy5VxfeomyIwF69q1a+LtP2Wg0U/tO737lYFG3d3dRUwtKkSgsBBIT2CtWLEC8LgeMyiguDp16pSISUelcBAggVU41jlXZhn1NIpfn3Ydnh9+nuNipsfrHnzSnenwb8AJVd+b26+VuskjGLswLMfHo68FyIzAwj4xPtWtW7fAz88PMP5VdhX0w0ILFsbSql27dnZ1Q+0SgTxHID2B9fz5cxg0aBAsXbpUHKWvX78+z42fBpR9BEhgZR9bahkA3t55y31X+YLvupzL86cpLIOwXo0J5LCtOEDCu0IjsGgTEgEikL0EUgssW1tbhn9gYEBeMzMz0THmAzU3N4c+ffqI/1MpPARIYBWetc61mb71essDDgTAnV/u5IiwwbAM7Y90hUcRfqr+/tdho9QpIYSxSyNyZAzZBTuzFqzsGge1SwSIQDKBj+Ng4Vu49+/fFxkNMEbchg0bROBdtCRnpxWZ1iPvESCBlffWpECOKPx+OA+5EAJXJ17NVoHTalsr6W6DB2zUmfFq/bwb8wJfbwRIis7W/rN78UhgZTdhap8IZI5ARpHcjx49CidPnoS//vorcw1T7XxPgARWvl/C/DOB2Fex0uurr9mZvmeyTeRoCstwqPNWRfOoRzK4+k229ZtTq0ACK6dIUz9EQDcC6QmsmJgY6Nu3L8yfP1+8SUilcBEggVW41jvXZxv/Nl6KehbFDjU8pHex0/V2V2mTfBssThWWASf8buxLDpusAORxWevTyBag6d8ATm2xDYDr0wD896TPsst5gLhQgLP9k+s1+gPA2A6AywEujki5t+YsACkR4P4SndaGBJZOmKgSEcgxAtoE1pkzZ8TbgwMHDhQ5QakUPgIksArfmuf6jOUxcik2NJbtKr8ra4JHwwwc6ztKLS62ZK6bqqi1eazLdkWT9/dlcG1y1vtqthqgiBnAxZEA9jUBOp8C2Fsd4MNLzSwrDgOoMxfgzY1kgWVdCaDhbwAnPwdovgHAd23yZ+alAdruAzjcJFl46VBIYOkAiaoQgRwkkNERYQ4OhbrKYwRIYOWxBSlEw+HvfN9xj/EeLPhCcNbFz3/AerzuySfdmQb/BpxU970a94rDemMASZ71Pvo/AzjTD+CtZ3JvrXcDvL4M8FCDT4WJA0C3KwAPlgGUbJEssMp0AyjVGuDqRIDaPwBEBwI82QrQZg/Aw78BQi7qvOx+dU9LFWu3yVPpiHQePFUkAgWQgLe3t8LKyoo5ODgwMzOzrP+eKYBsCvuUSGAV9h2Qu/OX4t/GM3zD8PKYy1n+xeQ+1V0ynWHOWuxrp9bGic/3KBqEXZfBjelZblvg+VhgNVsLII8FuDYpLb3mmwBeXwJIjAZw7Z0ssJw7A5TuBOAxAaDuAoD3vgBxYQAVhwIEnQQo1z/5Gh49ZlAiHTopgstOZ7HcgkRWRrDocyKQzQRMTEwkmUwmWVhYyOzt7UlgZTPv/NY8Caz8tmIFcLwJEQkSALATXU9kKfK7prAMiOnduBCAtZ+mrQTuZmsAipgDXBwOYFU++YjwxTGAy2PVV6NE8+SjwWOtAFx6pwgss5IAbfcCHG4K0OkEwNXJAC03A5wbnGzFOlAXoNkqgKc7AYLPZbjCksyEh9t24XFm5YAZWXEUaJEHN6smalKlDo8sWR3O30mJ99WhoT0PfXAZ3gQ+UdVr2mckX392FygUSeIaYzI+ut1gCHq8Xg1ayfJfcM8nxyEmLqW9qs5NebxkB1efBKrqyhjj/RvVhCch6vcbFbHnTnad4NGZwzovhn2ZCtyxRCVQnHzODD9z5mBtDGBSROf7M4RIFYiAHgmYmJhg4nNmampKe1SPXPN7UySw8vsKFpzx85iXMRCwLwCuTbmm8y+pVttbSXfr3mejzk5Qu+fM5/ulOm8uAtz87tMtPUon9+KNASIeAMS9Sf66OSuFvswIoMfN5KPEyMfqAgtrVZsAULY7QNCZ5HukJICQSwC1ZgKc7g1QdRyAoSXAvd90WlHODHm8SRngzBCgpycEfdlKdZ9pnWbcaOBUGLfqlYrJl52K8zJyb/7PijkqHr2/X85Ph/jyDad3qK4tGzGfl2KPeNDjtaprTpXG8ESzitK2s98ZKDuxtSjJR3RcAYPXHFTjPrhJLd6kgly64z9ZVRfvqe78PU96bshv712n83o0HTlLsi9VgYNxEbxH5z2hE0CqRAT0TMDU1BSTOtM+1TPX/NwcCaz8vHoFcOxRz6IkA2MDhnkMwzwzzhc4UhrJi68tC0lSshUGSxFWRAob+4LBWln2/LLr+C+A3xaAZ7tTVsCxHkDnMwCKuORrBsYABibJzuxHW6TUM3f6z5rVDMChNkCNaQBn+gJUGQtgbAtw99fMr+oXYfzVxCE8/sFNIV4MnVx5qc0XoMHkR6r5t61ryyd2seRLulVSCZwmfUfwSn1H8PqzOquu9WrchS8bMoXfOVZHdc3EvDSv1/kyTPhT/QWCKX328AtPZHzFKQ810fRw4RTY4+GgNg8DmSnv2TAANg5qrNOaNBs9m5et3wqMzCw4AOgsyjIPj+4gAkSACGQPARJY2cOVWv0EAolRiVL8m3jmv98fbs68qfWBrC0sw/keh6WaIScBbqVYaz5hOAANfksOpXBrNoBTB4AWmwB2VQCQxwCUbImpdwDC76p3kfqIMPUn6CCPju3op4VhGzqdBDhYD6DxcoCg08lHj5ktPe/w8G17+Pu9a1RCpNyVMN500j0enyCJa8Vsjfi+eVVhTlNHFU8HJxf+zbbzUHR0rVTHfDL+frsfXNieUg/vr9vxLD/pfZSf9kzpo4JTAz6i01po+lPK8STWndS+mdS5Bocrvp3UhJGb83TJMakDHJk9UqtgcnStwtvNWA6GxqbcwMiYhFVm9wLVJwJEIM8QIIGVZ5aCBvIxgeiAaInJGDvR7QRE3I9QE1pFGxaVPjvfnJXbVFXtuoWhhfRyxCMG6/Tor4NWpza7AWyrAsSGAFwcBRDqkTzc1rsAIrwB7vyUscAq2Rqg0pcA5wen1K31PUCpVgDxEckO8VyR+Y3QZh+P9o7lbxZOUgkS5103+fwzMn78RoTq2rmlNfim8R14kM9t1bW5Jx/zfisn80s+11TXjn2/jbswT/7sznzVtWJle/IS1b/nM9c3VRM9C4Zf5ktO+PBjdx+pXfdeOIXv8yjOOcjVrvdo8Iwf++ErHhH4OI14qtljhKJ6p4EyI3NLZKCTpSvzsOgOIkAEiEDOECCBlTOcqZcsEkiKSpLkcXLmu9YXPOd4qh662sIyXOp1TKr+8iiA17zCY/2oO5/HQSse/FW3FEH08yZ+xqgx/2lroOran9+U5/L7O/mRpSl+ab1nL+dBxWz5sBUT0z0mxOVrMSCET11Vm39IeK+qW7/y57xjox9520Vb1Xh/3baJ1L12Ebjk017tupN9F+5efAHsHt9LtZZl6jbnLSbMRyd7ycDIWM13K4vbhm4jAkSACOQ6ARJYub4ENABdCMS9jpMUCQr2b4d/oUzXMtxkmilrsb+9mpXD3thOejrsLoP1RoXL+lHpSy4vP5MH9mmkEjM2A8bxN91mwZBfUvywBrQuyvvWjOErBjVR1avWrAPvOOM3KD+5eYbHhJXqL+Gh4MBXHx2rJpqWTfDhA1cd5H6v36pdv//rZH7ougtXSDFq19vVvMAf7zvJ/W+cZa0m/gK2pcujrxVZrXT5QaA6RIAI5BsCJLDyzVLRQAGAJ7xLgCTTJGh1sCM8ef9UTUh59D4hVQ3cC3D758JjvcJtUbIllz7bDgEd3FQ8TOu14NY/rIPm3weorlVzMefLxpeBX1qXShFTRQz5Qo8QsBpcQY2lpmNCC5uqvEabw/D13+5qddvVHcsruw7jff/arcZ9XOtGUp96pnDxoXpgVCuzSryV23EukwxZEWNTElb0o00EiECBJEACq0Aua8GeVKw8ViFjMtnX56bwfU8Piod6KfMSkvegaww2mBQu6xVO3tKFQ6+78KyFi2ruBnZFufM+L6g/0VeNx601dfisRkW5Qp7iGzXpfxf4ihtH+MoTm1QCqXfjrnzp4En87j911URTg243+G6Pv/l1n70p1xnjaya/gGozl6Zhf+/nSfyYZxWeKA8X9Z0dekv1Ky5nnCskA5kZHQcW7B9Vmh0RKNQESGAV6uXP15PnMUkxPCbpA/v28ncwu85kXsn/fwB3FxUu65VYQsZhtATPGturCRzXcy95z5/9+cs3CSomO3+owr13/Miv7l2vutZy6CRerF1X3uLH3qprMqb5bcKSFYZy23Kj+A9b2qpxHtj6Zy43bMK/2XpY7frolg2kgQ2t4EnIUla73EIwYGbcsIgljrPwCeF8/eNGgycCRCCzBEhgZZYY1c9rBHhUQhQ3l+LBwHM2A991hfPBPTScB03oxxNSvSHotPkC//uuLd9xNlQler7uWYo3tHrI13/TR3WtZEU3PmrlISgxrl6Gx4QY7b3FwFAYs9RZra6tZQk+b9gFqP3DarXrrauV5/N6tuIWxgpuWMQa+yyc65PXfmpoPESACGQ7ARJY2Y6YOsghAhIkRTMoYgbgsxrg/hKA6BT/oxwaQ+510+sef7t5O4/cn2KZcpyxlN9y/pzPWP1MJaY+q2HDZ/W2hd86l1cTOj9dCORNfuzNH796muExYbVmm7hfVAzfdmq6mrXqqx5b+O1XNvy3YxdknWpW5l+1acStTE2YrbkpBQvNpp0RHR0tWra0FKEtcqW06PIYLl6N0anv5o0t4MKxSjrVpUpEIL8TIIGV31eQxv8xAQ6KeA6KRAZvvQDuLQZ4ebzgW03a7udR96J42G9TVaLH6vMhPH7wAugx3081f2vzIvzfRdVhdmMHNSZfLNrCPfkH/s2G7zM8JrR2bMCrNNsMk1bVVj+SLFGLf91zB0+SijBDAwMwMzIkYZXNP5+PHz/G9CxQsWJFnXsKeyuHIWMDICg4EbyvVlO775elr8HEmMGUCcXg/OVoGDc1EF6FJMFnjS1h+1oXsLFO6zZ34Uo0tOzmp1P/549UhBZNc08M6jRIqkQE9ESABJaeQFIzeZIAhw+vAJgM4P7vAN5/YUT2gim26i7gcbwFD/76c5VAMqlWhzv8vg+aTE9J8IyrdGKRO9//fT/+9OYFVd1a7XvxZhNm86pTW6lZpTS9TYhtNO75kK/5dxp/GHhB5mhTRmpXZyz7rMZgFhMbLrcwE75g5MCezT8SaL3y80sWNiiwdLFiRUdL0LCdL3Rpbw3/nIpMI7BadvWD5QtLQ+lSRlClgTfs2uAKDetawPS5QVC1kgmMHe6ocVa6WLHIepXNG4Kaz3MESGDluSWhAWUDAQ4xLwHMSwI83gTg/SdAxP2CJbQqDefy8tN5YJ/GKoHETM2568lnUHeC+lwXjXHlli+O8r0/p0R+N7Wy4XOO+4LtMPXI+NreJnSu+hW3LDNYSpAnyOysSoK5iS0uW8Fimg0bUZ9NovUqJib5aA7FlS5WrJgYCV6/SRJfY6cEqgms2FgJKjfwhsD77rB5RzicvhAFO9a56DRkXaxYZL3SCSVVKkAESGAVoMWkqehAID5cAi5nEOUP4L0cEzYXDFFQshWXPtsGAR2qq82n7L9P+Ji1Yfzus5Rgnz2bOfChTYEv61NLzVo144An/+7o33znlUOq6wYyA/5u22O4sCM5N6GlbQ2pZMVhrIRrf0iSxymMjKzQUlUwGOqwffJKldTWK+WYdLViYf0r12PSCKwTZ6Jgy663sHO9K0ya9RKSkjj4PYuHp/4J4ojwz0WlwUost+aSnhWLrFd5ZefQOHKSAAmsnKRNfeUlAsnHh0bWAA//Qsd4DjEpaWXy0kB1GotVOQ4978CzFmXVxE7J1cf59pel+eojwSrRVK6UKV8/tTzMb1FcrW6HcbO5rH5D3u2XIWrC68KCA1IFG7lkalHWQFIkcGOzksr7SFjptDj6r5TaeqVsXVcrljaBNe2HIKhS0QRGDHGAYeOfg8eNGDh3uCI4OhjC4DH+ULK4EaxYVFrrZNKzYpH1Sv97gFrM+wRIYOX9NaIRZi8BdMQGSPrAgScxCDoL8OIoQPA5gJiX+UdAMAMOo5Lg2UfO63ZjZ3O/eiP4hGVP1ETT1b9r84Vdq/LotykhHFxqNOADF2+F0hMasqZVG/DmVRtJo9oOkhkaGIKVmSVmoSZrVfbuRZ1a12S9yqwVS5MFq2YzHzi0vTyUdTaCiTNfgkwG8McvyYLq8rUYGD8tEB54qDvFfzxgTVYssl7ptKxUqQASIIFVABeVppRlAv+Jrf9eOU98D/DyRLLYCj4PEPs6bwuuYe940LjePMH3jkpMWbTpwQ3HLYR2PwaqjX3LzEr8xfFl/NympaKug5MLL1+/OXT8ai43NDPnsQmxMmszK+RBQUGzvJ2y50ZN1qvMWrE+FlhvwuTQpMMjeOLlJppa+nco3H8YB5tXllUJrG9mvIA7l6qmOylNViyyXmXPPqBW8z4BElh5f41ohLlHgIMijsOHEAYmdgBxbwBenU3+QsGVEJG3BFevezxs0zYedWCjSmAZuVTmJVafgIbTUkI1IM6Zg8pIn5V5B2GBT1mpSu5gamENqKZMzC1IVOXefsuw5/SsV5mxYn0ssHbsi4CLHtGw5o8yopmAwESo29IHzh+tJI4NB48JAGcnI1g83ynDMaa2YpH1KkNcVKEAEyCBVYAXl6amdwIcPgRxABkDfGsu9j/BFXgYIPgCh6So3E3T0/4oj7odxsMWT0t5k9DQmLteDIb5W55D1bLmUMLeWGpU1UoWl6AAuTxJbm1pUgSTaANA7o5d70tVMBtMz3qlixXr4LH3MGCkP3AOwondyIhBpfImUKemGXRsYw19uou3QUXZfeAdTP8xCOLiJGjTwgrWLC0DlpYZb5PUViyyXhXMfUiz0o0ACSzdOFEtIqCJQPKRIpaEdxyMrRkkRuFRIkCkH0BsiIav18nXuCLr1i8DEw6GlgBGViD+Vf6/8kiexNx4rOcFmaFzBTCpWB1kFtaQGBUlyc2swMzYAJ+OZKHKp3tZF+uVcmqZeaMwO3CgFQsLRW3PDrrUZn4hQAIrv6wUjTM/EEgRXEJ0hXNIjGYgMwKQR/8nhiyS/02KBoh7i3UA4iMAovw4JEYxNcGUWjyhmDKyAZEKiCsAFPEAkgJASkh2k2JoiJI4mDhK/zmjk5DKDzsmE2PUxXqlbC4zbxRmYgg6V0UrFhaK2q4zMqpYAAmQwCqAi0pTyhcE1MVYimVJl8Fn3fqlS+tUhwgQASJABD6ZAAmsT0ZIDRABIkAEiAARIAJEQJ0ACSzaEUSACBABIkAEiAAR0DMBElh6BkrNEQEiQASIABEgAkSABBbtASJABIgAESACRIAI6JkACSw9A6XmiAARIAJEgAgQASJAAov2ABEgAkSACBABIkAE9EyABJaegVJzRIAIEAEiQASIABEggUV7gAgQASJABIgAESACeiZAAkvPQKk5IkAEiAARIAJEgAiQwKI9QASIABEgAkSACBABPRMggaVnoNQcESACRIAIEAEiQARIYNEeIAJEgAgQASJABIiAngmQwNIzUGqOCBABIkAEiAARIAIksGgPEAEiQASIABEgAkRAzwRIYOkZKDVHBIgAESACRIAIEAESWLQHiAARIAJEgAgQASKgZwIksPQMlJojAkSACBABIkAEiAAJLNoDRIAIEAEiQASIABHQMwESWHoGSs0RASJABIgAESACRIAEFu0BIkAEiAARIAJEgAjomQAJLD0DpeaIABEgAkSACBABIkACi/YAESACRIAIEAEiQAT0TIAElp6BUnNEgAgQASJABIgAESCBRXuACBABIkAEiAARIAJ6JkACS89AqTkiQASIABEgAkSACJDAoj1ABIgAESACRIAIEAE9EyCBpWeg1BwRIAJEgAgQASJABEhg0R4gAkSACBABIkAEiICeCZDA0jNQao4IEAEiQASIABEgAiSwaA8QASJABIgAESACREDPBEhg6RkoNUcEiAARIAJEgAgQARJYtAeIABEgAkSACBABIqBnAiSw9AyUmiMCRIAIEAEiQASIAAks2gNEgAgQASJABIgAEdAzARJYegZKzREBIkAEiAARIAJEgAQW7QEiQASIABEgAkSACOiZAAksPQOl5ogAESACRIAIEAEiQAKL9gARIAJEgAgQASJABPRMgASWnoFSc0SACBABIkAEiAARIIFFe4AIEAEiQASIABEgAnomQAJLz0CpOSJABIgAESACRIAIkMCiPUAEiAARIAJEgAgQAT0TIIGlZ6DUHBEgAkSACBABIkAESGDRHiACRIAIEAEiQASIgJ4JkMDSM1BqjggQASJABIgAESACJLBoDxABIkAEiAARIAJEQM8ESGDpGSg1RwSIABEgAkSACBABEli0B4gAESACRIAIEAEioGcCJLD0DJSaIwJEgAgQASJABIgACSzaA0SACBABIkAEiAAR0DMBElh6BkrNEQEiQASIABEgAkSABBbtASJABIgAESACRIAI6JkACSw9A6XmiAARIAJEgAgQASJAAov2ABEgAkSACBABIkAE9EyABJaegVJzRIAIEAEiQASIABEggUV7gAgQASJABIgAESACeiZAAkvPQKk5IkAEiAARIAJEgAiQwKI9QASIABEgAkSACBABPRMggaVnoNQcESACRIAIEAEiQARIYNEeIAJEgAgQASJABIiAngmQwNIzUGqOCBABIkAEiAARIAIksGgPEAEiQASIABEgAkRAzwRIYOkZKDVHBIgAESACRIAIEAESWLQHiAARIAJEgAgQASKgZwIksPQMlJojAkSACBABIkAEiAAJLNoDRIAIEAEiQASIABHQMwESWHoGSs0RASJABIgAESACRIAEFu0BIkAEiAARIAJEgAjomQAJLD0DpeaIABEgAkSACBABIkACi/YAESACRIAIEAEiQAT0TOD/Z9A2+8kWZIgAAAAASUVORK5CYII=" /></span>moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-26847004724800534562016-03-07T16:20:00.002-08:002016-03-07T16:39:50.107-08:00My last meetup slides - Apache Phoenix and Titan over Hbase<div style="text-align: justify;">
<br />
<div style="text-align: justify;">
<div style="text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8wbQlIk0x5ea-TNlAorPwWTzspGJJ1hvdHGWbg88YksI1lrkMd6XZHmU84EK3qIQhpl7npPBSgWYqP8yE-EA___oS8gxvzXRS_JjLMRu-aBppH8rB5yVVT_hcQRzJI55Sxy-xDHJZZEQ/s1600/Screen+Shot+2016-03-08+at+11.15.52+AM.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: left;"><img border="0" height="158" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8wbQlIk0x5ea-TNlAorPwWTzspGJJ1hvdHGWbg88YksI1lrkMd6XZHmU84EK3qIQhpl7npPBSgWYqP8yE-EA___oS8gxvzXRS_JjLMRu-aBppH8rB5yVVT_hcQRzJI55Sxy-xDHJZZEQ/s200/Screen+Shot+2016-03-08+at+11.15.52+AM.png" width="200" /></a><span style="font-family: "georgia" , serif; font-size: large;">It's been a while since the Hadoop meetup that me and @YRodenski hosted - But it's better late than never.</span></div>
</div>
<div style="text-align: justify;">
<span style="font-family: "georgia" , serif; font-size: large;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhroXKmz4nutVx8gnG9l_Mn2tjToMCSAmeyU9MA6q1jVu8Z4gm8CDzmMyBxmMGO6AhdG6OnLd1orR8qQ9ZocM9aVu8ldOj0h7XyZwTIfX1CK6BNC6TG77tXF-JLmZSsn9l-dpYsyWUEHKo/s1600/Screen+Shot+2016-03-08+at+11.19.16+AM.png" imageanchor="1" style="clear: left; color: #1155cc; display: inline; margin-bottom: 1em; margin-right: 1em;"></a></span></div>
<div style="text-align: justify;">
<div style="text-align: start;">
<div style="text-align: left;">
<span style="font-family: "georgia" , serif; font-size: large;">You are welcome to have some fun with the slides:</span></div>
</div>
<div style="text-align: start;">
<div style="text-align: left;">
<span style="font-family: "georgia" , serif; font-size: large;"><span style="color: rgba(0 , 0 , 0 , 0.670588); line-height: 22.4px;"> </span><a class="" href="http://www.slideshare.net/roadan/not-your-dads-h-base-new" rel="nofollow" style="color: #2a9bc7; display: inline; line-height: 22.4px; text-decoration: none; word-wrap: break-word;" target="_blank" title="http://www.slideshare.net/roadan/not-your-dads-h-base-new">http://www.slideshare.net/roadan/not-your-dads-h-base-new</a><span style="color: rgba(0 , 0 , 0 , 0.670588); line-height: 22.4px;"> </span></span></div>
</div>
<div style="text-align: start;">
<div style="text-align: left;">
<span style="color: rgba(0 , 0 , 0 , 0.670588); font-family: "georgia" , serif; font-size: large; line-height: 22.4px;">and learn some phoenix </span><span style="color: rgba(0, 0, 0, 0.670588); font-family: georgia, serif; font-size: large; line-height: 22.4px;">code and concepts here:</span></div>
</div>
<div style="text-align: start;">
<div style="background-color: white; font-family: arial, sans-serif;">
<div style="text-align: left;">
<a class="" href="https://github.com/moscovig/phoenix_demo/blob/master/README.md" rel="nofollow" style="color: #2a9bc7; display: inline; line-height: 22.4px; text-decoration: none; word-wrap: break-word;" target="_blank" title="https://github.com/moscovig/phoenix_demo/blob/master/README.md"><span style="font-family: "georgia" , serif; font-size: large;">https://github.com/moscovig/phoenix_demo/blob/master/README.md</span></a></div>
</div>
</div>
</div>
</div>
<div style="text-align: justify;">
<div style="text-align: start;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6OFBI5rfgA4VkQ2a67lTBZSbCVEPOvcaTByJ_TdwsfhfZhOw5-XgboSzoaD3ThvKOO7GCtIjmU5iPZzYuvvh88WsZDBQIFyegM52P9TT1kD1FfP4kwpMNnPV8XV7EuRDHY19yvGFUU8k/s1600/Screen+Shot+2016-03-08+at+11.19.16+AM.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6OFBI5rfgA4VkQ2a67lTBZSbCVEPOvcaTByJ_TdwsfhfZhOw5-XgboSzoaD3ThvKOO7GCtIjmU5iPZzYuvvh88WsZDBQIFyegM52P9TT1kD1FfP4kwpMNnPV8XV7EuRDHY19yvGFUU8k/s320/Screen+Shot+2016-03-08+at+11.19.16+AM.png" width="320" /></a></div>
</div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-7497061643418135712016-03-06T04:12:00.000-08:002016-03-07T16:38:37.637-08:00Cheat sheet: Playing with "spark-notbook" and "Ashley Medison" dump<h2>
<span style="font-family: Georgia, Times New Roman, serif; font-size: small; font-weight: normal;"><span style="font-size: medium;">I will try loading "<span style="color: orange;">Ashley Medison</span>" <span style="color: #d0e0e3; font-size: small;"> </span>emails' dump from the local file-system into Cassandra, so that it would be easy to look for an email. </span><span style="font-size: medium;">In addition I will do some batch processing for "bigger" questions such as emails domain distribution - </span><span style="font-size: medium;"> classic data pipeline.</span></span></h2>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">This is a description of the full dump of Ashley Medison lick:</span></span><br />
<a href="http://www.hydraze.org/2015/08/ashley-madison-full-dump-has-finally-leaked/"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">http://www.hydraze.org/2015/08/ashley-madison-full-dump-has-finally-leaked/</span></a><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">I don't want to get in troubles, so I won't publish any real emails, but only some distributions and interesting facts.</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span>
<h2>
<span style="font-family: Georgia, Times New Roman, serif; font-size: small;">
Downloading and Running the notebook. </span></h2>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">From here: <a href="http://spark-notebook.io/">http://spark-notebook.io</a>/ choose the spark and hadoop version, type your mail and download. you can download a binary or running the source code using 'sbt run'.</span></div>
<div>
<a href="https://s3.eu-central-1.amazonaws.com/spark-notebook/tgz/spark-notebook-master-scala-2.10.5-spark-1.6.0-hadoop-2.2.0.tgz?max-keys=100000"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">you can try this for direct download</span></a></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;">Then just run </span><span style="background-color: rgba(0 , 0 , 0 , 0.0392157); color: #333333; line-height: 21.7600002288818px;">bin/spark-notebook</span></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">It is a good idea to print "hello" from time to time to see if you are still connected. I was struggling when trying to add dependencies and kept loosing my kernel. <b>It's also important to keep watching the notebook's log (/logs/notbook_name.log).</b></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><u>A really good thing is all the sample notebooks. Just great. They have so many visualization tools, including maps. (remember to start the notebook from its hope directory, and don't get into /bin cause it will not be able to find the sample notebooks with</u></span></span><br />
<h1 class="gh-header-title" style="background-color: white; box-sizing: border-box; color: #333333; font-weight: normal; line-height: 1.1; margin: 0px 150px 0px 0px; word-wrap: break-word;">
<span class="js-issue-title" style="box-sizing: border-box;"><span style="font-family: Georgia, Times New Roman, serif; font-size: small;"><u>notebooks load "not found"</u></span></span></h1>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<h2>
<span style="font-family: Georgia, Times New Roman, serif; font-size: small;">writing simple loading process</span></h2>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">The raw data is a sql insert command for all records. We need to extract the emails using regex.</span><br />
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">So first, before loading to cassandra, lets make this :</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
</span><br />
<pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); line-height: 14px; overflow: auto; padding: 5px; width: 653px;"><div style="line-height: normal; white-space: normal;">
<i><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">"INSERT INTO `aminno_member_email` VALUES ('email@gmail.com','email2@gmail.com') ...</span></i></div>
<span style="font-family: Georgia, Times New Roman, serif;">
</span></pre>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">Into this:</span><br />
<pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); line-height: 14px; overflow: auto; padding: 5px; width: 653px;"><div style="line-height: normal; white-space: normal;">
<i><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">email@gmail.com</span></i></div>
<span style="font-family: Georgia, Times New Roman, serif;">
</span><div style="line-height: normal; white-space: normal;">
<i><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">email2@gmail.com</span></i></div>
<span style="font-family: Georgia, Times New Roman, serif;">
</span></pre>
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span></div>
<div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">Here is the Spark code:</span></div>
<div>
<pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); color: black; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> <code style="color: black; word-wrap: normal;">
val textFile = sc.textFile("/home/spark/aminno_member_email.dump")
val email_records = textFile.flatMap(a => "[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+".r findAllIn a)
<b>email_records.saveAsTextFile("/home/spark/note_out/emails1.txt")</b>
</code>
</span></pre>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">You have to be patient and not refreshing, and eventually the processing bar/success message will appear</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1ZH8VsScvq9uK5JEDBLula9E7HjJXb_zm7WDWjbZTqNqmqfxdKPNspdl7p36CJGNXvtPNsHn9X0XZf1nY4NuGn42xIGX4zfICdBQEatROC-uYZ-b4s6zZCdr1mwHK-rsgICIUfwi4cAg/s1600/note7.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1ZH8VsScvq9uK5JEDBLula9E7HjJXb_zm7WDWjbZTqNqmqfxdKPNspdl7p36CJGNXvtPNsHn9X0XZf1nY4NuGn42xIGX4zfICdBQEatROC-uYZ-b4s6zZCdr1mwHK-rsgICIUfwi4cAg/s1600/note7.png" /></span></span></a></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoq7_vvzKM9l6CpIKb-2OGQdxdUEOdRe82RhwJrHtnwly0UYWro6uFN-KIQ2npi3PEPxz0B5sIC30EpKfKEPew2IdjBLCxSKFUIhUtBsQZLM_kn8FJkUC7ElnhcGnGZW6y2GewPJ3dLjs/s1600/note6.png" /></span></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">and the complete notebook till now:</span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<span style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmAVj_S5fMJoUpL3v41MVFpn_x7enzUseY6Nscs0K4dY2NPuoOLk-ubbQ3d6GiS4StMQXj1lhL5wcodxeaZhZ09VOYMcduI1bDZuOrgk2rNOHUvKWsDvrWcgzKffgGXET5bT0n3v2gAOg/s1600/note8.png" /></span></span></div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">Now, we can do very cool stuff using bash commands. Counting with `ll | wc -l` or looking for emails with grep. Still, we will now load the output file to Cassandra in order to save us the 30 seconds `grep` time :)</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">Small little thing before cassandra - Lets play a bit with spark over the data:</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">Register the txt file as a data frame:</span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<span style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9EbJ9uihCS94l_wxgVkxHwuqC4zDlYYJZJ1wcTPBc4OqEkwkXbomIlVNQ25o1Y6n62TWjEsqIO82GvZmB_r1vgcPT27gAwHF_RSB5tmd-fjCqJuA_Lop2w9YqMsJp1Yr8niXDYLXRex8/s1600/note9.png" /></span></span></div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">W</span><span style="font-size: medium;">e can browse the results using the notebook:</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
</span><br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYgdoJGabZW1JJmURU1kvdIC384fKkKSCZUJgLmQpBWI3NPTCgyrXSvTx5ZxoUMCaLXaeEu5khASkCk9215VFXuqJOY6fSB6ayGxQfjFwHs4QQx5Jwgp8OCX5UCHD3W0_AFAIOyyK-Tus/s1600/note11.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYgdoJGabZW1JJmURU1kvdIC384fKkKSCZUJgLmQpBWI3NPTCgyrXSvTx5ZxoUMCaLXaeEu5khASkCk9215VFXuqJOY6fSB6ayGxQfjFwHs4QQx5Jwgp8OCX5UCHD3W0_AFAIOyyK-Tus/s400/note11.jpg" width="400" /></span></a></div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">Or look for the most popular email domain in the site:</span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<span style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-pTJMImKc3wY0TEMEuXQ9Oye-FIya6AuGeJ_qoqtvp94OKiLytnClwEB2XYIQ7Azj4BfHzUobcuPSYrFaqFn33abfExmmFYX-VFAflMThwhurFbjAE958oGxr_yyZqCaVLVSfrVYLrME/s1600/note14.png" /></span></span></div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9o4-83yR0oDA_qaJd1zu6mBxMohfCFp4_jq8B1POaQ1wBE69AVuhLC6dQUBcGYHXSoXPeuXXJEr8elgGxHOEEsWgn-uv-w_IQkeMbM4xq6WDDUvwl0cYmjlko8OMy8DGiA5dZZni7_AE/s1600/note13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><img border="0" height="374" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9o4-83yR0oDA_qaJd1zu6mBxMohfCFp4_jq8B1POaQ1wBE69AVuhLC6dQUBcGYHXSoXPeuXXJEr8elgGxHOEEsWgn-uv-w_IQkeMbM4xq6WDDUvwl0cYmjlko8OMy8DGiA5dZZni7_AE/s640/note13.png" width="640" /></span></a></div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">The notebook has different display and visualization options for every type of results. For instance, when we just "show" results, as above, we will see a nice text output. When we are creating a dataframe, we have a nice table view with search option.</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
</span><br />
<h3>
<span style="font-family: Georgia, Times New Roman, serif; font-size: small;">Visualize:</span></h3>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">When we have a sql result set, as you can see below, we can out of the box choose a custom chart: </span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtx4P7trPxwCTtrkLSJNGD2GAqDdeE55o0WUcSYnwM7qsYr1UOcD2Cq85W6aqHraqrxQ42IJXmuoOExngWPb44tCLN65FYS7QxnfO4q5lpEiJtU-eXt-tLA-5DtS2wh4IAu1E88jWYvTo/s1600/note15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><img border="0" height="418" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtx4P7trPxwCTtrkLSJNGD2GAqDdeE55o0WUcSYnwM7qsYr1UOcD2Cq85W6aqHraqrxQ42IJXmuoOExngWPb44tCLN65FYS7QxnfO4q5lpEiJtU-eXt-tLA-5DtS2wh4IAu1E88jWYvTo/s640/note15.png" width="640" /></span></a></div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">And,</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">we can also use c3js -<a href="http://c3js.org/samples/chart_pie.html"> http://c3js.org/samples/chart_pie.html </a> </span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4upAJxPhODL0WQoIy7CzAffoCm_SDBhgssM3xyYPvNFVYuMkgkRJo7tUrgNGtjv-zzTHUVZ2_FgA8Yq0NSmkw9u6VOaQzTcLya9dFqXLK9k6a9QyyYr30Ig93132Gd14T9dPJawm2k6E/s1600/note16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><img border="0" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4upAJxPhODL0WQoIy7CzAffoCm_SDBhgssM3xyYPvNFVYuMkgkRJo7tUrgNGtjv-zzTHUVZ2_FgA8Yq0NSmkw9u6VOaQzTcLya9dFqXLK9k6a9QyyYr30Ig93132Gd14T9dPJawm2k6E/s640/note16.png" width="640" /></span></a></div>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">And much much more! So much fun.</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span>
<h2>
<span style="font-family: Georgia, Times New Roman, serif; font-size: small;">Now it is time to load into Cassandra</span></h2>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">I couldn't set the cassandra connector in a reasonable time because of dependencies problem, as described here: <a href="http://alvincjin.blogspot.com.au/2015/01/spark-cassandra-connector.html">http://alvincjin.blogspot.com.au/2015/01/spark-cassandra-connector.html</a></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">I decided to go for the spark shell for that.</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">Create table in cassandra:</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">cqlsh> create keyspace "notebook" WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 3};</span><br />
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">cqlsh> CREATE TABLE notebook.email (address varchar PRIMARY KEY) ;</span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">(Using Spark 1.6)</span></span><br />
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<b><span style="font-size: medium;">spark-shell --packages com.datastax.spark:spark-cassandra-connector_2.10:1.5.0-M3</span></b></span><br />
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><br /></span></div>
<pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); overflow: auto; padding: 5px; width: 653px;"><div style="line-height: normal; white-space: normal;">
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">import com.datastax.spark.connector._</span></div>
<span style="font-family: Georgia, Times New Roman, serif;">
</span><div style="line-height: normal; white-space: normal;">
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">val emails = sc.textFile("/home/spark/note_out/emails4.txt")</span></div>
<span style="font-family: Georgia, Times New Roman, serif;">
</span><div style="line-height: normal; white-space: normal;">
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">val emails_df=emails.toDF("address")</span></div>
<span style="font-family: Georgia, Times New Roman, serif;">
<span style="font-family: "times new roman"; font-size: medium;"><span style="white-space: normal;">emails_df.write.format("org.apache.spark.sql.cassandra").options(Map( "table" -> "email", "keyspace" -> "notebook")).save()</span></span>
</span><div style="line-height: 14px;">
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;">
</span></div>
<span style="font-family: Georgia, Times New Roman, serif;">
</span></pre>
<span style="font-family: Georgia, Times New Roman, serif;"><span style="font-size: medium;"><br /></span>
<span style="font-size: medium;">And once we have all the data loaded, we can look for mail in 0 time!</span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-L9zDDN-hzJdb-jY_XfN-yPerFaNU3ZtISV1sH9lMi33q8VedGWfgMpMza6PgcmQB0-5XZbHu9-eSeEggNiq2uz_yPuRp1Fv7iYd2E-oRiW7NdlHS2BIzq3Dcuyu8hI2C5a1uh-9f7bo/s1600/note18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><img border="0" height="106" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-L9zDDN-hzJdb-jY_XfN-yPerFaNU3ZtISV1sH9lMi33q8VedGWfgMpMza6PgcmQB0-5XZbHu9-eSeEggNiq2uz_yPuRp1Fv7iYd2E-oRiW7NdlHS2BIzq3Dcuyu8hI2C5a1uh-9f7bo/s640/note18.png" width="640" /></span></a></div>
<h3>
<span style="font-family: Georgia, Times New Roman, serif; font-size: small;"><br /></span></h3>
</div>
</div>
</div>
</div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"><b>What can you take from that?</b></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> - spark-notebook is is one of the easiest and fastest ways to start playing with spark, and doing more than basic stuff.</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> - It is a bit difficult and not smooth when it comes to dependencies adding.</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> - The Ashley Madison most popular domain is gmail of course. Hotmail is the second?! and that they had 36 M emails.</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> - How to extract an email in spark</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> - How to draw nice charts with spark-notbook</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> - How to write a DataFrame into cassandra, and how to use Cassandra connector to spark from spark-shell</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif; font-size: medium;"> - And most importantly - don't expect any kind of privacy, anywhere on the web.</span></div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-56431181823152277772016-03-01T01:03:00.000-08:002016-03-01T01:03:56.186-08:00Scala slick - Multiple update with different values<span style="font-size: large;">Hi. Again, something that I was expecting to easily find online, but had to implement by myself.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Slick, by typesafe,<span style="background-color: white; color: #505050; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; line-height: 18px;"> </span><a href="http://slick.typesafe.com/"><span style="background-color: white; color: #505050; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; line-height: 18px;">is a modern database query and access library for Scala.</span><span style="background-color: white; color: #505050; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; line-height: 18px;"> </span></a></span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">We are using<b> slick 3.1.1</b> in order to access our db.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">At some day, we had to update multiple rows, with different values, depending on one of the columns. The new values are coming out of a sequence of one of our objects.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">In addition we wanted to do some more inserts, as part of the same transaction.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">A simple sql that looks like that:<b> "update table cars set proce=c.price where year=c.year"</b></span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Lets say that we want to update a person's details, and to update all of the person's cars' condition and price, according to the sequence of cars that are part of the person object.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">We find the corresponding cars in the db with filtering on the car Id column that links between the cars sequence to the cars table.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">We also want to update the person record with the person object in the same transaction</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Solution: We are preparing a dbio object of sequence of update actions, and then combining the DBIO of sequence with the person update action, under the same DBIO.seq call.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">With slick it looks like that:</span><br />
<div>
<span style="font-size: large;"><br /></span></div>
<span style="font-size: large;"><br /></span>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"><span style="font-size: large;"><span style="color: #cc7832; font-weight: bold;">def </span><span style="color: #ffc66d;">updatePerson</span>(pers: Person<span style="color: #cc7832;">, </span>action: <span style="color: #4e807d;">String</span>) = {
<span style="color: #cc7832; font-weight: bold;">val </span>cars = pers.cars
<span style="color: #cc7832; font-weight: bold;">val </span>update_cars=cars.map(b=> {
<span style="color: #9876aa; font-style: italic;">db_cars</span>.filter(cdb=> <span style="font-size: 9pt;">cdb</span><span style="font-size: 9pt;">.car_id===b.car_id</span><span style="font-size: 9pt;">).map(r=>(r.condition</span><span style="color: #cc7832; font-size: 9pt;">,</span><span style="font-size: 9pt;">r.price)).update(b.condition</span><span style="color: #cc7832; font-size: 9pt;">,</span><span style="font-size: 9pt;">b.price)</span>
})
<span style="color: #cc7832; font-weight: bold;">val </span>dbio_up_cars= <span style="color: #9876aa; font-style: italic;">DBIO</span>.<span style="font-style: italic;">sequence</span>(update_cars)
<span style="color: #9876aa; font-style: italic;">DBIO</span>.<span style="font-style: italic;">seq</span>(
<span style="color: #9876aa; font-style: italic;">db_persons</span>.filter(_.id === pers.id).update(fromPerson(pers))<span style="color: #cc7832;">,</span><span style="color: #cc7832;"> </span></span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"><span style="font-size: large;"><span style="color: #cc7832;"> </span>dbio_up_cars
)
}</span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"><span style="font-size: large;">//<span style="color: #cc7832; font-weight: bold;">val </span><span style="color: #9876aa; font-style: italic;">db_persons </span><span style="font-size: 9pt;">= </span><span style="color: #9876aa; font-style: italic;">TableQuery</span><span style="font-size: 9pt;">[DbPersons]</span></span></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"></pre>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: menlo;"></pre>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">where, for that example, the person db object looks like that:</span><br />
<span style="font-size: large;"><br /></span>
<table class="highlight tab-size js-file-line-container" data-tab-size="8" style="background-color: white; border-collapse: collapse; border-spacing: 0px; box-sizing: border-box; color: #333333; font-family: helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, 'apple color emoji', 'segoe ui emoji', 'segoe ui symbol'; line-height: 18.2000007629395px;"><tbody style="box-sizing: border-box;">
<tr style="box-sizing: border-box;"><td class="blob-code blob-code-inner js-file-line" id="LC38" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"></td></tr>
<tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="39" id="L39" style="border-color: rgb(238, 238, 238); border-style: solid; border-width: 0px 1px 0px 0px; box-sizing: border-box; color: rgba(0, 0, 0, 0.298039); cursor: pointer; font-family: consolas, 'liberation mono', menlo, courier, monospace; line-height: 18px; min-width: 50px; padding: 0px 10px; text-align: right; vertical-align: top; white-space: nowrap; width: 50px;"></td><td class="blob-code blob-code-inner js-file-line" id="LC39" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"><span style="font-size: large;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">DbPerson</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">tag</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Tag</span>) <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">extends</span> <span class="pl-e" style="box-sizing: border-box; color: #795da3;">Table</span>[(<span style="color: #a71d5d;">Long</span>)](tag, <span class="pl-s" style="box-sizing: border-box; color: #183691;"><span class="pl-pds" style="box-sizing: border-box;">"</span>Persons<span class="pl-pds" style="box-sizing: border-box;">"</span></span>) {</span></td></tr>
<tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="40" id="L40" style="border-color: rgb(238, 238, 238); border-style: solid; border-width: 0px 1px 0px 0px; box-sizing: border-box; color: rgba(0, 0, 0, 0.298039); cursor: pointer; font-family: consolas, 'liberation mono', menlo, courier, monospace; line-height: 18px; min-width: 50px; padding: 0px 10px; text-align: right; vertical-align: top; white-space: nowrap; width: 50px;"></td><td class="blob-code blob-code-inner js-file-line" id="LC40" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"><span style="font-size: large;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">def</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">id</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> column[<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">Long</span>](<span class="pl-s" style="box-sizing: border-box; color: #183691;"><span class="pl-pds" style="box-sizing: border-box;">"</span>ID<span class="pl-pds" style="box-sizing: border-box;">"</span></span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">O</span>.<span class="pl-en" style="box-sizing: border-box; color: #795da3;">PrimaryKey</span>)</span></td></tr>
<tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="41" id="L41" style="border-color: rgb(238, 238, 238); border-style: solid; border-width: 0px 1px 0px 0px; box-sizing: border-box; color: rgba(0, 0, 0, 0.298039); cursor: pointer; font-family: consolas, 'liberation mono', menlo, courier, monospace; line-height: 18px; min-width: 50px; padding: 0px 10px; text-align: right; vertical-align: top; white-space: nowrap; width: 50px;"><span style="font-size: large;"><br /></span></td><td class="blob-code blob-code-inner js-file-line" id="LC41" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"><span style="font-size: large;"><br /></span></td></tr>
<tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="49" id="L49" style="border-color: rgb(238, 238, 238); border-style: solid; border-width: 0px 1px 0px 0px; box-sizing: border-box; color: rgba(0, 0, 0, 0.298039); cursor: pointer; font-family: consolas, 'liberation mono', menlo, courier, monospace; line-height: 18px; min-width: 50px; padding: 0px 10px; text-align: right; vertical-align: top; white-space: nowrap; width: 50px;"></td><td class="blob-code blob-code-inner js-file-line" id="LC49" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"><span style="font-size: large;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">def</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">*</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> (Id)</span></td></tr>
<tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="51" id="L51" style="border-color: rgb(238, 238, 238); border-style: solid; border-width: 0px 1px 0px 0px; box-sizing: border-box; color: rgba(0, 0, 0, 0.298039); cursor: pointer; font-family: consolas, 'liberation mono', menlo, courier, monospace; line-height: 18px; min-width: 50px; padding: 0px 10px; text-align: right; vertical-align: top; white-space: nowrap; width: 50px;"></td><td class="blob-code blob-code-inner js-file-line" id="LC51" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"><span style="font-size: large;">}</span></td></tr>
<tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="52" id="L52" style="border-color: rgb(238, 238, 238); border-style: solid; border-width: 0px 1px 0px 0px; box-sizing: border-box; color: rgba(0, 0, 0, 0.298039); cursor: pointer; font-family: consolas, 'liberation mono', menlo, courier, monospace; line-height: 18px; min-width: 50px; padding: 0px 10px; text-align: right; vertical-align: top; white-space: nowrap; width: 50px;"></td><td class="blob-code blob-code-inner js-file-line" id="LC52" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"></td></tr>
<tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="53" id="L53" style="border-color: rgb(238, 238, 238); border-style: solid; border-width: 0px 1px 0px 0px; box-sizing: border-box; color: rgba(0, 0, 0, 0.298039); cursor: pointer; font-family: consolas, 'liberation mono', menlo, courier, monospace; line-height: 18px; min-width: 50px; padding: 0px 10px; text-align: right; vertical-align: top; white-space: nowrap; width: 50px;"></td><td class="blob-code blob-code-inner js-file-line" id="LC53" style="box-sizing: border-box; font-family: consolas, 'liberation mono', menlo, courier, monospace; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre; word-wrap: normal;"><span style="font-size: large;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">db_person</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">TableQuery</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">DbPerson</span>]</span></td></tr>
</tbody></table>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Good Luck!</span>moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-30147616700010592152016-02-26T19:48:00.003-08:002016-02-26T19:54:40.221-08:00SprayJson Scala case classes for Google Maps API responseHi<br />
<br />
I have played a little with Spray Json - <span style="background-color: white; color: #666666; font-family: "helvetica" , "arial" , "nimbussansl" , "liberationsans" , "freesans" , "clean" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 16px; line-height: 22.4px;"><a href="https://github.com/spray/spray-json">A lightweight, clean and simple JSON implementation in Scala</a></span><br />
<span style="background-color: white; color: #666666; font-family: "helvetica" , "arial" , "nimbussansl" , "liberationsans" , "freesans" , "clean" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 16px; line-height: 22.4px;"><br /></span>
<span style="font-family: "helvetica" , "arial" , "nimbussansl" , "liberationsans" , "freesans" , "clean" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"><span style="background-color: white; line-height: 22.4px;">Here is an example of how to easily marshalling a google get reponse for some adderss, using Spray Json and case classes that were built for the responding json.</span></span><br />
<span style="font-family: "helvetica" , "arial" , "nimbussansl" , "liberationsans" , "freesans" , "clean" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"><span style="background-color: white; line-height: 22.4px;"><br /></span></span>
<span style="font-family: "helvetica" , "arial" , "nimbussansl" , "liberationsans" , "freesans" , "clean" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"><span style="background-color: white; line-height: 22.4px;">I hope that anyone who are trying to do so, would save a little time by using the ready to use case classes (Maybe it's a good idea to create a central repository of case classes for common used jsons).</span></span><br />
<br />
<span style="font-family: "helvetica" , "arial" , "nimbussansl" , "liberationsans" , "freesans" , "clean" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"><span style="background-color: white; line-height: 22.4px;">That's the 1st result for searching "melbourne australia" in google maps (originally an array of 2 results).:</span></span><br />
<span style="font-family: "helvetica" , "arial" , "nimbussansl" , "liberationsans" , "freesans" , "clean" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"><span style="background-color: white; line-height: 22.4px;"><br /></span></span>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> {
"results" : [
{
"address_components" : [
{
"long_name" : "Melbourne",
"short_name" : "Melbourne",
"types" : [ "colloquial_area", "locality", "political" ]
},
{
"long_name" : "Victoria",
"short_name" : "VIC",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "Australia",
"short_name" : "AU",
"types" : [ "country", "political" ]
}
],
"formatted_address" : "Melbourne VIC, Australia",
"geometry" : {
"bounds" : {
"northeast" : {
"lat" : -37.4598457,
"lng" : 145.76474
},
"southwest" : {
"lat" : -38.2607199,
"lng" : 144.3944921
}
},
"location" : {
"lat" : -37.814107,
"lng" : 144.96328
},
"location_type" : "APPROXIMATE",
"viewport" : {
"northeast" : {
"lat" : -37.4598457,
"lng" : 145.76474
},
"southwest" : {
"lat" : -38.2607199,
"lng" : 144.3944921
}
}
},
"partial_match" : true,
"place_id" : "ChIJ90260rVG1moRkM2MIXVWBAQ",
"types" : [ "colloquial_area", "locality", "political" ]
}
],
"status" : "OK"
}
</code></pre>
<br />
And that's the Code, including the case classes that are used for parsing the json:
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> /**
* Created by gilad on 24/02/16.
*/
import spray.json._
import DefaultJsonProtocol._
import scala.io.Source.fromURL
object FetchCoordinates extends App{
@throws(classOf[java.io.IOException])
@throws(classOf[java.net.SocketTimeoutException])
def get(url: String,
connectTimeout:Int =5000,
readTimeout:Int =5000,
requestMethod: String = "GET") = {
import java.net.{URL, HttpURLConnection}
val connection = (new URL(url)).openConnection.asInstanceOf[HttpURLConnection]
connection.setConnectTimeout(connectTimeout)
connection.setReadTimeout(readTimeout)
connection.setRequestMethod(requestMethod)
val inputStream = connection.getInputStream
val content = io.Source.fromInputStream(inputStream).mkString
if (inputStream != null) inputStream.close
content
}
try {
val address = "melbourneaustralia"
val content = get("http://maps.googleapis.com/maps/api/geocode/json?address="+address+"&sensor=true")
val contentJs=content.parseJson
val googleRes = contentJs.convertTo[GoogleMapResponse]
val form_address= googleRes.results(0).formatted_address
println(googleRes.status)
} catch {
case ioe: java.io.IOException => // handle this
case ste: java.net.SocketTimeoutException => // handle this
}
}
</code></pre>
<b><u><br /></u></b>
<b><u>Case classes:
</u></b><br />
<b><u><br /></u></b>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> import spray.json._
import DefaultJsonProtocol._
case class JsonCoords(lat:Long,lng:Long)
object JsonCoords { implicit val f = jsonFormat2(JsonCoords.apply)}
case class JsonBounds(northeast:JsonCoords,southwest:JsonCoords)
object JsonBounds { implicit val f = jsonFormat2(JsonBounds.apply)}
case class JsonGeometry(bounds:JsonBounds,location_type:String, viewport: JsonBounds)
object JsonGeometry { implicit val f = jsonFormat3(JsonGeometry.apply)}
case class JsonAddress(long_name:String,short_name:String,types:Seq[String])
object JsonAddress { implicit val f = jsonFormat3(JsonAddress.apply)}
case class JsonGoogleResult(address_components:Seq[JsonAddress],formatted_address:String,
geometry:JsonGeometry,partial_match:Boolean,place_id:String,types:Seq[String]
)
object JsonGoogleResult { implicit val f = jsonFormat6(JsonGoogleResult.apply)}
case class GoogleMapResponse(results:Seq[JsonGoogleResult],status:String)
object GoogleMapResponse { implicit val f = jsonFormat2(GoogleMapResponse.apply)}
</code></pre>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-25191684280119066702016-02-23T03:05:00.000-08:002016-02-23T03:09:46.847-08:00HTTPS From Java to Logstash<span style="font-size: large;">The goal: securely sending data from a server that has access to a rabbitMq based system to a remote server (and then to s3)</span><br />
<div>
<span style="font-size: large;">How would you do that?</span></div>
<div>
<span style="font-size: large;">Due to a special protocol when accessing the rabbitMq system, we decided to do that with a custom consuming java code in the Rabbit side and with Logstash listening on 443 for HTTPS posts.</span></div>
<div>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;">We really wanted to do that with Logstash to Logstash but couldn't due to special requirements.</span></div>
<div>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;">Why https? We tried TCP over SSL but when a new connection is being established, and then the listener Logstash is going down, we had to work harder in order to re-establish the connection from the java side. And in addition, no response code is being sent back when working over tcp with no http.</span></div>
<div>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;">We though of using Logstash Lumberjack on the listener side, but then you have to use Logstash with Lumberjack output.</span></div>
<div>
<span style="font-size: large;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCtdWfcfRVBhmwInDCewgifZOZJxcP0FMwdnwRm6RbO9W96iPpq5UJhyphenhyphenumiS1HpZWoyTG9KwbK95MTuAIa4htH2xx2_6dvLKX5rokEBEAjXslyWj1a71LjVAPFsgISPN9niAE8Mh4tcDM/s1600/logsta.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-size: large;"><img border="0" height="220" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCtdWfcfRVBhmwInDCewgifZOZJxcP0FMwdnwRm6RbO9W96iPpq5UJhyphenhyphenumiS1HpZWoyTG9KwbK95MTuAIa4htH2xx2_6dvLKX5rokEBEAjXslyWj1a71LjVAPFsgISPN9niAE8Mh4tcDM/s320/logsta.jpg" width="320" /></span></a></div>
<span style="font-size: large;"><u><b>Step #1:</b></u> Creating certificates (if you don't have any) and import them into the keystore.</span><br />
<span style="font-size: large;"><br /></span>
<b><span style="font-size: large;">On the Logstash box:</span></b><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<ol>
<li><span style="font-size: large;"><span style="background-color: white; color: #222222; font-family: "arial" , sans-serif;"> keytool -genkey -keyalg RSA -alias mycert -keystore keystore.jks -storepass 123pass -ext SAN=ip:172.18.22.22,ip:172.22.22</span><span style="background-color: white; color: #222222; font-family: "arial" , sans-serif;">.24 -validity 360 -keysize 2048</span> </span></li>
<li><span style="font-size: large;">The 2 ips are the ips that we want the java box to trust when it is connecting Logstash. In that case we had to pass via another routing box in the middle.</span></li>
<li><span style="background-color: white; color: #222222; font-family: "arial" , sans-serif; font-size: large;">Export the created certificate for the java consuming client use.</span></li>
<li><span style="background-color: white;"><span style="color: #222222; font-family: "arial" , sans-serif; font-size: large;">keytool -exportcert -file client.er -alias mysert -keystore keystore.jks </span></span></li>
</ol>
<div>
<span style="color: #222222; font-family: "arial" , sans-serif; font-size: large;"><b>On the Java side:</b></span></div>
<div>
<ol>
<li><span style="color: #222222; font-family: "arial" , sans-serif;"><b><span style="background-color: white; font-size: large; font-weight: normal;">Create a truststore with the exported certificate:</span></b></span></li>
<ol>
<li><span style="color: #222222; font-family: "arial" , sans-serif;"><b><span style="background-color: white; font-size: large; font-weight: normal;">keytool -importcert -file client.cer -alias mycert -keystore truststore.jks</span></b></span></li>
</ol>
<li><span style="color: #222222; font-family: "arial" , sans-serif; font-size: large;">You can import another certificates if needed</span></li>
</ol>
</div>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;"><u><b>Step #2:</b></u> Java app</span><br />
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;">I will focus on the https part with basic authentication.</span></div>
<div>
<span style="font-size: large;">In order to send data through https you can use the next code that is based on apache http client (it was harder than I expected it would be to find the right way to do that with SSL):</span></div>
<div>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;">Imports:</span></div>
<div>
<span style="font-size: large;"><br /></span></div>
<div>
<pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><span style="font-size: large;"> import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
</span></code></pre>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;">Some setups before sending (We setup the apache https client. It also has a basic authentication headers with username and password).</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">We point the java keystore location that we have created on the java side, in part #1.</span><br />
<span style="font-size: large;"><br /></span>
<pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><span style="font-size: large;"> httpclient = HttpClients.createDefault();
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(new File(truststore_location), truststore_password.toCharArray(),
new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,null, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
final RequestConfig params = RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(5000).build();
httppost = new HttpPost("https://"+logstashUrl);
httppost.setConfig(params);
String credentials = this.httpuser+":"+httppassword;
byte[] encoding=Base64.encodeBase64(credentials.getBytes());
String authStringEnc = new String(encoding);
httppost.setHeader("Authorization", "Basic " + authStringEnc);
</span></code></pre>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Sending chunks of data:</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><span style="font-size: large;"> ByteArrayEntity postDataEntity = new ByteArrayEntity(data.getBytes());
httppost.setEntity(postDataEntity);
//Actual post to remote host
CloseableHttpResponse httpResponse = httpclient.execute(httppost);
BufferedReader reader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
String inputLine;
StringBuffer stringResponse = new StringBuffer();
while ((inputLine = reader.readLine()) != null) {
stringResponse.append(inputLine);
}
reader.close();
</span></code></pre>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><u style="font-weight: bold;">Part #3:</u> Logstash with HTTPS INPUT</span><br />
<span style="font-size: large;">h</span><br />
<span style="font-size: large;">That's the easy and well documented part.</span><br />
<span style="font-size: large;">A sample of configuration for that kind of logstash:</span><br />
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;"><br /></span>
<pre style="background: rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><span style="font-size: large;"> input {
http {
host => "127.0.0.1" # default: 0.0.0.0
port => 443
keystore=>"/home/user/keystore.jks"
keystore_password => "123pass"
ssl => true
password => "my_basic_auth_pass"
user => "my_basic_auth_user"
}
}
</span></code></pre>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><u style="font-weight: bold;">Path #5: </u>Logstash output (Epilogstash)</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">So we needed the data to get to S3 and amazon Kinesis.</span><br />
<span style="font-size: large;">Logstash, as the component that gets the data via https gives us lots of flexibility.</span><br />
<span style="font-size: large;">It supports S3 out of the box. The problem is that it doesn't support S3 server side encryption. So it wasn't good for us.</span><br />
<span style="font-size: large;">There is a github project for Kinesis output. We preferred not using that.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">The temporary solution is to wrtie to files with logstash,</span><br />
<span style="font-size: large;">and then reading the files with python script and sending to s3 using the aws cli.</span><br />
<span style="font-size: large;">Kinesis has a nice agent that is able to consume a directory and to send new lines to kinesis.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">That's it. Good luck!</span><br />
<br /></div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-46979367798615834442016-02-18T01:19:00.002-08:002016-02-18T01:19:49.830-08:00Too many HDFS blocks!<span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Don't underestimate the number of blocks on your cluster.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px; margin-top: 0px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Well we had a feeling that the cluster became heavier and slower since we passed the 700 K blocks per node threshold, but things kept working and we have been waited for the right time to fix it. Until the day the Namenode didn't start up because of JVM pause.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">It all started with a human mistake that ended with hdfs restarting. The active namenode and the standby, in an unexpected behavior, were both in standby state and went down after several minutes. Our automatic failover is disabled and the manual failover didn't work out. We tried many combinations of restarting and stopping of the 2 namenodes, but the error log showed that the namenode (the one we wanted to activate) can't tell at what state it is (standby or active). Weird. </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">We enabled the automatic failover controller. Then we saw at its logs that it can't figure out the namenode service id ip (the one who should be active). Then we saw that the namenode service id that the failover controller mentioned is different than what we see on the hdfs-site.xml (at dfs.namenode.rpc-address confs) or in the zookeeper's server confs. Weird.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">We didn't really know where this id comes from so we tried restarting the zookeeper that is responsible for the hadoop-ha, playing with the zookkeper /hadoop-ha directory,and deploying configurations, but nothing helped. </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">We decided to disable high availability. We installed a secondary namenode and started the hdfs. The namenode got started! But then it crushed! We saw this message: Detected</span><b style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">pause</b><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> in </span><b style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">JVM</b><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> or host machine (eg GC). This is bad.. what should we do? </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">We decided to go back to high availability mode. It all went good and at this time, there was an elected active namenode and a standby namenode. I guess the configurations got fixed somehow. The problem was that the jvm pause was still happening. </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Nimrod (</span><span class="qlink_container" style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"><a class="external_link" href="https://www.linkedin.com/profile/view?id=241096864" rel="noreferrer" style="background-attachment: initial; background-clip: initial; background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20xmlns%3Asketch%3D%22http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%2Fns%22%3E%0A%20%20%20%20%3C!--%20Generator%3A%20Sketch%203.5.2%20(25235)%20-%20http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%20--%3E%0A%20%20%20%20%3Ctitle%3Eexternal_link%3C%2Ftitle%3E%0A%20%20%20%20%3Cdesc%3ECreated%20with%20Sketch.%3C%2Fdesc%3E%0A%20%20%20%20%3Cdefs%3E%3C%2Fdefs%3E%0A%20%20%20%20%3Cg%20id%3D%22Page-1%22%20stroke%3D%22none%22%20stroke-width%3D%221%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%20sketch%3Atype%3D%22MSPage%22%3E%0A%20%20%20%20%20%20%20%20%3Cg%20id%3D%22external_link%22%20sketch%3Atype%3D%22MSLayerGroup%22%20fill%3D%22%23ccc%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cpath%20d%3D%22M10%2C8%20C9.447%2C8%209%2C8.448%209%2C9%20L9%2C12%20L2%2C12%20L2%2C5%20L5%2C5%20C5.553%2C5%206%2C4.552%206%2C4%20C6%2C3.448%205.553%2C3%205%2C3%20L1%2C3%20C0.447%2C3%200%2C3.448%200%2C4%20L0%2C13%20C0%2C13.552%200.447%2C14%201%2C14%20L10%2C14%20C10.553%2C14%2011%2C13.552%2011%2C13%20L11%2C9%20C11%2C8.448%2010.553%2C8%2010%2C8%20L10%2C8%20Z%22%20id%3D%22Shape%22%20sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cpath%20d%3D%22M13%2C0%20L8%2C0%20C7.447%2C0%207%2C0.448%207%2C1%20C7%2C1.552%207.447%2C2%208%2C2%20L10.586%2C2%20L4.293%2C8.293%20C3.902%2C8.684%203.902%2C9.316%204.293%2C9.707%20C4.488%2C9.902%204.744%2C10%205%2C10%20C5.256%2C10%205.512%2C9.902%205.707%2C9.707%20L12%2C3.414%20L12%2C6%20C12%2C6.552%2012.447%2C7%2013%2C7%20C13.553%2C7%2014%2C6.552%2014%2C6%20L14%2C1%20C14%2C0.448%2013.553%2C0%2013%2C0%20L13%2C0%20Z%22%20id%3D%22Shape%22%20sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E%0A%20%20%20%20%20%20%20%20%3C%2Fg%3E%0A%20%20%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E'); background-origin: initial; background-position: 100% 0.25em; background-repeat: no-repeat; background-size: 0.7em; color: #2b6dad; padding-right: 1em; text-decoration: none;" target="_blank">https://www.linkedin.com<wbr></wbr>/profile...</a></span><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">), suggested increasing the namenode jvm heap size. We found the configuration on the cloudera manager and saw a warning about a ratio between the number of blocks and the namenode heap size: 1 G per 1 M blocks. We had 6 M blocks, and 4 GB! After increasing the heap to 12 GB, the namenode got started and stayed up. Victory :) </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">There are many unsolved questions about what happend. Why couldn’t the namenode determined its state, and why the zookeeper, at the stage we moved to automatic failover, was not able to elect an active namenode. Why did the failover controller tried communicate with wrong namenode service id. We will look into the hdfs core-site.xml that we didn't check during the problems and will read more about the failover process (who elects the active in a manul state, where did the zookeeper took the namenodes ids).</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px; margin-bottom: 0px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">But there is 1 thing we are certain about and it's the potential disaster of the too many blocks alerts.</span>moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-83576458562572849022016-02-18T01:12:00.000-08:002016-02-18T01:12:07.357-08:00A Property graph over Elasticsearch?<span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">I would like to share with you a very interesting architectural argument that is happening on my company.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">It all started when Datastax acquired Titan (by thinkaurelius). Titan is a graph database that sits on top of Cassandra, Hbase or berkeley db.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Till then, we used Titan as our property graph that allowed us integrate lots of entities with a schema that kept changing. We were able to index edges and vertices properties in order to get them by filtering on one of their properties, and in addition we could travel from the vertex to its neighbors in a O(1) time.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"> We didn't really need to ask graph oriented questions such as finding central vertices.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">We liked titan for being scalable and even though we used Hbase, we were happy with it.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">So, what have been changed ? </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">We had lots of plans to use Titan in many upcoming projects, and the Datastax announcement brought us to realize that Titan will stay behind and won't get developed from now on. </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">As an enterprise, that wished titan to get to its stable version and offer support, we ere very disappointed.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">In the meanwhile, some guys started playing with Elasticsearch as a property graph. they even implemented a Thinkerpop API for Elasticsearch - and after some benchmarks we found out that the performances of the Elasticsearch were significantly better than the Titan performances on many use cases.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Oh, and i didn't mention that we also need some text search capabilities, that are part of Elasticsearch, but when using Titan, it is only possible with an Elasticsearch cluster that gets its data from Titan, without having the possibility to configure the Elasticsearch index in a custom and more optimized ways.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">A very interesting argument started on a the mail.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Some said that we should keep using Titan cause it is working, and use Elasticsearch only as the Titan's text search store. Datastax announced that it would be simple to move from titan to their property graph and when reaching this bridge, we would just move to Datastax DSE grpah db. we are not talking about other graph dbs cause we are not familiar with a scalable and stable graph db that is better than Titan (OrientDB for instance).</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Other said that we must try Elasticsearch as our property graph, and keep develop Thinkerpop above it (mostly for being able to write in Gremlin). It is worth trying cause we had good benchmarks and it is better to use one back-end than two (Titan and Elastic).</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">On the other hand, there were voices that eliminated Elasticsearch and were very upset with the idea of threat it as a property graph. They said that in its nature it won't be able to support the type of indexing, masses and continues online writes that our property graph requires "Elasticsearch is not a Back End!". And in addition, why should we take care of a new thnikerpop implementation.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">For summary I would say that property graph (RDF also, but that's for another talk) is a great data model for data that keep changing and help us make the data available and findable while avoiding hundreds of relational tables. </span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">I think that while having a working Titan property graph cluster, we should keep using it in other projects mainly because of the experience with it. I don't think that having 2 back ends (Titan and elastic) for 1 app is wrong, while using each back properly.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">Elasticsearch has gone through a long way since its beginning. Many databases are now offering more than only "document store" or "key-value store" even if that is their nature. You can find Quora questions about [</span><span class="qlink_container" style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;"><a href="https://www.quora.com/Why-shouldnt-I-use-Elasticsearch-as-my-primary-datastore" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #2b6dad; text-decoration: none;">Why should I NOT use ElasticSearch as my primary datastore</a></span><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">]. That is a good question that scientifically might be answered as a big NO but down there on the tech teams, it might work.</span><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><br style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;" /><span style="color: #333333; font-family: Georgia, Times, 'Times New Roman', serif; font-size: 15px; line-height: 21px;">So, in case The Titan + Elastic won't work for us, trying Elastic is probably the next thing to do.</span>moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-11120495661331249922016-02-15T04:16:00.002-08:002016-02-16T17:51:33.752-08:00Sentiment Analysis with Spark streaming (Twitter Stream) and databricks/spark-coreNLPHi<br />
I want to share a piece of code that I a have written, not long ago, and that might be good base for a nice Sentiment Analysis with spark streaming.<br />
<br />
The example is going to analyse twits stream, despite the fact that the CoreNlp was trained with movies reviews, what makes that sentiment analysis less accurate.<br />
<br />
You can train the algorithm by yourself but that's for another post :)<br />
<br />
So - spark 1.5.2, scala 2.10, <span style="background-color: white; color: #333333; font-family: "helvetica neue" , "helvetica" , "segoe ui" , "arial" , "freesans" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 16px; line-height: 25.6000003814697px;">CoreNLP 3.6.0, protobuf-java 2.6.1, java 1.8</span><br />
<br />
Fortunately, coreNlp is available as dependency since Jan 16,<br />
<br />
<a href="https://github.com/databricks/spark-corenlp">Databricks spark core nlp wrapper </a>- working with coreNLP 3.6:<br />
<br />
The guys from data bricks are using SparkSql in order to wrap coreNlp. Interesting.<br />
Take a look at the git hub page of the project, in order to understand a bit more.<br />
<br />
For twitter:<br />
you must log in to twitter public streams<br />
https://dev.twitter.com/streaming/public<br />
<br />
you need to create your own application and get the id and access tokens.<br />
<br />
Now back to spark<br />
<br />
sbt.build:<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: "andale mono" , "lucida console" , "monaco" , "fixed" , monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"> <code style="color: black; word-wrap: normal;">
libraryDependencies ++= Seq(
"edu.stanford.nlp" % "stanford-corenlp" % "3.6.0",
"com.google.protobuf" % "protobuf-java" % "2.6.1",
"org.apache.spark" %% "spark-core" % "1.5.2" % "provided",
"org.apache.spark" %% "spark-streaming" % "1.5.2" % "provided",
"org.apache.spark" %% "spark-streaming-twitter" % "1.5.2" % "provided",,
"org.apache.spark" % "spark-mllib_2.10" % "1.5.2" % "provided",
"org.twitter4j" % "twitter4j-core" % "3.0.3",
"org.twitter4j" % "twitter4j-stream" % "3.0.3")
</code></pre>
<br />
Building you spark with protobuf 2.6.1 (comes with 2.4 by default) - that was really disappointing. I had to build it by myself, waste time and replace the server's jars.<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: "andale mono" , "lucida console" , "monaco" , "fixed" , monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"> <code style="color: black; word-wrap: normal;">
mvn -Phadoop-2.6 -Dhadoop.version=2.6.0 -Dprotobuf.version=2.6.1 -DskipTests clean package
</code></pre>
<br />
I copied Databrick's code to my local project. There are 2 files:
<br />
<a class="js-directory-link js-navigation-open" href="https://github.com/databricks/spark-corenlp/blob/master/src/main/scala/com/databricks/spark/corenlp/StanfordCoreNLPWrapper.scala" id="0c95180aa6197631d1dee693172c4c61-febd0c4f3dc2675d710915bacd61ec0192b86b32" style="background-color: #eeeeee; box-sizing: border-box; color: #4078c0; font-family: Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; line-height: 20px; text-decoration: none; white-space: nowrap;" title="StanfordCoreNLPWrapper.scala">StanfordCoreNLPWrapper.scala</a><br />
<a class="js-directory-link js-navigation-open" href="https://github.com/databricks/spark-corenlp/blob/master/src/main/scala/com/databricks/spark/corenlp/CoreNLP.scala" id="7f0eaef4d9dd3e0727808969a804daa5-58d6525df690c46f4242d437454039a2d56259ce" style="background-color: whitesmoke; box-sizing: border-box; color: #4078c0; font-family: Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 13px; line-height: 20px; text-decoration: none; white-space: nowrap;" title="CoreNLP.scala">CoreNLP.scala</a><br />
<br />
And in addition we will create our spark streaming object:<br />
<br />
imports:
<br />
<pre><code><pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); font-family: 'andale mono', 'lucida console', monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 653px;"><code style="word-wrap: normal;"><pre style="line-height: normal;"><code>
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.Column
import org.apache.spark.sql.SQLContext
import org.apache.spark.streaming.api.java.JavaStreamingContext
import org.apache.spark.streaming.twitter._
import org.apache.spark.{rdd, SparkContext, SparkConf}
import org.apache.spark.SparkContext._
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.twitter._
import twitter4j.TwitterFactory
import twitter4j.auth.Authorization</code></pre>
</code></pre>
</code></pre>
<br />
and the code:
<br />
<pre><code><pre><code><pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); overflow: auto; padding: 5px; width: 653px;"><code style="word-wrap: normal;"><pre><pre style="font-family: 'andale mono', 'lucida console', monaco, fixed, monospace; font-size: 12px; line-height: normal;"><code><span style="color: navy; font-family: "dejavu sans mono"; font-size: 9pt; font-weight: bold;">object </span><span style="background-color: #eeeeee; font-family: "dejavu sans mono"; font-size: 9pt;">TwitterStream {</span> </code></pre>
<pre style="font-family: 'andale mono', 'lucida console', monaco, fixed, monospace; font-size: 12px; line-height: normal;"><code> def main( args:Array[String] ):Unit = {
val consumerKey =""
val consumerSecret =""
val accessToken =""
val accessTokenSecret =""
object auth{
val config = new twitter4j.conf.ConfigurationBuilder()
.setOAuthConsumerKey(consumerKey)
.setOAuthConsumerSecret(consumerSecret)
.setOAuthAccessToken(accessToken)
.setOAuthAccessTokenSecret(accessTokenSecret)
.build
}
<span style="font-family: "dejavu sans mono"; font-size: 9pt;"><span style="color: navy; font-weight: bold;">val </span>filters:<span style="color: #20999d;">String</span>=args(<span style="color: blue;">0</span>) //</span><span style="color: #795da3; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace;"><span style="font-size: 13.6000003814697px; line-height: 19.7199993133545px;">filter for twits</span></span>
<span style="color: navy; font-family: "dejavu sans mono"; font-size: 9pt; font-weight: bold; line-height: normal;">val <span style="font-family: "dejavu sans mono";"><span style="font-size: 9pt;">filters_array:Array[</span></span><span style="color: #20999d; font-family: "dejavu sans mono"; font-size: 9pt; line-height: normal;">String</span><span style="font-family: "dejavu sans mono";"><span style="font-size: 9pt;">]=filters.split(</span></span><span style="color: green; font-family: "dejavu sans mono"; font-size: 9pt; font-weight: bold; line-height: normal;">","</span><span style="font-family: "dejavu sans mono";"><span style="font-size: 9pt;">)</span></span><span style="color: navy; font-family: "dejavu sans mono"; font-size: 9pt; font-weight: bold; line-height: normal;">val </span><span style="font-family: "dejavu sans mono";"><span style="font-size: 9pt;">filter:</span></span><span style="color: #20999d; font-family: "dejavu sans mono"; font-size: 9pt; line-height: normal;">Seq</span><span style="font-family: "dejavu sans mono";"><span style="font-size: 9pt;">[</span></span><span style="color: #20999d; font-family: "dejavu sans mono"; font-size: 9pt; line-height: normal;">String</span><span style="font-family: "dejavu sans mono";"><span style="size: 9pt;">]= filters_array</span></span>
<pre style="background-color: #eeeeee; font-family: 'DejaVu Sans Mono'; font-size: 9pt; line-height: normal;"><pre style="font-family: 'DejaVu Sans Mono'; font-size: 9pt;"><span style="color: navy; font-weight: bold;">val </span>sparkHome = <span style="color: green; font-weight: bold;">"/root/spark"</span><span style="color: green; font-weight: bold;">
</span><span style="color: navy; font-weight: bold;">val </span>checkpointDir = <span style="color: green; font-weight: bold;">"/home/spark/checkpoint/"</span><span style="color: green; font-weight: bold;">
</span><span style="color: navy; font-weight: bold;">val </span>conf = <span style="color: navy; font-weight: bold;">new </span>SparkConf().setAppName(<span style="color: green; font-weight: bold;">"Tutorial"</span>)
<span style="color: navy; font-weight: bold;">val </span>ssc = <span style="color: navy; font-weight: bold;">new </span>StreamingContext(conf, <span style="font-style: italic;">Seconds</span>(<span style="color: blue;">10</span>))</pre>
</pre>
<pre style="font-family: 'andale mono', 'lucida console', monaco, fixed, monospace; font-size: 12px; line-height: normal;"><code><pre style="background-color: #eeeeee; font-family: 'DejaVu Sans Mono'; font-size: 9pt;"><span style="color: navy; font-weight: bold;">val </span>twitter_auth = <span style="color: navy; font-weight: bold;">new </span>TwitterFactory(auth.<span style="color: #660e7a; font-style: italic;">config</span>)
<span style="color: navy; font-weight: bold;">val </span>a = <span style="color: navy; font-weight: bold;">new </span>twitter4j.auth.OAuthAuthorization(auth.<span style="color: #660e7a; font-style: italic;">config</span>)
<span style="color: navy; font-weight: bold;">val </span>atwitter : Option[twitter4j.auth.Authorization] = <span style="font-style: italic;">Some</span>(twitter_auth.getInstance(a).getAuthorization())
<span style="color: navy; font-weight: bold;">val </span>tweets = TwitterUtils.<span style="font-style: italic;">createStream</span>(ssc, atwitter, filter, StorageLevel.<span style="color: #660e7a; font-style: italic;">DISK_ONLY_2</span>)</pre>
<pre style="background-color: #eeeeee; font-family: 'DejaVu Sans Mono'; font-size: 9pt;"><span style="color: navy; font-weight: bold;">val </span>twText = tweets.map(x=>x.getText).map(x=>x.replaceAll(<span style="color: green; font-weight: bold;">"/[^A-Za-z0-9 ]/"</span>, <span style="color: green; font-weight: bold;">""</span>)) </pre>
<pre style="background-color: #eeeeee; font-family: 'DejaVu Sans Mono'; font-size: 9pt;">//I am removing non Alphabetic characters cause the sentiment analysis is not so good with emotions</pre>
<pre style="background-color: #eeeeee; font-family: 'DejaVu Sans Mono'; font-size: 9pt;"><pre style="font-family: 'DejaVu Sans Mono'; font-size: 9pt;">twText.foreachRDD(rdd=> {
<span style="color: navy; font-weight: bold;"> val </span>sqlContext = SQLContext.<span style="font-style: italic;">getOrCreate</span>(rdd.sparkContext)
<span style="color: navy; font-weight: bold;">import </span>sqlContext.implicits._
<span style="color: grey; font-style: italic;"> </span><span style="color: navy; font-weight: bold;">val </span>fields:Array[<span style="color: #20999d;">String</span>] = args(<span style="color: blue;">1</span>).split(<span style="color: green; font-weight: bold;">","</span>) //"text.token" for instance, according to the json schema of coreNLP
<span style="color: navy; font-weight: bold;">val </span>annots:Array[<span style="color: #20999d;">String</span>]=args(<span style="color: blue;">2</span>).split(<span style="color: green; font-weight: bold;">","</span>) //<span class="pl-s" style="box-sizing: border-box; color: #183691; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; font-size: 13.6000003814697px; line-height: 1.45;"><span class="pl-pds" style="box-sizing: border-box;">"</span>tokenize<span class="pl-pds" style="box-sizing: border-box;">"</span></span><span style="background-color: #f7f7f7; color: #333333; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; font-size: 13.6000003814697px; line-height: 1.45;">,</span><span class="pl-s" style="box-sizing: border-box; color: #183691; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; font-size: 13.6000003814697px; line-height: 1.45;"><span class="pl-pds" style="box-sizing: border-box;">"</span>cleanxml<span class="pl-pds" style="box-sizing: border-box;">"</span></span><span style="background-color: #f7f7f7; color: #333333; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; font-size: 13.6000003814697px; line-height: 1.45;">,</span><span class="pl-s" style="box-sizing: border-box; color: #183691; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; font-size: 13.6000003814697px; line-height: 1.45;"><span class="pl-pds" style="box-sizing: border-box;">"</span>ssplit<span class="pl-pds" style="box-sizing: border-box;">"</span></span><span style="background-color: #f7f7f7; color: #333333; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; font-size: 13.6000003814697px; line-height: 1.45;"> for instance</span>
<span style="color: navy; font-weight: bold;">var </span>select_fields:<span style="color: #20999d;">List</span>[Column]= <span style="color: #660e7a; font-style: italic;">List</span>()
fields.foreach(x=></pre>
<pre style="font-family: 'DejaVu Sans Mono'; font-size: 9pt;"> {
<span style="color: navy; font-weight: bold;">val </span>c:Column=<span style="color: navy; font-weight: bold;">new </span>Column(<span style="color: green; font-weight: bold;">"parsed."</span>+x)
select_fields=c::select_fields
}
)
<span style="color: navy; font-weight: bold;">val </span>coreNLP = <span style="color: navy; font-weight: bold;">new </span>CoreNLP()
.setInputCol(<span style="color: green; font-weight: bold;">"text"</span>)
.setAnnotators(annots)
.setFlattenNestedFields(fields)
.setOutputCol(<span style="color: green; font-weight: bold;">"parsed"</span>)
<span style="color: navy; font-weight: bold;">val </span>input = rdd.map(k=>(<span style="color: blue;">1</span>,k)).toDF(<span style="color: green; font-weight: bold;">"id"</span>, <span style="color: green; font-weight: bold;">"text"</span>)
<span style="color: navy; font-weight: bold;">if</span>(input.count()><span style="color: blue;">0</span>) {
<span style="color: navy; font-weight: bold;">val </span>parsed = coreNLP.transform(input)
.select(select_fields:_*)</pre>
<pre style="font-family: 'DejaVu Sans Mono'; font-size: 9pt;"></pre>
<pre style="font-family: 'DejaVu Sans Mono'; font-size: 9pt;">//printing results!
parsed.foreach(<span style="font-style: italic;">println</span>)
<span style="color: grey; font-style: italic;"> </span>}</pre>
</pre>
</code></pre>
<pre style="font-family: 'andale mono', 'lucida console', monaco, fixed, monospace; font-size: 12px; line-height: normal;"><code> }</code></pre>
}
)
<pre style="background-color: #eeeeee; font-family: 'DejaVu Sans Mono'; font-size: 9pt;"><pre style="font-family: 'DejaVu Sans Mono'; font-size: 9pt;"> ssc.start()
ssc.awaitTermination()
}
}</pre>
</pre>
</span></code></pre>
</pre>
</code></pre>
That should work. This is very fun and quite short. </code></pre>
<pre><code>Run the jar with 3 input parameters: </code></pre>
<pre><code>twitter filters strings, coreNlp json fields, Annotations strings</code></pre>
<pre><code>I hope that it would help you building twitter stream,</code></pre>
and in addition to do some cool stuff with it.
Good luck!
</code></pre>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-49380248867593932152015-12-03T15:28:00.002-08:002015-12-17T14:20:11.081-08:00Creating a standalone HiveMetastore (Not in Hadoop cluster)When benchmarking Presto database on top of S3 files, I found out that I have to install a Hive metastore instance.<br />
<br />
<img alt="Image result for lonely bee" src="data:image/jpeg;base64,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" /><br />
<br />
(A standalone bee)<br />
<br />
I didn't need HiveServer, Mapreduce, or Hadoop cluster. So how do you do that?<br />
<br />
Here are the steps:<br />
<br />
<br />
<ol>
<li><span style="font-stretch: normal; text-indent: -18pt;"><span style="text-indent: 0px;">Install Hive Metastore Repository - an instance of one of the dbs that hive metastore works with (MySql, PostgresSql, MsSql, Oracle .. check documentation)</span></span></li>
<li><span style="font-stretch: normal; text-indent: -18pt;">Install Java</span></li>
<li><span style="font-stretch: normal; text-indent: -18pt;"> </span><span style="text-indent: -18pt;">Download Vanilla Hadoop </span><a href="http://hadoop.apache.org/releases.html" style="text-indent: -18pt;">http://hadoop.apache.org/releases.html</a><span style="text-indent: -18pt;"> and unpack on the hive metastore instance (let's say that you unpacked to /apps/hadoop-2.6.2)</span></li>
<li><span style="text-indent: -18pt;">Set environment variables :</span></li>
<ol>
<li><span style="font-stretch: normal; text-indent: -18pt;"> </span><span style="text-indent: -18pt;">export HADOOP_PREFIX=/apps/hadoop-2.6.2</span></li>
<li><span style="font-stretch: normal; text-indent: -18pt;"> </span><span style="text-indent: -18pt;">export HADOOP_USER_CLASSPATH_FIRST=true</span></li>
</ol>
<li>Download hive <span style="font-family: "calibri" , sans-serif; line-height: 16.8667px;"><a href="http://www.apache.org/dyn/closer.cgi/hive/">http://www.apache.org/dyn/closer.cgi/hive/</a> and upack on you instace</span></li>
<li><span style="text-indent: -18pt;">Create a schema (user) for hive user and build the hive schema in the the hive metastore repository db using hive scripts (a sample script for mysql): </span></li>
<ol>
<li><span style="font-stretch: normal; text-indent: -18pt;"> </span><span style="font-family: "calibri" , sans-serif; line-height: 16.8667px; text-indent: -18pt;">/apps/apache-hive-1.2.1-bin/scripts/metastore/upgrade/mysql/hive-schema-1.2.0.mysql.sql</span></li>
</ol>
<li><span style="text-indent: -18pt;">configure hive-site.xml with the right parameters for:</span></li>
<ol>
<li><span style="text-indent: -18pt;">ConnectionUrl (</span>jdbc:mysql://localhost:3666/hive for example)</li>
<li>ConnectionDriverName </li>
<li><span style="text-indent: -18pt;">ConnectionUserName (the created database user)</span></li>
<li><span style="text-indent: -18pt;">ConnectionPassword </span><span style="text-indent: -24px;">(the created database user password)</span></li>
<li><span style="text-indent: -24px;">hive.metastore.warehouse.dir - set it to a local path (</span>file:///home/presto/ for example)</li>
</ol>
<li>Copy the required jar for jdbc connection to the metastore repository in the hive class path. (Ojdbc6 for oracle, mysql-jdbc-connector for mysql and so on)</li>
<li>Start hive metastore - <span style="text-indent: -18pt;"> </span><span style="text-indent: -18pt;">/apps/apache-hive-1.2.1-bin/bin/hive
--service metastore</span><div class="MsoListParagraph" style="margin-left: 72.0pt; mso-add-space: auto; mso-list: l0 level2 lfo1; text-indent: -18.0pt;">
<o:p></o:p></div>
</li>
<li><span style="text-indent: -18pt;">For accessing S3:</span></li>
<ol>
<li><span style="text-indent: -18pt;">copy these jars to the classpath:</span></li>
<ol>
<li>aws-java-sdk-1.6.6.jar (http://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk/1.6.6)</li>
<li>hadoop-aws-2.6.0.jar (http://central.maven.org/maven2/org/apache/hadoop/hadoop-aws/2.6.0/hadoop-aws-2.6.0.jar)</li>
</ol>
<li><span style="text-indent: -18pt;"> you can specify these parameters on the hadoop core-site.xml</span></li>
<ol>
<li><span style="text-indent: -18pt;"><span style="text-indent: 0px;">fs.s3.awsAccessKeyId</span></span></li>
<li><span style="text-indent: -18pt;"><span style="text-indent: 0px;">fs.s3.awsSecretAccessKey</span></span></li>
<li><span style="font-family: "arial" , sans-serif;"><span style="background-color: white; line-height: 14.56px;"><span style="color: #333333; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; line-height: 16.8px; white-space: pre;"><span style="line-height: 16.8px;"><pre class="code-java" style="color: black; font-family: 'Courier New', Courier, monospace; font-size: 11.3999996185303px; line-height: 1.3; overflow: auto; padding: 0px;"><property>
<name>fs.s3n.impl</name>
<value>org.apache.hadoop.fs.s3native.NativeS3FileSystem</value>
</property></pre>
</span></span></span></span></li>
</ol>
<li>for secured access to s3, use S3A connection in your URL, </li>
<ol>
<li>add <span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">fs</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">.</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">s3a</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">.</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">connection</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">.</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">ssl</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">.</span><span style="background-color: white; font-family: "arial" , sans-serif; line-height: 14.56px;">enabled to haddop_home/etc/hadoop/core-site.xml</span></li>
<li><span style="font-family: "arial" , sans-serif;"><span style="background-color: white; line-height: 14.56px;">you also need to set these parameters for s3 access in the hadoop core-site.xml file:</span></span></li>
<ol>
<li><span style="font-family: "arial" , sans-serif;"><span style="background-color: white; line-height: 14.56px;"><span style="color: #333333; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; line-height: 16.8px; white-space: pre;">fs.s3a.secret.key</span></span></span></li>
<li><span style="font-family: "arial" , sans-serif;"><span style="background-color: white; line-height: 14.56px;"><span style="color: #333333; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace; line-height: 16.8px; white-space: pre;"><span style="line-height: 16.8px;">fs.s3a.access.key</span></span></span></span></li>
</ol>
<li><span style="color: #333333; font-family: "consolas" , "liberation mono" , "menlo" , "courier" , monospace;"><span style="line-height: 16.8px; white-space: pre;">Unfortunately, There is no support currently for temporary S3 credentials</span></span></li>
</ol>
</ol>
</ol>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Finally, when running presto, we will use the thrift address and port of the hive metastore service.</div>
<div>
<br /></div>
If you are running EMR from time to time, you are able to use that external metastore repository according to <a href="http://docs.aws.amazon.com/ElasticMapReduce/latest/DeveloperGuide/emr-dev-create-metastore-outside.html">AWS documentation</a><br />
<div>
<br /></div>
<div>
That's it. No need for additional Hadoop libraries or settings.</div>
<div>
Good luck!</div>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-88986251095365355442015-12-01T03:23:00.003-08:002015-12-01T03:48:57.425-08:00Secured ELK (including filebeat)It is not so difficult to start an ELK topology with an application server that forwards logs to a logstash server and a logstash server that sends logs in the right schema to Elastisearch.<br />
<br />
Then, it's also quite straight forward to start an Elasticserach cluster with Kibana for some visualizations.<br />
<br />
The problem started for us when we wanted to secure the whole pipeline - from the app server to the client through Logstash, Elasticsearch and Kibana.<br />
<br />
We also got to know filebeat tand we almost immediately felt in love.<br />
<br />
I will explain here, step by step, the whole installation and deploying process that we have gone through. All the configuration files are attached. Enjoy.<br />
<br />
General Architecture:<br />
<div>
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEir90qSWG2afzT4caV10ysueY-H30ybyxZGILHAKVfXQSxEoYsh8fKq1FmMvIST1wis2FjjCxamlZd8UZiXv4T-0u7a3DLRTRtpMoYal5isDOzOy-n0b9cIyZ-sjnG1gF30v2BoB-ThqLk/s1600/Untitled.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEir90qSWG2afzT4caV10ysueY-H30ybyxZGILHAKVfXQSxEoYsh8fKq1FmMvIST1wis2FjjCxamlZd8UZiXv4T-0u7a3DLRTRtpMoYal5isDOzOy-n0b9cIyZ-sjnG1gF30v2BoB-ThqLk/s1600/Untitled.png" /></a><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br /></div>
<br />
<ol>
<li><b><span style="font-size: large;">Components:</span></b></li>
<ol>
<li><a href="https://www.elastic.co/products/beats/filebeat">Filebeat</a> - An evolution of the old forwarder. Light and an easy to use tool for sending data from log files to Logstash.</li>
<li><a href="https://www.elastic.co/products/logstash">Logstash</a> - A ready to use tool for sending logs data to Elasticsearch. It supports many other outputs, inputs and manipulations of its input log records. I will show the integration with filebeat as input and Elasticsearch and s3 as output.</li>
<li>Elaticsearch - Our back-end for storing and indexing the logs</li>
<li>Kibana - The visualization tool on top of elasticsearch</li>
</ol>
<li><b><span style="font-size: large;">Filebeat agent installation (talking with HTTPS to logstash)</span></b></li>
<ol>
<li>As for the project time, the newest version of filebeat (1.0.0 -rc1) had a bug when sending data to logstash. It's automatically creating the "@timestamp" field, which also get created by logstash, and makes it fail. We worked with <a href="https://www.elastic.co/downloads/past-releases/packetbeat-1-0-0-beta4">1.0.0 -beta4</a> version (the first one) of filebeat.</li>
<li><a href="https://drive.google.com/file/d/0B7C0igwWj_KAX1MwQ0xqSU9Hems/view?usp=sharing">configuration file</a></li>
<ol>
<li>Take a look at the Logstash as output scope.</li>
<li> We want to make filebeat trust logstash server. If you signed the certificate that logstash is using with some CA, you can give filebeat a certificate that is signed by that CA. Configuration - "certificate_authorities"</li>
<li>If you didn't use a sign certificate for the logstash server, you can use the certificate and the public key of logstash server (I'll explain later how to create them) as demonstrated in the configuration file</li>
<li>Keep in mind that we don't use java keystores for that filebeat - logstash connection.</li>
</ol>
<li>Starting command: <i>filebeat_location/filebeat -v -c filebeat_location/filebeat.yml</i><div class="MsoNormal">
<o:p></o:p></div>
</li>
</ol>
<li><b><span style="font-size: large;">Logstash (Https communication to Elasticsearch)</span></b></li>
<ol>
<li>Download and unpack</li>
<li>generate certificate:</li>
<ol>
<li><div class="MsoNormal">
openssl req -new -text -out server.req</div>
</li>
<li><div class="MsoNormal">
###When asking for common name, enter the server's ip : --Common Name (eg, your name or your server's hostname) []:10.184.2.232</div>
</li>
<li><div class="MsoNormal">
openssl rsa -in privkey.pem -out server.key</div>
</li>
<li> rm privkey.pem<br /><o:p></o:p></li>
<li><div class="MsoNormal">
sign the req file with the CA certificate</div>
</li>
</ol>
<li><a href="https://drive.google.com/file/d/0B7C0igwWj_KAdjVFZEYyX3NUNTQ/view?usp=sharing">Logstash configuration</a></li>
<ol>
<li>The input is configured to get data from filebeat</li>
<li>In the Filer and groke scope:</li>
<ol>
<li> we are creating the json documents out of the "message" field that we get from filebeat.</li>
<li>duplicating our "msg" field. We call it msg_tokenized - that's important for Elasticsearch later on. From one hand, we want to search the logs so we save it as an analyzed field ("msg") but we also want to visualize the logs so we also save the "msg" as an unanalysed field (the whole messages). I will explain later how to set an Elasticsearch template for that. </li>
<li>creating a new field with the file_name of the source log file</li>
<li>removing the "logtime" field</li>
<li>removing the "message" field</li>
</ol>
<li>Output scope:</li>
<ol>
<li> S3 and Elasticsearch outputs</li>
<li>For ssl between Logstash and Elasticsearch, you can use the CA name in the cacert configuration under elasticsearch output.</li>
<li>If we specify a new name for our index in the logstash elasticsearch output, the index would automatically get created. We decided to create a new index for each log file and then we created an abstraction layer for all application logs with Kibana with queries on top of couple of indices.</li>
<li>The s3 bucket path is not dynamic currently</li>
</ol>
</ol>
<li><b><span style="font-size: large;">Elasticseach (open for HTTPS only)</span></b></li>
<ol>
<li>Download Elasticsearch, and Shield (<a href="https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/shield/2.0.0/shield-2.0.0.zip">shield</a>, <a href="https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/license/2.0.0/license-2.0.0.zip">license</a>)</li>
<li>install the 2 addons for shield:</li>
<ol>
<li>bin/plugin install file:///path/to/file/license-2.0.0.zip</li>
<li>bin/plugin install file:///path/to/file/shield-2.0.0.zip</li>
</ol>
<li>Create Certificate for elasticsearch<o:p></o:p></li>
<ol>
<li>openssl req -new -text -out server.req</li>
<li><div class="MsoNormal">
<span style="font-family: inherit;">openssl rsa -in privkey.pem -out server.key<o:p></o:p></span></div>
</li>
<li><div class="MsoNormal">
<span style="font-family: inherit;">rm privkey.pem<o:p></o:p></span></div>
</li>
<li><div class="MsoNormal">
<span style="font-family: inherit;">sign the req file (organisation ca sign) and save the signed certificate as signed.cer</span></div>
</li>
<li><div class="MsoNormal">
<i>Create a java keystore with the sign ertificate and the private key (2 steps)</i></div>
</li>
<ol>
<li>
openssl pkcs12 -export -name myservercert -in signed.cer -inkey server.key -out keystore.p12<o:p></o:p>
</li>
<li><div class="MsoListParagraph">
<span style="font-family: inherit;">keytool -importkeystore -destkeystore node01.jks -srckeystore keystore.p12 -srcstoretype pkcs12 -alias myservercert<o:p></o:p></span></div>
</li>
</ol>
<li><span style="font-family: inherit;"><span style="font-family: inherit;">If you are not using a signing CA you can use that command in roder to create the keystore: keytool -genkey -alias node01_s -keystore node01.jks -keyalg RSA -keysize 2048 -validity 712 -ext </span>san=ip:10.184.2.238</span></li>
</ol>
<li><a href="https://drive.google.com/file/d/0B7C0igwWj_KAR2hJNmNUUnROZkE/view?usp=sharing"><span style="font-family: inherit;">Configuration File</span></a></li>
<ol>
<li><span style="font-family: inherit;"><span style="font-family: inherit;">Take a look at the security shield options. We are using the local java keystore that holds our sig</span>ned certificate and the priv</span>ate key</li>
<ol>
</ol>
</ol>
<li>Start Elasticsearch: /apps/elasticsearch-2.0.0/bin/elasticsearch</li>
<li>Add user: <span style="font-family: "calibri" , "sans-serif"; font-size: 11.0pt; line-height: 115%; mso-ansi-language: EN-AU; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">elasticsearch/bin/esusers useradd alog -p alog123 -r admin</span></li>
<ol>
</ol>
<li><span style="font-size: 11pt; line-height: 115%;">Add Template for elasticsearch - We want all the indexes that are created by logstash to contain both "msg" field for search and "msg" field for visualization. The template would help us get one </span><span style="font-size: 14.6667px; line-height: 16.8667px;">analyse field and one that is not.</span></li>
<ol>
<li><span style="font-family: "calibri" , sans-serif;"><span style="font-size: 11pt; line-height: 115%;"><div class="MsoNormal">
curl -XPUT https://alog:alog123@elastic-server:9200/_template/template_1 -d '<o:p></o:p></div>
<div class="MsoNormal">
{<o:p></o:p></div>
<div class="MsoNormal">
"template" : "alog-*",<o:p></o:p></div>
<div class="MsoNormal">
"mappings" : {<o:p></o:p></div>
<div class="MsoNormal">
"_default_" : {<o:p></o:p></div>
<div class="MsoNormal">
"properties": {<o:p></o:p></div>
<div class="MsoNormal">
"msg":{"type":"string", "index" : "not_analyzed"}}<o:p></o:p></div>
<div class="MsoNormal">
}<o:p></o:p></div>
<div class="MsoNormal">
}<o:p></o:p></div>
<div class="MsoNormal">
<o:p></o:p></div>
<span style="font-size: 11pt; line-height: 115%;">}'</span></span></span></li>
<li><span style="font-family: "calibri" , sans-serif;"><span style="font-size: 11pt; line-height: 115%;"> This template will catch for all indices that start with "alog" in their name.</span></span></li>
</ol>
</ol>
<li><b style="font-family: calibri, sans-serif; line-height: 16.8667px;"><span style="font-size: large;">Kibana (HTTPS communication to Elasticsearch, users logon via HTTPS )</span></b></li>
<ol>
<ol>
<li><span style="font-family: "calibri" , sans-serif;"><span style="font-size: 14.6667px; line-height: 16.8667px;">Download and unpack</span></span></li>
<li><span style="font-family: "calibri" , sans-serif;"><span style="font-size: 14.6667px; line-height: 16.8667px;">Start command: </span></span>bin/kibana<div class="MsoNormal">
<o:p></o:p></div>
</li>
<li><a href="https://drive.google.com/file/d/0B7C0igwWj_KAaGxqNllzT3hoWDA/view?usp=sharing">Configuration</a></li>
<ol>
<li>In order to enable https connection of users we create another pair of certificate and key with openssl tool and set the certificate and key in the configuration</li>
<li>We set the elasticsearch.username and password for Shield authentication</li>
<li>We didn't supply Kibana the Elasticsearch certificate and key becuase we used a signed certificate</li>
</ol>
</ol>
</ol>
</ol>
</ol>
<div>
<br /></div>
<div>
And that's it :)</div>
<div>
I hope that all of the above would help you go through some of the obstacles that we encountered, whether with the SSL setup, with Groke command or with anything else.</div>
<div>
<br /></div>
<div>
Good Luck !</div>
<div>
</div>
<ol><ol><ol><ol>
</ol>
</ol>
</ol>
</ol>
moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-81284549314372793402015-10-18T21:36:00.001-07:002015-10-18T21:49:56.448-07:00Amazon Quick Insight<h3>
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">Amazon QuickSight - a new BI platform from amazon (and some related stuff that jumped to my mind while reading about it)</span></h3>
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">It was<b> last year</b> that one of the hottest trend for BI was<b> Elasticsearch + Kibana.</b></span><br />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">A great Database with a great visualization tool on top of it. Pretty close to what Splunk does.</span><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">You could hear everywhere things like "you just throw inside all of your data and can query and visualize it in any way you wish".</span><br />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">Managers went after that as they were hypnotized. It was revolutionary. </span><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;"><b>But it didn't last long.</b> People started to get that it is cool and nice to get your data sliced and diced so fast and in such beautiful graphs, but in many cases it was only nice to have.</span><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">Amazon announced QuickSight last month - </span><a class="linkified" href="https://aws.amazon.com/quicksight/" rel="nofollow noreferrer" style="background-color: white; border: 0px; color: #386cbb; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; font-stretch: inherit; line-height: 20px; margin: 0px; padding: 0px; text-decoration: none; vertical-align: baseline;" target="_blank">https://aws.amazon.com/quicksight/</a><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">For me it is a super cheap, <b>easy and elegant way to implement that model of Elasticsearch + Kibana, </b>in case that you are already on AWS.</span><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">I don't see that as a revolution, but as another classic case of platform that gets migrated to the cloud, which is quite a lot and enabler for businesses that can't develop these kind of platforms by themselves.</span><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #741b47; font-family: Segoe, Helvetica, Arial, sans-serif; line-height: 20px;"><u><b>I also have problem with that name. Insights are result of data model, relations, and semantics that you give your data</b>.</u> You can throw the best ingredients to the best cooking pot and boil it, but it won't get tasty by itself. Some ingredients require special treatment, some are only go with others, some are not for boiling at all, but for baking. </span><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif;"><span style="font-size: 15px; line-height: 20px;">In general, there is no one solution for BI, and that's why platforms as<b> Hadoop and S3</b> are so important for business</span><b style="font-size: 15px; line-height: 20px;">. They are actually a supermarket of data, as my friend Evgeni says. </b><span style="font-size: 15px; line-height: 20px;">Data analysts can then become chefs and cook their own desired and tasty meal, and Amazon quick insight just another recipe.</span></span><br />
<br style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;" />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">Still, I believe that customers that are considering moving to AWS, would get a lot from quick sight. It is one of the worthwhile amazon services to get familiar with because it is more than just a db. It is a while solutions that customers might really really like. </span><br />
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;"><br /></span>
<span style="background-color: white; color: #343a41; font-family: Segoe, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px;">And correct me if I am wrong :)</span>moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-8195550923787644142015-07-01T02:34:00.003-07:002015-07-01T02:38:11.332-07:00Playing with Apache Nifi I decided to play a little with apache NiFi.<br />
<br />
What is NiFi? <br />
<br />
I had some problems with a Flume agent and someone wrote that i should check out NiFi.<br />
<br />
I can't tell that it replaces Flume. Neither replacing any other tool. <br />
According to the official website, it is an easy to use, powerful, and reliable system to process and distribute data.<br />
<br />
Well, too general for me. You can read more <a href="https://nifi.incubator.apache.org/">here.</a><br />
<br />
For me, it is kind of a very configurable streaming engine, with some basic common built in features, together with nice graphical interface that is very helpful for building and monitoring the data process.<br />
<br />
<br />
From a first look these guys have thought of almost everything - Events Merger before writing to HDFS, success/failure routes, It is easy to build new custom java processors, data provenance interface, an ability to store event on disk for recoverability and have many more configurations and features.<br />
<br />
That's how it looks like:<br />
<br />
<img alt="Updated Merged Flow" class="screenshot" height="346" src="https://blogs.apache.org/nifi/mediaresource/fe110d5e-7b5f-440b-b475-f0fbfc77748c" width="640" /><br />
<br />
<br />
<br />
<br />
In my case, I only had to replace 1 flume agent for a specific data source. Flume is probably the best and simplest tool to ingest data into the HDFS, but it might have some difficulties in some cases. One of them is when ingesting large events (more than 100M).<br />
<br />
So, I needed to get NiFi getting data from an IBM MQ. It only supports ActiveMq right now so I had to build a new IBMMQ processor.<br />
<br />
With a good instructions of developing custom processors (<a href="http://www.nifi.rocks/developing-a-custom-apache-nifi-processor-json/">here</a>) I was managed to add my own GetDataFromIbmMq processor (which is much simpler to use than the Flume to JMS source implementation that requires a binding file). (I hope to upload the source code as soon as possible).<br />
<br />
The new data stream is actually working very well. Large events are coming out of the IbmMq processor, get merged, get transformed to sequence file and get written to hdfs. In addition it is very easy now to send the data anywhere else, or playing with the topology in any way we wish (adding more data sources, more etl processes and more data stores to save the data in). It is also fantastic that we are able to see how many events went through any processor, how many succeeded and how many failed.<br />
<br />
<br />
The trend for us right now is storing first on hdfs, and it is kind of opposit to NiFi that focuses on stream processing. But still, even for simple use case of getting data, compression and storing, it is very easy to use and enable new capabilities of data monitoring and provenance. <br />
<br />
<br />
Must read posts regarding NiFi:<br />
<a href="https://blogs.apache.org/nifi/entry/basic_dataflow_design">https://blogs.apache.org/nifi/entry/basic_dataflow_design </a><br />
<br />
<a href="https://blogs.apache.org/nifi/entry/stream_processing_nifi_and_spark">https://blogs.apache.org/nifi/entry/stream_processing_nifi_and_spark </a><br />
<br />
<br />moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-19424183593242314692015-06-06T09:48:00.001-07:002015-06-06T09:52:10.053-07:00Oozie Hive2 step from Hue's Oozie's workflow editor <br />
Here is a warm recommendation for moving to hive2 step when running Hive from Oozie and a way to that even when using Hue workflows editor.<br />
<br />
<div class="" style="clear: both; text-align: center;">
<a href="http://oozie.apache.org/images/oozie_200x.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"> </a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://www.mapr.com/sites/default/files/apache-hive-bee-image.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div style="text-align: center;">
<img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQ4AAAGfCAIAAAAh6asUAAAgAElEQVR4nOydd1gUxxvHh440UUFR6gEqTQTBir3FXrBg78bYQVGp0juIoogdu8YajSYxaoz+jNEYNcYYS4xJ7OUO7o4rcMvd/P5YXJe9vWWvcnfu93kfH9zbmZ2bdz43fRZARowY0RBo7AQwYmQYYlBhxIiWGFQYMaIlBhVGjGiJQYURI1piUGHEiJYYVBgxoiUGFUaMaIlBhREjWmJQYcSIlhhUGDGiJQYVRoxoiUGFESNaYlBhxIiWGFQYMaIlBhVGjGiJQYURI1piUGHEiJYYVBgxoiUGFUaMaIlBhREjWmJQYcSIlhhUGDGiJQYVRoxoiUGFEYQQ5gJnFayxU61TMagwUpGTT40ZBpVPXepz8okww6DyqUizSHyCtDCoGLm0WsQ/KVoYVIxTOq4KPgVaGFSMUI3SZDJ6WhhUjE2N2Isw7sYYg4pRqdELa6MnQHtiUDEe6UkZNda+PoOKoUrPB231M1XqiEHFIKXnnGDS8+QpJWNARdkBH0P3n2F9EYNIJB0ZJCqaYsNQShsm40t5Y6dLCRkAKjoAQ88dqefJoy+D/hb6jkojcqInvtTPVKkjA/0ueopKo5RmfaNF39KjQRnil9JHVPThl77RfWnEnKAyuK+md6joT/lorGToydfXjQzom+oXKvrDie7TY/TViCIZyvfVF1T0vIhoNW2N29TUBxnEt9YLVAyiiGgphZ84JHjp+ddvfFQMqIhoNqkMIfLS56xoZFQMrqBoKsEMJIqkt9nSmKjobaZQS/00M5xQSz8zp9FQ0c/soCmVU85AQlN6mEv6gkpjJUM1qeZIhhOlpG951TioELIAGKDkHanUzXryrRvF+/SlV7Q0QmbpYYlRTTRLv95yAvQeFVR6QouuM0v+a2u7NLRs2bJZs2bai5+aAX3mBBgIKlA/aGlMVOpSoDUFBASQXreysgoICLC3t9fUg0gx0HNIUOnY++qo0RtjOs0s0q+q7dJw9uwqcdVOiOy9+mNSWkpku7Yu+E9DQ0PVfwQpFXoOCSpdel99Kcpb3Txdd5ml6OtprxwE+reB8AgiLEdEdSar2QPhYQgP/Hx17YD+H+ucVq1aeXh4qPMsQ+QEGBoqsFFp0VFmUXwxbZaDYxgk8iar2SOr2V1cNBW7397e3tPTU7VnGSInwABRgY1Hiy4yi/oraa0QnKDgBG8S4S4IT9/9LQ8L26lTJxWeaFiQoNKB97UnI0eFJAVa0O3L7RHxIZqoYAbh4Ud/5vv7tUEjsbOz00ba9Eo68L5WpUtajBCVhbPtoaA1wpuFcAcj/IWIaDsiPoGI9tGrYcoh/PLgvkVoVFZWVkoNNIeGhrZo0QL7r62trbu7u0ZGDrQkHXhf29IZLVrPrAa/iWZ936aVGfLeC+HIGbsZUtkFqUpGRPsR0e6GsRGW/3o9zcrKAo22Y8eOFA/FhgSunmsNoS+E3hD6QhFr6gQ7AEz3lS8ICnTT7NfUlLTtfd3IGFChQ7xGXG5tbQ0AaOlkBqEPCScfzR1ht0R4MxDRPkR0gEaT7NCN6+l0ErA+p/nHR1e0RzieCKcF5LlDxBXKDkDp/ju/ZgAAfH19NfJ9NSWtel9n0k3FYvCoYDOJXcOsIPSl5ARfyTRHON6IIAURHWygkhHugvDwyOEh6FOaNGkin4b/HrhLeSxc/B5IRSDCX4GIShB2C4TthIiPIuLddnbWAAA/Pz81v7IGpVXv61I6oEWLmUUz9Rpx+ZXvWtfShIRoLITtgFQEIFXRiPg4ItpDUcOUbJiBPg5tjzk5OQEAIOKtMHLuEER8Gta0Q9gtENFmqWTfvt0LAQBaXWijlLTnfd1L27ToCBWqFNBTq1at0D8iull362zVu4e1na0J/oaVSxygzBsKWSoB44VwPBF2C4Q3FhFtUTQGUCsqh/ArAD4+F0q9JWyKuqtlrbh8Zcw4KPZG2C0Q8WFYuyc5YbSmCrr60p73dS8GlXraU+YEa7xhjTes9obQB0IfyGfd/p/risUOpqYAANDS2Uz01gtWK/6lb7ht5oxUdEIEGYj4GCLaRVa9nPjhQgKaHg9X8zf/eEhIBxLq+i3BEJ6OHGkDER+ENw0R7YXwSwCAKZrcxpb2vN8o0iothoGKra0tQJs6FD/hHC/IZ0Hou3VDCwBAn57WEPpQ/eQ3bG0QTmuksj8i2oeIDxOxEe5683yjtZU5AMAEAO4rz9pK8nhqeZMBAM0cTSXvvRBOMCI+AuFZbZZ/JaQ97zeWtEeLAaBiZmaG/iGlXcoh9Pn1WhtrS5PK555qoILVMy2Qio5IVRwiPihXwxzPzpyAJm9TYQtyktlOTx8XAwAg9EU4HghvtkS4SyTYrbXyr4S05/1GlJZo0VZm0U9ug+50dnYGALx44qFk+faCUu+cjOa7y5waGkGmaa4Ixw2piiX0ZKBk74ypPdCkvvjbg3R0QYYcWbpowJ7NTrUVXginFSI+LqvevWPLrJCQEO2i0JC05P1GlzZo0QUqDaSAhlq7mKnWX5ewvZAKr3kz7O78r01tBXrRE+F4IWwXVYHxRCo7I6IdiOhjDQNr9126WNd7OXqgJUndwm4J4TkAQB203L6IaB+Ex7UIAT1pyfv6II3TopXMUiqVdDwqeK1uO0rC9npw062OFnZLpCIMERYjvMkIxx3htFYlTrYTIixBRHtxjbGTaGqTYh1lPDmwK9pFTeg6ebwtrGIhHBZS2R8R7f/q2DJtgtCwtOF9/ZFmadE6Kg2ngFLo9DaEbTXRgpJrUFX2RkS7EUEOwh2MsFsjHFclY3BB+HPxtNy+kf6hCMpNhrKbifi7AFaxcFohot0QntE2DNTShvf1ShqkRfOZpWziqH0ZFBTUzNEMytQY/G2wcuAOR0T7kervEGEpUrUUqYxAOG0QdnN6MbRBqlZ9rFiQfUsXDQQAPP3NTe5OFoTnTEytpHUDZZ4Ib1KteNeSRYPat2+vEy5IpHHv66E0RYuGM0uFZFH70szMzMHeVEP9cmpmnBHucES0BxF/uaV09tnTqw8dSPrq+KLvz07+8azLj2ddfvzGhfOPB+cfD9EbTwh9YBXWxPJERF9iQ8mw9gAAYMQQG8iXb4MF/X6nMC+tyYcr7oioHMoO6AQKcmnW+3orjdCi76igk/TaaYDJmyfCcUEqgiGyB8JjEB68eS11w7qpncPb4mfoUc2bYf9xorOChYiOYHULAMDEBMBqOVTYLSHcPqC3yYf/2iOicggPAA1t8VdBmvW+Pkt9WjSZWaqlhtqX6HCqGstVVKthWiAcL0QQj4jKEdEJCA9CeALCk9z3W+7eygoMcEXT5utj/qG680R4kVinpbhwCvjYJ6lnUOj/2aAP2LMdEFE5lO0HALRp00bLUJBLg97Xf6lJixZR0UhfBVVhVnMZV4eo1DM3hONZN5ciLIfwZPduvl26tg3r5PWhtKE9eBZS2e/DUNhZAMDhXc6kFdfNyyEfUGmKiMpPn4zR4DFLykqD3jcIqUOL1lHRyMKWpg6muq5YPpoHwm6NiA9CeGjp4sHyabv1Y5sPRd8FER9DROVScXn/fgFRkbakEcq4AQgHnU71liJ7u4R7u7u7axoButKg9w1FKtOiI1Qo0kTbqdrv2dczFsJuilR0QqqiEdE+afXuY4eXCLjbxfydZ75aERHRrq1v3UpnR0fTj6E+NMP+fVwEAFAQ84c5oopwZrBY9zIAVBSljKZTTU2AqjtSVLDWCH8+UrUSEaQiwnWIaB8i/goRH5ZbAHbKrY0jAABC3GhYZV9EtAvCEwAAKKCsCbnD83OitMgBDWnQ+wYkFTvV2n58g8wo41df6pXF2jEPhO2MsFsgFSFIVSwiOoiI9uNo+drGxvL6hfYI+0NdwXZDxEek4vLc7ElUu744HghvKmi8sa8PWfqJSpWhWh08mLqeoenUTp06ubo0uHVe2+aGsFshlb0RYREiPvphO/FRl1YtoBRXgfAmIaLdAu7uiv88yM/E4HghHDcoidMqBnSkEe8bqPQRFfkbVAZmzDAbxYVPl9Ya4Xgh4iOIqLy2ejcAAFZ1+/gp2xkRbYHwMADg2KGWsIa0GeYGYZr2GKApjXjfQKX0UK0uH6k+MCYmJvExTdXa5KhJ80QqghDRUQhPmFs4Qpk77qNWtcLiaRPMTh1qaWFhUiO3ehJWeU2Lata2bVsd8EAh9b1v0GpkVOg8WE1gVkc3lYkba+xY3lwQ0d5HD4of3/Grf93pi/nhUOoNq705/3p82ALwARWJN2jsjgpgUFGm0GoLFZrtPxWAQYvXmmVN9aMl5oVwWAhvQm31wQd30wjX9+5OhJB8Rxqs8QYANOJCSVTqe9/QpReo0K3XFB8jT+3m+OimuDWLjU0LpyUiPouwnerxUOv11x35JcZeCMcLillBgZY6wYFK6nvf0KU7VOTBUBYYzG0qALNvq7OU1+icfKClIgzh1G+Dsb2kFeT3Syu8tqxvYU12AJ8upab3jUN0C6qmnpGrzFxKvRTUF31amjdvDgD457672qVcU+aGCNIRDnk1Im/8V56B7S0dHR21DoRiqeN9o1FjoiL/EUVq5P1Hv3qxsLAAAMhE2mqGQZ4XhD7C157fHm81uF8T7rMGj8LwRDh0EwMl3m8euwEAvLy8tEyEQqnjfaMRzRaQ6plF5wF0aFHkRfrVi0zTKymhgAWh96WzLrOm2lmY486S1PS2GYgEa774KyOVvW9k0h0q9O+UD0LtywZpsbGxiV7ooJHRMAi9IeJ99VzrNm3MyAuWpvctV732Xp3QHgDg5tY4b5VQ2ftGJn1BRf5+mr12VHSCqLzgpbbCS8ZjrV3jaGVJ3OcoL/rRSt57QSGriTVo6Ww+aEjru9faSMn220CB69WbA1Qp4xqSyt43PjXcqVY/XpqoyIdSBxjCp2XrWihdh/BZUMhaNM+BfsFSpoLyMTUF+7/s/OhxBOSR93BklZ7nf+xz4XIvT08bAECHDh2UKuUakWreN0rpAhWVA2qqenF2drZpYqpEOZZ6V7/zWjK/bu9h505WD391Q6fPqUX/EZyn7jNms3ivfGWVVCeYQa4HwvEYOsJVneKujlTzvlGqwV9/XaMinyalgCG9H309kEzB6dofy2U1C8q8nz9wb9rUFABgYWFhZWV1/25XbH9lt3ArikdbmCmBym+/+P/zVwjNm6HYu31A4wwZq+Z9Y5XeoUIRFR1mSG9GR42bNzNr0dwspINlt85WI4bYjBhiE7+i6do1jiOG2Dh82L6Orotp08b1zz/yJYIihN0KK7I1770oSpU5bVQgz/3Rn6HKtQYRf3ML8rEErUpN3xmZqCuWxkcFKD9PT3pzaGhou3btWrZsSbjZxsa+Q4cO/v7+2BUfHx8AAITfIJXd65VXmU9TB4WvPTE1pYuK8K0Pvt0F+SwoYtUqmLavu6fKLTmD6as0vvQdFaAhWmj2eepKCXKAsGRLwvbat40quFIVBcLxgtBnV6mTp4e5s7NZh0DLjQXNEQXAQJ77nXv9gM4792r6zvhkAKigUr/r0mBYtKkGALh0PomscHubKW4H0Yfk+UOvTqG2bVzrLfH64+EAWWX90TC2F5T4QDELQo9zl/rQKdyalZq+Mz4ZDCqoNEgLITj+xK3lCx1IS/nJA8Qm3MeCJX9gpAKTcb0g3yck1O7Ro0eXL19Gg1+8HAG59VCBIo+4JL9B/Zt0CLQq39eJ8DiM6uBgbU3qq+k745OBoQKUpEX+fkXA2NjYYEGsrExISzmECl8tr9TCFsh3j0voBCG8f/8BGjwgsOnRPS1qcAsLINf95OkOpBu8WCwW/r9a2gSmpu+MT4aHClC+MaYoFEUMJCdwc7wQjhdF/LVcFs1TY2CVV68+rc5+34VX2V38zkda4Ql57rJKD/wxmRK256TJPf779xnuCXWbWBybmh7a2fzFq57RsQEWltoaHFPTd8Yng0QFqEqLorDy8ShaDnPpTGuKmMVvvRo44IvjJa3wfHAv9OU/QZCnYI8A2wtK0Jcks3LSm6Mxf33I+e6v/hD6QegDawJ+u9cHAPD3Q9epMzzof3GlpKbvjE+GigpQZn0x/eB4I1+aBX1MKFeEubua3b/hSgFMbYXnq6cBij6VsL2g7OPigLnTbWqkQwAAEPq+fOgKgKmJWd1kKBSwZBWei5Y2vJJANanpO+MToXjgP9J3VIDatCiKgWCEolxa2KLBaLuGWZUWtVBhCwCEvtZWJhD6Qql3lzArAECPiBYAgFqUIujDfe4BoQ+E3gjHC/I8unRrptT3pS81fWeUMmBUUKlJCwAAwjP0gYEIrR9yqPyuMljtLROy0Bcg+7LMIfSFEm97O5NZU+1I7//vSTDQ2lsl1PSdUcrgUQFqVy8QHr52JUlRPERm2F5+7SyoIxzQp4kqJ5KxvRC2Vy3H68T+lmhnCT29hfeKZGEl5Hr88mtPwIyA6VDGgApQgxZLS0sID0N4zNenFXVs9GO+dl6lVxOjfXohqyC9uYTthXC85ky3CwuxhCIS6qSVHld/6adUFiklNX1nlFLUXTEwVICqtLBY3hAeRETlUHaQZpwNRq7y/uFaHov73FPyvq7fMmuqnaK3xzx52PHE112VzSL6UtN3xiojQQWVsrS0b++HooKIyjcWT6ITIQ1U1D1rXCpk9e5hTdGK++l6r0GfOXt6eqqWSw1KTd8Zq4wKFaDkCks/P38MFQjXRHQxpxmbNmoVzL7c5SyTKOQEcj1u/zEIANCuXTuVc4laavrOWGVsqKCiWbjt7Owh/PAuIWFRLbvV79fbUMSQ+2E0rFbx5H3nMCtYK1fQ2V7XL7Z+cKP1279cq99RbYFs0F78HTRkWCtFT9eI1PSdsco4UQG0afmIingPwm4h47IoahL8qZD/3FV4nMr1C60Jh8VA6OvJstu4NfzkyVDxO9VbaJDvfuFyb/Uzh1pq+s5YZbSooGqQFghPSYS7EFE5Ij7eYHMrNrqpjFvX1YbVrGEDbeQe+CFa3IlHUMgaNshaJgw8ecAJylSvT2SVnr/d6XzvQX8AgJ2dnaayiCTxjMhEKBLoReNBBdCghYIQ0oro7LFWKAnSCq/MBIXb36H44xpKKPDE1iZDmYpn+d34pesfD/trMGcUppyRAmkGFVLmVJZmfU/BAAUkEPoE+ZOfSx8SZAnF3siHN6IoLHPSj3XLlbMu6MURo1zQxSnKNb147j/f7NOnnxMAwNdX4Y4AjUhN3xmxNI+K+mnSRgmgrj3k++7oMpM50+xJY/Nvb1G3HKua5dyCfD38oV3OdfMkNfWI8vBygLJApbooP/yv196DxG1eWpL67jNWfSqoABq0yA9eoUsVF8whB6YgqzkUsvArgvEyMQEQ+tZWeB3aLt+QM63ltSduFSblhOvx49WeN+/obuew+u4zVn1CqAAaEy9Q4i0lOz0Miliit17m5iQL8Vcubor+gS3KMjMzQ/+Oi3Gs/Jc4V2hnZxYa2hQAULQhhPPCH3I9askgkVZ48l75Xvpfz9/u1y1j0XbTC5X67jNWfSqo0O+7H9nTUqLgaHAIvSGflULWm8dvPMbr0U3iOZG/3O6zflPdzviYVe3u/zXw38dBsMqt3ulHXI+7d8LPX+71zffd0Tt19jpV9d1nrPokUKE/wIVqc4kThD6KtgFL2F4Q+j77033takdLXD0TEhKCjwT9L4RtQ4Isj+x2bufX1Mzc5MLliLRMPwCAh4eHqWndCWMTolyv/tL//oNeD+6HPb4fcvNm1//d6H/nXt91JUEaz4oGpb77jFXGjEqDnRMAgJ+fwteUmpuZQAELyrwpzrarfusFAHBxcSGNAV1+YmkOijcEnT3XbUWsD3odD1WDL4cwozhbSQtS333GKqNFhc5gFwDAwsIiYc0IingsLU3Kip0g9CVF5fj+unOPFE0LNm3alHDF2tpa/raQkBBnZ2ItFxTE1Cp6JONERREYilpffz8sNDdTeOAqqm5drDgvPSH0wS+PjxxlAwBwcHAAAFhaKnw/cKtWrUJDQ52cnNT/atqW+u4zVhkhKhTdEkWoQHhUItwF4ddd6M1e+LWzKMhsJnjt5eRkBj4ceYwqJCTE0tLSwcHB1fVjh/67E26it37id/7id/6it34/nm3149lW+CutWjU//1Ub4Zu6K8I3fls3BH5zvDX6X/E7f/Hbtj0j2gMALCwsvj/ZBrv+zbE2RTnBgtd+H++sb0sWKjcho777jFVGhUqDHXdFH127Eo8IyxFRORRPgBKWtXXD7+tqUPhOyI2LzfEfHdppd2hnvQYbhN/+datecFjdc8nn9fYn5+R0BABMmtQRfzE3tSlE/CiSkZPdkeJTeanvPmOV8aBCZ4BL0Q3+fm0h/AoRlSOiQwjbCUIfznPPUcMULoiUF2H4i6Cbl+qhcnSP3dE9RFSe3K4XBNYu+vl8vTbhjz+2AQCUltbrwNy/5ghre1E8OieLQUUzMhJUKBpdFHfir3cO94fwS0RUjggycIuvWBD6/vfI3a99AwdQAACsrKy8vcln7lVApSg/eOeWAPyVrw5YAgD4L33wF3NyQg4doJpyYVDRlIwWFZp34j8aPSIAEZUj4sMI25kw0gV5XhB6P7rlVpzbfGA/kiEseTk7Ozs61k1WqoBK2cagRYvqncnyxRwrAMBft+udpT9qZNCpE/XOMiaIQUVTMgZUaEJCej/+uq2t7Z2bGRCeQLjjEI7iXbtiFoQ+EPGGfJb4radEQFVSUamGiqtrvRGzsFBzFsulrLDebaamJmdOeVE8Oi+XQUUzMnhU6NcnioIQPrW3s4bwKygNpXlot7TSCwur6HQI1VCRj2fSpI5TJxAvElCRsD2XLAmNju6EWru2DUxxEqS++4xVho2KspDQCYhN/JkA8PxPd1jj3eDR3RD6kD3kowionDpkd+oQLVQIh/RJeUOcnD4elzGoHwByqFS9dHd3b9m+vXKE4FLCiFwMKiQBTeof5T1hjG3lMw8IfSHZyd/o5panv9cVTdKhMAIqQQGWQQH15isVofLFnHopefO4HgB7tlgDOVTs7U2DA8H40aYtW6pyrrH67jNWGTYqAFfolS0T1LTgJxAxhXSwnD3NLjOp2aaCFqhtXd9izjS7iG7W7dvW+/n39vbGM0NARV6KULl+vt7F3WX10vn0TlMghwqW1ObNHagfqiAljMhl8KioIxX6OXR0+gAwN6+3xvGXHxo4RR/Cs6SoHNpDNRCcl9cRMKjoSp80KoAGLej6LpryaFNHyPzp4PK3oOYNuHKldVlZEORblBaCP34Cv10Bdy4DKAAQAsgDFU8BhABWAhlymhSV2FiFy1JcWpn16eMHGFR0pU8dFUCDFvwb7hXJrY35sb0tIWxrakq+KIZwNbwTeHQHZKcCezsAFDfAOnTwDA4kf+KE0XV/kKLiwzJnUNGsGFQAoNcS8/Fp+/03yyD8HsJJELpA6Atl3lDMktXv60PEm9BvweTYFLz6C1T+B/73re2BHfV6LxSDxYd21ptzxPT4li36BwGVH35oExbmPWhQAEkYGlLffcYqBpU60ey3hIaGFeSPhbK9Ut5whN0c4XggHMLZqp7it8QXNW4sAhCC9dl1/1VqXuXtX+RDAmeO1c3hEFD58Tuq11Y2KPXdZ6wyQlRU7qbT7+KHhYUO+azD73cLINwGa6ciFUEIh4VwWEhFMJTMyUwbht7WzBE8vgl++KHNrR9b4oMrhcqBA+T7NBMS6pa9EFD5/Zrry4fuLx+6vXzo9uKBW3Feh4a+dz2p7z5jlZGjoiwzKoTy9W3n5ubZormjs5O9paWVY1OQmWxz/LgPi/VxF7E6s/Xdu7Vt0Zy4Z3jMCCusU0S9sOXAXoU7okmlvvuMVZ8KKlqlpUGpg4qlhUXUWGKEm9d9nMFkUNGNjBAVoH+0qLkG7OvDxAi5zz4ub2FQ0Y2MExVU+kOLmqhU/kt8mfCePR/HrxlUdCNjRgVTo9Py3z3r6IWmMYtNYxabRi80vXPF7M4VM/yVjsFedy6D5V/UXVm+0HT3to8wFBcFo9ex+wcN/DgWfOnb1ksXmOBvwNvJw7TeIY5JffcZqz4JVIAe0GIoUt99xqpPFBX6Rf9To0V99xmr9BEVLUlRX7/BNCt1MyNj1SeECmRoYaSGPi1UICUt1IlnaPnE9cmhgkkFYBhaPmV9uqhgYmhhREcMKhAytDCiIQaVj2JoYUQhBpV6Ynr5jBSJQYUopm5hRCoGFRIxtDCSF4MKuRhaGBHEoKJQKtNiTJnACBODCpUYWhhhYlBpQAwtjKCCZjaDClEMLYxIfcqgQiKGlk9cDCpKSB1ajDhbPhHpHSqNvQWwYckzQHqbTZc5NO80OKnjXwOVol89BpUG1CADjlHlbjE/Tyx7apS0qONfQxRF64BBpWFRAGBiadch6Xbut7wtV8RbroiNjxZ1/GuI0iIq6tDS2MVACZECYGJp5xhVvv6iAOXEKGlR2bmGKOqCrWJefOKooACgTS88J4poMVxgVHauIYq6VGsAFZVpwfxhYmln4RqqrJk5+VL4WOMRypd+t5ifU05z5FHZckW8YO8LZWlRLRMaLOtmTr7qxKmaZw1UWkGlwXjpCCvTTkuuqmZ2AxJM7V0IhcOS1csxqly1CJvP/9bE0g4oEKHoK+JEBVo0ngkAAFN7F5XjdIwqR5lRuXgYnBr89W9kVKz8hjotubrm+DvVrEPS7ebzvyUUEaclVwcXPlAtwgV7XzSf/y1F9ULIUApU6NOiTiYsP/SaNBPMnHybz/92wd4XqkU7uPCB05KrnywqpDc0MipOS64u2PuCusBR2+DCB/h6wJLVS80IO6ffdYwqV4QKgZYGY3NacpW664LWJ9RpHpF/c+aWP+lngomlXbMZRwcXPlAnHxbsfWHhGqpy8TA46QgV1WgBAKjpzi1XxOsvCqw7TsRKSbMZR9WP0C3mZ5qo0KlYFNFi02WOTZc5jlHlFJmwsAsOLWkAACAASURBVPxJi1lHQGQ5iCwfkHGNZiZYd5zoFvMzYWhOBXMYU6Jy8TA4aREVOrFTCwCw+ui7zZdFaprDmBK0iKCtc/Uj7JX9hyJO7AYkyBd66tjm73mOdgBIgXFacnXpwdeKwoLI8r4JcZBrUvnKFkSWd0v6scFMAAA4jCkZueGx+vngtOSqOsXDsKTvqKw88mbjJSGFrTnyMvXUO+p7sFKCNmaob162/59l+/+hvmdc6RNFnLjF/Jx48r18iaeOEDNSWiiCh0SnQa4JasVHB4PI8mX7yBNPQGVc6ROaSaIwBhW8GhmV6MOviy9WyduqL180n1nX8EBt7Lo7a0++Jb2ZgArpPUv2Pu2ScAkfoe/SM9nfVJDeHLmJHBW3mJ+XH3rZYeX3vkvPEMq679IzpNZh5ffJcslWBAzhtuSTb09e6oShArkmXp8X9Ekj/4IEVCI3PcF/OiTnhqIU4m3Vly/woRhU8GpkVJYdelV4nk8wnyVnQGT5rKJ5xUcHX7rhf+mGf+qeMWj5zjjDlr+fgIr8DSsOPQOR5V6fF+Aj7JsQ12Ty/sHZ1+XvH7PxL9IqZdWxt4Ozr4PI8tQ9Y1L3jCEUdPQiwUBkOekjFNGSC5yxexbu/vvSDX88Kn0T4nyWnJGPDZ8JKCpjNv6F/xRLM7URImdQwUtjqKhACwBg8YGXeee4eFtQ/sRxWmn52Z74IoI21lP3jGkz/0TcsVeEIARUCJ9OLPm9yaT98hFCrsmsonkgsjz19HtCkFElj+U5cY35Oe8cd2DWdRBZjsVAzAG5R4DI8gklv6PRdkq5QzAKYPLOcZcf+I+ASkh0WljcRUJqCZmAojKq5DH+U3yaKcx7ydf4UJ8OKnRKsroD52qisnD/i6xvKjGbVvYniCyvfGWryJeO00qtJ+1P/uodPhQBFfxHWd9UgsjyMRnLFEV48lKn1vNPECIcueERARXXmJ+jD7/K+qZyQObPhGJHQUvlK1vW4q+xaAcVPkBn99CxL9QUdfdzgXPWN5X4lF+64Q8iy8etv0v4goRMAHXd+keETKCDCj61Wd9UfiKo0PzFb2RUPt/7PP0MBzPrSfvxHVl5u/O7p+O00tbzT+BDEVDBfzR7x+NZRfOoy4fjtFLW4q/xoYavr4eK3YCEpQdfoh/1y7gmX+wUoZK6Z8zCvU/xMffKuYdO7WFyWnIVzQRFwESXTcHS6TKv3hcnzQQUleHrH+E/pYmKV/18+BRQod8yamRU5u15lnKajZnX5wUUVQpGC4gsx4cioIJdX3PsteOMLxssHycvdQKR5Z/l/oIFHFr8sF7TK/oa9lGf9J+oUcFo+eeJMyGdqPXMvmflNxSPCj4TSGnpmxDn9XmBy7zja469lo+QkAkoKkOLH+I/pYvKotP4UJ8aKtR3NjIqs8v/S/zqHWoji24TmuaKLHXPmCX7PwYkoIJd751GUqxJbUzGMutJ+7GAQ9Y9wIpdm+hrX+x73mCc8rT0TYhrOuMwFhBvTkuuYrQ4LbmKzwTU5GnpkvgjaVTymYCiMmTdA/ynNPPBc9FpQjrVLB76L4NBZeauf+JOvEXNY+EpOu6EXJPKV7Y9U69iAQmoYNebzjjcYOsLXwNgAQcX/YlVKfP3PsOux514O7zgFn7EGUSWO04rRWtCQuEGkeWRG+7iw2LWPet3rBnmtOQqPhMwI61e0CeSxklAZXDRn/hPSVHBxhUx6xB7Hh/K6FFRalyqkVGZtuNp7LHXscdeLz34jOYvH2ot5x5HA8Yee91sxlG0i2w3IMFpyVX04sztj0BkOWFegsJCotOwCAcW3rfpMse251IsNrxFld7vkfI/1Ny/OIUfW5Mv3PLBUeuWeRfr1mOZQHiKot4LaYRYJth0mdNsxtGBhffxn9JBJXDl94Q4PylUGry5kVGZsu3vmCOvYo68mrDpD1J3lp/teed3T/nrILIcDRhz5BVhATl6sW8GcagKteKjg0mrmtQ9Y7AI+xfcR6PqmnEXuyhvi/b/5zD9MHVLTFHYxQefYwnGMgFvWIaQ0iJ/PyET+hfcx39KmhX/PHFGZ5nQsbUJm/6Qj1PN4qHPUqpKgY2OStTWJ8sOv1h2+EXX5Mvy7kRdiLVwCKhEltxDw5Ja1+TLXp8XyIMHIsvlyzf6EUVsih5Bp9+SC5yVjXnZ4ReRJfewyElpUSq2Bmts0vz8dFChc38jozKh7K9FB58vOvi8cxJJscNaCPLdfRBZPmbD72hYUuucdBldaEiI0H764c5Jl+XLyqUb/hSxkRo6BU4HFWVjXnTw+ZgNv1NP4CgVLR1U5PPTiFFRtkqBjY5KZOnjBfv+W7Dvv7DEH+Xd+c8TZ8dppaQzLSCyfGTxb2hYUgtL/FEeFbSaIi03l274U8RGaqRD22jFJV+slY18ZPFvdKosmrHRQUU+P40VFRU4gY2OyphNj+bt+Xfenn+7rf2fovY06UwLiCwfW3IPDeuXcBNv6MVOCZccp5WSRkg6JF18dDAacN6efwcX3Uej6pnzO3aRYL0zfiYdM3CcVuq5+Ot5e/6VpwUffOaup1iCJ5Q9lo9/eNEd0gzpmxBHGi0hEwYX3cfHRgeV4UV3CGkwSlRU4wQ2OiojSx7MKn86q/zpkMLbSo2AgchyNOCs8qeEETD0Ys/0a9RrZAgWXTYFi7BH9l1sBAy7iLcp2x5bRu0jjccyat/40j/R2whewccQmnIbGwHDMgFvijKkb0IcacyEEbAe2XfxsSmqSPHLOocU3iakwehRUSpgI6MybMOf03c+QU0pVFrNP4EFJMyroBfHbroPIstJV0mSmtfnBViE3bJ+w+ZVRm96iF3HzH/l94oKn2XUPpf5JzEjlGk0eEjKLfy8Cj4TMBuUf0sRKugNhJgJ8yrdsn7Dx0ZnsLj5rKNjN93Hh2JQwauRURlSfH/y9r9Qc1t4mmax/ueJc7fUq1hAAirY9Wazjsh3V0gN7cNgAbtm3sGKncvyn0aUPMA+Qk1Rhz66bErfhDi8WUbtk2+JEWbr8ZmA2YC8XxWhgt1DiBaPStfMO4QEy0dVfrYnIbVuC0/jQxkfKiq3vmCjozJo3R8Ttz5CrW/OzeKjg+mU7DEZy8aU/okFJKCCXe+soP9DWv5spx78GDDjIyp2AxJclv+EfYSa/DC0IiPt4hPWgOEzAZ8bilDB30ZKi8OYks4Zd/C30cwH53kn8KGMDBV1OIGNjsqAwnvjyh5i5jitlHTCkfBbCCLL8aEIqOA/cp57vMHuCtoO6Z39CxYqPP02wMluQMJnxX98/DT5fzTXqqGojCt7KE8LHhVCJqDWO/sXRagQ7pSP3GFMSXj6bfw9dFGZexwfikEFL/1CxWbqQeofbJQTzyVnaKIydN3dkOg0alpAZLlv9Hf4UARU0GYYSsuojX9YTCTv0FOgQlqmHcaUOIwpUR8VUlpygTODCl5qcgL1DZUB+bcsJu6j+M0GkeWOM4+M2vjHOHqojCt7aDFxH8UemNQ9YwjgkaKCNsPGlT30XHKGdAyaDirh6bdJy7SWjEEFLzU5gfqGCloPoBvrCV68dMO/b0Kc55IzBE4aRGVA/i2bqQejy6b884S4odfr8wJCW04RKigtvbJvKerQq1CrMKjoTOpXKVAPURlX9jBg1QW0KphVNA8d9R+TsQxElttMPSh/c4OoYK0mlEA0wr4JcY7TSp3nHsd3UahRAQA4LrgIIstDotMIA0cUZjFxX6NwQkDFYuI+Oqlt88Upo0dF5Uj0ERW0cHsuOeP44Xwj57nHu6ddI72TDipohOHJ/8NHSOif0EHFbkCCw7zvPJZ8F7DqAk3Dkt24qHRPu0YntYQa2zhQ0UiVAvUWFaWMcLqk+hH6xf9Cigq+00LfInLuUp8u2Sf/dw1mAgDAYUxJSMot9eM0AlQ0xQlsdFSCkn5V052jNz0gnFmsfoQuy39ShAqQGzumw4mFaygpJxauoQ5jSjSeCdYdJ7os/2n0pgfq46dm8Wh0GQ8qTkuuRuTcVcedQUm/Ek7CV/NH2nfNDeqT8AFu7LhBozi7KBc4gw+NRs1mAnoSvpoERuTcNfST8DXICWx0VNBXiwwovKeaecVeJ32/SlDSr6pFGJFzl/r9Kqgco8rpNMMicu5SdyfQ2NTJhD75v5NmAvp+lYicu6pFG5T0q6G/X0WznMBGRwUY1Fu78D/bjlHl1BULKSdAwRtYmbd2aVYa5wTqAypYWdHzd0HK00LRDFNUn6BhSS+qluYG06nCuyDxPxZqFo9GkTY4gfqDiiGKohkm30UhhKX+VH+kZvHQvbTECdQsKsqmrLGLgbpCm2HyA00ROXfRtcPUJBgELWoWDx1Le5xA9VGB6uyVMXyZWNp5xV4fXvInnhOn+qcSU0j/aVG/eOhMWuUEMqioL7QZFp5+Gx2Pwm/balAMKpqStjmBDCoaEbqdHR07otPVxkvPaVG/eOhA8sMn2niKhlHRUiqNQ4qyiMk9daQbTqBGUIEMLTREkT9M7qksnXECNYUKZH4aGxK1RxlaVJAuOYEMKroUfVoaJXmGJR1zAhlUdCwK7zIVi1LSfXZpBRU997Rf8sWxJT+nHbv38AWPK5I0dnIYKa1GKWyaHA3UQ1TO3H4pf9Ev+SJmHvHn6cRz/WnFsj23CaZCemL2/0aI5PD1Z6R3/v1eePj6s7Rj95btub1g+82Z2xq2Bdtvph27d/j6sxeVYhXSZihqrB9lY0blzO2XM7fdlL+OR8U38QJ68ca3f1BH5RF/npVwATOajBEUkHoJHwkr4ULhub/kbys891fPrMsoyYT7qc0j/rxf8sWeWZdJfyOMQI3YeDFOVO694HnEn+9dQL7flRSVPSnf7En5hl8hJA1y5vZL38QLpAGVUnj6JXwkfskXCaikHbvHSrhAuEc184g/zxYYVfOycRv5xolK74KrfskXd1x8QvqpIlQOZJ07VXqFNIhuUHlRKdYUJ6jF7P9NhUTqpxq9M2yEqKQdu+ebeIGVcEGMSElvoEDlQNa536+SAKYbVMaW/KxBTtCKRVEmGJwYVDSsmdtuNviDSo0KKS06QOXhC55mOUEt7dg9FdKpb2p0TqCRocIVSdB+rW/ihb/fk/c6IA1UDmZ/TwiiA1Q2fvuItKwTnqushadfUiGdeiV94AQaEypiRIpy4pd8MSD1EsWdilC58e0fB7O/R2k5uu4HfBAdoIIOeRFswfabZ26/fPiC9/d74YtKMYXde8HbcfGJPFcOq86pkE79kZ5wAo0JFXxppp70IC3xaIceQ2VPyjeKItcSKhjn+FKu7BCWfG9HtUFt/RGDiuaFL2rXn1ZQ3Ela4m9deHjp8C0MlQNZ587tvo4F0TYqWNMRbyrMjRy+/syYUNEfTqDRoHIP1yf2Tbxw+PqzS7+/VmSkJf7c7uv8CuGp0isYKgeyPjZdtI0KWyCRHyZ++IKnbPxGjEpjp8VYUJEvgjQNK/F4QjB7/tdb9FNto/KiUiwfP8XIhCIZEyp6VaVA40AF36FXGRX8CBhmlw7fQj9tFFRUWMplrKg0dlogNA5U7r3gqTyiSo0K1gZjUNGx9K1KgYaCCvVRCWnH7qnGSYOoYONgukfFL/liz6zLypp8/IaIih5yArWHima/ITUq/dZd0xIqjVir+Kk0+SgfxOBQ0U9OoGZRgVqrWKhRcVh1TnuooGuNGwUVjRiDiqbEoNIAKuggGIOKbqS3nEDjQEXl4S98iedXCPHzj5j9dec5ZFDRifSZE8iggi/xty481CtUHFadU99AzHea8oJWpeecQAYVQomXb4ahqFx/WkEdkKbkE0CByuHrzy4+eK++acQFWpX+cwK1iooGv7MO+iqoLh2+RdpXkUfFL/miCttx5VevaHa23hBlEJxAjaMCtVOxUKMCYr6Tb6BTtEkoUHn+11vSETDSWU5Fh60oEldEstALXRP5yaJiKJxA40Bl2Z7bhEJG3eqgQEUsqCGdV4FkFYJf8kWltuPG7P9NUQykK4up10cbhwyFE6htVDT1/alR2XHxCaGQbfz2EUVs1F0OfHcFv2uFFBXSo4lI9fd7IekkIwabfDOSfuQGKgOqUqA2UIFa+KkgJcTkg7EFEkIppB4hpY8K/gCXgFTyxcse8edBzHfUpqg3hd+iSLrmYMfFJy8qxWyBhCtS2jSS81qVAXECjQMVCKE8KhRNI/qo4M+jwA640KDhTyojbZ45rDrXu+Dqsj23Vx66q5Tp/7FGhlWlQB2gopFcAMAUAACAKe6Pj/+FEBae+4tQyMaW/KwoNqq+CiLFUCHsGVZnqT+p+SZeIPRGNDgLqefHGhkcJ1BLqEBNVyyWFraWFrbmZk2srextbRxtmjRtYtPUyqaphbW9lZ0jeg+hnFEcv0CBCr9CiKEif3we6UkRKpt8CrWEir6VRUPkBBoKKnFrUuLWpCQmpK+KTUL/SEhIX7M2c3VyRnxqNnoP4SefYr8HNSpY6+vWhYeEgPK7QdQx+TaS/PiEplDRqxJpiJxAQ0Elell89LL4mOUJyYnZqWvzUtfmpaTkJWcXpeQWpxeUoPf8/V5IGKRSNKFOcc+53ddJW1+Y0AkWNX/+PeLPKzok9uKD9xo5i1UeFT0plwZapUBDQWX5ytXLV65euSZhVXzSqvikuKSUxJT0xJT05LTM5LRM7DZCA8k38cI9spMcKFDBWl/yVQqmw9efoQe9qlyIqU98zD39p/qdIlJU9KFo6lVilJJRoQLJhsLk57wVoYKtasEfa6RIXJHk4oP3uaf/nLntpqJxZOwRM7fdzD3958UH7+mvhWELJIevP5u57SZ6VLlxoGK4VQo0DlTSs/Oy8gqz8grzitZjVlBcUrRhE2YbSregtrFs28aybfj/Fm3YlJ6dV7J56083fn326u2Lt+wX7Ipn79gvOZWvKrivKrhvuHw2n8/mc9/zKiur+EIeX1wlwFuNUERhIpGopkZcK+CNHtjX2cLi/X//1opEiFjwwaoxkw9LeJBAIOCKBJVVosoqEYcneMPhPn/PIZQ/+QzXkwKqJ8lQWYaBSnTsmpVrElBbnZCMopKclpmYkr42PYuASkFxCWbrSkoxVFBICLZpy/YNpVuy8grXbyq7cu0GKSrv+IL3lVwun1fJ43K5XEWEEIq7tEYirZEg4mpJjUwikcjEvD1lBcO6Bb198odUWCWtEX0wibRGUltdg4JBjQqfz68Q8Dk8AWrvKvkvOZUv2BX6T0uDKdR/GRgqaJWSsDYNrU+S0zJTM3OoUSneuLl44+YGUVlXUvrj1Z8xVF6wK15X8t5w+W+4/Hd8QUUFl8/lVfH4VbgqhRQVzFAGpDUSSQ2USGpl4sqi9Ohpw8LFb59CsZAUlQZrFUWo6DktRsAJNDhU1iSujV+blpSakZKRnZyWmZKRnZ6dR40KoemlCJXC9RsvXvkJReXV+4o3HO5bXtU7vuBdFf8dX8CprODxeELKdheBEFm1RFojkSKyGgmskUCp4P2r++fXzBwE+S9gTbX0o31ERb4NRkCFV8VnC1FU+Bgqr95XvHpf8fItWz9pMQ5OoKGgErMqLjYucVV80urEtWtS0hLSMxPSM9emZ6GooJzkFKzLKViXv24DxknRhk3rN5UpQqV0647SrTs2b9u5sWxbZm5BfnHJ+f/99M/rt8/esV+/r3hfyX9fyX9XxUWNzedyBVXiKkE1n7xzUltdgxZ6iNSiJquWSKtrkVoIa8WSGgirK+7sj9+fPHHO0DAx5z1SU4vU1EoRWR1XEgSPGSkwAoGAU8V7J+RxeNwPJnhXwXvDrnz5lo2avtFiHJCgMgxUVqyORzv0eFRSM3M0iEreug3nLv9Pg6hIayRopQF5lVX/vnp48Ujl1eLCeaHbMuYJ+K8kEkkNIqmtrUWUQaVCwH8v4lOgIk8LtWu0XXyNhhOoG1TUz6bYuMQ1iWsJqKRl5WoQlZzC4m8vXdZorVINRVWQ/bp49twhLi7nile/v1qwM3ngsW2xSO07iaRagohqayVKocIVCTjVAmpUGsx8nbWIjKlKgdpDBWr0FwWtVdYkro1LTo1Py0BRScnITs3MycjJx1DJK1pfuH4jfowYQwVjA29bdpRv3bkbQ+W7H6+gqLzlcOv6zSgnQn6FgF9ZxRfxq8S8KnlOqgVCrKBDpBbKoEQkhvyqe6dOTvJxn97KeaZLy1+2ZV86kBozpdvUoaGSin8hIoISIayugrW1UAbr10USwmAaAZUKPq+Cz+PwuJVVIja36i2H++od5/X7CgwVqDwtGi/KRsYJNHRUsCpFN6hUC4Q1VUJ5VCQiMVYhYIW+6o9Hk1nesT6ey92cFrZuHslyczAB1gC4W1v8dHhv1V+P9uTlbEldC5FaiUisFCpcQRVXUIWh8r6S/4ZdSUAFNiotxscJNBRUVsYlxiatXZ24Ni41PS41PT4tIykzOy0rNyMnPzu/KDO3AOUEa3cVbdhUtKFkQ+nmDaWbNpZt2Vi2eWPZltKt20q31v1btn3H1p27tu6s+zcjJxdF5e+Xr1+wK95X8utKIa+SV8Wv4vGFVQKh4iEviEggUg0RCUQkUAZhrfjepW9mtHVb6sda5uo8v7XdOBuzbpZWTiZmlibA1cEyyAqEWZmHWZl3s2sy97OB1a9eyARCtG7BkJNvhtUIRcIqgUBQhw2bz2Xzue+quO+43Hdc7lsOl4AKbDxajI8TqDNU1MyymPjE2KS1sUn0USkp2rB+Q+mmDaWbNpZtRq106xbMyrZv27pzB2YZOdnZBeu+vXSZFBURl089OvwBlWqISGBNDRRVTusRMtgcLGvrNruZzVg7MNQSdLWyamkC7M2Bm4NZxyagu7V5d2vzHk0sPAHIXxENxWIokTSICpqMan4dKmil94ZX8YZXQYoKHS9onBajrFKgVlGBmvt10QEqmXmFZy78oCYq0tpqyKtYPWbkNJbrGCsw38VxZlProbZgsA3obG3qYgI8bM09bUEXW/Petla9ba16NrHoZGvpbW56dt8+hMcnoCI/L6kCKjS9oCVU1IxKr2RgqKxem5qUmY2ignXosflHFJXC9RuLN24q3lhCgQqek607d+QU5KOo/PP67VteFTYdzqniVQnrRr3kUcF1TiQQqUbEAiiTbF2z6gtfn7kt7Ba2br7c02WqncUgG/CZHehpA/wsQZCdSYAN6O1gM9Desr+teW8rEG5l2rGJZWAzR+HrV4QeS91kv0hMjQpq7yv5dFBR5AhNVQXGWqVABhUMlfSc/K/PX1QHFShDED5nlq9Ppp9/PKtVvIfrstZO0+wtIx3N57q3mORi95mTWZ+mJoFmIMLCbKAtGGxv2r8J6GYJwqxNA+2awJpq3aCiPVqMmBPIoIKhkpqVe+rceXVQkUnEP393Nq1jWFlAh6wOnuk+rDgvt5mOTaY72Ue7t5rvbDOuORjsAMItQISZ+aAmYHhT80FNQIQ56GJlEupgw3v0QEuoQNqFWM2ybsScQENBBR0sjk9OjU9OTUrNWJuehXZUUEiy84vwq79QVDBI8ISgvZT6HZXdW3fuzilYl5lXePTrs09fvXnL56GFjyOqEvGrasgW/KJzjljJhlKZAApq+M9W9w/PCnbfHOK7LtgrM9Anvq3rHCfrpa6Osd6t5rnYTmxuMdjetKeVSRcLk952ZgObN+ljb97ZAnRuAro0tTm7ZRNSWQkl6PDAR2DwqOCBqark8vgiNrcO7PeV/FfvOIpQgSrVLUr5yLirFMigohlUamXVMkEt+595AR5Zwe7FAR55Qe45we3i27rOa2mz1NVxTds2X7g1jWphiaHS08akv6NVL1tTFJWQJuaLRg6V8fnqoPL6fcWrdxya7qA5IEbTQUbPCWRQ0QwqEpkMEfx39UJK99DcIPciP7e8EK/8UP843zYzm1ksbu0Q395tmbfzJCcrDJUe1qCvg0VEE4CiEmZn1b8tC0ok6qDylsN9w66k7xFSv9CsfCiCqO5m/ZZhoLJyTQLKCbpHhYAKuvoLW/qlAip5Resz8woPf3X675ev3wuqMFQo1tvLJAiuVoGymqpjeRkpwQHFHVlFgR7ZYayc4HarvV2mOZiuZLVM6eC9zNt5QjPzgbaghwUINwNdLUAPaxDRBHS1AuHWdagQahXUFA0Zi6sEVYLqCr4QQ+XlW3aDqMj7pUFa1IzNaMSgUodKek7+wRNfqYaKFKmtgcJt8SvSQoLWB7M2hvpmdfXJC/Fb4uIwyQbkdA6Ib++20N0RRQWtVVBUulvVoRJqY9HH26OazdYNKlAZWtSJx5hkMKjEfUAlKS1zbXpW6gdUMslR2byhdDO6pIUGKrvyitanZuXuP3ZCNVSqJTViWHOsOC+zc+j6YJ8NHX1TQjyT/T0XONtMsALZ4f4xrJbTmluMsgGD7UAfa5NuVqZdLED3JqCrZR0qHa1N+ni5Q6Hgw6y/1lGh9g59AD4RTqChoIIOFq9em4oulEzMyFqbnZuam5+eX5ieX5hbWIxf/fUBlS0fFttTo7Jj684dBcXrUFQeP3shj4o8J4SVkRCR1Ehl986eTQvrWBDUbmNIYFZo++VtXaKamoy1AqkhbRe4OkQ6giG2YISDySA7s55WJuFWJuFWJp0sQGdz0NkchFqCkKbWshpRjaxaWkucYEGX6FOjwuEJXr+veP1eifdMUPCgwnAZg4paMixUdh8+oiIqEkmNVAZfPE/tHpbfod2GTkExbV36AlA8stel5KX7Z46Zx2o23cthVAuTz/1c+zcB3c1BJ4s6w6MCpRJdogKVoaXB4Kp710DEoFKHSlp23s4Dh9RCpVqY0qdLdoe2q9ycxjc3TRoafmdHzuNDJX/sLji2avaBmGlJA0KWhfj0swbdzECIGQgxI6Ki41pF3kfK0sKgojHpBhVssLhw/UbUUFRKNm8t2bx105aysu3bFKGyvXzX9vJdBcXrMvMKd+w/+PDfZ2yhgBoV+a0pEKlFZ19dfQAAIABJREFUIIS1oqQB3dICWDMczE6vXfjH6bI/z2y9fWzDLwcLf9icfDpr2Zb5Y2K7+PdvArqaElEJszYJb2EnEfIRKIEyiWqooBtX1HQT3lnUzbBPqvUFjQyV/HUb8tdtwFBZv6ls/aYypVD548lTjkioAioSCKFUtKJ36LI2jqXDe/Ovf/Xf9SN/Xtp946uS/x0p/HZr8sn86L0rpk33bE6KSlc7i7DmtoioqlFQgbRpUXS/Os41FBkGKujWrrjk1KS0zIT0zOT0rLXZuek5+Zl59VDB6hYUFfQEsE1btpZt31a6dRu2qats+7atO3ehm7q2l+/eXr6roHg9isrvj59gqFQKlUClGkIIRTkThs1wMHtSlsv++diLO6cfXt77y6mS/x0p+HZb0ul1sYcS5i7o4NnXBoSbgRDzeqh0s7cKa24vE1XJoARKa2mjIqpDhStUExWoJC2fGifQUFDBZuuTUjPQ2Xp0aiUztyAztyA7vwi1zNyChLVpSakZuYXFxRs3oxuG8YdOlG7dUbZ9V9n2Xeh0CmaF6zdm5hVu3bPvzoNH76r476q4HB5XwKeNSo2MDyUSCT994oToQLeaC1++/uXIfzdOPbhQfvfEul8P5V/ckngyf+nuuBnFs0Yt6h06KaRdqBUIszbpaGba2cw83Ny8l3MLPztriIhhrQTWErfaY7uIiagI+RV8Hocr5nDFHJ4A3WSvKX9hXqNz5VOQ7lBRJ09RVFYnJCesTUNhQIFBz8tDgcnMLUjLyl22YtWS6JXJaZnYiS10UFlXUoqicvPefVVQkchEUAJh9XelGzOH9a68fOTpncPPb379+NLeuyfW3dyfc6E07kTOoq3LJ+ZO/mzV0J7RQ3pPDPEJMAcBpqCTuXmohXn3Fo4scwBrqxsXFUUuk0foU+MEahsVqKGKBX+65JrEtdiZxailZeWmZeVm5xclpWZMmzU3cuLk2LjEvKL1pMdQbN62k8DJ1p27N5RuyS5Yt2X33lv3H7zmVr7hVVRWfTxzVR4VfNMLRQWRSqRSofTN8+iBEe8uH/3n1/2v73z79H8Hfzte9NOO1MulCaeyFu2IjsqaOHDN8N4rh/ebN7DzrP5dJ3YOCbAyC7FvEu5g629j9REVqQxKZfJnUxAmIlFU2JWiSn4NetDRWw5Xsy4jrUY+QU6gMaGSmVuwck3CmPFRfQd+NufzhRk5+fiDWBtEJTOvsKx8j8qoQEQCYTWs5k6OCLuze/3h+Kkvfj3z5/ldt47kX9uWkj+t/7Rgp7md3Zf3DV75WY/Pe4dPHdhp+uBuw0IDWgLQ3trM3wz4mAAortIHVCBlNcKgoi3pDJX07Lzo2DVDR47p1rPPlBmzE1PS0S4+TVTSc/I37dilOio1ElgrlELh9a+OrekdttKv2ctbZ//96fD1A1lXNiXM69R6rKflYCcQYQfG+7nO6REytk9gmFczlr15N1/XyK4hX/Tvs2bCOITL1hNUID1aNPUsg5BOUVE5czFUYuOTViWnxK9NS0jPTErLTErLTM7KQVFJy8pdGhM7ZMTo8G4RYydMWrE6Pj07b11JKdZdwXdaCN2VDaVbMnILCjdtvnb7tzc87rsqLikq2Hlf2HqWulNaZLVQJoEQefD4XjNzMLFjQPGoYQ++2ffv5WM3jm48WRSbNLbvgl6hk7p16Ovm2I/VcvZnPXxbNgls3XRIaOCEiOCpvUM+j+i0eGAvKBVARAgRCU1UxFVcPq+Sza2qqKrrq2gQFXnfMahoXRpHBTuLNSH9IyopGdkLl0b3GzQkJLzrsFFjFy2LSU7LzCtaj7bBtINKdZ3JJDIoycpOs7I2G9yn64rJ45PHjkr/fMr2tNi0RdMm9unQ36tl91bObe2btG1i0coMOADg08p2aNcOMZPGju8WMLNPyOe9Oy0Z0lNa9Q4iQohU00Sluoor4HIYVHQj40ElKTVj3heLu/ToFdixU9+Bn82at2B1QnJ6dh46zaIDVMI7h7i7ubT1aMlqZe/jbB3q6+rb0tbV2sQOAE9L02DH5t3b+vbw8+seGNDSwTbqs27TP+sRM2FYZOf2s/qGzhsQvnh4bxH7uV6hAhuiRbPP0nMZEiqr4pMoUElOy5zz+cLQzt0CgkO79Og1adrMpTGxa9OzcgrWoeNgeFS27tyNHzXeWLYtPSd/3eYt127/xhEJ2UI+TyjA3qbSACpSCYqKuLpq5IghEKnavW3dyS+3BrZ3HdY3fFREuDUAw7t0njOgX9ysaZkrVkwdOcK1hePsUX3mjeyzKmrElB6Bs/qGzugXOnVwZyiuIEWFMAVJQIXDE7B5Qi2hgupTJgST8aCSsDZt8vRZQSFhfkEdw7tFjBkftWDxsriklIycfHzFgp9dwaOSlV9UuGnz1V9vvxdUqYaKDEqcWjSFYu73x/cf2JjTzrfpnze+u3JwW7dA74kR4XMjwoa0a5P5xbylkyM97C3mjYxYMCJi1fhB07oFzOwTMnNIl/J1SQjvjf6joo34DULGg0p8cuq4qCn+HULaBwaHde0xbNTYuQsWxcYlpmbmoD0WirnIjWXbcgqLCzaWXvnl13dVfNVQqZEIra3MoJh7/eyJI8XZXcNc/rh6/MLOgmXTxkSG+q0Y2HXxwLC0uVNXThrNcgDzRnVfMKL76sj+c/uEzvusa/rSKbWcx7CGy6CitzJIVNDjWLF3QWbk5Gfk5MfGJY6dMNG/Q1C7AP+wrl36Dx4UNXXKshUx6DoXdNR405btGDBYxVK2fdfGsm0ZuQUZBUU/XLuuTK3ycWsXRKprq4UtmjtCKbz948UfDm8f2T34jx++vLwvZ/2auRG+zReM6Loyql/c1OGzB0SM7NhhwYiuC0ZExEwYtnjkgF25ydUVL6FEIKsRfcCPFipVPD6vqu4lMByeAD0VX7O+g0zr64OMB5XlK1cPGzW6XYC/X1BgSHhYr359R0WOnb/wi1XxSWlZuXlF64s3bi7ZvJW0bsFQuXD12ls+jy3k0+vW10MFypD9+/Z8ffzkn9d/Olqa28HZ9v73h346mPdVSYITAMO7Bw7vETKpf2cvGzB7RASKSvT4oYmzon46dRiK+FBWI9U/VD7xrjxexoPK4uUrBg4Z6uvX3r9DUHCn0O69en42fNiMObOjY9egFQthSRj6BwGV769cfc2tZAv5HB6XV8lVDpXaGmmtxLO1a7+wkM9CfQObmf+wt/CHnUmHMuYOC3JyNAVWADQFIMTDZs64cAyVhNlTfjx+CNaIIEQQiUhaW10r1V9UNBu5YcmwUUFfNZyZW5CSkT3n84V9Bgz0bte2XYB/h9CQLj269x88aPL0aYuWxcQnp6Zn56GbJbG6BV+rlG7dgaLyzQ8/UgwWY2/nIqIirYVSCZRKoKwWEYrf/vXwy43Zd8/sf/z9njtH875bv/SrgmX5y6ekLZ6+dt6E6Kh+C0Z3XjS6x/xh3ZeMGbR65uTLp09CRFIjrUWgBDVC/DRRUXMRPrXjPnFOoKGjgr23Pjktc+bcz3v07uPl64OiEt6ta58B/cdFTVyweNnqhOSUjGz0TZHYjCQpKmcu/PCqskIdVGANAqu4D6+cffT94b/P7755IO38+qVfps46mDJre9y0gsUTU2eOWDay56LRPWYNCl88emDszCk/nj4lRWqrZTIJlKCmh6hoMFoDlWGjkpKRnZadl5aVm5iSPnXmnPBu3T28WSgqnbp07tm3z8ixY+YuWLRidXxSakZWXiHav0cHjklROf39hZcVnHdV3AoBn89XFpVaKK2tFghhtejxlW/un911/UDe5e1rDiVN2bFs2L4Vw/atHFm6dFzGlCErhvWdN6TTxIiARaP6r5w15YevT0mR2hqpTKlaRVgl4PMZVHQnXaOiWqZjqKBvGEZRSUjPTMrMTsnJS83MWRWfNC5qSofQTu4sL1+/9mit0rNvnyEjhk+ZMXtJ9Mo1iWtTM3Oy84uw5cYlm7dinfvN23Zm5Bak5OSdOnf+BYeNHS9P9xgKtGshlUGpTFoNpSLpH+dO3DiQceNAxpVdScdSZmz+YvDG+QPKFg0tXTouddrA5aN7zO3nF9XNd+Hovsunj7t85oRMJIAyKTo5I4UNbBhGT8LnigSV1UK28CMqGtmvokGvGZl0gQpU+/eJGpX07LwVq+NHRU5oHxiERyWiT+9+gwaOi5oyf+GSFavj16ZnZeYWoMuNsWWUBFROfnvu2ft3aqICJfDW6UMXtqzevmbCqrEd40cEJowIKJgekT2pS8HMgTkzBy8f0WV6d9bEzt6fD41YMG7I3SvnoUgAZYik7tDiGpqocERVWFI1iwoz8CWvRkBFhXzHN8BQSFBDUcnIyV+xOv6z4aM8fXzdvDzb+vsFdgzu1KVz9149+wzoP3rcxNnzv1i+cjVWsaAHgeMHxEo2b80pLE7LKzj81en/3r2lPl2S7Mg8vEFYC2+c2Pf95tgfdyRc2h7/dfb87UuHb5jbLzUyJHdK9/TJvZYOCZ7ayS0qnDV3QJeYySP/d+rQwa0b+ZzXYshHoKgGivARyp8uiaHCFvLf8iux96toagqS4YRUOkIFqlexUKOSlpUbHbumz4DBBFS69Yzo3b/fsFFjp82auyR6ZWxcYnJaJvriyPx1G9CWmOZRqYW1wtof9m25eSjr1pc5Nw5kfFew6MvEyftWjds4p1/OhPA1wwLndveaGtx6UrDHrJ4hswd1P7m1cEdBhr9nSzb7XwgFUimvEVFhOFGkxkFFWQdQo5KambN85epuPfsQUOnSo3vPvn0GDR0RNXXGwqXRMaviEtamoW9azS0szl+3oWjDJm2gggiQm18d+OVg5t3jBXePF3xXsOhg3MS9sZElc3pnjA1eOch3erjLpMCWEwJdp3YNnDmg86bk5esSY4dEhJXkp9QK3spE7xsLFQYSCukOFahGxYIeQ4H2VTBOMFQSU9LnLlgUEt7V1dPLw5vl69c+KKRjpy6dO3fvFtGnd79BQyInTp67YNGiZTFxSSnoUBh6InjRhk3YHsn8dRvSc/K/PP7Vf69fE/oqpN0VBcBIBNJaBEL+v7+f27rywamiJ0eKfyqNOZIwftuigdkTQ+I/azcv1GlSO/tJ/k3HBzSfEOw+bXD3L4Z2XTSkZ9+ADv0DffbkLIGiu7IaoVRShSA8aY1IIqqqEQpqhAICKhUCPv4Fd++43Bfv3j17/0Yj3mE4kZcxoBKfnDp99rzgTp1JUekzYPCIMeNmzJm/YPGyVfFJWMWSnV+EDhyjY8d5RevTsnIPHzv5z8uXKqMiRWoFCBTUSKHgv1Mbl17Ztub3Haln0qfvWDKwYGpY4nCfpRFtZnVwnNzWfmJ72/F+TSODWkf1CprbN2hm786tzK1HdvaPHRdSnjdJKnwGayukkqoPqPBrhHxqVN7wKp69f6MpVFSOxIhlDKisTkieOGV6QHAoKSq9+g0cNHTEpGkz53y+MDp2TVxSCnpmRUZOPnqqC9oMy84vSsnIPvDlsb+fP1cdFQmUVEMxny1589uWhElf5y/6Pmv+wRUjN87sljay3er+rp+Ht5jqZzvJ12582yaR7eyjQj1m9PCe1Tewl7+3vZn53KGdFgxyWTze6+TuNbD2laxGJK2p1g0qTJXSoAwPFfwK/KTM7KTM7JVrEsZPmhrYsRMelZDwsLCuXbr36tlnwOBBQ0dETpw8ffa8JdErV8UnoQeIoTOSKCrFGzfnFhYnp2XuO3Tk8b//yqNCAAaPCp4WqQRCMQIlT7/cFH983YqDydP3Rw8vndk9c5RfXF/XBaG2s4Lto3ytonxsx7dtMimoxbQuPnN6tp3SJ7i5BXBzNF8yxj9mlEv0ONf4WX5/3zoGJRgkdaiIcW8YxlB5L+C94VW8qmSrjArDCR01GipKuYQalZhVcWPGRwUEh7qzvL18fTBU0EGwXv0GDvhs2JjxUVNmzF64NHrlmoSk1Ax0kSU6I4kOHKOo7D345aN//sFQEfL4wg/LW2ihgkAoYVc//2b+yLCr+4u+XbesdHaPDVGdUgezVvds9UWI7cwOdpPbNZnc1j7Kz25qx5bTu/rO7OnXr4O3LQBDu3ouHu25YlTzmHGt4qJYO3LnQuRtjYinY1RUCP6JSKeoQFW9Qo3KshWrhowY7d8hxNPH17tdWwyVjmGdOnfvFtGnf79BQ4aPjpw4Zfr8hUuWrVi1JnEtdo4r9r5VFJXyfQcf/P03hkpVJbeqkqQZpggViNSK31797cyq3u0crh3ccGN36uY5PdaND17b3yOmc7MvgptMC2gy1c92avumU4McZ4a1mdWj/aSeIc2tzNrYg+iojktHt44dZR8zzjlxnNeqaWGP7n1dI+JoGxWmSqEpg0QlPi0jKTMbQ2VpTGy/QUP8O4T4tPfzad/Op307/w5BHUJDwrp26dKje/deffsMGPzZ8FHjoqbM+Xzh4uUrYuMSE9ampWRk499OjKKyY/e+B389xc6WZ3MrK6s+bvNS1GNBJCKJpLZWjMhEVc+ulT37ITrQBZzdkXt1z5qdXwwuHheaNogV281pSUfH+X4t5ra3n+VvOz24zYIuLlP7dWI5t3YAYGQX+xXj2qSPc8+c4Ll2dOvY8a6rJ7UrTRov4fwtEIqFIokEh4qwSsATCt6L6lB5y698VcnWCCrKhv2kZAyoLFi8rO/AzwKCQ9v6B7T192sX4B/YMZiAysAhw0ePm4iOg0XHrlmTuDY5LTMjJx+dkcRQ2bZrz/1HT5RFRYIIkZpaqUBYU/H478vpry6vDG4NStcuubo3+XTy9E2TuxaNC04dzFrZzWVRSJt5wS1nd3SaEeY9vat3iI+7NTD1bW4eN8k/Y0qbdTPbr5vZPncyK3GSx4rx3mumhv1x5URttZAOKi8471RAhalS6MsYUJm7YBGKSvvAoPaBAe0DAzqEhhBQ6T946PDRkVNmzJ73xeKlMbGrE5ITU9LRMymz8goxVLbsKL/34LGyqNTUCpEaGazmcv79+sml5e8ur+jUBnw+vvfl8oxrpasOLB9eOrN79piA1X3dlnXxnNPJbWpnr+ldA8Z2C7UGwN4UzB7aMS3KfeMMl83/b++8w5rK0gYedWb3252dZ2dndqqjjqPOiB2pgnRC7wmB0FsKgQAJJfRepXdC6Ejv2LGiKCi9SCf0HiAJQafsLt8fZybjYBkdRQnc3/P+ATx5Lpdz7o/3vOfcew/+CNXmeDyGz99oN0lrp6cxfzgJ9b/HCyuPOT8+Yr1YFeDJ66jyij255eANVbjvAQOqPFmuuPkFmFhgJGTkjwsI8x09xnf0yOHjx0BW4RcSFBQVET0tJS4lK6ugrKyupYs2MsfgCXYksPbPnQoLCY8KDA339A2gpmd19vTPLbJ+tYW5wGI++Zz9M9/L+vP/Vv7z+H+rPzLGOpLG7xLnbtlpCX9ydNdfbudG3U1xu+BnkEGQizIQcIN/43B6P0H6EFrsoL7IiS//sv0DGOy7j2AOWof8kV8kW3yd4XAy21Eo2fpwuOUBV91drnqHQghKP7NHVx6zf3zE4p7AOmWVV+zJLQePqbLmjS0eAUHOXj5oY7NTEtL8QqLHTgocO8kPUsoaVcCUsbauvrG5FZZAtHd0caS4e/j4g6mwoLAIoEoyLaP9Ye8aVZaW2S9Q5efHP/y4uvyfR6urC/30Ru+JuzaMG9bpgRr8335YEObdnON/N5FY5qWbhJX01zrsJMuHlTmmIsK38/2/fgiDnfwnzFV5pz9iVzDqi0TMvgp/qcoA6Xw34WSbQ4HG+11RB4MsxOYH7678sPT48dJve0Ww2EvL7NdXBRp9vRI8pgqZ4u7o4cV9Z7GXfxDZ3RNlYCwsJi4gIsYvJMgvJHhc4ORxgZNH+U/yCwlzVZGUhcvAldS1kQYm5pY4gq2DI7glDEyFgaLF0zcgkZrW2tn9hCrsBRZzgfVcVR6DL1ZX/vP4v4+H7041u03VERdrbfuvOR3dtePU7s/Pxbo2ZLhWBZmk2amEoEXctcWl93362V9hH8Jghz77wFN9XwzysxCtj0L1v4rHHbweo34tWq06SDaDzB9hxeeO3OeG/P7C2dDHj+cfP2L/wP4lpTCZTAab+WZVefWe3HLwmCqgYnH39gNPC3v7B5GcXXVQuuJSkiLiYoKiIvxCgicEBU4ICvALifILiQqKiguLSZySkJaQkZeUhSuqaoAxmDXRwcGJAioWMAzzDQxx9/aLTUxpauucX2IzmMsM5jK3aGGzOcss9sry74oWAIfDWfmJ89/H89OtmXMPKNO3STO3bJZqrVPc1IV3fyx8aDcFo10U753ka2ODkjv69T/2fvR//4LBpPb83QPJF2X8VZz+l5G6X4Sjv0rE7KtPRdWnomoilfMo4jHYIx6ove4GfCEktf9x5n7k/PAzk/OIyWaz2YwVNniia57DAjPFk4vzr6oKlFJeFd5WxdM3wN7RRUNHW1xKUkxSQljslICI8ElhoZPCQk+rclpaDq6spoXUMzKzxNnY2ZGdnVw9wKZ5voEh3v5Brp4+ETHx9Y0tswvMuUXWE7mFw2Jz2Gw2h8PhcJ6hCucR62fOGP1u9HyDy+hVwuw168mLJn1lWGfUoWPf7Dj03VeHD+45emjft19/tP8j2L73YXj5/dFWx1OxR+L1vojX+zJS94swvS8ijXe25pi0nzWrS0aU+8pS7QV8DPaRtHb6Wcutrsz+xHn0E+uXh+m5qsyyl0BKGWfMvo4qf6ontxy8oYod2Znk7Pq0Kl5+gTgbOy0kQl5JUVJW5pTEaWGxUyC3CIiIgeCqAubBNHR09QxNrPA2NvZkkrOri7uXm5cv2K2F4uEdGZtQ19AI7mafXWByVVlgsRlsJmuFs7zy20jsEZcV5k+srtG7gbN3yF1FBjOXsewbeMYFg94qbKwnXOf0p1J874l9CxPaBbOU+yqRIJJreySHsCvD4qtMw90pBnvCEZ8FIz8N0v2kNcfkYaFVY4bBhVD5LIqIn/FeF8QudyPhn+YGfmCzV1hsDoezsMx68r4vkE+AJyOzUyOzU6/aC5AqLwlvq+LpG4AlEDUROnBlJWl5OXEpSdHT4kKnRE8KCwmKioMQEZcUk5ThqqKujUQZGFtgrQl2JAcnirObJ9jYyNM3wMXdKyIm/k79gxnG0hpVGGz23DJz8TGH9SxVfvxhmT1TN3bPa/aWQ1eeXkOi/NIVS8YFfXqlfnOhVb6ndCBqp6/O55Fm3xd6yJa4CZaR9xcQPs+2/CrN6Otk9O4zOp8G6nwSgPhXU6ZhdxGmKdPwcgQ81100DPO9G3KXuwH/cMuN/3DYyyz28gpnnvO6qkCe/Dl4WxU3L18rvI26tpaSmipcWUlKTva0tBQoWoTFJECckpA+LS3HVUVVUwepb2higcHb2tuRnR0p7i7uXq6ePu7efmAHyVt19ZOzjDWqzC2zZznMhR84zEfPUOWnx+yx7tKxu5SJGnwTTaPM9eDSJYvZC6YTlaojhbqjBSY9mbptSer1kfI3/QQvexwsI39WZLM7C/t1mvmuBP2vQrU+8dX4p4/mh5nkY70luNYck5vxKsX+p8PxB330dzkj+S7nJfywyGAts5mPOHMrb1KV1+jJLQdPquLs5unh4+/h4+/u7WdqidXQ0VZQUVZQUZZTVJCQkRaTlBARFxMRlwQhLiULQkJGVlpeTlFVTV1bhzsPBo4Mjkl2cQsMDb968/bsAnNyljHDWOKWKwwWh8FmLy4/YrE5j5jsH9icH9ic5RX242XmTyz2j+yRoY6YmWangfPWDYny5aTdC+cN5y5hx8r1R4r0RvKR/ZmaXSla9yPh131PXHTjKyHtKbDbk269KxGzMxT1pbf6Jx4qH7mpfkSGvzdSRew6q18To1QerBRnK+5lJkxECcd4k1fmZ8HE1yznN0/A/SyTi/OgShmdm4ZUWT/epSov31XPU8XNy9fEAqOho62ioa6kpgoqltPSUqckTp+SkAaxRhW4spKqpoaeoYmZFc6a6GDr4Gjv6OLk6gFU8Q8Oq7lROz2/uEYVEEtrVWE+Xmb+zGL+vNRDbw6daSD1V2LvxEidc9o/f95g7hJ2tNx4qBg9kq8zmKnRnaLZGAG/6nvygvuRUtK+Artd6YSdCdidQXo7PdU/dVP52E3lYwth2GCFbUe+4Z1U/QwvFZzaMb7PYV/+DSZ85Fvm/OTTqoB88kqqQKOvP83bVmX1T/1Xe54qLu5eRmaWGjramggdFQ11RVUVGbi8pKyMuNRvyWSNKnKKCsrqagg9AyMzS4y1LcGORCQ5gRtnyC5uvoEhl6/dnJxlvEAV7qwxVxXmSP3AnZDpW6T+YrObIWKlxN2MK2ZcVegFun1Z2p2p6g3Rsld9T17yPFbh9F2B3d50690JmD1PqoI9Besus28rwt/NsdWX2fPhDtg/tsH+8T5s99cf/ed/y0+rAgx5eVUgT14H3lDF3tHF3tEF2AKqCzcvX3dvP0eKuy7aSBOh85KqSMhIy8DlFVVVtJB6ILHgbOyIJCdwcJKzq5df4KWrN4Aqk7OMqbmF38+DcRZYbCaTyWQyl1hM1vLiCmuRMzcb5WX98Hpkb5VdS7p+tevxMru9i9dwMxeshksNBwr1+s4iuzK1W1OU6mOkr/sLAlWKHb5Lt/4mCbcvSO9rripE6b/eTDZ6UEBM8dDc/wnsgx2wD7Zt/273Tn7+A0vsaQ6H86QqYNlxYmFunDELqhT69AR9euLNtjwEl3esykv22RpVnFw9wJbc9o4uSH1DbV2kti5STUtTSU1VVgEuLS8nISN9WlrumapIycnClZU0ESiUgbGpJdYKb0OwI9mRncGv8PQNuHDl2hpVnlyL/HX9nrnwqyr/XWZ//H8whNR3DYVuV6MQlaQj+fg98zesgSp9+bq9Z/VbaZoPkuXvRIv/poo9Xzp+f4LV/idVIcv9M4546no26eDnsJ1/hwns3WXpe5bFAAAZXUlEQVSFUMYZajoQTCZG+9lsNlBlhrUICpVXUgVKKa/JO1Bl9dW7bY0qYLzk4u5lTXRA6hsi9FAIPZSGjraqpgZcWUlWAS4lJyspC3+mKhIy0nKKCmpaCKS+oZGZpaklFmNta+vgSCQ5EUlO7t5+5y7VrFHl98MwNoPNBMHNKnLS4gd3/1vu0AfxdmJnLfdmmn4xV2sPVOk5i6CXWrZnIe4lydyKEnpClcPp+O/iLH5TxUPlY4riZ5qHYfBj2w/t/chMRSLRmRBij7AzlnMw1e1va2GxWEAVUM2/jipvtDO3Cu9GldVX7DlQfIN7HF3cvbgViyWOgNQ31EUb6aKNNHR0ldW1FFTUZRWUpeQUpOQUnh6DiUvJSssryimqqGhoa+vqG5iYA1WsiQ5EkpONPdnF3avy/KXx6TmgyvT84vT84uwCc2aBOctanmNzGCzOAnOFwWYyHi8triz88NPSY85ky81iAlr587/B/gWDIcT3khB7OLctxy+Y9Zfqtedq9JaZtp3F3ErUvhQpUxWgWO4rn0sRy3YUTcQfiDT/wg/xqYf6xx4qH7mq/ttB4R+6J2Bf/mU7XGh3QbxrgodRqL2WD1bJQVe6LN7/EWdmlrM4x16ZWmROLTInGIsTC4wny/rBybHBybE/bG1IlT8Hj6lCcnYFs1WgCjezwumijfQMTfQMTbR19VU1dZTUNOWVVGXgSjJwJQkZ+acDqKKoqqGJQOkbmRqZWVpgrcF9LuBFYVeu3+JmlWerwuIssJhLjxaXOYzHK7PtD2ruVGfcKEl1Mkd8/d72z2Aw/k9hjGue4+ct+kvQXXmIvirb7jK3mljjbA+JwkBktpd6upsi1RkeYysabMnnqfsVRfPfzuofO6v921HhQ4z43/a8B6OGkQsT3JK9TSMckQEENRJKJtjWcIU1NcNizC0tr1GFm1VeUpU33ZNbBR5Txd7RhfuYF8nZ1cQCgzIwRhuboY3NdFBodW2kioa2goq6nKIKN7esCaCKgoq6ho4uysDY0NTCHIPHEoi2Do5kF7fQiOjb9+5PzS08T5U5NmeBxV5iLS5zGJNjfVMjXdX5SZmRlEpacKovSY3/4L9gMPF9H0xd9xk/j+0vMWzPQfaUO3SX+l+KxCXYyZwNNEly0UpwUo+naCV7agdan6ag9jpqfUHS+Iyk8QVJ4ROC5D8FPt+eEkqqoPlSfc2jXfSCiZoOutKeFtqL00Mzi3OzDNYaVUbnpsHQC1JlXeENVcCULlcVULHY2JONza3MMXicjR3Oxs4SR7DAWhuaWoDQRRsh9Q25oYNCgy900Ub6RqYGJuZgwd7GnmxHdnbz8vX2D4qMTSgoKW9u7wLrKk/mlqn5xekl1hybM7fMXlxeYjHnmdP04qxkWpT3taKUBB+CD0bjDE7b10heX2qvPfr7sRv4oTLzzly9+1T1hixMS35AqT/B1+j02RDrEIJamK1GkJ1GjKsR1Q8X56obZCtP0j9BRB6xUfrWSvJLmYN/D3I2yThjn+hpHOGIDLRRpxgpOuortt6uWVhizM8xJxiLE4zFsTnG2PwcmCPmejIwMfripoZU+dPwsCrWRAczKxxYDAG3BvsGhoC7Xdy8fCke3iBcPX1cPX0oHt7g52BPPL+g0JDwKPB2ybgkampGdtbZgopzF+saGnsG6NxaZY0qM0z2DIvJYM0zZoZbb1zMjgl2sUSeTw2jBth6mMHjrFX9tQWcNA7l+GlMXcH2FZl35hreS9KoSdJ/UOBHczbx0pegUYx9DGVCsaqBNmrBtohoJ8vSxICiBD9asGNCgH2Uu4UfVhulzGehI04NIsS6ooEqPlaaJBQ8Lyl6cZHBmH2uKv3jI31jw6/Z1BDPY0Oo8odd+ExVcDZ21kSHkPCotKxcELTMnPTss9l5hdl5hbkFxQUl5QUl5XlFpXlFpUVllSUV1SUV1cXlVWVV5yvOXaw8f6nqwuVzl2ou1ly/Xlt3p/5BR3ff8PjUxMz8yMQ015OJubnp+cUJxuI0c2lqkTm1tDg1O5ZLi/HCGVTRIsOdLPLOOGcF27sbyKQ5IGJMZUgyh24nE+ll+PZsdEsmuvOsWVUU8n6BX7qbaY6PebyNtrO6QIKtjpuBRAzZwAEhl+RDKUyMzooMifRxCfMixfo4UqNcE4PsotzNI53REY66ATaagdZIO6SctwNuaXHuaVXGZ2fp0xP0iYn+kZHeUfqLm3p9OnNL8M5UWX0VW8BIiUhyAsv2ZBc3sosblkB0cvWgZeaUVp4rrTxXef5S9cUrF65cu3brDjdu3rl3p/7BnfoHdQ2Nd+83NTS13m9uu9/c1tze1f6wt/1hb0d338O+we7+ob6hkcGR8aHRiaHRidHJGeAJmFkCc7IgphhzY0P96ZG+9gjxqniPCDtUvLNxlifWQ1c6hWwcbKaMFvjHwHm31iL9+hSt2kRkR4nTVarj7WzvcIJiVQwxEq9qr3I0wV7HRpE/09eWYqzhYYnOjQ5OCPYgYdBEU6Sdma4L3jjExSLSE58e4UoNIoaQDLxwWs5mqnbGiLmxYcbC0tgcA8T0/OLEzPzY1Ozw+NTw+NTA8NjA8NpaBRp9vSl4TBVQ3zs4URycKOYYvCPFPTM3v/L8JRBAlRu373Kj9m5DfWNLfWPLvQfN95vbmto6m9u7mtu7gCFcSXoHh3sG6P300ZdRZaj3YWKQq62WaOEZcihBJxCrkepi5o6USrQ38NaXIasdaCsi30xRr4lSvByj05BrdzvL/WoqJRgjWxFlE2Aig5f7LhKvag0/Gm6r52Sg7GaCiPcgBTvhbYy1sChlS4QCFqVsa6BANlMLIBnHuGMyw8ipgfZnnC1cccZ97S1z8wsvrwrkyRvkXaqy+tJ9yVUFVOFgJGZqiXV288wtKK44d7Hi3MVzl2ouXLl2+drNmhu1NTdqb965d/POvdv37oM0Ut/Y0tja0dLxsLWzu7Wzu7Onv2eA3jNA7x0cHhge6xsaefmsQu/rjvVzckCepnlhvEwVnfWkoghIkqpwrA3KBy3rpXfiZrJlTZz6+TD581Fa19Osr6e7XEpyDMPD8wLNfQ0lcbIHAk1l7VX5I+31nAwVXQw0ggimFCsUXl/ZTEPaUlsOg4BjtSVt9eRdzDUcjVQCiYZRFEy8N9GDYNbZ2DD7qyd/qArkyZvlHauy+nIjaTDHZWNPBqo4OFFs7MnmGDzFwzs7r7Cs6nxZ1Xlu4VFzo/Z6bd312jqgyt37TXfvN9U3toCs0tTW2drZDUZfXb0DIKv0DY2ArAKutqHRiZGJ6fHpObBewX3F1tj8zMTczHB/T5y/cwBO09dCxQklSUKIuyIlbeAnfNDyfkayFO1DZz1VC7xkigOk8wMVq+PMLyY5lEcSkpx10jz03ZAiRKXDQWZyWLkDgVgVVzMlOw0pkq4iTkvaSFnURFnUTFXMTFXMSlnUUlnUUknEQlXcSl0Kry1nj1a1N0F2Nd2fnpkDnozOzk/MzI9OzgBPhkYnwB/ydKtCqrwRNpYqz+tRoAp3bteO7Iy3tTfH4D18/POKStdDFfrY5OjkzDNVGR3sy40PjnMx8bdSo6BlSAhxsoaovbJgoIlyJF7DRYsvBidOtTtFdRFMcpHICdYri8RRXXXLIqzjyZo+BhKuCGFPPTFbZT4fC3k3c0VrJRG80imMqpipioih/EkDOX4DOX4zuKAZXNBcQchM4ZSRvKiJgpi5qqShivRwTxc3qwxPz3LzyRpVIE/Wg3evyupL2GKFtwHzXdwnTKzwNlgC0T84rKCkvLTyHBiDVV24fP7y1Ys1169cv8VVpa6hEZQrYCTW1NbZ2tHT3jXQ0d3X1dvTNTTYMzTQ29s/RB8ZGKYPDI8MDNMHx0boE2ODk2PDM5Pg6UL69NTo3OzUInNmcXFufnJ+eqCmPMPXxoCIlCajpPFKAu56cvZqp+Ls0Y6aR0PNxcJMBEKx3wdb8mX5KmYGIENspXKC0GeIMuF28m5GJ530jlJ0T4VgVJxRYjjl42bwQ2byx8zgJ43kjhrJHUVLHzKS5dOX/s5Q5qCRBJ+BxFHU6eMIiRMGGrKzk2Nj0wtg4os+PTUyOzM0NQ4mvvpHRnrp9M7BPkiSdYI3VLHAWmOsbfG29jgbOzBxbIG1tiY6BJ+JfJ4q127duV5bd6uuHiQWrioPWtqb2x62tPe2dfXcaairunb5fM1l+uDQ0DC9b2hwYJjeP0LvH6cPTo4MTo4MTY3/cifi1Mzo7DxYIJ+dn5qdHW6svxpCNncyhLsZyjlpi1OQklYyR5OcDD1RJynq3/shD3vrnfBFn6A5qac464Vbq2d5WwRbKUXZakUQNEiaAj6GIiGWUt4Gws66xzGKB8yl9lvIHjaROmwidRgtcdBIcr/e6b1o8b0GYgf0xL9DnDqiIX40JtgLqPLkzZEDE6O9o3QQ3cODa1R5+125idkQqqz+UcVijsGDNIIlEK2JDqBQsbEnn4mKLSyteKYqNTdqwWRx7d0GkFiAKveb25paux40d92935RbeDY0PtrF0724sKh/cKB3cOBXVcYGJ39Z/x6emeSqMj6/MM6Yn2bMzM6NjQ53RLtiPc3V3NDSJHUhR00RU/F9FeEkJ/UjZKVvPdS+9dTm90MJx2Dg0dY6EXgdqrNpGEYzxhYVb492R0mHWAiFWgoHmwv6Ggs46Ryxhh+wkNxvInHIROKQodj3BmIH9ES+0RX6Rk/kW4ToPg0RPhXRI3evVs9OToxNzw/PTA7PTNKnJ4amxvvHR55UBUop6wdvqGJigQG2YKxtQXoxs8Lhbe1jE1O4tUpZ1flzl2rAPFjNjdqrN2+DiuVWXf2d+gcNTa1gXaWxteNBc0f9g/aaG7Up6VTPkACyqwvJ3oFKS33Y19vd39dLp/cMj/WPTfaPTfaNDQ9OjtGnJ+hTM/SpmV/qacbM+MzY1MxQUoCTh4UGXk3YRPaIldJJjLKAh6miseRegtIBkvIBa+WvyDr7XPX5QnCSYbbyoXjZM0R4HFktmaJ9hggPxpyMJIoFWfGHYESCrE556AnZKB0xPs1nKPa9wanvkALfahz9GnFyr9KRz5RPfIWSE8TpqSyM0afHR0amJrh3fPWPj4AV+u7hQciT9YY3VEEbm4FnS8yscGZWOEscwdDUwsaenJBCyysqLS6vAmvwYHXlwpVrF65cu3L9FgigSl1DI1haaWzt6HjY39jysPrileiEGCcfD4qXB8XZBYPD0sdGu/v7Wrq6WrsHO/qGO/qGOwZ6H9IH+saG+0bH+0bHByemBian+qemBifHRsYHr1QWuROtTNVlDNQlDDWlyrNj0WqnEbInDOAnzBWPGat9Y6z5LQZ50BUr6YaT8bZV8CEqBjtphLpo+dkre2KlvHDSvgQ5b5yED1bS01zOES1pqSJoIHNYTWCXyok9mkL71QW+1RDaoyy0S0n0IIPeOT88PD0+MjI98kxVIE/WG95QBaFngDIwBrc5GpiYG5tbIfUNsQRieHRcSlomLTOHlpmTkpaZTMtIpmUkUtPikqhRcYlgn8eYhOT45NSk1PREalpKWmZqRnZRadW9+20FJeWBYUEOHhSyq4udLRGLx52/fOl+c1NDS0t9SzeIB51tzd2dzd2dzQ97QDR199zv6W3pe9je297T29/T3dXb1tzf39Hd0zo61t/d09ryoLbjwc3ephtdjbeb667eqamqv3bxzuXqWxcqrleXXig+e74ot7oguyI3soAWVJgWnB1Hyo51SA4mJIfYRfliI7ytfMn6KEVBuPA+OcG9muL7UPJHHLG6zInemUH69MTwyMzQ06pAnrwFeEMVNS1NNS1NLSRCC4lA6KHAFyYWGDcvX/Aee7DxQ/CZ8OAzYSDCIiMiYqIjYqLBdtsg4pKocUlUWmZWWVVlMi3Vw8ebRCLh8XgMBmNuaeHt73fjdu3Fmiu36upv1dXfvd/U1Nba1Nba0tHe2tnR2tnR1tXZ1tXZ2dP9sK/3YV9vZ2dnZ2dnR0dHR0dbR2dLa1tja1sjoLW1tbWzo7G1pbm9rbOnv7m9q6XjYVNrV2tLd3NTe3NzY1NTU1NTU11d3b179+7du1dfX3+3vu5OQ23d/duFZfl2DpZ4grGpOdLESEtXWwFvhZ6dGube7zw4NT04Nd03PtE/MjIwTIemht8OG1GVpztbUVVFUVUFrqwEV1ZSUlNVVldTVldDG5s5u3mCDVLAdkLh0THh0VEguKqA3BIVlxibmJKQQotPTqWmZ6RlZVLT06Lj4+Lj4xMSElJTU9My0rPO5ubk54WEn/EPDgsMDfcPDgsMDQkMDQmNCAeHSqSmJFJTaJkZmbk5mbk5xcXFxcXFVVVVV69euXrtUu3t67W3r9+7d6+hoeH+/fsPWpobW1saW1ua2joftLQ/aGlv7ehpauxsqG9qaLjX0NDQ0NDQ2NhYX19fX1/f0NBwu672dv2tW3dvRMaFCwgegytIG5voW2PNCRhzT4pjU8NdcLdBS8fDlt6+lt6+1r7+jt5eyJO3xkZRZfWFtkjKykjKykjLy4G3E8kqwGUV4LpoI7KLG9h+Hmx9eiYq+kxUJIiwyIigsNCgsFC/oFC/oNCImHjuMCwplZZITUlKpaak0VJTU6lUam5ubm7e2Zz8vLyiwoKS4pz8opz8ooKS8uLysuLysoKS4tyC/NyC/IKS4oKS4ryiQqBKWlpaWlpacnJyfHxsfEJ0XHxUXHxUdHR0bGxsZGRkWGREWGREYGhIUFgE2JfCLzAsKDAiNCQiNDQ4PDw8PDw8Pj6eSqVSqdTMzMyz+XlZedm5hWddPCjf7Nm/f9/Bk/zCyopqPl7+KUm05CRafHIqiNgkamwSNS45NT455ckWi09Oik9Oelfdt+nhDVWExU5xQ0JG+rS01GlpKVVNHTMrHHgcBbx32NXTy9XTw93by93bi+LhTvFwd3KlODhRSM6u/sFhcUnURGpaIjUtkZqaTPsl8vLy8vLy8vPzC4oKgScFJcX5xWWFpRWFpRWllRWllRXl1VUgwLclFeVlVZVlVZXV1dXV1dWVlZXV1ZXV58p/iV+pOPdLcCfoyiovVFZcqig/V1FRBigpKamqqiopKcnPz8/IyqSmZyRSUxOpqakpudlZhZnphTk5pTk5xZmZRZmZBakZ2dT0LGp6FjU1g5qa8XQ+Af8j3lX3bXo2kCqrz69Yjpw4fuTE8WMn+Y+d5D8hKAC2HDotLYcyMMYSiAQ7EsGOZGNPtibaWRNtcTYEnA0BY43H2RCs8Djw0nvfwBBuVkmkplLT00DExMTExMQkJCTEJybEJiaAf8wJKbSk1PSk1HTwGa5X4NvUjHQQABqNlp5OS8+ggsj8lbSszLSszNSM9JS0TDCdkJqeQ0vNSaNlpaWlcklOTk5PT8/MzExLz6SmZafQslJoWanUDBotnZqSnpSankijxafQ4lNocUnUmITkmITk+Nikpz3xDw4CI8931XebHt5QZduO7bAd22HbYL8L2Hvv/+XvsB3vwba9D9u+A7btfdj2bbDt27gf2P7eDvAx2LbtMNh7v32x7bdDbfuVX34CjgDjHhP2oniSNd/CnvzY9t9OAPY+DPbeUx+FwWCwbdt2/HqS7+3YsW0bOOB2GGw7DPb+Nth7O7hn9bQnsG2wbb+2z7vqu03PxmrZ56nyzGtry7LWk9/zrvpu07OxWhZS5Q95sScwSJV1Y2O1LKTKH/JiT2CQKuvGxmpZSJUX84cpBQapsm5srJZ93nzxW74iNyDPKOWfwzvsvs3NhmvZZ9ryNi/KDcjLewKDVFk3NmLLPm3LW7soNyCv5AkMUmXd2KAt+0oXx+bmVZviXXfdpmXjtiykCuzVPYFBqqwbG7dlt6wqT4+4XqkR3nW/bVo2bstuTVVe0xMYpMq6sXFbdguq8vqewCBV1o2N27JbTZU34gkMUmXd2Lgtu6VUeX1DuLzrftu0bNyWfd7K/ebjaU/e9RlBPIONq8rq1rAF8oRX2NCqrG52W55ZnLzrk4J4NhtdldXNawvkCW8BqfIOgAzhRXhAldXNtfEn5AmPAqnyVoE84V0gVd4ekCc8De+pwrtX2Ob4K7YsvKHKKu8nFsgTXocnVeGtSw0ad20OeEaVVd60BfJk08BLqqzy2jAM8mQzAamyXkCebDJ4WJWNfPHxynlCvDw8psrqhr8KoWSyWYFUeWM8LcmGOj2I14T3VFndeLY8U5KNcGIQbxCeVGV1I9X3kCdbhM2gyju8LiFJtg68qsrqxkgskCRbB0iVN3MCkCebnk2iytu/UqFx11aDh1VZfXeJBfJkC7J5VHkL1ys02bWV4W1VVt9iYoEk2eJAqrzyb4E82ZpsKlXW6fKFPIFY3QSqrK5zYoEkgQBsNlXe7KUM5RMILptBldX1SSyQJBBPAqnyxweEPIFY3ZSqvP5lDXkC8TSbRJXVN2QLVJxAPI/No8rq69kCrcRDvJhNpcrqnypanicJ5AnEk2xmVf7wWockgXh5Npsqqy+XWCBJIF6VragKJAnEn2DLqQJ5AvHn2OSqrHEAMgTiT7MJVVl9TmKBPIF4HbaKKpAnEK/J5lRl9YVzXJAnEH+CTasKAJIE4k2xmVWB8gnEG2Qzq7K6AV6rB7Fp2OSqQEC8KSBVICBeCkgVCIiXAlIFAuKlgFSBgHgpIFUgIF6K/wcCA1hQvLoTywAAAABJRU5ErkJggg==" /></div>
<br />
<ul>
</ul>
<a href="https://www.blogger.com/blogger.g?blogID=5526699856389982581" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a>The developer wanted to schedule a complex hive query on our cdh 5.3.3 cluster (join over literal views over json serde table ) with <span style="color: #0b5394;">oozie </span><span style="color: #bf9000;">Hive </span>step. The query worked when running from regular hive client. The step failed on Oozie due to log4j permissions exception, before it started the hive query mapreduce. Not so indicative. <br />
<br />
<br />
After 2 days of debugging (Other queries have worked) we decided to try the <b>Hive 2 </b>step (It happened before that the basic hive step didn't work while Hive2 step did work).<br />
I remember that when upgrading to cdh 5.2, Cloudera wrote that it is recommended to migrate to hive2 step (<a href="http://www.cloudera.com/content/cloudera/en/documentation/core/v5-2-x/topics/cdh_rn_oozie_ic.html">the link</a>), but didn't write anything about specific functionality that won't work with regular hive step.<br />
<br />
We wrote a testing workflow xml with<b> hive2 step, ran it with Oozie CLI , and it worked</b>! That was great and made sense because hive2 is acting just as a regular hive client that connects the hive server. That's the natural thing to do, and I don't understand exactly how the basic hive step is working.<br />
<br />
Unfortunately, <b>The </b><b><b>Hue's </b>Oozie workflows editor doesn't support Hive2 step</b>. That's why manny people probably aren't familiar with this step.<br />
<br />
That's too bad cause we didn't want to force the developer writing and maintaining the Oozie workflow xml without having a convenience GUI or API (who wants to edit xml files?) (There is an api that someone from my organization had built but it supports only FS step, pig step and hive step <a href="https://github.com/pavel-lazar/pyoozie">pyoozie</a> ).<br />
<br />
The last resort was using a generic step from Hue. We copied the hive2 step block from the XML to the generic step text box on the workflow editor on hue, and it worked! Victory :)<br />
<br />
So remember to prefer Hive2 step with Generic step rather than the classic Oozie Hive step that is full of bugs and doesn't work right (not via hive-server). In addition, you can try the <a href="https://github.com/pavel-lazar/pyoozie">pyoozie</a>, that make it easier to create Oozie workflows from code.<br />
<br />
<br />
<br />
<ul>
</ul>
<br />
<br />
<br />
<br />
<br />
<br />moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-63194083876568142292015-06-04T11:49:00.000-07:002015-06-04T11:52:36.419-07:00Just for fun - my new Surprising alarm clock appI can't think of better place than my own blog to officially announce my new android application - a Surprising Alarm Clock.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY9bGCrcJMK3Nn7b0pcwCQUPFV6m_gPA6S0cS9tr-t_vOZc67mRcDw9u6ZiKgSr8_05X6QgEx_Jcz5Cz0ReyYqR7BhTwRGyRGT7jARL93rjgrAtAu7gxwOPPCFCCd3xlXNbLLduIXJ4ZI/s1600/surprising.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="117" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY9bGCrcJMK3Nn7b0pcwCQUPFV6m_gPA6S0cS9tr-t_vOZc67mRcDw9u6ZiKgSr8_05X6QgEx_Jcz5Cz0ReyYqR7BhTwRGyRGT7jARL93rjgrAtAu7gxwOPPCFCCd3xlXNbLLduIXJ4ZI/s400/surprising.jpg" width="400" /></a></div>
<br />
<br />
It won't take humanity to a better place, but it is fun and i love it :)<br />
<br />
The idea is to take a regular alarm clock and add a surprise feature. By surprise i mean choosing x surprise minutes so that the alarm would wake up somewhere between x minutes before the alarm and the alarm itself.<br />
<br />
It is good for people who don't have an exact time to wake up at, and find it amusing to find out what is the actual time that the alarm picked.<br />
<br />
<span style="font-size: large;">That's the <a href="https://play.google.com/store/apps/details?id=com.zebzeb.surprising_alarm">link </a>to the Google play store</span><br />
<br />
<br />
<br />
I believe that the feature would defiantly become a built in feature in any alarm clock, just like the Snooze button. In that case, i should start writing the SDK for alarm clock surprise :)moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-65298748574824161862015-05-26T12:55:00.005-07:002016-02-28T18:29:48.660-08:00Parquet Multiple Schema Output FormatFrom time to time (too many times) i am getting surprised by an open sourced development that lacks some basic features. I am getting even more surprised by the number of Google returned results (small one :) ) that correspond to that missing feature.<br />
<br />
One example is the Parquet multiple output format. <br />
<br />
<img alt="Apache Parquet logo" src="http://parquet.apache.org/assets/img/parquet_logo.png" height="53" width="200" /><br />
<br />
If you have more than 1 parquet schema to write within the same mapreduce, it is impossible to do that with the current combination of Multiple output format and the Parquet output format.<br />
<br />
How come that no one has ever needed to do that? We have found only 1 Google result of someone who asks about that and no one answered.<br />
<br />
The positive side is that it makes our job relevant and interesting. We, the engineers, can proudly tell stories about how we opened the code and implemented our own classes, contributing our part to the community. It is much more interesting than using ready to use products and integrating stuff. <br />
<br />
Now back to the parquet thing. The reason that you can't write different schemes within the same job is that the schema is being set in the Main class of the mapreduce. All the classes that are taking part of the parquet records writing are using the first schema that was set on the initiating of the Output Format.<br />
<br />
You have to find a way to override the schema from the Reducer, according to the current record schema.<br />
<br />
<br />
<br />
In order to do that, there are 3 places that you should take care of. <br />
<br />
1+2. on the ParquetOutputFormat.java class<br />
<br />
It is being called from the reducer in order to get the RecordWrtier. We got the schema out of the path, cause we had configuration with schema to path mapping<br />
<br />
We replaced the schema in the <span style="color: blue;">blue </span>parts:<br />
<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKjC7OGzpVfhRZcTYBNPL7JhpBNah6P3t_uUqWD16G23YJXGFMwzQRY8plaop4dQdMWyMLciOKnegCECPArbBs6OEJ9DkewsDBeO5cSjlN49l7FYS9__0i6wfstUBAbdYsLx_KkBT9vnXP/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public RecordWriter<Void, T> getRecordWriter(Configuration conf, Path file, CompressionCodecName codec)
throws IOException, InterruptedException {
final WriteSupport<T> writeSupport = getWriteSupport(conf);
CodecFactory codecFactory = new CodecFactory(conf);
long blockSize = getLongBlockSize(conf);
if (INFO) LOG.info("Parquet block size to " + blockSize);
int pageSize = getPageSize(conf);
if (INFO) LOG.info("Parquet page size to " + pageSize);
int dictionaryPageSize = getDictionaryPageSize(conf);
if (INFO) LOG.info("Parquet dictionary page size to " + dictionaryPageSize);
boolean enableDictionary = getEnableDictionary(conf);
if (INFO) LOG.info("Dictionary is " + (enableDictionary ? "on" : "off"));
boolean validating = getValidation(conf);
if (INFO) LOG.info("Validation is " + (validating ? "on" : "off"));
WriterVersion writerVersion = getWriterVersion(conf);
if (INFO) LOG.info("Writer version is: " + writerVersion);
WriteContext init = writeSupport.init(conf);
ParquetFileWriter w = new ParquetFileWriter(conf, <strike>init.getSchema()</strike> <span style="color: blue;"><b>our schema</b></span></code><code style="color: black; word-wrap: normal;"><b><span style="color: blue;"></span></b>, file);
w.start();
float maxLoad = conf.getFloat(ParquetOutputFormat.MEMORY_POOL_RATIO,
MemoryManager.DEFAULT_MEMORY_POOL_RATIO);
long minAllocation = conf.getLong(ParquetOutputFormat.MIN_MEMORY_ALLOCATION,
MemoryManager.DEFAULT_MIN_MEMORY_ALLOCATION);
if (memoryManager == null) {
memoryManager = new MemoryManager(maxLoad, minAllocation);
} else if (memoryManager.getMemoryPoolRatio() != maxLoad) {
LOG.warn("The configuration " + MEMORY_POOL_RATIO + " has been set. It should not " +
"be reset by the new value: " + maxLoad);
}
return new ParquetRecordWriter<T>(
w,
writeSupport,
<strike>init.getSchema()</strike> </code><code style="color: black; word-wrap: normal;"> <span style="color: blue;"><b>our schema</b></span></code><code style="color: black; word-wrap: normal;"></code><code style="color: black; word-wrap: normal;">,
init.getExtraMetaData(),
blockSize, pageSize,
codecFactory.getCompressor(codec, pageSize),
dictionaryPageSize,
enableDictionary,
validating,
writerVersion,
memoryManager);
} </code></pre>
<br />
<br />
3. on<b> GroupWriter </b>class. <b class="final-path" style="background-color: white; box-sizing: border-box; font-family: Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; line-height: 25.2000007629395px;"> </b>that is being called from the groupWriteSupport instance.<br />
<br />
<pre style="background: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKjC7OGzpVfhRZcTYBNPL7JhpBNah6P3t_uUqWD16G23YJXGFMwzQRY8plaop4dQdMWyMLciOKnegCECPArbBs6OEJ9DkewsDBeO5cSjlN49l7FYS9__0i6wfstUBAbdYsLx_KkBT9vnXP/s320/codebg.gif) rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 450.828125px;"><code style="word-wrap: normal;"> public void write(Group group) {
recordConsumer.startMessage(); <span style="color: blue;"><b>override schema with group.</b></span>
writeGroup(group, <strike>schema</strike> </code><code style="word-wrap: normal;"><span style="color: blue;"><code style="word-wrap: normal;">group.getType()</code></span>);
recordConsumer.endMessage();
} </code></pre>
<br />
<br />
<span class="css-truncate css-truncate-target">Thats it.</span><br />
<br />
<span class="css-truncate css-truncate-target">Thanks to <a href="https://www.linkedin.com/profile/view?id=241096864">Nimrod Parasol</a> and <a href="https://www.linkedin.com/profile/view?id=160400100">Kiril Yershov</a> </span><br />
<span class="css-truncate css-truncate-target">for the implementation. </span>
<!-- Twitter tweet button Start -->
<b:if cond="data:blog.pageType != "static_page"">
</b:if><br />
<div style="padding: 5px 5px 5px 0; text-align: left;">
<a class="twitter-share-button" data-count="vertical" data-related="" data-via="BloggerSentral" expr:data-text="data:post.title" expr:data-url="data:post.url" href="http://twitter.com/share">Tweet</a>
<script src="http://platform.twitter.com/widgets.js" type="text/javascript"></script>
</div>
<!-- Twitter tweet button End -->moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com1tag:blogger.com,1999:blog-5526699856389982581.post-30470384137602365292015-05-22T00:38:00.002-07:002015-05-22T00:38:59.021-07:00Migrating to Yarn On a Mapreduce and Impala Cluster<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value">Hey</span></span></span></span><br />
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br />I would like to share with you some insights from the yarn 2.5 migration we have gone through.<br />We are using cdh 5.2.1 with Impala and Mapreduce as the main workloads on the cluster.<br />Don't take it as a technical guide but as a technical experience with some interesting points we share.</span></span></span></span><br />
<ul>
<li><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><u><b>Let The Dogs Out</b></u>: The cluster is a multi-tenant cluster that is used
for Mapreduce and Cloudera Impala. Before migrating to yarn everything
worked great: we were allocating static resources to Impala (X g RAM on
a daemon, no limit for cpu) and y and z mappers and reducers for each
Mapreduce tasktracker while setting max java heap size for mappers and
reducers.We had enough memory for both Impala and Mapreduce jobs and no
problem with the cpu eaither: We set 14 mappers and 14 reducers per 32
cores node. The map and reduce slots are always full so it didn't
leave much cpu for the Impala process, but The good thing is that the
Impala didn't really care and it always had enough cpu. The charts
showed that the Impala uses 1/10 cpu in compare to the Mapreduce jobs.
We said that moving to yarn is like letting the dogs out - and we
don't know whether they will bite each other or play with each other.
</span></span></span></span></li>
</ul>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br /></span></span></span></span>
<ul>
<li><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"> <u><b>The Dogs Bite Each Other</b></u>: After migrating there were X
available VCORES for all applications (Impala and Mapreduce) on the
cluster. The mapreduce jobs behaved as expected and asked for 1 VCORE
per mapper/reducer. The problem was that the Impala asked for lots of
VCORES - much more than we expected and much higher than the Minimum
configuration of the fair scheduled we set for users. Simple query on a
parquet 100K rows table with stats, asked for X/2 VCORES (half of the
cluster capacity). We have around 2 queries per second (requires huge
amount of vcores). It all resulted in a 50 % Impala failures because of
5 minutes time-out of waiting for resources. In the other hand,
important Mapreduce jobs didn't get lots of vcores and spent lot of
time waiting for resources. We saw that users that are running
important mapreduce jobs gets 1/10 vcores than a simple Impala user
that decided to run some queries. That is an undesirable situation that
YARN brought.</span></span></span></span></li>
</ul>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br /></span></span></span></span>
<ul>
<li><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><b><u> An Impala CPU is not a Mapreduce CPU</u></b>: Why
doe's Impala ask for so many vcores? Cloudera is talking about lack of
table statistics. In addition, according to <a href="https://www.linkedin.com/pub/nimrod-parasol/68/8ab/794">Nimrod Parasol</a> </span></span></span></span><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value">Impala opens 1 thread per disk. We have 360
disks on cluster so every query was<span style="color: red;"><b> asking for 360 vcores from yarn, and
it is not acceptable in a total of 720 vcores cluster</b></span>.</span></span></span></span></span></span></span></span><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"></span></span></span></span></span></span></span></span><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"> </span></span></span></span>You can take this as an immature
implementation of LLAMA - the yarn and impala mediator. We see that
when all the cpu slots is used by mappers, the load average is
extremely high. But when they are used by impala queries, the load
average is quite low. The conclusion is that impala is using the cpu in
a lighter way than the mapreduce. So why should we allocate the same
vcores to both mapreduce and impala, and let the mapreduce jobs wait
for a cpu that is being used by Impala, but could have serve each other
at the same time.</span></span></span></span></li>
</ul>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br /></span></span></span></span>
<ul>
<li><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"> <b><u>Solutions</u></b>? Lets say that we have 1000
vcores on cluster. We were willing to set 1,000 vcores for mapreduce,
because setting it higher would result in a high load average on
servers (The mappers are usually cpu intensive). We were also willing
to set 10,000 vcores for the impala because we know that when it asks
for 100 vcores, it is probably physically using much less cpus. That
allocation would create a situation where we give virtual cpus to
impala, but it's ok cause we see that before yarn we gave all the cpus
to Mapreduce, and the Impala did great. The problem is that yarn and
llama won't allow us setting hierarchic queues - upper limits for
mapreduce and impala, and more allocations for applicative users inside
each pool </span></span></span></span><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"> (In order to use hierarchic queues, each user have to set it manually. eg. set "mapreduceQueue.userA") </span></span></span></span>.</span></span></span></span></li>
</ul>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br /></span></span></span></span>
<ul>
<li><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><u><b>Eventually, we gave up</b></u> on managing Impala resource with llama and yarn, and stayed with Mapreduce 2 (YARN) by itself.</span></span></span></span></li>
</ul>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br /></span></span></span></span>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br /></span></span></span></span>
<h3>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><u>2 More things you should be aware of:</u></span></span></span></span></h3>
<ul>
<li><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><u><b>High Load Average on cluster</b></u>: another thing that we saw is that after
moving to mapredcue 2, the servers load average got higher by almost
100% (250 with yarn, 150 without yarn). We think that the reason is
that more mappers are running simultaneously (because we only set
number of containers per tasktracker, but not number of mappers and
reducers ), in compare to when we set y mappers and z reducers per
servers, and mappers are usually heavier than reducers.</span></span></span></span></li>
</ul>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><br /></span></span></span></span>
<ul>
<li><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><u><b>Hive
on oozie issue</b></u>: After migrating to yarn, hive steps in oozie workflows
failed with "hive-site.xml (permission denied)". The message has lots
of references, but nothing that is related to yarn. Eventually the
solution was to create a new share lib that contains yarn jars,
cancelling the oozie share lib mapping parameter (that points to a
custom share lib directory ), and using the default shar elib path: The
latest /user/oozie/share/lib/lib<wbr></wbr>yyymmdd directory.</span></span></span></span><span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"> </span></span></span></span></li>
</ul>
<span href="http://distcp.quora.com/Migrating-to-Yarn-On-a-Mapreduce-and-Impala-Cluster#" id="__w2_QtgGpaQ_toggle_link"><span id="ld_ichkbz_50939"><span id="ld_ichkbz_50940"><span class="inline_editor_value"><b>Bottom line</b>: I don't find Impala mature enough to work with mapreduce
under YARN. A better scheduler and queues options (hierarchic pools),
or a better LLAMA implementation (So impala would ask for its real
vcores need) is required.<br /><br />Good luck everybody.</span></span></span></span>moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0tag:blogger.com,1999:blog-5526699856389982581.post-80712629971135990942015-05-22T00:22:00.000-07:002015-05-22T00:22:09.437-07:00Moving from quoraI finally moved from Quora (<a href="http://distcp.quora.com/">http://distcp.quora.com/</a>) to a more appropriate blog system.<br />
I have noticed that Quora blog system is very graphically minimalistic, and far from being user friendly.<br />
The reason that i picked it was that every time i searched Google for a technical or architectural issues i found Quora's results as the most professional answers, which were written by some of the leaders of these subjects in the world.<br />
My hopes were that some of these people would find my blog inside Quora and would even follow my blog.<br />
<br />
That didn't happen. I realized that it is hard to find my blog on Google, not even when searching my name together with the subjects i am writing about. Furthermore, i understood that people got into my blog only via my linked-in and tweeter shares which can be done from any blog system, so why not picking an easy to use and user friendly blog system, that in addition has more chances to get indexed by Google? <br />
<br />
So here i come Blogger.<br />
<br />
(A print screen of my previous blog, just to feel how it is like to upload an image to your blog :) )<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-636UT9D05JOzyk24b0j3Mx6nMr60lqXJSBa6fAx9-lihR34SRsGjEGc1c3EDkF2ZDlGRPAsBqix9A4WIM56j0iD3N_IlCpHuFp4HT23x3br2LiTQGC8UhmxiK_kDCnu5fhxCOxlUOrI/s1600/quorablog.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-636UT9D05JOzyk24b0j3Mx6nMr60lqXJSBa6fAx9-lihR34SRsGjEGc1c3EDkF2ZDlGRPAsBqix9A4WIM56j0iD3N_IlCpHuFp4HT23x3br2LiTQGC8UhmxiK_kDCnu5fhxCOxlUOrI/s320/quorablog.jpg" width="320" /></a></div>
.moscovighttp://www.blogger.com/profile/01356718357844206868noreply@blogger.com0