<?php
/**
* Pengakar: Indonesian stemmer
* (c) 2012–2015 Ivan Lanin <ivanlanin at gmail dot com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://w...content-available-to-author-only...u.org/licenses/>.
*/
namespace Kateglo;
/**
* Main class
*/
class Pengakar
{
private $dict;
private $rules;
private $options;
/**
* Konstruktor
*/
public function __construct()
{
// Read dictionary and create associative array
foreach ($tmp as $entry) {
$key = str_replace(' ', '', $attrib[0]); // remove space $this->dict[$key] = array('lemma' => $attrib[0], 'class' => $attrib[1]); }
// Options
'SORT_INSTANCE' => false, // sort by number of instances
'NO_NO_MATCH' => false, // hide no match entry
'NO_DIGIT_ONLY' => true, // hide digit only
'STRICT_CONFIX' => false, // use strict disallowed_confixes rules
);
// Define rules
$VOWEL = 'a|i|u|e|o'; // vowels
$CONSONANT = 'b|c|d|f|g|h|j|k|l|m|n|p|q|r|s|t|v|w|x|y|z'; // consonants
$ANY = $VOWEL . '|' . $CONSONANT; // any characters
),
array(0, "(di|ke|se)({$ANY})(.+)", ""), // 0 array(0, "(ber|ter)({$ANY})(.+)", ""), // 1, 6 normal array(0, "(be|te)(r)({$VOWEL})(.+)", ""), // 1, 6 be-rambut array(0, "(be|te)({$CONSONANT})({$ANY}?)(er)(.+)", ""), // 3, 7 te-bersit, te-percaya array(0, "(bel|pel)(ajar|unjur)", ""), // ajar, unjur array(0, "(me|pe)(l|m|n|r|w|y)(.+)", ""), // 10, 20: merawat, pemain array(0, "(mem|pem)(b|f|v)(.+)", ""), // 11 23: membuat, pembuat array(0, "(men|pen)(c|d|j|z)(.+)", ""), // 14 27: mencabut, pencabut array(0, "(meng|peng)(g|h|q|x)(.+)", ""), // 16 29: menggiring, penghasut array(0, "(meng|peng)({$VOWEL})(.+)", ""), // 17 30 meng-anjurkan, peng-anjur array(0, "(mem|pem)({$VOWEL})(.+)", "p"), // 13 26: memerkosa, pemerkosa array(0, "(men|pen)({$VOWEL})(.+)", "t"), // 15 28 menutup, penutup array(0, "(meng|peng)({$VOWEL})(.+)", "k"), // 17 30 mengalikan, pengali array(0, "(meny|peny)({$VOWEL})(.+)", "s"), // 18 31 menyucikan, penyucian array(0, "(mem)(p)({$CONSONANT})(.+)", ""), // memproklamasikan array(0, "(pem)({$CONSONANT})(.+)", "p"), // pemrogram array(0, "(men|pen)(t)({$CONSONANT})(.+)", ""), // mentransmisikan pentransmisian array(0, "(meng|peng)(k)({$CONSONANT})(.+)", ""), // mengkristalkan pengkristalan array(0, "(men|pen)(s)({$CONSONANT})(.+)", ""), // mensyaratkan pensyaratan array(0, "(menge|penge)({$CONSONANT})(.+)", ""), // swarabakti: mengepel array(0, "(mempe)(r)({$VOWEL})(.+)", ""), // 21 array(0, "(memper)({$ANY})(.+)", ""), // 21 array(0, "(pe)({$ANY})(.+)", ""), // 20 array(0, "(per)({$ANY})(.+)", ""), // 21 array(0, "(pel)({$CONSONANT})(.+)", ""), // 32 pelbagai, other? array(0, "(mem)(punya)", ""), // Exception: mempunya array(0, "(pen)(yair)", "s"), // Exception: penyair > syair ),
'disallowed_confixes' => array( ),
'be' => array('be-', 'ber-', 'bel-'), 'te' => array('te-', 'ter-', 'tel-'), 'pe' => array('pe-', 'per-', 'pel-', 'pen-', 'pem-', 'peng-', 'peny-', 'penge-'), 'me' => array('me-', 'men-', 'mem-', 'meng-', 'meny-', 'menge-'), ),
);
}
/**
* Ambil konten
*
* @param string $url
*
* @return string
*/
public function getContent($url)
{
// Curl
$agent = "Mozilla/5.0 (Windows; U; Windows NT 5.0; en; rv:1.9.0.4) Gecko/2009011913 Firefox/3.0.6";
$domain = 'http://' . parse_url($url, PHP_URL_HOST
); curl_setopt($curl, CURLOPT_REFERER
, $domain); // pseudo referer curl_setopt($curl, CURLOPT_USERAGENT
, $agent); // pseudo agent // Process HTML
$ret = $html;
$ret = preg_replace('/<(script|style)\b[^>]*>(.*?)<\/\1>/is', "", $ret); // remove script & style $ret = preg_replace('/<(br|p)[^>]*>/i', "\n", $ret); // new line for br & p $ret = preg_replace('/\n+/', "\n\n", $ret); // two new line: readability
return $ret;
}
/**
* Ambil hasil API
*
* @param string $query
*
* @return string
*/
public function getApi($query)
{
$words = $this->stem($query);
if ($query != '') {
} else {
$ret = 'API Pengakar<br /><br />' .
'Sintaks:<br />' .
'* <a href="./?api=1&q=pengakar">?api=1&q=...</a><br />' .
'* <a href="./?api=1&url=http://i...content-available-to-author-only...n.org/pengakar/">?api=1&url=...</a><br /><br />' .
'Hasil:<br />' .
'lemma => { <br />' .
' count,<br />' .
' roots => { <br />' .
' root => lemma, affixes {}, suffixes {}, prefixes {} <br />' .
' } <br />' .
'}' .
'';
}
return $ret;
}
/**
* Ambil hasil dalam format HTML
*
* @param string $query
*
* @return string
*/
public function getHtml($query)
{
$words = $this->stem($query);
$url_template = 'http://k...content-available-to-author-only...o.com/?mod=dict&action=view&phrase=%1$s';
// Render display
$word_count = count($words); foreach ($words as $key => $word) {
$roots = $word['roots'];
$root_count = count($roots); //if ($root_count <= 1) continue; // display disambig only
if ($word['count'] > 1) {
$instances = ' <span class="instance">x' . $word['count'] . '</span>';
} else {
$instances = '';
}
if ($root_count == 0) { // no match
'<li><span class="notfound">%s</span>%s</li>',
$key,
$instances
);
} else {
$i = 0;
foreach ($roots as $lemma => $attrib) {
$i++;
$affixes = $attrib['affixes'];
$url = sprintf($url_template, $attrib['lemma']); $lemma_url = sprintf('<a href="%s" target="kateglo">%s</a>', $url, $attrib['lemma']); $components .= $components ? '; ' : '';
if ($key == $lemma && $root_count == 1) { // is baseword
$components .= $lemma_url . $instances . $class;
} else {
// Multiroot
if ($root_count > 1 && $i == 1) {
$components .= $key . $instances . ': ';
}
if ($root_count > 1) {
$components .= "({$i}) ";
}
// Prefix, lemma, & suffix
$components .= implode('', $attrib['prefixes']); }
$components .= $lemma_url;
$components .= implode('', $attrib['suffixes']); }
// Single root
if ($root_count == 1) {
$components .= $instances;
}
}
}
$found .= sprintf('<li>%s</li>', $components); }
}
// Render display
if ($word_count >= 10) {
$ret .= '<div style="-webkit-column-count: 3; -moz-column-count: 3;">';
}
$ret .= '<ol style="margin:0;">';
$ret .= $lost;
$ret .= $found;
$ret .= '</ol>';
if ($word_count >= 10) {
$ret .= '</div>';
}
return $ret;
}
/**
* Tokenisasi
*
* @param string $query
*
* @return array
*/
private function stem($query)
{
$raw = preg_split('/[^a-zA-Z0-9\-]/', $query, -1, PREG_SPLIT_NO_EMPTY
); foreach ($raw as $r) {
// Remove all digit "word" if necessary
if ($this->options['NO_DIGIT_ONLY'] && preg_match('/^\d+$/', $r)) { continue;
}
$words[$key]['count']++;
}
foreach ($words as $key => $word) {
$words[$key]['roots'] = $this->stemWord($key);
// If NO_NO_MATCH, remove words that has no root
if (count($words[$key]['roots']) == 0 && $this->options['NO_NO_MATCH']) { continue;
}
$instances[$key] = $word['count'];
}
$word_count = count($words); if ($this->options['SORT_INSTANCE']) {
} else {
}
return $words;
}
/**
* Stem individual word
*
* @param string $word
*
* @return array
*/
private function stemWord($word)
{
// Preprocess: Create empty affix if original word is in lexicon
$roots = array($word => ''); $roots[$word]['affixes'] = array(); }
// Has dash? Try to also find root for each element
foreach ($dash_parts as $dash_part) {
$roots[$dash_part]['affixes'] = array(); }
}
// Process: Find suffixes, pronoun prefix, and other prefix (3 times, Asian)
foreach ($this->rules['affixes'] as $group) {
$is_suffix = $group[0];
$affixes = $group[1];
foreach ($affixes as $affix) {
$pattern = $is_suffix ? "(.+)({$affix})" : "({$affix})(.+)";
$this->addRoot($roots, array($is_suffix, $pattern, '')); }
}
for ($i = 0; $i < 3; $i++) {
foreach ($this->rules['prefixes'] as $rule) {
$this->addRoot($roots, $rule);
}
}
// Postprocess 1: Select valid affixes
foreach ($roots as $lemma => $attrib) {
// Not in dictionary? Unset and exit
continue;
}
// Escape if we don't have to check valid confix pairs
if (!$this->options['STRICT_CONFIX']) {
continue;
}
// Check confix pairs
$affixes = $attrib['affixes'];
foreach ($this->rules['disallowed_confixes'] as $pair) {
$prefix = $pair[0];
$suffix = $pair[1];
$prefix_key = substr($prefix, 0, 2); foreach ($this->rules['allomorphs'][$prefix_key] as $allomorf) {
}
}
}
}
}
// Postprocess 2: Handle suffixes and prefixes
foreach ($roots as $lemma => $attrib) {
$affixes = $attrib['affixes'];
$attrib['lemma'] = $this->dict[$lemma]['lemma'];
$attrib['class'] = $this->dict[$lemma]['class'];
// Divide affixes into suffixes and prefixes
foreach ($attrib['affixes'] as $affix) {
$type = (substr($affix, 0, 1) == '-') ?
'suffixes' : 'prefixes'; $attrib[$type][] = $affix;
}
// Reverse suffix order
}
$roots[$lemma] = $attrib;
}
return $roots;
}
/**
* Greedy algorithm: add every possible branch
*
* @param array $roots
* @param array $rule
*
* @return void
*/
private function addRoot(&$roots, $rule)
{
$is_suffix = $rule[0];
$pattern = '/^' . $rule[1] . '$/i';
$variant = $rule[2];
foreach ($roots as $lemma => $attrib) {
if (count($matches) > 0) { $affix_index = $is_suffix ? 2 : 1;
// Lemma
for ($i = 1; $i < count($matches); $i++) { if ($i != $affix_index) {
$new_lemma .= $matches[$i];
}
}
if ($variant) {
$new_lemma = $variant . $new_lemma;
}
// Affix, add - before (suffix), after (prefix)
$new_affix .= $is_suffix ? '-' : '';
$new_affix .= $matches[$affix_index];
$new_affix .= $is_suffix ? '' : '-';
$new_affix = array($new_affix); // make array if (is_array($attrib['affixes'])) { // merge $new_affix = array_merge($attrib['affixes'], $new_affix); }
// Push
$roots[$new_lemma] = array('affixes' => $new_affix); }
}
}
}
PD9waHAKLyoqCiAqIFBlbmdha2FyOiBJbmRvbmVzaWFuIHN0ZW1tZXIKICogKGMpIDIwMTLigJMyMDE1IEl2YW4gTGFuaW4gPGl2YW5sYW5pbiBhdCBnbWFpbCBkb3QgY29tPgogKgogKiBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQogKiBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieQogKiB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvcgogKiAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLgogKgogKiBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKICogYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKICogTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiBTZWUgdGhlCiAqIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiAqCiAqIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiAqIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtLiBJZiBub3QsIHNlZSA8aHR0cDovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLnUub3JnL2xpY2Vuc2VzLz4uCiAqLwpuYW1lc3BhY2UgS2F0ZWdsbzsKCi8qKgogKiBNYWluIGNsYXNzCiAqLwpjbGFzcyBQZW5nYWthcgp7CiAgICBwcml2YXRlICRkaWN0OwogICAgcHJpdmF0ZSAkcnVsZXM7CiAgICBwcml2YXRlICRvcHRpb25zOwoKICAgIC8qKgogICAgICogS29uc3RydWt0b3IKICAgICAqLwogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KCkKICAgIHsKICAgICAgICAvLyBSZWFkIGRpY3Rpb25hcnkgYW5kIGNyZWF0ZSBhc3NvY2lhdGl2ZSBhcnJheQogICAgICAgICRkaWN0ID0gZmlsZV9nZXRfY29udGVudHMoJy4vZGF0YS9rYW11cy50eHQnKTsKICAgICAgICAkdG1wID0gZXhwbG9kZSgiXG4iLCAkZGljdCk7CiAgICAgICAgZm9yZWFjaCAoJHRtcCBhcyAkZW50cnkpIHsKICAgICAgICAgICAgJGF0dHJpYiA9IGV4cGxvZGUoIlx0Iiwgc3RydG9sb3dlcigkZW50cnkpKTsgLy8gMDogbGVtbWE7IDE6IGNsYXNzCiAgICAgICAgICAgICRrZXkgPSBzdHJfcmVwbGFjZSgnICcsICcnLCAkYXR0cmliWzBdKTsgLy8gcmVtb3ZlIHNwYWNlCiAgICAgICAgICAgICR0aGlzLT5kaWN0WyRrZXldID0gYXJyYXkoJ2xlbW1hJyA9PiAkYXR0cmliWzBdLCAnY2xhc3MnID0+ICRhdHRyaWJbMV0pOwogICAgICAgIH0KICAgICAgICAvLyBPcHRpb25zCiAgICAgICAgJHRoaXMtPm9wdGlvbnMgPSBhcnJheSgKICAgICAgICAgICAgJ1NPUlRfSU5TVEFOQ0UnID0+IGZhbHNlLCAvLyBzb3J0IGJ5IG51bWJlciBvZiBpbnN0YW5jZXMKICAgICAgICAgICAgJ05PX05PX01BVENIJyAgID0+IGZhbHNlLCAvLyBoaWRlIG5vIG1hdGNoIGVudHJ5CiAgICAgICAgICAgICdOT19ESUdJVF9PTkxZJyA9PiB0cnVlLCAvLyBoaWRlIGRpZ2l0IG9ubHkKICAgICAgICAgICAgJ1NUUklDVF9DT05GSVgnID0+IGZhbHNlLCAvLyB1c2Ugc3RyaWN0IGRpc2FsbG93ZWRfY29uZml4ZXMgcnVsZXMKICAgICAgICApOwogICAgICAgIC8vIERlZmluZSBydWxlcwogICAgICAgICRWT1dFTCA9ICdhfGl8dXxlfG8nOyAvLyB2b3dlbHMKICAgICAgICAkQ09OU09OQU5UID0gJ2J8Y3xkfGZ8Z3xofGp8a3xsfG18bnxwfHF8cnxzfHR8dnx3fHh8eXx6JzsgLy8gY29uc29uYW50cwogICAgICAgICRBTlkgPSAkVk9XRUwgLiAnfCcgLiAkQ09OU09OQU5UOyAvLyBhbnkgY2hhcmFjdGVycwogICAgICAgICR0aGlzLT5ydWxlcyA9IGFycmF5KAogICAgICAgICAgICAnYWZmaXhlcycgPT4gYXJyYXkoCiAgICAgICAgICAgICAgICBhcnJheSgxLCBhcnJheSgna2FoJywgJ2xhaCcsICd0YWgnLCAncHVuJykpLAogICAgICAgICAgICAgICAgYXJyYXkoMSwgYXJyYXkoJ211JywgJ2t1JywgJ255YScpKSwKICAgICAgICAgICAgICAgIGFycmF5KDAsIGFycmF5KCdrdScsICdrYXUnKSksCiAgICAgICAgICAgICAgICBhcnJheSgxLCBhcnJheSgnaScsICdrYW4nLCAnYW4nKSksCiAgICAgICAgICAgICksCiAgICAgICAgICAgICdwcmVmaXhlcycgPT4gYXJyYXkoCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKGRpfGtlfHNlKSh7JEFOWX0pKC4rKSIsICIiKSwgLy8gMAogICAgICAgICAgICAgICAgYXJyYXkoMCwgIihiZXJ8dGVyKSh7JEFOWX0pKC4rKSIsICIiKSwgLy8gMSwgNiBub3JtYWwKICAgICAgICAgICAgICAgIGFycmF5KDAsICIoYmV8dGUpKHIpKHskVk9XRUx9KSguKykiLCAiIiksIC8vIDEsIDYgYmUtcmFtYnV0CiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKGJlfHRlKSh7JENPTlNPTkFOVH0pKHskQU5ZfT8pKGVyKSguKykiLCAiIiksIC8vIDMsIDcgdGUtYmVyc2l0LCB0ZS1wZXJjYXlhCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKGJlbHxwZWwpKGFqYXJ8dW5qdXIpIiwgIiIpLCAvLyBhamFyLCB1bmp1cgogICAgICAgICAgICAgICAgYXJyYXkoMCwgIihtZXxwZSkobHxtfG58cnx3fHkpKC4rKSIsICIiKSwgLy8gMTAsIDIwOiBtZXJhd2F0LCBwZW1haW4KICAgICAgICAgICAgICAgIGFycmF5KDAsICIobWVtfHBlbSkoYnxmfHYpKC4rKSIsICIiKSwgLy8gMTEgMjM6IG1lbWJ1YXQsIHBlbWJ1YXQKICAgICAgICAgICAgICAgIGFycmF5KDAsICIobWVufHBlbikoY3xkfGp8eikoLispIiwgIiIpLCAvLyAxNCAyNzogbWVuY2FidXQsIHBlbmNhYnV0CiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKG1lbmd8cGVuZykoZ3xofHF8eCkoLispIiwgIiIpLCAvLyAxNiAyOTogbWVuZ2dpcmluZywgcGVuZ2hhc3V0CiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKG1lbmd8cGVuZykoeyRWT1dFTH0pKC4rKSIsICIiKSwgLy8gMTcgMzAgbWVuZy1hbmp1cmthbiwgcGVuZy1hbmp1cgogICAgICAgICAgICAgICAgYXJyYXkoMCwgIihtZW18cGVtKSh7JFZPV0VMfSkoLispIiwgInAiKSwgLy8gMTMgMjY6IG1lbWVya29zYSwgcGVtZXJrb3NhCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKG1lbnxwZW4pKHskVk9XRUx9KSguKykiLCAidCIpLCAvLyAxNSAyOCBtZW51dHVwLCBwZW51dHVwCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKG1lbmd8cGVuZykoeyRWT1dFTH0pKC4rKSIsICJrIiksIC8vIDE3IDMwIG1lbmdhbGlrYW4sIHBlbmdhbGkKICAgICAgICAgICAgICAgIGFycmF5KDAsICIobWVueXxwZW55KSh7JFZPV0VMfSkoLispIiwgInMiKSwgLy8gMTggMzEgbWVueXVjaWthbiwgcGVueXVjaWFuCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKG1lbSkocCkoeyRDT05TT05BTlR9KSguKykiLCAiIiksIC8vIG1lbXByb2tsYW1hc2lrYW4KICAgICAgICAgICAgICAgIGFycmF5KDAsICIocGVtKSh7JENPTlNPTkFOVH0pKC4rKSIsICJwIiksIC8vIHBlbXJvZ3JhbQogICAgICAgICAgICAgICAgYXJyYXkoMCwgIihtZW58cGVuKSh0KSh7JENPTlNPTkFOVH0pKC4rKSIsICIiKSwgLy8gbWVudHJhbnNtaXNpa2FuIHBlbnRyYW5zbWlzaWFuCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKG1lbmd8cGVuZykoaykoeyRDT05TT05BTlR9KSguKykiLCAiIiksIC8vIG1lbmdrcmlzdGFsa2FuIHBlbmdrcmlzdGFsYW4KICAgICAgICAgICAgICAgIGFycmF5KDAsICIobWVufHBlbikocykoeyRDT05TT05BTlR9KSguKykiLCAiIiksIC8vIG1lbnN5YXJhdGthbiBwZW5zeWFyYXRhbgogICAgICAgICAgICAgICAgYXJyYXkoMCwgIihtZW5nZXxwZW5nZSkoeyRDT05TT05BTlR9KSguKykiLCAiIiksIC8vIHN3YXJhYmFrdGk6IG1lbmdlcGVsCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKG1lbXBlKShyKSh7JFZPV0VMfSkoLispIiwgIiIpLCAvLyAyMQogICAgICAgICAgICAgICAgYXJyYXkoMCwgIihtZW1wZXIpKHskQU5ZfSkoLispIiwgIiIpLCAvLyAyMQogICAgICAgICAgICAgICAgYXJyYXkoMCwgIihwZSkoeyRBTll9KSguKykiLCAiIiksIC8vIDIwCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKHBlcikoeyRBTll9KSguKykiLCAiIiksIC8vIDIxCiAgICAgICAgICAgICAgICBhcnJheSgwLCAiKHBlbCkoeyRDT05TT05BTlR9KSguKykiLCAiIiksIC8vIDMyIHBlbGJhZ2FpLCBvdGhlcj8KICAgICAgICAgICAgICAgIGFycmF5KDAsICIobWVtKShwdW55YSkiLCAiIiksIC8vIEV4Y2VwdGlvbjogbWVtcHVueWEKICAgICAgICAgICAgICAgIGFycmF5KDAsICIocGVuKSh5YWlyKSIsICJzIiksIC8vIEV4Y2VwdGlvbjogcGVueWFpciA+IHN5YWlyCiAgICAgICAgICAgICksCiAgICAgICAgICAgICdkaXNhbGxvd2VkX2NvbmZpeGVzJyA9PiBhcnJheSgKICAgICAgICAgICAgICAgIGFycmF5KCdiZXItJywgJy1pJyksCiAgICAgICAgICAgICAgICBhcnJheSgna2UtJywgJy1pJyksCiAgICAgICAgICAgICAgICBhcnJheSgncGUtJywgJy1rYW4nKSwKICAgICAgICAgICAgICAgIGFycmF5KCdkaS0nLCAnLWFuJyksCiAgICAgICAgICAgICAgICBhcnJheSgnbWVuZy0nLCAnLWFuJyksCiAgICAgICAgICAgICAgICBhcnJheSgndGVyLScsICctYW4nKSwKICAgICAgICAgICAgICAgIGFycmF5KCdrdS0nLCAnLWFuJyksCiAgICAgICAgICAgICksCiAgICAgICAgICAgICdhbGxvbW9ycGhzJyA9PiBhcnJheSgKICAgICAgICAgICAgICAgICdiZScgPT4gYXJyYXkoJ2JlLScsICdiZXItJywgJ2JlbC0nKSwKICAgICAgICAgICAgICAgICd0ZScgPT4gYXJyYXkoJ3RlLScsICd0ZXItJywgJ3RlbC0nKSwKICAgICAgICAgICAgICAgICdwZScgPT4gYXJyYXkoJ3BlLScsICdwZXItJywgJ3BlbC0nLCAncGVuLScsICdwZW0tJywgJ3BlbmctJywgJ3BlbnktJywgJ3BlbmdlLScpLAogICAgICAgICAgICAgICAgJ21lJyA9PiBhcnJheSgnbWUtJywgJ21lbi0nLCAnbWVtLScsICdtZW5nLScsICdtZW55LScsICdtZW5nZS0nKSwKICAgICAgICAgICAgKSwKICAgICAgICApOwoKICAgIH0KCiAgICAvKioKICAgICAqIEFtYmlsIGtvbnRlbgogICAgICoKICAgICAqIEBwYXJhbSBzdHJpbmcgJHVybAogICAgICoKICAgICAqIEByZXR1cm4gc3RyaW5nCiAgICAgKi8KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRDb250ZW50KCR1cmwpCiAgICB7CiAgICAgICAgLy8gQ3VybAogICAgICAgICRhZ2VudCA9ICJNb3ppbGxhLzUuMCAoV2luZG93czsgVTsgV2luZG93cyBOVCA1LjA7IGVuOyBydjoxLjkuMC40KSBHZWNrby8yMDA5MDExOTEzIEZpcmVmb3gvMy4wLjYiOwogICAgICAgICRkb21haW4gPSAnaHR0cDovLycgLiBwYXJzZV91cmwoJHVybCwgUEhQX1VSTF9IT1NUKTsKICAgICAgICAkY3VybCA9IGN1cmxfaW5pdCgpOwogICAgICAgIGN1cmxfc2V0b3B0KCRjdXJsLCBDVVJMT1BUX1VSTCwgJHVybCk7CiAgICAgICAgY3VybF9zZXRvcHQoJGN1cmwsIENVUkxPUFRfUkVUVVJOVFJBTlNGRVIsIDEpOwogICAgICAgIGN1cmxfc2V0b3B0KCRjdXJsLCBDVVJMT1BUX1JFRkVSRVIsICRkb21haW4pOyAvLyBwc2V1ZG8gcmVmZXJlcgogICAgICAgIGN1cmxfc2V0b3B0KCRjdXJsLCBDVVJMT1BUX1VTRVJBR0VOVCwgJGFnZW50KTsgLy8gcHNldWRvIGFnZW50CiAgICAgICAgJGh0bWwgPSBjdXJsX2V4ZWMoJGN1cmwpOwogICAgICAgIGN1cmxfY2xvc2UoJGN1cmwpOwogICAgICAgIC8vIFByb2Nlc3MgSFRNTAogICAgICAgICRyZXQgPSAkaHRtbDsKICAgICAgICAkcmV0ID0gcHJlZ19yZXBsYWNlKCcvPChzY3JpcHR8c3R5bGUpXGJbXj5dKj4oLio/KTxcL1wxPi9pcycsICIiLCAkcmV0KTsgLy8gcmVtb3ZlIHNjcmlwdCAmIHN0eWxlCiAgICAgICAgJHJldCA9IHByZWdfcmVwbGFjZSgnLzwoYnJ8cClbXj5dKj4vaScsICJcbiIsICRyZXQpOyAvLyBuZXcgbGluZSBmb3IgYnIgJiBwCiAgICAgICAgJHJldCA9IHRyaW0oc3RyaXBfdGFncygkcmV0KSk7IC8vIHN0cmlwIHRhZ3MKICAgICAgICAkcmV0ID0gcHJlZ19yZXBsYWNlKCcvXlxzKi9tJywgJycsICRyZXQpOyAvLyB0cmltIGxlZnQKICAgICAgICAkcmV0ID0gcHJlZ19yZXBsYWNlKCcvXHMqJC9tJywgJycsICRyZXQpOyAvLyB0cmltIHJpZ2h0CiAgICAgICAgJHJldCA9IHByZWdfcmVwbGFjZSgnL1xuKy8nLCAiXG5cbiIsICRyZXQpOyAvLyB0d28gbmV3IGxpbmU6IHJlYWRhYmlsaXR5CgogICAgICAgIHJldHVybiAkcmV0OwogICAgfQoKICAgIC8qKgogICAgICogQW1iaWwgaGFzaWwgQVBJCiAgICAgKgogICAgICogQHBhcmFtIHN0cmluZyAkcXVlcnkKICAgICAqCiAgICAgKiBAcmV0dXJuIHN0cmluZwogICAgICovCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0QXBpKCRxdWVyeSkKICAgIHsKICAgICAgICAkd29yZHMgPSAkdGhpcy0+c3RlbSgkcXVlcnkpOwogICAgICAgIGlmICgkcXVlcnkgIT0gJycpIHsKICAgICAgICAgICAgJHJldCA9IGpzb25fZW5jb2RlKCR3b3Jkcyk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgJHJldCA9ICdBUEkgUGVuZ2FrYXI8YnIgLz48YnIgLz4nIC4KICAgICAgICAgICAgICAgICdTaW50YWtzOjxiciAvPicgLgogICAgICAgICAgICAgICAgJyogPGEgaHJlZj0iLi8/YXBpPTEmcT1wZW5nYWthciI+P2FwaT0xJnE9Li4uPC9hPjxiciAvPicgLgogICAgICAgICAgICAgICAgJyogPGEgaHJlZj0iLi8/YXBpPTEmdXJsPWh0dHA6Ly9pLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5uLm9yZy9wZW5nYWthci8iPj9hcGk9MSZ1cmw9Li4uPC9hPjxiciAvPjxiciAvPicgLgogICAgICAgICAgICAgICAgJ0hhc2lsOjxiciAvPicgLgogICAgICAgICAgICAgICAgJ2xlbW1hID0+IHsgPGJyIC8+JyAuCiAgICAgICAgICAgICAgICAnJm5ic3A7Jm5ic3A7Y291bnQsPGJyIC8+JyAuCiAgICAgICAgICAgICAgICAnJm5ic3A7Jm5ic3A7cm9vdHMgPT4geyA8YnIgLz4nIC4KICAgICAgICAgICAgICAgICcmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDtyb290ID0+IGxlbW1hLCBhZmZpeGVzIHt9LCBzdWZmaXhlcyB7fSwgcHJlZml4ZXMge30gPGJyIC8+JyAuCiAgICAgICAgICAgICAgICAnJm5ic3A7Jm5ic3A7fSA8YnIgLz4nIC4KICAgICAgICAgICAgICAgICd9JyAuCiAgICAgICAgICAgICAgICAnJzsKICAgICAgICB9CgogICAgICAgIHJldHVybiAkcmV0OwogICAgfQoKICAgIC8qKgogICAgICogQW1iaWwgaGFzaWwgZGFsYW0gZm9ybWF0IEhUTUwKICAgICAqCiAgICAgKiBAcGFyYW0gc3RyaW5nICRxdWVyeQogICAgICoKICAgICAqIEByZXR1cm4gc3RyaW5nCiAgICAgKi8KICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRIdG1sKCRxdWVyeSkKICAgIHsKICAgICAgICAkd29yZHMgPSAkdGhpcy0+c3RlbSgkcXVlcnkpOwogICAgICAgICR1cmxfdGVtcGxhdGUgPSAnaHR0cDovL2suLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLm8uY29tLz9tb2Q9ZGljdCZhY3Rpb249dmlldyZwaHJhc2U9JTEkcyc7CgogICAgICAgIC8vIFJlbmRlciBkaXNwbGF5CiAgICAgICAgJHdvcmRfY291bnQgPSBjb3VudCgkd29yZHMpOwogICAgICAgIGZvcmVhY2ggKCR3b3JkcyBhcyAka2V5ID0+ICR3b3JkKSB7CiAgICAgICAgICAgICRyb290cyA9ICR3b3JkWydyb290cyddOwogICAgICAgICAgICAkcm9vdF9jb3VudCA9IGNvdW50KCRyb290cyk7CiAgICAgICAgICAgIC8vaWYgKCRyb290X2NvdW50IDw9IDEpIGNvbnRpbnVlOyAvLyBkaXNwbGF5IGRpc2FtYmlnIG9ubHkKICAgICAgICAgICAgaWYgKCR3b3JkWydjb3VudCddID4gMSkgewogICAgICAgICAgICAgICAgJGluc3RhbmNlcyA9ICcgPHNwYW4gY2xhc3M9Imluc3RhbmNlIj54JyAuICR3b3JkWydjb3VudCddIC4gJzwvc3Bhbj4nOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgJGluc3RhbmNlcyA9ICcnOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmICgkcm9vdF9jb3VudCA9PSAwKSB7IC8vIG5vIG1hdGNoCiAgICAgICAgICAgICAgICAkbG9zdCAuPSBzcHJpbnRmKAogICAgICAgICAgICAgICAgICAgICc8bGk+PHNwYW4gY2xhc3M9Im5vdGZvdW5kIj4lczwvc3Bhbj4lczwvbGk+JywKICAgICAgICAgICAgICAgICAgICAka2V5LAogICAgICAgICAgICAgICAgICAgICRpbnN0YW5jZXMKICAgICAgICAgICAgICAgICk7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAkaSA9IDA7CiAgICAgICAgICAgICAgICB1bnNldCgkY29tcG9uZW50cyk7CiAgICAgICAgICAgICAgICBmb3JlYWNoICgkcm9vdHMgYXMgJGxlbW1hID0+ICRhdHRyaWIpIHsKICAgICAgICAgICAgICAgICAgICAkaSsrOwogICAgICAgICAgICAgICAgICAgICRhZmZpeGVzID0gJGF0dHJpYlsnYWZmaXhlcyddOwogICAgICAgICAgICAgICAgICAgICR1cmwgPSBzcHJpbnRmKCR1cmxfdGVtcGxhdGUsICRhdHRyaWJbJ2xlbW1hJ10pOwogICAgICAgICAgICAgICAgICAgICRsZW1tYV91cmwgPSBzcHJpbnRmKCc8YSBocmVmPSIlcyIgdGFyZ2V0PSJrYXRlZ2xvIj4lczwvYT4nLCAkdXJsLCAkYXR0cmliWydsZW1tYSddKTsKICAgICAgICAgICAgICAgICAgICAkY29tcG9uZW50cyAuPSAkY29tcG9uZW50cyA/ICc7ICcgOiAnJzsKICAgICAgICAgICAgICAgICAgICBpZiAoJGtleSA9PSAkbGVtbWEgJiYgJHJvb3RfY291bnQgPT0gMSkgeyAvLyBpcyBiYXNld29yZAogICAgICAgICAgICAgICAgICAgICAgICAkY29tcG9uZW50cyAuPSAkbGVtbWFfdXJsIC4gJGluc3RhbmNlcyAuICRjbGFzczsKICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAvLyBNdWx0aXJvb3QKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCRyb290X2NvdW50ID4gMSAmJiAkaSA9PSAxKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkY29tcG9uZW50cyAuPSAka2V5IC4gJGluc3RhbmNlcyAuICc6ICc7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCRyb290X2NvdW50ID4gMSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgJGNvbXBvbmVudHMgLj0gIih7JGl9KSAiOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFByZWZpeCwgbGVtbWEsICYgc3VmZml4CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpc19hcnJheSgkYXR0cmliWydwcmVmaXhlcyddKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgJGNvbXBvbmVudHMgLj0gaW1wbG9kZSgnJywgJGF0dHJpYlsncHJlZml4ZXMnXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgJGNvbXBvbmVudHMgLj0gJGxlbW1hX3VybDsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGlzX2FycmF5KCRhdHRyaWJbJ3N1ZmZpeGVzJ10pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkY29tcG9uZW50cyAuPSBpbXBsb2RlKCcnLCAkYXR0cmliWydzdWZmaXhlcyddKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAvLyBTaW5nbGUgcm9vdAogICAgICAgICAgICAgICAgICAgICAgICBpZiAoJHJvb3RfY291bnQgPT0gMSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgJGNvbXBvbmVudHMgLj0gJGluc3RhbmNlczsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICRmb3VuZCAuPSBzcHJpbnRmKCc8bGk+JXM8L2xpPicsICRjb21wb25lbnRzKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAvLyBSZW5kZXIgZGlzcGxheQogICAgICAgIGlmICgkd29yZF9jb3VudCA+PSAxMCkgewogICAgICAgICAgICAkcmV0IC49ICc8ZGl2IHN0eWxlPSItd2Via2l0LWNvbHVtbi1jb3VudDogMzsgLW1vei1jb2x1bW4tY291bnQ6IDM7Ij4nOwogICAgICAgIH0KICAgICAgICAkcmV0IC49ICc8b2wgc3R5bGU9Im1hcmdpbjowOyI+JzsKICAgICAgICAkcmV0IC49ICRsb3N0OwogICAgICAgICRyZXQgLj0gJGZvdW5kOwogICAgICAgICRyZXQgLj0gJzwvb2w+JzsKICAgICAgICBpZiAoJHdvcmRfY291bnQgPj0gMTApIHsKICAgICAgICAgICAgJHJldCAuPSAnPC9kaXY+JzsKICAgICAgICB9CgogICAgICAgIHJldHVybiAkcmV0OwogICAgfQoKICAgIC8qKgogICAgICogVG9rZW5pc2FzaQogICAgICoKICAgICAqIEBwYXJhbSBzdHJpbmcgJHF1ZXJ5CiAgICAgKgogICAgICogQHJldHVybiBhcnJheQogICAgICovCiAgICBwcml2YXRlIGZ1bmN0aW9uIHN0ZW0oJHF1ZXJ5KQogICAgewogICAgICAgICR3b3JkcyA9IGFycmF5KCk7CiAgICAgICAgJHJhdyA9IHByZWdfc3BsaXQoJy9bXmEtekEtWjAtOVwtXS8nLCAkcXVlcnksIC0xLCBQUkVHX1NQTElUX05PX0VNUFRZKTsKICAgICAgICBmb3JlYWNoICgkcmF3IGFzICRyKSB7CiAgICAgICAgLy8gUmVtb3ZlIGFsbCBkaWdpdCAid29yZCIgaWYgbmVjZXNzYXJ5CiAgICAgICAgICAgIGlmICgkdGhpcy0+b3B0aW9uc1snTk9fRElHSVRfT05MWSddICYmIHByZWdfbWF0Y2goJy9eXGQrJC8nLCAkcikpIHsKICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICB9CiAgICAgICAgICAgICRrZXkgPSBzdHJ0b2xvd2VyKCRyKTsKICAgICAgICAgICAgJHdvcmRzWyRrZXldWydjb3VudCddKys7CiAgICAgICAgfQogICAgICAgIGZvcmVhY2ggKCR3b3JkcyBhcyAka2V5ID0+ICR3b3JkKSB7CiAgICAgICAgICAgICR3b3Jkc1ska2V5XVsncm9vdHMnXSA9ICR0aGlzLT5zdGVtV29yZCgka2V5KTsKICAgICAgICAgICAgLy8gSWYgTk9fTk9fTUFUQ0gsIHJlbW92ZSB3b3JkcyB0aGF0IGhhcyBubyByb290CiAgICAgICAgICAgIGlmIChjb3VudCgkd29yZHNbJGtleV1bJ3Jvb3RzJ10pID09IDAgJiYgJHRoaXMtPm9wdGlvbnNbJ05PX05PX01BVENIJ10pIHsKICAgICAgICAgICAgICAgIHVuc2V0KCR3b3Jkc1ska2V5XSk7CiAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICAkaW5zdGFuY2VzWyRrZXldID0gJHdvcmRbJ2NvdW50J107CiAgICAgICAgfQogICAgICAgICR3b3JkX2NvdW50ID0gY291bnQoJHdvcmRzKTsKICAgICAgICBpZiAoJHRoaXMtPm9wdGlvbnNbJ1NPUlRfSU5TVEFOQ0UnXSkgewogICAgICAgICAgICAka2V5cyA9IGFycmF5X2tleXMoJHdvcmRzKTsKICAgICAgICAgICAgYXJyYXlfbXVsdGlzb3J0KCRpbnN0YW5jZXMsIFNPUlRfREVTQywgJGtleXMsIFNPUlRfQVNDLCAkd29yZHMpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGtzb3J0KCR3b3Jkcyk7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gJHdvcmRzOwogICAgfQoKICAgIC8qKgogICAgICogU3RlbSBpbmRpdmlkdWFsIHdvcmQKICAgICAqCiAgICAgKiBAcGFyYW0gc3RyaW5nICR3b3JkCiAgICAgKgogICAgICogQHJldHVybiBhcnJheQogICAgICovCiAgICBwcml2YXRlIGZ1bmN0aW9uIHN0ZW1Xb3JkKCR3b3JkKQogICAgewogICAgICAgIC8vIFByZXByb2Nlc3M6IENyZWF0ZSBlbXB0eSBhZmZpeCBpZiBvcmlnaW5hbCB3b3JkIGlzIGluIGxleGljb24KICAgICAgICAkd29yZCA9IHRyaW0oJHdvcmQpOwogICAgICAgICRyb290cyA9IGFycmF5KCR3b3JkID0+ICcnKTsKICAgICAgICBpZiAoYXJyYXlfa2V5X2V4aXN0cygkd29yZCwgJHRoaXMtPmRpY3QpKSB7CiAgICAgICAgICAgICRyb290c1skd29yZF1bJ2FmZml4ZXMnXSA9IGFycmF5KCk7CiAgICAgICAgfQogICAgICAgIC8vIEhhcyBkYXNoPyBUcnkgdG8gYWxzbyBmaW5kIHJvb3QgZm9yIGVhY2ggZWxlbWVudAogICAgICAgIGlmIChzdHJwb3MoJHdvcmQsICctJykpIHsKICAgICAgICAgICAgJGRhc2hfcGFydHMgPSBleHBsb2RlKCctJywgJHdvcmQpOwogICAgICAgICAgICBmb3JlYWNoICgkZGFzaF9wYXJ0cyBhcyAkZGFzaF9wYXJ0KSB7CiAgICAgICAgICAgICAgICAkcm9vdHNbJGRhc2hfcGFydF1bJ2FmZml4ZXMnXSA9IGFycmF5KCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIC8vIFByb2Nlc3M6IEZpbmQgc3VmZml4ZXMsIHByb25vdW4gcHJlZml4LCBhbmQgb3RoZXIgcHJlZml4ICgzIHRpbWVzLCBBc2lhbikKICAgICAgICBmb3JlYWNoICgkdGhpcy0+cnVsZXNbJ2FmZml4ZXMnXSBhcyAkZ3JvdXApIHsKICAgICAgICAgICAgJGlzX3N1ZmZpeCA9ICRncm91cFswXTsKICAgICAgICAgICAgJGFmZml4ZXMgPSAkZ3JvdXBbMV07CiAgICAgICAgICAgIGZvcmVhY2ggKCRhZmZpeGVzIGFzICRhZmZpeCkgewogICAgICAgICAgICAgICAgJHBhdHRlcm4gPSAkaXNfc3VmZml4ID8gIiguKykoeyRhZmZpeH0pIiA6ICIoeyRhZmZpeH0pKC4rKSI7CiAgICAgICAgICAgICAgICAkdGhpcy0+YWRkUm9vdCgkcm9vdHMsIGFycmF5KCRpc19zdWZmaXgsICRwYXR0ZXJuLCAnJykpOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGZvciAoJGkgPSAwOyAkaSA8IDM7ICRpKyspIHsKICAgICAgICAgICAgZm9yZWFjaCAoJHRoaXMtPnJ1bGVzWydwcmVmaXhlcyddIGFzICRydWxlKSB7CiAgICAgICAgICAgICAgICAkdGhpcy0+YWRkUm9vdCgkcm9vdHMsICRydWxlKTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy8gUG9zdHByb2Nlc3MgMTogU2VsZWN0IHZhbGlkIGFmZml4ZXMKICAgICAgICBmb3JlYWNoICgkcm9vdHMgYXMgJGxlbW1hID0+ICRhdHRyaWIpIHsKICAgICAgICAvLyBOb3QgaW4gZGljdGlvbmFyeT8gVW5zZXQgYW5kIGV4aXQKICAgICAgICAgICAgaWYgKCFhcnJheV9rZXlfZXhpc3RzKCRsZW1tYSwgJHRoaXMtPmRpY3QpKSB7CiAgICAgICAgICAgICAgICB1bnNldCgkcm9vdHNbJGxlbW1hXSk7CiAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBFc2NhcGUgaWYgd2UgZG9uJ3QgaGF2ZSB0byBjaGVjayB2YWxpZCBjb25maXggcGFpcnMKICAgICAgICAgICAgaWYgKCEkdGhpcy0+b3B0aW9uc1snU1RSSUNUX0NPTkZJWCddKSB7CiAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBDaGVjayBjb25maXggcGFpcnMKICAgICAgICAgICAgJGFmZml4ZXMgPSAkYXR0cmliWydhZmZpeGVzJ107CiAgICAgICAgICAgIGZvcmVhY2ggKCR0aGlzLT5ydWxlc1snZGlzYWxsb3dlZF9jb25maXhlcyddIGFzICRwYWlyKSB7CiAgICAgICAgICAgICAgICAkcHJlZml4ID0gJHBhaXJbMF07CiAgICAgICAgICAgICAgICAkc3VmZml4ID0gJHBhaXJbMV07CiAgICAgICAgICAgICAgICAkcHJlZml4X2tleSA9IHN1YnN0cigkcHJlZml4LCAwLCAyKTsKICAgICAgICAgICAgICAgIGlmIChhcnJheV9rZXlfZXhpc3RzKCRwcmVmaXhfa2V5LCAkdGhpcy0+cnVsZXNbJ2FsbG9tb3JwaHMnXSkpIHsKICAgICAgICAgICAgICAgICAgICBmb3JlYWNoICgkdGhpcy0+cnVsZXNbJ2FsbG9tb3JwaHMnXVskcHJlZml4X2tleV0gYXMgJGFsbG9tb3JmKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChpbl9hcnJheSgkYWxsb21vcmYsICRhZmZpeGVzKSAmJiBpbl9hcnJheSgkc3VmZml4LCAkYWZmaXhlcykpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuc2V0KCRyb290c1skbGVtbWFdKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0gZWxzZWlmIChpbl9hcnJheSgkcHJlZml4LCAkYWZmaXhlcykgJiYgaW5fYXJyYXkoJHN1ZmZpeCwgJGFmZml4ZXMpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHVuc2V0KCRyb290c1skbGVtbWFdKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy8gUG9zdHByb2Nlc3MgMjogSGFuZGxlIHN1ZmZpeGVzIGFuZCBwcmVmaXhlcwogICAgICAgIGZvcmVhY2ggKCRyb290cyBhcyAkbGVtbWEgPT4gJGF0dHJpYikgewogICAgICAgICAgICAkYWZmaXhlcyA9ICRhdHRyaWJbJ2FmZml4ZXMnXTsKICAgICAgICAgICAgJGF0dHJpYlsnbGVtbWEnXSA9ICR0aGlzLT5kaWN0WyRsZW1tYV1bJ2xlbW1hJ107CiAgICAgICAgICAgICRhdHRyaWJbJ2NsYXNzJ10gPSAkdGhpcy0+ZGljdFskbGVtbWFdWydjbGFzcyddOwogICAgICAgICAgICAvLyBEaXZpZGUgYWZmaXhlcyBpbnRvIHN1ZmZpeGVzIGFuZCBwcmVmaXhlcwogICAgICAgICAgICBmb3JlYWNoICgkYXR0cmliWydhZmZpeGVzJ10gYXMgJGFmZml4KSB7CiAgICAgICAgICAgICAgICAkdHlwZSA9IChzdWJzdHIoJGFmZml4LCAwLCAxKSA9PSAnLScpID8gJ3N1ZmZpeGVzJyA6ICdwcmVmaXhlcyc7CiAgICAgICAgICAgICAgICAkYXR0cmliWyR0eXBlXVtdID0gJGFmZml4OwogICAgICAgICAgICB9CiAgICAgICAgICAgIC8vIFJldmVyc2Ugc3VmZml4IG9yZGVyCiAgICAgICAgICAgIGlmIChpc19hcnJheSgkYXR0cmliWydzdWZmaXhlcyddKSkgewogICAgICAgICAgICAgICAga3Jzb3J0KCRhdHRyaWJbJ3N1ZmZpeGVzJ10pOwogICAgICAgICAgICB9CiAgICAgICAgICAgICRyb290c1skbGVtbWFdID0gJGF0dHJpYjsKICAgICAgICB9CgogICAgICAgIHJldHVybiAkcm9vdHM7CiAgICB9CgogICAgLyoqCiAgICAgKiBHcmVlZHkgYWxnb3JpdGhtOiBhZGQgZXZlcnkgcG9zc2libGUgYnJhbmNoCiAgICAgKgogICAgICogQHBhcmFtIGFycmF5ICRyb290cwogICAgICogQHBhcmFtIGFycmF5ICRydWxlCiAgICAgKgogICAgICogQHJldHVybiB2b2lkCiAgICAgKi8KICAgIHByaXZhdGUgZnVuY3Rpb24gYWRkUm9vdCgmJHJvb3RzLCAkcnVsZSkKICAgIHsKICAgICAgICAkaXNfc3VmZml4ID0gJHJ1bGVbMF07CiAgICAgICAgJHBhdHRlcm4gPSAnL14nIC4gJHJ1bGVbMV0gLiAnJC9pJzsKICAgICAgICAkdmFyaWFudCA9ICRydWxlWzJdOwogICAgICAgIGZvcmVhY2ggKCRyb290cyBhcyAkbGVtbWEgPT4gJGF0dHJpYikgewogICAgICAgICAgICBwcmVnX21hdGNoKCRwYXR0ZXJuLCAkbGVtbWEsICRtYXRjaGVzKTsKICAgICAgICAgICAgaWYgKGNvdW50KCRtYXRjaGVzKSA+IDApIHsKICAgICAgICAgICAgICAgIHVuc2V0KCRuZXdfbGVtbWEpOwogICAgICAgICAgICAgICAgdW5zZXQoJG5ld19hZmZpeCk7CiAgICAgICAgICAgICAgICAkYWZmaXhfaW5kZXggPSAkaXNfc3VmZml4ID8gMiA6IDE7CgogICAgICAgICAgICAgICAgLy8gTGVtbWEKICAgICAgICAgICAgICAgIGZvciAoJGkgPSAxOyAkaSA8IGNvdW50KCRtYXRjaGVzKTsgJGkrKykgewogICAgICAgICAgICAgICAgICAgIGlmICgkaSAhPSAkYWZmaXhfaW5kZXgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgJG5ld19sZW1tYSAuPSAkbWF0Y2hlc1skaV07CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgKCR2YXJpYW50KSB7CiAgICAgICAgICAgICAgICAgICAgJG5ld19sZW1tYSA9ICR2YXJpYW50IC4gJG5ld19sZW1tYTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvLyBBZmZpeCwgYWRkIC0gYmVmb3JlIChzdWZmaXgpLCBhZnRlciAocHJlZml4KQogICAgICAgICAgICAgICAgJG5ld19hZmZpeCAuPSAkaXNfc3VmZml4ID8gJy0nIDogJyc7CiAgICAgICAgICAgICAgICAkbmV3X2FmZml4IC49ICRtYXRjaGVzWyRhZmZpeF9pbmRleF07CiAgICAgICAgICAgICAgICAkbmV3X2FmZml4IC49ICRpc19zdWZmaXggPyAnJyA6ICctJzsKICAgICAgICAgICAgICAgICRuZXdfYWZmaXggPSBhcnJheSgkbmV3X2FmZml4KTsgLy8gbWFrZSBhcnJheQogICAgICAgICAgICAgICAgaWYgKGlzX2FycmF5KCRhdHRyaWJbJ2FmZml4ZXMnXSkpIHsgLy8gbWVyZ2UKICAgICAgICAgICAgICAgICAgICAkbmV3X2FmZml4ID0gYXJyYXlfbWVyZ2UoJGF0dHJpYlsnYWZmaXhlcyddLCAkbmV3X2FmZml4KTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvLyBQdXNoCiAgICAgICAgICAgICAgICAkcm9vdHNbJG5ld19sZW1tYV0gPSBhcnJheSgnYWZmaXhlcycgPT4gJG5ld19hZmZpeCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9Cn0K