<?php
/*
Есть продукты A, B, C, D, E, F, G, H, I, J, K, L, M. Каждый продукт стоит определенную сумму.
Есть набор правил расчета итоговой суммы:
Если одновременно выбраны А и B, то их суммарная стоимость уменьшается на 10% (для каждой пары А и B)
Если одновременно выбраны D и E, то их суммарная стоимость уменьшается на 5% (для каждой пары D и E)
Если одновременно выбраны E,F,G, то их суммарная стоимость уменьшается на 5% (для каждой тройки E,F,G)
Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта уменьшается на 5%
Если пользователь выбрал одновременно 3 продукта, он получает скидку 5% от суммы заказа
Если пользователь выбрал одновременно 4 продукта, он получает скидку 10% от суммы заказа
Если пользователь выбрал одновременно 5 продуктов, он получает скидку 20% от суммы заказа
Описанные скидки 5,6,7 не суммируются, применяется только одна из них
Продукты A и C не участвуют в скидках 5,6,7
Каждый товар может участвовать только в одной скидке. Скидки применяются последовательно в порядке описанном выше.
Необходимо написать программу на PHP с использованием ООП которая имея на входе набор продуктов (один продукт может встречаться несколько раз) рассчитывала суммарную их стоимость.
*/
class ProductCollection
{
private $products ;
public function __construct
( array $products = [ ] ) {
foreach ( $products as $product ) {
$this -> addProduct ( $product ) ;
}
}
public function addProduct( Product $product )
{
$this -> products [ ] = $product ;
}
public function getByName( $name )
{
foreach ( $this -> products as $product ) {
if ( $product -> name === $name ) {
return $product ;
}
}
return null ;
}
public function getCount( )
{
return count ( $this -> products ) ; }
public function getWithoutNames
( array $withoutNames ) {
$this -> products ,
function ( Product $p ) use ( $withoutNames ) {
return ! in_array ( $p -> name , $withoutNames ) ; }
) ;
return new self ( $productsWithoutNames ) ;
}
public function getSum( )
{
$this -> products ,
function ( $prev , $next ) { return $prev + $next -> price ; } ,
0
) ;
}
public function hasNames
( array $names ) {
$productsNames = array_map ( function ( $p ) { return $p -> name ; } , $this -> products ) ; }
public function getOneOfName
( array $names ) {
foreach ( $this -> products as $product ) {
return $product ;
}
}
return null ;
}
public function deleteByNames
( array $names ) {
$deleted = [ ] ;
foreach ( $names as $name ) {
$deleted [ ] = $this -> deleteByName ( $name ) ;
}
return new self ( $deleted ) ;
}
public function deleteByName( $name )
{
$productCount = count ( $this -> products ) ; for ( $i = 0 ; $i < $productCount ; $i ++ ) {
if ( $this -> products [ $i ] -> name === $name ) {
}
}
return null ;
}
}
class Product
{
public $name ;
public $price ;
public function __construct( $name , $price )
{
$this -> name = $name ;
$this -> price = $price ;
}
}
// Скидка, формирующаяся на основе количества элементов в списке товаров
class CountDiscount
{
private $count ;
private $percent ;
private $withoutNames ;
public function __construct
( $count , $percent , array $withoutNames = [ ] ) {
$this -> percent = $percent ;
$this -> withoutNames = $withoutNames ;
}
public function match( ProductCollection $pc )
{
return $pc -> getWithoutNames ( $this -> withoutNames ) -> getCount ( ) >= $this -> count ; }
public function getPercent( )
{
return $this -> percent ;
}
}
interface CombinationDiscountInterface
{
public function getSumAfterApplyingDiscount( ProductCollection $productCollection ) ;
}
// Скидка, формирующаяся на основе комбинации определённых товаров
class CombinationDiscount implements CombinationDiscountInterface
{
private $productNames ;
private $percent ;
public function __construct
( array $productNames , $percent ) {
$this -> productNames = $productNames ;
$this -> percent = $percent ;
}
public function getSumAfterApplyingDiscount( ProductCollection $pc )
{
if ( $pc -> hasNames ( $this -> productNames ) ) {
$deleted = $pc -> deleteByNames ( $this -> productNames ) ;
return $deleted -> getSum ( ) - $deleted -> getSum ( ) * $this -> percent ;
}
return 0 ;
}
}
// Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта...
class CombinationOneOfDiscount implements CombinationDiscountInterface
{
private $productName ;
private $oneOf ;
private $percent ;
public function __construct
( $productName , array $oneOf , $percent ) {
$this -> productName = $productName ;
$this -> oneOf = $oneOf ;
$this -> percent = $percent ;
}
public function getSumAfterApplyingDiscount( ProductCollection $pc )
{
$oneOf = $pc -> getOneOfName ( $this -> oneOf ) ;
$product = $pc -> getByName ( $this -> productName ) ;
if ( $oneOf && $pc -> hasNames ( [ $this -> productName ] ) ) {
$pc -> deleteByNames ( [ $oneOf -> name , $product -> name ] ) ;
return $product -> price + ( $oneOf -> price - $oneOf -> price * $this -> percent ) ;
}
return 0 ;
}
}
class Calculator
{
private $combinationDiscounts ;
private $productCollection ;
private $countDiscounts ;
public function __construct( ProductCollection $productCollection )
{
$this -> productCollection = $productCollection ;
$this -> combinationDiscounts = [ ] ;
$this -> countDiscounts = [ ] ;
}
public function addCombinationDiscount( CombinationDiscountInterface $cd )
{
$this -> combinationDiscounts [ ] = $cd ;
return $this ;
}
public function addCountDiscount( CountDiscount $cd )
{
$this -> countDiscounts [ ] = $cd ;
return $this ;
}
public function calculateTotalPrice( )
{
$totalPrice = 0 ;
$discountFromOrderSum = $this -> getDiscountFromOrderSum ( ) ;
foreach ( $this -> combinationDiscounts as $combinationDiscount ) {
$totalPrice += $combinationDiscount -> getSumAfterApplyingDiscount ( $this -> productCollection ) ;
}
$totalPrice += $this -> productCollection -> getSum ( ) ;
return $totalPrice - $discountFromOrderSum ;
}
private function getDiscountFromOrderSum( )
{
foreach ( $this -> countDiscounts as $countDiscount ) {
if ( $countDiscount -> match ( $this -> productCollection ) ) {
return $this -> productCollection -> getSum ( ) * $countDiscount -> getPercent ( ) ;
}
}
return 0 ;
}
}
// ======================================================================
$pc = new ProductCollection( [
new Product( 'a' , 100 ) ,
new Product( 'b' , 300 ) ,
new Product( 'c' , 200 ) ,
new Product( 'd' , 200 ) ,
new Product( 'e' , 100 ) ,
new Product( 'c' , 100 ) ,
] ) ;
// Тесты класса ProductCollection
assert ( $pc -> getCount ( ) === 6 ) ; assert ( $pc -> hasNames ( [ 'a' , 'c' , 'e' ] ) === true ) ; assert ( $pc -> hasNames ( [ 'a' , 'Z' ] ) === false ) ; assert ( $pc -> getOneOfName ( [ 'a' , 'Z' ] ) == true ) ; assert ( $pc -> getOneOfName ( [ 'Q' , 'Z' ] ) == false ) ; assert ( $pc -> getSum ( ) === 1000 ) ; assert ( $pc -> getWithoutNames ( [ 'a' , 'b' , 'e' ] ) -> getSum ( ) === 500 ) ;
$c = new Calculator( $pc ) ;
$c
// Если пользователь выбрал одновременно 3 продукта, он получает скидку 5% от суммы заказа
-> addCountDiscount ( new CountDiscount( 3 , 0.05 , [ 'a' , 'c' ] ) )
// Если пользователь выбрал одновременно 4 продукта, он получает скидку 10% от суммы заказа
-> addCountDiscount ( new CountDiscount( 4 , 0.1 , [ 'a' , 'c' ] ) )
// Если пользователь выбрал одновременно 5 продуктов, он получает скидку 20% от суммы заказа
-> addCountDiscount ( new CountDiscount( 5 , 0.2 , [ 'a' , 'c' ] ) )
;
// Суммарно - 1000
// сработал первый Discount, т.к. осталось 3 товара после того, как избавились от всех A,C
// 1000 минус 5 процентов от 1000
assert ( $c -> calculateTotalPrice ( ) == 950 ) ;
// ======================================================================
$pc2 = new ProductCollection( [
new Product( 'a' , 100 ) ,
new Product( 'a' , 100 ) ,
new Product( 'a' , 100 ) ,
new Product( 'b' , 100 ) ,
new Product( 'b' , 100 ) ,
] ) ;
$pc2 -> deleteByNames ( [ 'a' , 'a' , 'b' ] ) ;
assert ( $pc2 -> getCount ( ) === 2 ) ;
// ======================================================================
$pc3 = new ProductCollection( [
new Product( 'a' , 50 ) ,
new Product( 'b' , 50 ) ,
new Product( 'e' , 50 ) ,
new Product( 'f' , 25 ) ,
new Product( 'g' , 25 ) ,
] ) ;
$c = new Calculator( $pc3 ) ;
$c
// Если одновременно выбраны А и B, то их суммарная стоимость уменьшается на 10% (для каждой пары А и B)
-> addCombinationDiscount ( new CombinationDiscount( [ 'a' , 'b' ] , 0.1 ) )
// Если одновременно выбраны E,F,G, то их суммарная стоимость уменьшается на 5% (для каждой тройки E,F,G)
-> addCombinationDiscount ( new CombinationDiscount( [ 'e' , 'f' , 'g' ] , 0.05 ) )
;
assert ( $c -> calculateTotalPrice ( ) == 90 + 95 ) ;
// ======================================================================
$pc4 = new ProductCollection( [
new Product( 'a' , 100 ) ,
new Product( 'k' , 100 ) ,
new Product( 'p' , 100 ) ,
] ) ;
$c = new Calculator( $pc4 ) ;
$c
// Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта уменьшается на 5%
-> addCombinationDiscount ( new CombinationOneOfDiscount( 'a' , [ 'k' , 'l' , 'm' ] , 0.05 ) )
;
assert ( $c -> calculateTotalPrice ( ) == 100 + 95 + 100 ) ;
// ======================================================================
$pc5 = new ProductCollection( [
new Product( 'a' , 100 ) , // 1 шаг
new Product( 'b' , 100 ) , // 1 шаг
new Product( 'c' , 100 ) , // 4 шаг
new Product( 'd' , 100 ) , // 2 шаг
new Product( 'e' , 100 ) , // 2 шаг
new Product( 'f' , 100 ) , // 4 шаг
new Product( 'g' , 100 ) , // 4 шаг
new Product( 'h' , 100 ) , // 4 шаг
new Product( 'i' , 100 ) , // 4 шаг
new Product( 'j' , 100 ) , // 3 шаг
new Product( 'a' , 100 ) , // 3 шаг
] ) ;
$c = new Calculator( $pc5 ) ;
$c
-> addCountDiscount ( new CountDiscount( 3 , 0.05 , [ 'a' , 'c' ] ) )
-> addCountDiscount ( new CountDiscount( 4 , 0.1 , [ 'a' , 'c' ] ) )
-> addCountDiscount ( new CountDiscount( 5 , 0.2 , [ 'a' , 'c' ] ) )
-> addCombinationDiscount ( new CombinationDiscount( [ 'a' , 'b' ] , 0.1 ) )
-> addCombinationDiscount ( new CombinationDiscount( [ 'd' , 'e' ] , 0.05 ) )
-> addCombinationDiscount ( new CombinationDiscount( [ 'f' , 'e' , 'g' ] , 0.05 ) )
-> addCombinationDiscount ( new CombinationOneOfDiscount( 'a' , [ 'k' , 'j' , 'm' ] , 0.05 ) )
;
// За вычетом всех AC остаётся больше 5-и продуктов, от totalPrice нужно будет вычитать 20 процентов "суммы заказа"
// Сработал Discount для AB, теперь к totalPrice вместо 100 + 100 нужно добавить 90 + 90 = 180 (шаг 1)
// Сработал Discount для DE, к totalPrice вместо 200 нужно прибавить 95 + 95 = 190 (шаг 2)
// Не сработал Discount для FEG, так как E уже использовался
// CombinationOneOfDiscount сработал, так как есть j (шаг 3), выходит a + 5 процентов от j = 100 + 95
// Складываем оставшиеся cfghi = 5 * 100 (шаг 4)
// Считаем: 180 + 190 + 195 + 500 = 1065
// 20 процентов от 1100 это 55
assert ( $c -> calculateTotalPrice ( ) == 1065 - 55 ) ;
PD9waHAKCi8qCtCV0YHRgtGMINC/0YDQvtC00YPQutGC0YsgQSwgQiwgQywgRCwgRSwgRiwgRywgSCwgSSwgSiwgSywgTCwgTS4g0JrQsNC20LTRi9C5INC/0YDQvtC00YPQutGCINGB0YLQvtC40YIg0L7Qv9GA0LXQtNC10LvQtdC90L3Rg9GOINGB0YPQvNC80YMuCtCV0YHRgtGMINC90LDQsdC+0YAg0L/RgNCw0LLQuNC7INGA0LDRgdGH0LXRgtCwINC40YLQvtCz0L7QstC+0Lkg0YHRg9C80LzRizoK0JXRgdC70Lgg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+INCy0YvQsdGA0LDQvdGLINCQINC4IEIsINGC0L4g0LjRhSDRgdGD0LzQvNCw0YDQvdCw0Y8g0YHRgtC+0LjQvNC+0YHRgtGMINGD0LzQtdC90YzRiNCw0LXRgtGB0Y8g0L3QsCAxMCUgKNC00LvRjyDQutCw0LbQtNC+0Lkg0L/QsNGA0Ysg0JAg0LggQikK0JXRgdC70Lgg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+INCy0YvQsdGA0LDQvdGLIEQg0LggRSwg0YLQviDQuNGFINGB0YPQvNC80LDRgNC90LDRjyDRgdGC0L7QuNC80L7RgdGC0Ywg0YPQvNC10L3RjNGI0LDQtdGC0YHRjyDQvdCwIDUlICjQtNC70Y8g0LrQsNC20LTQvtC5INC/0LDRgNGLIEQg0LggRSkK0JXRgdC70Lgg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+INCy0YvQsdGA0LDQvdGLIEUsRixHLCDRgtC+INC40YUg0YHRg9C80LzQsNGA0L3QsNGPINGB0YLQvtC40LzQvtGB0YLRjCDRg9C80LXQvdGM0YjQsNC10YLRgdGPINC90LAgNSUgKNC00LvRjyDQutCw0LbQtNC+0Lkg0YLRgNC+0LnQutC4IEUsRixHKQrQldGB0LvQuCDQvtC00L3QvtCy0YDQtdC80LXQvdC90L4g0LLRi9Cx0YDQsNC90Ysg0JAg0Lgg0L7QtNC40L0g0LjQtyBbSyxMLE1dLCDRgtC+INGB0YLQvtC40LzQvtGB0YLRjCDQstGL0LHRgNCw0L3QvdC+0LPQviDQv9GA0L7QtNGD0LrRgtCwINGD0LzQtdC90YzRiNCw0LXRgtGB0Y8g0L3QsCA1JQrQldGB0LvQuCDQv9C+0LvRjNC30L7QstCw0YLQtdC70Ywg0LLRi9Cx0YDQsNC7INC+0LTQvdC+0LLRgNC10LzQtdC90L3QviAzINC/0YDQvtC00YPQutGC0LAsINC+0L0g0L/QvtC70YPRh9Cw0LXRgiDRgdC60LjQtNC60YMgNSUg0L7RgiDRgdGD0LzQvNGLINC30LDQutCw0LfQsArQldGB0LvQuCDQv9C+0LvRjNC30L7QstCw0YLQtdC70Ywg0LLRi9Cx0YDQsNC7INC+0LTQvdC+0LLRgNC10LzQtdC90L3QviA0INC/0YDQvtC00YPQutGC0LAsINC+0L0g0L/QvtC70YPRh9Cw0LXRgiDRgdC60LjQtNC60YMgMTAlINC+0YIg0YHRg9C80LzRiyDQt9Cw0LrQsNC30LAK0JXRgdC70Lgg0L/QvtC70YzQt9C+0LLQsNGC0LXQu9GMINCy0YvQsdGA0LDQuyDQvtC00L3QvtCy0YDQtdC80LXQvdC90L4gNSDQv9GA0L7QtNGD0LrRgtC+0LIsINC+0L0g0L/QvtC70YPRh9Cw0LXRgiDRgdC60LjQtNC60YMgMjAlINC+0YIg0YHRg9C80LzRiyDQt9Cw0LrQsNC30LAK0J7Qv9C40YHQsNC90L3Ri9C1INGB0LrQuNC00LrQuCA1LDYsNyDQvdC1INGB0YPQvNC80LjRgNGD0Y7RgtGB0Y8sINC/0YDQuNC80LXQvdGP0LXRgtGB0Y8g0YLQvtC70YzQutC+INC+0LTQvdCwINC40Lcg0L3QuNGFCtCf0YDQvtC00YPQutGC0YsgQSDQuCBDINC90LUg0YPRh9Cw0YHRgtCy0YPRjtGCINCyINGB0LrQuNC00LrQsNGFIDUsNiw3CtCa0LDQttC00YvQuSDRgtC+0LLQsNGAINC80L7QttC10YIg0YPRh9Cw0YHRgtCy0L7QstCw0YLRjCDRgtC+0LvRjNC60L4g0LIg0L7QtNC90L7QuSDRgdC60LjQtNC60LUuINCh0LrQuNC00LrQuCDQv9GA0LjQvNC10L3Rj9GO0YLRgdGPINC/0L7RgdC70LXQtNC+0LLQsNGC0LXQu9GM0L3QviDQsiDQv9C+0YDRj9C00LrQtSDQvtC/0LjRgdCw0L3QvdC+0Lwg0LLRi9GI0LUuCgrQndC10L7QsdGF0L7QtNC40LzQviDQvdCw0L/QuNGB0LDRgtGMINC/0YDQvtCz0YDQsNC80LzRgyDQvdCwIFBIUCDRgSDQuNGB0L/QvtC70YzQt9C+0LLQsNC90LjQtdC8INCe0J7QnyDQutC+0YLQvtGA0LDRjyDQuNC80LXRjyDQvdCwINCy0YXQvtC00LUg0L3QsNCx0L7RgCDQv9GA0L7QtNGD0LrRgtC+0LIgKNC+0LTQuNC9INC/0YDQvtC00YPQutGCINC80L7QttC10YIg0LLRgdGC0YDQtdGH0LDRgtGM0YHRjyDQvdC10YHQutC+0LvRjNC60L4g0YDQsNC3KSDRgNCw0YHRgdGH0LjRgtGL0LLQsNC70LAg0YHRg9C80LzQsNGA0L3Rg9GOINC40YUg0YHRgtC+0LjQvNC+0YHRgtGMLgoqLwoKY2xhc3MgUHJvZHVjdENvbGxlY3Rpb24KewogICAgcHJpdmF0ZSAkcHJvZHVjdHM7CgogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KGFycmF5ICRwcm9kdWN0cyA9IFtdKQogICAgewogICAgICAgIGZvcmVhY2ggKCRwcm9kdWN0cyBhcyAkcHJvZHVjdCkgewogICAgICAgICAgICAkdGhpcy0+YWRkUHJvZHVjdCgkcHJvZHVjdCk7CiAgICAgICAgfQogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiBhZGRQcm9kdWN0KFByb2R1Y3QgJHByb2R1Y3QpCiAgICB7CiAgICAgICAgJHRoaXMtPnByb2R1Y3RzW10gPSAkcHJvZHVjdDsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0QnlOYW1lKCRuYW1lKQogICAgewogICAgICAgIGZvcmVhY2ggKCR0aGlzLT5wcm9kdWN0cyBhcyAkcHJvZHVjdCkgewogICAgICAgICAgICBpZiAoJHByb2R1Y3QtPm5hbWUgPT09ICRuYW1lKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gJHByb2R1Y3Q7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGdldENvdW50KCkKICAgIHsKICAgICAgICByZXR1cm4gY291bnQoJHRoaXMtPnByb2R1Y3RzKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0V2l0aG91dE5hbWVzKGFycmF5ICR3aXRob3V0TmFtZXMpCiAgICB7CiAgICAgICAgJHByb2R1Y3RzV2l0aG91dE5hbWVzID0gYXJyYXlfZmlsdGVyKAogICAgICAgICAgICAkdGhpcy0+cHJvZHVjdHMsCiAgICAgICAgICAgIGZ1bmN0aW9uIChQcm9kdWN0ICRwKSB1c2UgKCR3aXRob3V0TmFtZXMpIHsKICAgICAgICAgICAgICAgIHJldHVybiAhaW5fYXJyYXkoJHAtPm5hbWUsICR3aXRob3V0TmFtZXMpOwogICAgICAgICAgICB9CiAgICAgICAgKTsKCiAgICAgICAgcmV0dXJuIG5ldyBzZWxmKCRwcm9kdWN0c1dpdGhvdXROYW1lcyk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGdldFN1bSgpCiAgICB7CiAgICAgICAgcmV0dXJuIGFycmF5X3JlZHVjZSgKICAgICAgICAgICAgJHRoaXMtPnByb2R1Y3RzLAogICAgICAgICAgICBmdW5jdGlvbiAoJHByZXYsICRuZXh0KSB7IHJldHVybiAkcHJldiArICRuZXh0LT5wcmljZTsgfSwKICAgICAgICAgICAgMAogICAgICAgICk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGhhc05hbWVzKGFycmF5ICRuYW1lcykKICAgIHsKICAgICAgICAkcHJvZHVjdHNOYW1lcyA9IGFycmF5X21hcChmdW5jdGlvbiAoJHApIHsgcmV0dXJuICRwLT5uYW1lOyB9LCAkdGhpcy0+cHJvZHVjdHMpOwogICAgICAgIHJldHVybiAhYXJyYXlfZGlmZigkbmFtZXMsICRwcm9kdWN0c05hbWVzKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0T25lT2ZOYW1lKGFycmF5ICRuYW1lcykKICAgIHsKICAgICAgICBmb3JlYWNoICgkdGhpcy0+cHJvZHVjdHMgYXMgJHByb2R1Y3QpIHsKICAgICAgICAgICAgaWYgKGluX2FycmF5KCRwcm9kdWN0LT5uYW1lLCAkbmFtZXMpKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gJHByb2R1Y3Q7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGRlbGV0ZUJ5TmFtZXMoYXJyYXkgJG5hbWVzKQogICAgewogICAgICAgICRkZWxldGVkID0gW107CgogICAgICAgIGZvcmVhY2ggKCRuYW1lcyBhcyAkbmFtZSkgewogICAgICAgICAgICAkZGVsZXRlZFtdID0gJHRoaXMtPmRlbGV0ZUJ5TmFtZSgkbmFtZSk7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gbmV3IHNlbGYoJGRlbGV0ZWQpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiBkZWxldGVCeU5hbWUoJG5hbWUpCiAgICB7CiAgICAgICAgJHByb2R1Y3RDb3VudCA9IGNvdW50KCR0aGlzLT5wcm9kdWN0cyk7CiAgICAgICAgZm9yICgkaSA9IDA7ICRpIDwgJHByb2R1Y3RDb3VudDsgJGkrKykgewogICAgICAgICAgICBpZiAoJHRoaXMtPnByb2R1Y3RzWyRpXS0+bmFtZSA9PT0gJG5hbWUpIHsKICAgICAgICAgICAgICAgIHJldHVybiBhcnJheV9zcGxpY2UoJHRoaXMtPnByb2R1Y3RzLCAkaSwgMSlbMF07CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICB9Cn0KCmNsYXNzIFByb2R1Y3QKewogICAgcHVibGljICRuYW1lOwogICAgcHVibGljICRwcmljZTsKCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJG5hbWUsICRwcmljZSkKICAgIHsKICAgICAgICAkdGhpcy0+bmFtZSA9ICRuYW1lOwogICAgICAgICR0aGlzLT5wcmljZSA9ICRwcmljZTsKICAgIH0KfQoKLy8g0KHQutC40LTQutCwLCDRhNC+0YDQvNC40YDRg9GO0YnQsNGP0YHRjyDQvdCwINC+0YHQvdC+0LLQtSDQutC+0LvQuNGH0LXRgdGC0LLQsCDRjdC70LXQvNC10L3RgtC+0LIg0LIg0YHQv9C40YHQutC1INGC0L7QstCw0YDQvtCyCmNsYXNzIENvdW50RGlzY291bnQKewoJcHJpdmF0ZSAkY291bnQ7Cglwcml2YXRlICRwZXJjZW50OwoJcHJpdmF0ZSAkd2l0aG91dE5hbWVzOwoJCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJGNvdW50LCAkcGVyY2VudCwgYXJyYXkgJHdpdGhvdXROYW1lcyA9IFtdKQogICAgewogICAgICAgICR0aGlzLT5jb3VudCA9ICRjb3VudDsKICAgICAgICAkdGhpcy0+cGVyY2VudCA9ICRwZXJjZW50OwogICAgICAgICR0aGlzLT53aXRob3V0TmFtZXMgPSAkd2l0aG91dE5hbWVzOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiBtYXRjaChQcm9kdWN0Q29sbGVjdGlvbiAkcGMpCiAgICB7CiAgICAgICAgcmV0dXJuICRwYy0+Z2V0V2l0aG91dE5hbWVzKCR0aGlzLT53aXRob3V0TmFtZXMpLT5nZXRDb3VudCgpID49ICR0aGlzLT5jb3VudDsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0UGVyY2VudCgpCiAgICB7CiAgICAgICAgcmV0dXJuICR0aGlzLT5wZXJjZW50OwogICAgfQp9CgppbnRlcmZhY2UgQ29tYmluYXRpb25EaXNjb3VudEludGVyZmFjZQp7CiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U3VtQWZ0ZXJBcHBseWluZ0Rpc2NvdW50KFByb2R1Y3RDb2xsZWN0aW9uICRwcm9kdWN0Q29sbGVjdGlvbik7Cn0KCi8vINCh0LrQuNC00LrQsCwg0YTQvtGA0LzQuNGA0YPRjtGJ0LDRj9GB0Y8g0L3QsCDQvtGB0L3QvtCy0LUg0LrQvtC80LHQuNC90LDRhtC40Lgg0L7Qv9GA0LXQtNC10LvRkdC90L3Ri9GFINGC0L7QstCw0YDQvtCyCmNsYXNzIENvbWJpbmF0aW9uRGlzY291bnQgaW1wbGVtZW50cyBDb21iaW5hdGlvbkRpc2NvdW50SW50ZXJmYWNlCnsKICAgIHByaXZhdGUgJHByb2R1Y3ROYW1lczsKICAgIHByaXZhdGUgJHBlcmNlbnQ7CgogICAgcHVibGljIGZ1bmN0aW9uIF9fY29uc3RydWN0KGFycmF5ICRwcm9kdWN0TmFtZXMsICRwZXJjZW50KQogICAgewogICAgICAgICR0aGlzLT5wcm9kdWN0TmFtZXMgPSAkcHJvZHVjdE5hbWVzOwogICAgICAgICR0aGlzLT5wZXJjZW50ID0gJHBlcmNlbnQ7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGdldFN1bUFmdGVyQXBwbHlpbmdEaXNjb3VudChQcm9kdWN0Q29sbGVjdGlvbiAkcGMpCiAgICB7CiAgICAgICAgaWYgKCRwYy0+aGFzTmFtZXMoJHRoaXMtPnByb2R1Y3ROYW1lcykpIHsKICAgICAgICAgICAgJGRlbGV0ZWQgPSAkcGMtPmRlbGV0ZUJ5TmFtZXMoJHRoaXMtPnByb2R1Y3ROYW1lcyk7CiAgICAgICAgICAgIHJldHVybiAkZGVsZXRlZC0+Z2V0U3VtKCkgLSAkZGVsZXRlZC0+Z2V0U3VtKCkgKiAkdGhpcy0+cGVyY2VudDsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIDA7CiAgICB9Cn0KCi8vINCV0YHQu9C4INC+0LTQvdC+0LLRgNC10LzQtdC90L3QviDQstGL0LHRgNCw0L3RiyDQkCDQuCDQvtC00LjQvSDQuNC3IFtLLEwsTV0sINGC0L4g0YHRgtC+0LjQvNC+0YHRgtGMINCy0YvQsdGA0LDQvdC90L7Qs9C+INC/0YDQvtC00YPQutGC0LAuLi4KY2xhc3MgQ29tYmluYXRpb25PbmVPZkRpc2NvdW50IGltcGxlbWVudHMgQ29tYmluYXRpb25EaXNjb3VudEludGVyZmFjZQp7CiAgICBwcml2YXRlICRwcm9kdWN0TmFtZTsKICAgIHByaXZhdGUgJG9uZU9mOwogICAgcHJpdmF0ZSAkcGVyY2VudDsKCiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHByb2R1Y3ROYW1lLCBhcnJheSAkb25lT2YsICRwZXJjZW50KQogICAgewogICAgICAgICR0aGlzLT5wcm9kdWN0TmFtZSA9ICRwcm9kdWN0TmFtZTsKICAgICAgICAkdGhpcy0+b25lT2YgPSAkb25lT2Y7CiAgICAgICAgJHRoaXMtPnBlcmNlbnQgPSAkcGVyY2VudDsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0U3VtQWZ0ZXJBcHBseWluZ0Rpc2NvdW50KFByb2R1Y3RDb2xsZWN0aW9uICRwYykKICAgIHsKICAgICAgICAkb25lT2YgPSAkcGMtPmdldE9uZU9mTmFtZSgkdGhpcy0+b25lT2YpOwogICAgICAgICRwcm9kdWN0ID0gJHBjLT5nZXRCeU5hbWUoJHRoaXMtPnByb2R1Y3ROYW1lKTsKICAgICAgICBpZiAoJG9uZU9mICYmICRwYy0+aGFzTmFtZXMoWyR0aGlzLT5wcm9kdWN0TmFtZV0pKSB7CiAgICAgICAgICAgICRwYy0+ZGVsZXRlQnlOYW1lcyhbJG9uZU9mLT5uYW1lLCAkcHJvZHVjdC0+bmFtZV0pOwogICAgICAgICAgICByZXR1cm4gJHByb2R1Y3QtPnByaWNlICsgKCRvbmVPZi0+cHJpY2UgLSAkb25lT2YtPnByaWNlICogJHRoaXMtPnBlcmNlbnQpOwogICAgICAgIH0KICAgICAgICByZXR1cm4gMDsKICAgIH0KfQoKY2xhc3MgQ2FsY3VsYXRvcgp7CiAgICBwcml2YXRlICRjb21iaW5hdGlvbkRpc2NvdW50czsKICAgIHByaXZhdGUgJHByb2R1Y3RDb2xsZWN0aW9uOwogICAgcHJpdmF0ZSAkY291bnREaXNjb3VudHM7CiAgICAKICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdChQcm9kdWN0Q29sbGVjdGlvbiAkcHJvZHVjdENvbGxlY3Rpb24pCiAgICB7CiAgICAgICAgJHRoaXMtPnByb2R1Y3RDb2xsZWN0aW9uID0gJHByb2R1Y3RDb2xsZWN0aW9uOwogICAgICAgICR0aGlzLT5jb21iaW5hdGlvbkRpc2NvdW50cyA9IFtdOwogICAgICAgICR0aGlzLT5jb3VudERpc2NvdW50cyA9IFtdOwogICAgfQogICAgCiAgICBwdWJsaWMgZnVuY3Rpb24gYWRkQ29tYmluYXRpb25EaXNjb3VudChDb21iaW5hdGlvbkRpc2NvdW50SW50ZXJmYWNlICRjZCkKICAgIHsKICAgICAgICAkdGhpcy0+Y29tYmluYXRpb25EaXNjb3VudHNbXSA9ICRjZDsKICAgICAgICByZXR1cm4gJHRoaXM7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGFkZENvdW50RGlzY291bnQoQ291bnREaXNjb3VudCAkY2QpCiAgICB7CiAgICAgICAgJHRoaXMtPmNvdW50RGlzY291bnRzW10gPSAkY2Q7CiAgICAgICAgcmV0dXJuICR0aGlzOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiBjYWxjdWxhdGVUb3RhbFByaWNlKCkKICAgIHsKICAgICAgICAkdG90YWxQcmljZSA9IDA7CiAgICAgICAgJGRpc2NvdW50RnJvbU9yZGVyU3VtID0gJHRoaXMtPmdldERpc2NvdW50RnJvbU9yZGVyU3VtKCk7CgogICAgICAgIGZvcmVhY2ggKCR0aGlzLT5jb21iaW5hdGlvbkRpc2NvdW50cyBhcyAkY29tYmluYXRpb25EaXNjb3VudCkgewogICAgICAgICAgICAkdG90YWxQcmljZSArPSAkY29tYmluYXRpb25EaXNjb3VudC0+Z2V0U3VtQWZ0ZXJBcHBseWluZ0Rpc2NvdW50KCR0aGlzLT5wcm9kdWN0Q29sbGVjdGlvbik7CiAgICAgICAgfQoKICAgICAgICAkdG90YWxQcmljZSArPSAkdGhpcy0+cHJvZHVjdENvbGxlY3Rpb24tPmdldFN1bSgpOwoKICAgICAgICByZXR1cm4gJHRvdGFsUHJpY2UgLSAkZGlzY291bnRGcm9tT3JkZXJTdW07CiAgICB9CgogICAgcHJpdmF0ZSBmdW5jdGlvbiBnZXREaXNjb3VudEZyb21PcmRlclN1bSgpCiAgICB7CiAgICAgICAgZm9yZWFjaCAoJHRoaXMtPmNvdW50RGlzY291bnRzIGFzICRjb3VudERpc2NvdW50KSB7CiAgICAgICAgICAgIGlmICgkY291bnREaXNjb3VudC0+bWF0Y2goJHRoaXMtPnByb2R1Y3RDb2xsZWN0aW9uKSkgewogICAgICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5wcm9kdWN0Q29sbGVjdGlvbi0+Z2V0U3VtKCkgKiAkY291bnREaXNjb3VudC0+Z2V0UGVyY2VudCgpOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHJldHVybiAwOwogICAgfQp9CgovLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgokcGMgPSBuZXcgUHJvZHVjdENvbGxlY3Rpb24oWwogICAgbmV3IFByb2R1Y3QoJ2EnLCAxMDApLAogICAgbmV3IFByb2R1Y3QoJ2InLCAzMDApLAogICAgbmV3IFByb2R1Y3QoJ2MnLCAyMDApLAogICAgbmV3IFByb2R1Y3QoJ2QnLCAyMDApLAogICAgbmV3IFByb2R1Y3QoJ2UnLCAxMDApLAogICAgbmV3IFByb2R1Y3QoJ2MnLCAxMDApLApdKTsKCi8vINCi0LXRgdGC0Ysg0LrQu9Cw0YHRgdCwIFByb2R1Y3RDb2xsZWN0aW9uCmFzc2VydCgkcGMtPmdldENvdW50KCkgPT09IDYpOwphc3NlcnQoJHBjLT5oYXNOYW1lcyhbJ2EnLCAnYycsICdlJ10pID09PSB0cnVlKTsKYXNzZXJ0KCRwYy0+aGFzTmFtZXMoWydhJywgJ1onXSkgPT09IGZhbHNlKTsKYXNzZXJ0KCRwYy0+Z2V0T25lT2ZOYW1lKFsnYScsICdaJ10pID09IHRydWUpOwphc3NlcnQoJHBjLT5nZXRPbmVPZk5hbWUoWydRJywgJ1onXSkgPT0gZmFsc2UpOwphc3NlcnQoJHBjLT5nZXRTdW0oKSA9PT0gMTAwMCk7CmFzc2VydCgkcGMtPmdldFdpdGhvdXROYW1lcyhbJ2EnLCAnYicsICdlJ10pLT5nZXRTdW0oKSA9PT0gNTAwKTsKCiRjID0gbmV3IENhbGN1bGF0b3IoJHBjKTsKJGMKICAgIC8vINCV0YHQu9C4INC/0L7Qu9GM0LfQvtCy0LDRgtC10LvRjCDQstGL0LHRgNCw0Lsg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+IDMg0L/RgNC+0LTRg9C60YLQsCwg0L7QvSDQv9C+0LvRg9GH0LDQtdGCINGB0LrQuNC00LrRgyA1JSDQvtGCINGB0YPQvNC80Ysg0LfQsNC60LDQt9CwCiAgICAtPmFkZENvdW50RGlzY291bnQobmV3IENvdW50RGlzY291bnQoMywgMC4wNSwgWydhJywgJ2MnXSkpCiAgICAvLyDQldGB0LvQuCDQv9C+0LvRjNC30L7QstCw0YLQtdC70Ywg0LLRi9Cx0YDQsNC7INC+0LTQvdC+0LLRgNC10LzQtdC90L3QviA0INC/0YDQvtC00YPQutGC0LAsINC+0L0g0L/QvtC70YPRh9Cw0LXRgiDRgdC60LjQtNC60YMgMTAlINC+0YIg0YHRg9C80LzRiyDQt9Cw0LrQsNC30LAKICAgIC0+YWRkQ291bnREaXNjb3VudChuZXcgQ291bnREaXNjb3VudCg0LCAwLjEsIFsnYScsICdjJ10pKQogICAgLy8g0JXRgdC70Lgg0L/QvtC70YzQt9C+0LLQsNGC0LXQu9GMINCy0YvQsdGA0LDQuyDQvtC00L3QvtCy0YDQtdC80LXQvdC90L4gNSDQv9GA0L7QtNGD0LrRgtC+0LIsINC+0L0g0L/QvtC70YPRh9Cw0LXRgiDRgdC60LjQtNC60YMgMjAlINC+0YIg0YHRg9C80LzRiyDQt9Cw0LrQsNC30LAKICAgIC0+YWRkQ291bnREaXNjb3VudChuZXcgQ291bnREaXNjb3VudCg1LCAwLjIsIFsnYScsICdjJ10pKQo7CgovLyDQodGD0LzQvNCw0YDQvdC+IC0gMTAwMAovLyDRgdGA0LDQsdC+0YLQsNC7INC/0LXRgNCy0YvQuSBEaXNjb3VudCwg0YIu0LouINC+0YHRgtCw0LvQvtGB0YwgMyDRgtC+0LLQsNGA0LAg0L/QvtGB0LvQtSDRgtC+0LPQviwg0LrQsNC6INC40LfQsdCw0LLQuNC70LjRgdGMINC+0YIg0LLRgdC10YUgQSxDCi8vIDEwMDAg0LzQuNC90YPRgSA1INC/0YDQvtGG0LXQvdGC0L7QsiDQvtGCIDEwMDAKYXNzZXJ0KCRjLT5jYWxjdWxhdGVUb3RhbFByaWNlKCkgPT0gOTUwKTsKCi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCiRwYzIgPSBuZXcgUHJvZHVjdENvbGxlY3Rpb24oWwogICAgbmV3IFByb2R1Y3QoJ2EnLCAxMDApLAogICAgbmV3IFByb2R1Y3QoJ2EnLCAxMDApLAogICAgbmV3IFByb2R1Y3QoJ2EnLCAxMDApLAogICAgbmV3IFByb2R1Y3QoJ2InLCAxMDApLAogICAgbmV3IFByb2R1Y3QoJ2InLCAxMDApLApdKTsKCiRwYzItPmRlbGV0ZUJ5TmFtZXMoWydhJywgJ2EnLCAnYiddKTsKYXNzZXJ0KCRwYzItPmdldENvdW50KCkgPT09IDIpOwoKLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKJHBjMyA9IG5ldyBQcm9kdWN0Q29sbGVjdGlvbihbCiAgICBuZXcgUHJvZHVjdCgnYScsIDUwKSwKICAgIG5ldyBQcm9kdWN0KCdiJywgNTApLAogICAgbmV3IFByb2R1Y3QoJ2UnLCA1MCksCiAgICBuZXcgUHJvZHVjdCgnZicsIDI1KSwKICAgIG5ldyBQcm9kdWN0KCdnJywgMjUpLApdKTsKCiRjID0gbmV3IENhbGN1bGF0b3IoJHBjMyk7CiRjCiAgICAvLyDQldGB0LvQuCDQvtC00L3QvtCy0YDQtdC80LXQvdC90L4g0LLRi9Cx0YDQsNC90Ysg0JAg0LggQiwg0YLQviDQuNGFINGB0YPQvNC80LDRgNC90LDRjyDRgdGC0L7QuNC80L7RgdGC0Ywg0YPQvNC10L3RjNGI0LDQtdGC0YHRjyDQvdCwIDEwJSAo0LTQu9GPINC60LDQttC00L7QuSDQv9Cw0YDRiyDQkCDQuCBCKQogICAgLT5hZGRDb21iaW5hdGlvbkRpc2NvdW50KG5ldyBDb21iaW5hdGlvbkRpc2NvdW50KFsnYScsICdiJ10sIDAuMSkpCiAgICAvLyDQldGB0LvQuCDQvtC00L3QvtCy0YDQtdC80LXQvdC90L4g0LLRi9Cx0YDQsNC90YsgRSxGLEcsINGC0L4g0LjRhSDRgdGD0LzQvNCw0YDQvdCw0Y8g0YHRgtC+0LjQvNC+0YHRgtGMINGD0LzQtdC90YzRiNCw0LXRgtGB0Y8g0L3QsCA1JSAo0LTQu9GPINC60LDQttC00L7QuSDRgtGA0L7QudC60LggRSxGLEcpCiAgICAtPmFkZENvbWJpbmF0aW9uRGlzY291bnQobmV3IENvbWJpbmF0aW9uRGlzY291bnQoWydlJywgJ2YnLCAnZyddLCAwLjA1KSkKOwoKYXNzZXJ0KCRjLT5jYWxjdWxhdGVUb3RhbFByaWNlKCkgPT0gOTAgKyA5NSk7CgovLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgokcGM0ID0gbmV3IFByb2R1Y3RDb2xsZWN0aW9uKFsKICAgIG5ldyBQcm9kdWN0KCdhJywgMTAwKSwKICAgIG5ldyBQcm9kdWN0KCdrJywgMTAwKSwKICAgIG5ldyBQcm9kdWN0KCdwJywgMTAwKSwKXSk7CgokYyA9IG5ldyBDYWxjdWxhdG9yKCRwYzQpOwokYwogICAgLy8g0JXRgdC70Lgg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+INCy0YvQsdGA0LDQvdGLINCQINC4INC+0LTQuNC9INC40LcgW0ssTCxNXSwg0YLQviDRgdGC0L7QuNC80L7RgdGC0Ywg0LLRi9Cx0YDQsNC90L3QvtCz0L4g0L/RgNC+0LTRg9C60YLQsCDRg9C80LXQvdGM0YjQsNC10YLRgdGPINC90LAgNSUKICAgIC0+YWRkQ29tYmluYXRpb25EaXNjb3VudChuZXcgQ29tYmluYXRpb25PbmVPZkRpc2NvdW50KCdhJywgWydrJywgJ2wnLCAnbSddLCAwLjA1KSkKOwoKYXNzZXJ0KCRjLT5jYWxjdWxhdGVUb3RhbFByaWNlKCkgPT0gMTAwICsgOTUgKyAxMDApOwoKLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKJHBjNSA9IG5ldyBQcm9kdWN0Q29sbGVjdGlvbihbCiAgICBuZXcgUHJvZHVjdCgnYScsIDEwMCksIC8vIDEg0YjQsNCzCiAgICBuZXcgUHJvZHVjdCgnYicsIDEwMCksIC8vIDEg0YjQsNCzCiAgICBuZXcgUHJvZHVjdCgnYycsIDEwMCksIC8vIDQg0YjQsNCzCiAgICBuZXcgUHJvZHVjdCgnZCcsIDEwMCksIC8vIDIg0YjQsNCzIAogICAgbmV3IFByb2R1Y3QoJ2UnLCAxMDApLCAvLyAyINGI0LDQswogICAgbmV3IFByb2R1Y3QoJ2YnLCAxMDApLCAvLyA0INGI0LDQswogICAgbmV3IFByb2R1Y3QoJ2cnLCAxMDApLCAvLyA0INGI0LDQswogICAgbmV3IFByb2R1Y3QoJ2gnLCAxMDApLCAvLyA0INGI0LDQswogICAgbmV3IFByb2R1Y3QoJ2knLCAxMDApLCAvLyA0INGI0LDQswogICAgbmV3IFByb2R1Y3QoJ2onLCAxMDApLCAvLyAzINGI0LDQswogICAgbmV3IFByb2R1Y3QoJ2EnLCAxMDApLCAvLyAzINGI0LDQswpdKTsKCiRjID0gbmV3IENhbGN1bGF0b3IoJHBjNSk7CiRjCiAgICAtPmFkZENvdW50RGlzY291bnQobmV3IENvdW50RGlzY291bnQoMywgMC4wNSwgWydhJywgJ2MnXSkpCiAgICAtPmFkZENvdW50RGlzY291bnQobmV3IENvdW50RGlzY291bnQoNCwgMC4xLCBbJ2EnLCAnYyddKSkKICAgIC0+YWRkQ291bnREaXNjb3VudChuZXcgQ291bnREaXNjb3VudCg1LCAwLjIsIFsnYScsICdjJ10pKQogICAgLT5hZGRDb21iaW5hdGlvbkRpc2NvdW50KG5ldyBDb21iaW5hdGlvbkRpc2NvdW50KFsnYScsICdiJ10sIDAuMSkpCiAgICAtPmFkZENvbWJpbmF0aW9uRGlzY291bnQobmV3IENvbWJpbmF0aW9uRGlzY291bnQoWydkJywgJ2UnXSwgMC4wNSkpCiAgICAtPmFkZENvbWJpbmF0aW9uRGlzY291bnQobmV3IENvbWJpbmF0aW9uRGlzY291bnQoWydmJywgJ2UnLCAnZyddLCAwLjA1KSkKICAgIC0+YWRkQ29tYmluYXRpb25EaXNjb3VudChuZXcgQ29tYmluYXRpb25PbmVPZkRpc2NvdW50KCdhJywgWydrJywgJ2onLCAnbSddLCAwLjA1KSkKOwoKLy8g0JfQsCDQstGL0YfQtdGC0L7QvCDQstGB0LXRhSBBQyDQvtGB0YLQsNGR0YLRgdGPINCx0L7Qu9GM0YjQtSA1LdC4INC/0YDQvtC00YPQutGC0L7Qsiwg0L7RgiB0b3RhbFByaWNlINC90YPQttC90L4g0LHRg9C00LXRgiDQstGL0YfQuNGC0LDRgtGMIDIwINC/0YDQvtGG0LXQvdGC0L7QsiAi0YHRg9C80LzRiyDQt9Cw0LrQsNC30LAiCi8vINCh0YDQsNCx0L7RgtCw0LsgRGlzY291bnQg0LTQu9GPIEFCLCDRgtC10L/QtdGA0Ywg0LogdG90YWxQcmljZSDQstC80LXRgdGC0L4gMTAwICsgMTAwINC90YPQttC90L4g0LTQvtCx0LDQstC40YLRjCA5MCArIDkwID0gMTgwICjRiNCw0LMgMSkKLy8g0KHRgNCw0LHQvtGC0LDQuyBEaXNjb3VudCDQtNC70Y8gREUsINC6IHRvdGFsUHJpY2Ug0LLQvNC10YHRgtC+IDIwMCDQvdGD0LbQvdC+INC/0YDQuNCx0LDQstC40YLRjCA5NSArIDk1ID0gMTkwICjRiNCw0LMgMikKLy8g0J3QtSDRgdGA0LDQsdC+0YLQsNC7IERpc2NvdW50INC00LvRjyBGRUcsINGC0LDQuiDQutCw0LogRSDRg9C20LUg0LjRgdC/0L7Qu9GM0LfQvtCy0LDQu9GB0Y8KLy8gQ29tYmluYXRpb25PbmVPZkRpc2NvdW50INGB0YDQsNCx0L7RgtCw0LssINGC0LDQuiDQutCw0Log0LXRgdGC0YwgaiAo0YjQsNCzIDMpLCDQstGL0YXQvtC00LjRgiBhICsgNSDQv9GA0L7RhtC10L3RgtC+0LIg0L7RgiBqID0gMTAwICsgOTUKLy8g0KHQutC70LDQtNGL0LLQsNC10Lwg0L7RgdGC0LDQstGI0LjQtdGB0Y8gY2ZnaGkgPSA1ICogMTAwICjRiNCw0LMgNCkKLy8g0KHRh9C40YLQsNC10Lw6IDE4MCArIDE5MCArIDE5NSArIDUwMCA9IDEwNjUKLy8gMjAg0L/RgNC+0YbQtdC90YLQvtCyINC+0YIgMTEwMCDRjdGC0L4gNTUKYXNzZXJ0KCRjLT5jYWxjdWxhdGVUb3RhbFByaWNlKCkgPT0gMTA2NSAtIDU1KTs=