import java.util.* ;
import java.lang.* ;
import java.io.* ;
import java.util.function.* ;
import java.text.* ;
import static java.
lang .
Math .
*;
static String humanFloat
( double x
) { return humanFloatFormat.
format ( x
) ; } return sw.toString ( ) ;
}
static double sqr( double x) { return x * x; }
}
class Vec2 {
public final double x, y;
public Vec2( double x, double y) { this .x = x; this .y = y; }
public static Vec2 Vec2( double x, double y) { return new Vec2( x, y) ; }
public double get( int cid) { if ( cid == 0 ) return x; if ( cid == 1 ) return y; throw badComponentID( cid) ; }
public Vec2 add( Vec2 b) { return Vec2( x + b.x , y + b.y ) ; }
public Vec2 sub( Vec2 b) { return Vec2( x - b.x , y - b.y ) ; }
public Vec2 mul( Vec2 b) { return Vec2( x * b.x , y * b.y ) ; }
public Vec2 mul( double b) { return Vec2( x * b, y * b) ; }
public Vec2 div( Vec2 b) { return Vec2( x / b.x , y / b.y ) ; }
public Vec2 div( double b) { return Vec2( x / b, y / b) ; }
@Override
public String toString
( ) { return String .
format ( "(%s, %s)" ,
Util .
humanFloat ( x
) ,
Util .
humanFloat ( y
) ) ; } public Vec2 min
( Vec2 b
) { return Vec2
( Math .
min ( x, b.
x ) ,
Math .
min ( y, b.
y ) ) ; } public Vec2 max
( Vec2 b
) { return Vec2
( Math .
max ( x, b.
x ) ,
Math .
max ( y, b.
y ) ) ; } public double length( ) { return sqrt( x* x + y* y) ; }
public double sqrLength( ) { return x* x + y* y; }
public double distanceTo( Vec2 b) { return sub( b) .length ( ) ; }
public double sqrDistanceTo( Vec2 b) { return sub( b) .sqrLength ( ) ; }
// public Vec2 normalized() { return mul(1 / length()); }
}
// Пересекаются ли отрезки a0–b0 и a1–b1.
// https://www. geeksforgeeks.org/check-if-two-given-line-segments-intersect/
public static boolean intersects( Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1) {
int o1 = orientation( a0, b0, a1) ,
o2 = orientation( a0, b0, b1) ,
o3 = orientation( a1, b1, a0) ,
o4 = orientation( a1, b1, b0) ;
return o1 != o2 && o3 != o4
|| o1 == 0 && onSegment( a0, a1, b0)
|| o2 == 0 && onSegment( a0, b1, b0)
|| o3 == 0 && onSegment( a1, a0, b1)
|| o4 == 0 && onSegment( a1, b0, b1) ;
}
static double cross0( Vec2 a, Vec2 b, Vec2 c) {
return ( b.y - a.y ) * ( c.x - b.x ) - ( b.x - a.x ) * ( c.y - b.y ) ;
}
static int sign( double x) {
return x > 0 ? + 1 : x < 0 ? - 1 : 0 ;
}
static int orientation( Vec2 a, Vec2 b, Vec2 c) {
return sign( cross0( a, b, c) ) ;
}
static boolean onSegment1( double a, double x, double b) {
return a < b ? x >= a && x <= b : x >= b && x <= a;
}
static boolean onSegment( Vec2 a, Vec2 p, Vec2 b) {
return onSegment1( a.x , p.x , b.x ) && onSegment1( a.y , p.y , b.y ) ;
}
}
// Прямоугольник со сторонами, параллельными осям координат. Легко вокруг чего-нибудь описать.
// a — минимальная точка, b — максимальная.
class AABB {
public final Vec2 a, b;
public AABB( Vec2 a, Vec2 b) { this .a = a; this .b = b; }
public static AABB AABB( Vec2 a, Vec2 b) { return new AABB( a, b) ; }
@Override
public String toString
( ) { return String .
format ( "A = %s, B = %s" , a, b
) ; }
public static AABB bound( AABB b0, AABB b1) {
return AABB( b0.a .min ( b1.a ) , b0.b .max ( b1.b ) ) ;
}
/*public static AABB bound(int count, IntFunction<AABB> ithBB) {
if (count <= 0) throw Util.badArgf("AABB.bound: count = %d.", count);
var bb = ithBB.apply(0);
Vec2 a = bb.a, b = bb.b;
for (int i = 1; i < count; i++) {
bb = ithBB.apply(i);
a = a.min(bb.a);
b = b.max(bb.b);
}
return AABB(a, b);
}*/
public boolean contains( Vec2 p) { return p.x >= a.x && p.x <= b.x && p.y >= a.y && p.y <= b.y ; }
// Остальные 2 точки прямоугольника.
public Vec2 bxay( ) { return Vec2.Vec2 ( b.x , a.y ) ; }
public Vec2 axby( ) { return Vec2.Vec2 ( a.x , b.y ) ; }
public boolean intersectsSegment( Vec2 a, Vec2 b) {
Vec2 bxay = this .bxay ( ) , axby = this .axby ( ) ;
return contains( a) || contains( b)
// Если ни начало, ни конец отрезка не лежат внутри AABB,
// то отрезок, пересекающий AABB, пересекает минимум 2 его стороны,
// поэтому на самом деле достаточно проверить какие-нибудь 3, а не все 4.
|| Segment .
intersects ( this .
a , axby, a, b
) || Segment .
intersects ( this .
a , bxay, a, b
) || Segment .
intersects ( axby,
this .
b , a, b
) || Segment .
intersects ( bxay,
this .
b , a, b
) ; }
public double sqrDistanceTo( Vec2 p) {
return p.x < a.x ?
p.y < a.y ? p.sqrDistanceTo ( a) :
p.y > b.y ? p.sqrDistanceTo ( axby( ) ) :
p.x > b.x ?
p.y < a.y ? p.sqrDistanceTo ( bxay( ) ) :
p.y > b.y ? p.sqrDistanceTo ( b) :
:
p.
y < a.
y ? Util .
sqr ( a.
y - p.
y ) : p.
y > b.
y ? Util .
sqr ( p.
y - b.
y ) : 0 ;
}
}
// Дерево описанных объёмов (bounding volume hierarchy).
// Объекты наследуются от Item, добавляются методом add.
// Дерево перестраивается ВРУЧНУЮ методом rebuild,
// Объекты, пересечённые произвольным отрезком, запрашиваются методом castSegment.
class BVH {
public static class Item {
public final AABB bb;
Item next;
Node node;
public Item( AABB bb) { this .bb = bb; }
}
// Узлы разбиваются до не более чем MIN_LEAF_NODE_SIZE объектов.
// Также узел объединяется с родительским, если после удаления объекта
// в нём осталось менее MIN_LEAF_NODE_SIZE объектов.
static final int MIN_LEAF_NODE_SIZE = 3 ;
// Узел с описанным объёмом bb.
// Объекты, пережившие rebuild, содержатся в листовых узлах,
// а добавленные с последнего вызова rebuild — в корневом
// (так что в запросах будут проверены безусловно).
static class Node {
AABB bb;
BVH bvh;
Node left, right, parent;
Item first, last;
int nItems;
// Просто добавляет объект в список, не расширяя описанный объём.
void addToList( Item item) {
if ( item.
next != null ) throw Util .
impossible ( ) ; if ( first == null ) first = item; else last.next = item;
last = item;
item.node = this ;
nItems++;
}
// Добавляет все объекты из списка other.
void consumeList( Node other) {
if ( other.first == null ) return ;
if ( first == null ) first = other.first ; else last.next = other.first ;
for ( Item it = other.first ; it != null ; it = it.next ) it.node = this ;
last = other.last ;
other.first = null ;
other.last = null ;
nItems += other.nItems ;
other.nItems = 0 ;
}
// Сплющивает в node содержимое узла this и его сыновей.
void collapseTo( Node node) {
if ( this != node) node.consumeList ( this ) ;
if ( left != null ) { left.collapseTo ( node) ; left = null ; }
if ( right != null ) { right.collapseTo ( node) ; right = null ; }
}
static AABB bound( AABB b0, AABB b1) {
return b0 == null ? b1 : AABB.bound ( b0, b1) ;
}
// Перерассчитывает описанный объём. Если есть дети, то предполагается, что их объёмы уже рассчитаны.
void recalculateBB( ) {
AABB bb = null ;
for ( Item it = first; it != null ; it = it.next ) bb = bound( bb, it.bb ) ;
if ( left != null ) bb = bound( bb, left.bb ) ;
if ( right != null ) bb = bound( bb, right.bb ) ;
if ( bb
== null ) throw Util .
impossible ( ) ; this .bb = bb;
}
// Переразбивает node. Предполагается, что он не имеет детей, только объекты
// (состояние после collapseTo в самого себя), и что описанный объём рассчитан.
void divide( ) {
if ( left
!= null || right
!= null ) throw Util .
impossible ( ) ; // после разбиения в каждом из 2 детей должно остаться MIN_LEAF_NODE_SIZE, так что
// 2 * MIN_LEAF_NODE_SIZE — абсолютный минимум.
// Но желательно взять больше, чтобы уменьшить шанс выполнить лишнюю работу (см. ниже).
if ( nItems < 3 * MIN_LEAF_NODE_SIZE) return ;
// разбиение по максимальному измерению
int dim = bb.b .y - bb.a .y > bb.b .x - bb.a .x ? 1 : 0 ;
// в качестве точки разбиения используется среднее арифметическое центров
double divp = 0 ;
for ( Item it = first; it != null ; it = it.next )
divp += it.bb .a .get ( dim) + it.bb .b .get ( dim) ;
divp /= 2 * nItems;
// разделение на два узла по положению центров относительно divp
Node nL = new Node( ) , nR = new Node( ) ;
Item next = null ;
for ( Item it = first; it != null ; it = next) {
next = it.next ;
it.next = null ;
if ( 0.5 * ( it.bb .a .get ( dim) + it.bb .b .get ( dim) ) < divp)
nL.addToList ( it) ;
else
nR.addToList ( it) ;
}
this .nItems = 0 ;
this .first = null ;
this .last = null ;
// Если всё хорошо, завершить изменения, пересчитать объёмы детей
// и рекурсивно продолжить разбиение.
if ( nL.nItems >= MIN_LEAF_NODE_SIZE && nR.nItems >= MIN_LEAF_NODE_SIZE) {
this .left = nL; nL.parent = this ; nL.bvh = bvh;
this .right = nR; nR.parent = this ; nR.bvh = bvh;
nL.recalculateBB ( ) ; nL.divide ( ) ;
nR.recalculateBB ( ) ; nR.divide ( ) ;
} else {
// Иначе разбиение выполнено слишком сильно (но кто ж знал),
// вернуть объекты из несостоявшихся детей в this.
consumeList( nL) ;
consumeList( nR) ;
}
}
// Вызывает cb для объектов, пересекающих отрезок a–b.
void castSegment( Vec2 a, Vec2 b, Consumer< Item> cb) {
bvh._aabbXsegCalls++;
if ( ! bb.intersectsSegment ( a, b) ) return ;
for ( Item it = first; it != null ; it = it.next ) {
bvh._aabbXsegCalls++;
if ( it.bb .intersectsSegment ( a, b) ) cb.accept ( it) ;
}
if ( left != null ) left.castSegment ( a, b, cb) ;
if ( right != null ) right.castSegment ( a, b, cb) ;
}
}
Node root;
int _aabbXsegCalls, _maxPqSize;
// add просто добавляет объект в корень, так что он будет проверяться при любых запросах.
// Он попадёт в место получше после rebuild.
public void add( Item item) {
if ( item.
node != null ) throw Util .
badArgf ( "%s уже добавлен." , item
) ; if ( root == null ) {
root = new Node( ) ;
root.bb = item.bb ;
root.bvh = this ;
} else {
root.bb = AABB.bound ( root.bb , item.bb ) ;
}
root.addToList ( item) ;
item.node = root;
}
public void remove( Item item) {
Node node = item.node ;
if ( node
== null || node.
bvh != this ) throw Util .
badArgf ( "%s не %s." , item, node
== null ? "добавлен" : "в том дереве" ) ; // Удалить item из связного списка, хранящегося в item.node. Можно было бы не искать, если бы он был двусвязным...
for ( Item it = node.first , prev = null ; it != null ; prev = it, it = it.next )
if ( item == it) {
if ( prev == null ) node.first = it; else prev.next = it.next ;
if ( it.next == null ) node.last = prev;
node.nItems --;
item.node = null ;
break ;
}
if ( item.
node != null ) throw Util .
impossible ( "узел не найден" ) ;
// В листовом узле стало слишком мало объектов? Удалить его, передав оставшиеся объекты родительскому.
// Этот процесс может повториться рекурсивно.
while ( node.nItems < MIN_LEAF_NODE_SIZE && node.left == null && node.right == null ) {
if ( node.parent == null ) {
if ( node.nItems == 0 ) { node = null ; root = null ; }
break ;
}
node.parent .consumeList ( node) ;
if ( node == node.parent .left ) node.parent .left = null ;
else if ( node == node.parent .right ) node.parent .right = null ;
else throw Util .
impossible ( ) ; node = node.parent ;
}
if ( node != null ) node.recalculateBB ( ) ;
}
public void rebuild( ) {
if ( root == null ) return ;
root.collapseTo ( root) ;
root.divide ( ) ;
}
public void castSegment( Vec2 a, Vec2 b, Consumer< Item> cb) {
if ( root == null ) return ;
root.castSegment ( a, b, cb) ;
}
public List< Item> castSegment( Vec2 a, Vec2 b) {
var r = new ArrayList< Item> ( ) ;
castSegment( a, b, it -> r.add ( it) ) ;
return r;
}
class QueryAroundUnion {
boolean isNode;
double dist;
public QueryAroundUnion
( boolean isNode,
Object obj,
double dist
) { this .isNode = isNode;
this .obj = obj;
this .dist = dist;
}
public QueryAroundUnion( Item it, Vec2 p) { this ( false , it, it.bb .sqrDistanceTo ( p) ) ; }
public QueryAroundUnion( Node n, Vec2 p) { this ( true , n, n.bb .sqrDistanceTo ( p) ) ; }
}
@FunctionalInterface
public static interface QueryAroundShoggoth {
boolean feed( Item it, double sqrDist) ;
}
void chopBabies( Node n, PriorityQueue< QueryAroundUnion> q, Vec2 p) {
if ( n.left != null ) q.add ( new QueryAroundUnion( n.left , p) ) ;
if ( n.right != null ) q.add ( new QueryAroundUnion( n.right , p) ) ;
for ( Item it = n.first ; it != null ; it = it.next ) q.add ( new QueryAroundUnion( it, p) ) ;
_maxPqSize = max( _maxPqSize, q.size ( ) ) ;
}
public void queryAround( Vec2 p, QueryAroundShoggoth shoggoth) {
var q
= new PriorityQueue
< QueryAroundUnion
> ( Comparator .
comparingDouble ( qu
-> qu.
dist ) ) ; if ( root != null ) chopBabies( root, q, p) ;
for ( QueryAroundUnion qitem; null != ( qitem = q.poll ( ) ) ; )
if ( qitem.isNode ) chopBabies( ( Node) qitem.obj , q, p) ;
else if ( ! shoggoth.feed ( ( Item) qitem.obj , qitem.dist ) ) return ;
}
public List< Item> queryAround( Vec2 p) {
var r = new ArrayList< Item> ( ) ;
queryAround( p, ( it, _sqrDist) -> { r.add ( it) ; return true ; } ) ;
return r;
}
if ( root == null ) return "<empty>" ;
var sb = new StringBuilder( ) ;
sb.append ( "root = {\n " ) ;
dump( root, sb, 1 ) ;
sb.append ( "}" ) ;
return sb.toString ( ) ;
}
static StringBuilder tab( StringBuilder sb, int depth) {
return sb.append ( " " .repeat ( 2 * depth) ) ;
}
static void dump( Node node, StringBuilder sb, int depth) {
tab( sb, depth) .append ( node.bb ) .append ( "\n " ) ;
if ( node.nItems != 0 ) {
tab( sb, depth) .append ( "items(" ) .append ( node.nItems ) .append ( "): " ) ;
for ( Item it = node.first ; it != null ; it = it.next ) {
sb.append ( it != node.first ? ", " : "" ) .append ( it) ;
}
sb.append ( "\n " ) ;
}
for ( int child = 0 ; child < 2 ; child++ )
if ( ( child == 0 ? node.left : node.right ) != null ) {
tab( sb, depth) .append ( child == 0 ? "left" : "right" ) .append ( " = {\n " ) ;
dump( child == 0 ? node.left : node.right , sb, depth + 1 ) ;
tab( sb, depth) .append ( "}\n " ) ;
}
}
int resetAabbXsegCalls( ) { int r = _aabbXsegCalls; _aabbXsegCalls = 0 ; return r; }
int resetMaxPqSize( ) { int r = _maxPqSize; _maxPqSize = 0 ; return r; }
}
class MyItem extends BVH.Item {
MyItem
( AABB bb,
String name
) { super ( bb
) ; this .
name = name
; } @Override
public String toString
( ) { return name
; } }
class Ideone
{
static char cyclicLetter( int index) {
final int N_LETTERS = ( int ) 'Z' - ( int ) 'A' + 1 ;
return ( char ) ( ( int ) 'A' + ( index % N_LETTERS + N_LETTERS) % N_LETTERS) ;
}
{
try {
var bvh = new BVH( ) ;
// var toRemove = new ArrayList<MyItem>();
for ( int y = 0 ; y < /*10*/ 100 ; y++ ) {
for ( int x = 0 ; x < /*10*/ 100 ; x++ ) {
var item = new MyItem(
AABB.AABB (
Vec2.Vec2 ( x+ 0.2 , y+ 0.2 ) ,
Vec2.Vec2 ( x+ 0.8 , y+ 0.8 ) ) ,
"" + cyclicLetter( x) + cyclicLetter( y) ) ;
// if ((x + y) % 2 < 1) toRemove.add(item);
bvh.add ( item) ;
}
}
Vec2 segA = Vec2.Vec2 ( 2 , 4.3 ) , segB = Vec2.Vec2 ( 7 , 3.3 ) ;
System .
out .
println ( "1. Запрос на неперестроенном дереве." ) ; System .
out .
printf ( "%s\n " , bvh.
castSegment ( segA, segB
) ) ; System .
out .
printf ( "Вызовов AABB.intersectsSegment: %d.\n \n " , bvh.
resetAabbXsegCalls ( ) ) ; // String treeDump0 = bvh.dump();
bvh.rebuild ( ) ;
System .
out .
println ( "2. Запрос на перестроенном дереве." ) ; System .
out .
printf ( "%s\n " , bvh.
castSegment ( segA, segB
) ) ; System .
out .
printf ( "Вызовов AABB.intersectsSegment: %d.\n \n " , bvh.
resetAabbXsegCalls ( ) ) ; // String treeDump1 = bvh.dump();
/*for (var rem: toRemove) bvh.remove(rem);
System.out.printf("3. Запрос после равномерного удаления %d элементов.\n", toRemove.size());
System.out.printf("%s\n", bvh.castSegment(segA, segB));
System.out.printf("Вызовов AABB.intersectsSegment: %d.\n\n", bvh.resetAabbXsegCalls());*/
Vec2 epicenter = Vec2.Vec2 ( 7.5 , 6.5 ) ; // HG
/*System.out.printf("3. Сортировка по расстоянию от точки %s.\n", epicenter);
bvh.queryAround(epicenter, (it, sqrDist) -> {
System.out.printf("%s (%s)\n", it, Util.humanFloat(sqrt(sqrDist)));
return true;
});
System.out.printf("Максимальное число элементов очереди: %d.\n\n", bvh.resetMaxPqSize());*/
double radius = 3 ;
System .
out .
printf ( "3. Сортировка по расстоянию от точки %s, макс. радиус %s.\n " , epicenter,
Util .
humanFloat ( radius
) ) ; bvh.queryAround ( epicenter, ( it, sqrDist) -> {
if ( sqrDist > radius * radius) return false ;
System .
out .
printf ( "%s (%s)\n " , it,
Util .
humanFloat ( sqrt
( sqrDist
) ) ) ; return true ;
} ) ;
System .
out .
printf ( "Максимальное число элементов очереди: %d.\n \n " , bvh.
resetMaxPqSize ( ) ) ;
/*System.out.printf("Дамп дерева после шага 1:\n%s\n\n", treeDump0);
System.out.printf("Дамп дерева после шага 2:\n%s\n\n", treeDump1);*/
}
}
}
aW1wb3J0IGphdmEudXRpbC4qOwppbXBvcnQgamF2YS5sYW5nLio7CmltcG9ydCBqYXZhLmlvLio7CmltcG9ydCBqYXZhLnV0aWwuZnVuY3Rpb24uKjsKaW1wb3J0IGphdmEudGV4dC4qOwppbXBvcnQgc3RhdGljIGphdmEubGFuZy5NYXRoLio7CgpjbGFzcyBVdGlsIHsKCXN0YXRpYyBJbGxlZ2FsQXJndW1lbnRFeGNlcHRpb24gYmFkQXJnZihTdHJpbmcgZm10LCBPYmplY3QuLi4gYXJncykgeyByZXR1cm4gbmV3IElsbGVnYWxBcmd1bWVudEV4Y2VwdGlvbihTdHJpbmcuZm9ybWF0KGZtdCwgYXJncykpOyB9CglzdGF0aWMgUnVudGltZUV4Y2VwdGlvbiBpbXBvc3NpYmxlKCkgeyByZXR1cm4gaW1wb3NzaWJsZSgiIik7IH0KCXN0YXRpYyBSdW50aW1lRXhjZXB0aW9uIGltcG9zc2libGUoU3RyaW5nIHMpIHsgcmV0dXJuIG5ldyBSdW50aW1lRXhjZXB0aW9uKFN0cmluZy5mb3JtYXQoItCS0L3Rg9GC0YDQtdC90L3Rj9GPINC+0YjQuNCx0LrQsCVzJXMuIiwgcy5pc0VtcHR5KCkgPyAiIiA6ICI6ICIsIHMpKTsgfQoJc3RhdGljIGZpbmFsIERlY2ltYWxGb3JtYXQgaHVtYW5GbG9hdEZvcm1hdCA9IG5ldyBEZWNpbWFsRm9ybWF0KCIjLiMjIyIpOwoJc3RhdGljIFN0cmluZyBodW1hbkZsb2F0KGRvdWJsZSB4KSB7IHJldHVybiBodW1hbkZsb2F0Rm9ybWF0LmZvcm1hdCh4KTsgfQoJc3RhdGljIFN0cmluZyB0cmFjZShFeGNlcHRpb24gZSkgewoJCVN0cmluZ1dyaXRlciBzdyA9IG5ldyBTdHJpbmdXcml0ZXIoKTsKCQllLnByaW50U3RhY2tUcmFjZShuZXcgUHJpbnRXcml0ZXIoc3cpKTsKCQlyZXR1cm4gc3cudG9TdHJpbmcoKTsKCX0KCXN0YXRpYyBkb3VibGUgc3FyKGRvdWJsZSB4KSB7IHJldHVybiB4ICogeDsgfQp9CgpjbGFzcyBWZWMyIHsKCXB1YmxpYyBmaW5hbCBkb3VibGUgeCwgeTsKCXB1YmxpYyBWZWMyKGRvdWJsZSB4LCBkb3VibGUgeSkgeyB0aGlzLnggPSB4OyB0aGlzLnkgPSB5OyB9CglwdWJsaWMgc3RhdGljIFZlYzIgVmVjMihkb3VibGUgeCwgZG91YmxlIHkpIHsgcmV0dXJuIG5ldyBWZWMyKHgsIHkpOyB9CglwdWJsaWMgZG91YmxlIGdldChpbnQgY2lkKSB7IGlmIChjaWQgPT0gMCkgcmV0dXJuIHg7IGlmIChjaWQgPT0gMSkgcmV0dXJuIHk7IHRocm93IGJhZENvbXBvbmVudElEKGNpZCk7IH0KCXN0YXRpYyBSdW50aW1lRXhjZXB0aW9uIGJhZENvbXBvbmVudElEKGludCBjaWQpIHsgcmV0dXJuIG5ldyBJbGxlZ2FsQXJndW1lbnRFeGNlcHRpb24oU3RyaW5nLmZvcm1hdCgi0J3QtdCy0LXRgNC90YvQuSDQuNC90LTQtdC60YEg0LrQvtC80L/QvtC90LXQvdGC0LAgVmVjMjogJWQsINC00L7Qu9C20LXQvSDQsdGL0YLRjCAwINC40LvQuCAxLiIsIGNpZCkpOyB9CglwdWJsaWMgVmVjMiBhZGQoVmVjMiBiKSB7IHJldHVybiBWZWMyKHggKyBiLngsIHkgKyBiLnkpOyB9CglwdWJsaWMgVmVjMiBzdWIoVmVjMiBiKSB7IHJldHVybiBWZWMyKHggLSBiLngsIHkgLSBiLnkpOyB9CglwdWJsaWMgVmVjMiBtdWwoVmVjMiBiKSB7IHJldHVybiBWZWMyKHggKiBiLngsIHkgKiBiLnkpOyB9CglwdWJsaWMgVmVjMiBtdWwoZG91YmxlIGIpIHsgcmV0dXJuIFZlYzIoeCAqIGIsIHkgKiBiKTsgfQoJcHVibGljIFZlYzIgZGl2KFZlYzIgYikgeyByZXR1cm4gVmVjMih4IC8gYi54LCB5IC8gYi55KTsgfQoJcHVibGljIFZlYzIgZGl2KGRvdWJsZSBiKSB7IHJldHVybiBWZWMyKHggLyBiLCB5IC8gYik7IH0KCUBPdmVycmlkZSBwdWJsaWMgU3RyaW5nIHRvU3RyaW5nKCkgeyByZXR1cm4gU3RyaW5nLmZvcm1hdCgiKCVzLCAlcykiLCBVdGlsLmh1bWFuRmxvYXQoeCksIFV0aWwuaHVtYW5GbG9hdCh5KSk7IH0KCXB1YmxpYyBWZWMyIG1pbihWZWMyIGIpIHsgcmV0dXJuIFZlYzIoTWF0aC5taW4oeCwgYi54KSwgTWF0aC5taW4oeSwgYi55KSk7IH0KCXB1YmxpYyBWZWMyIG1heChWZWMyIGIpIHsgcmV0dXJuIFZlYzIoTWF0aC5tYXgoeCwgYi54KSwgTWF0aC5tYXgoeSwgYi55KSk7IH0KCXB1YmxpYyBkb3VibGUgbGVuZ3RoKCkgeyByZXR1cm4gc3FydCh4KnggKyB5KnkpOyB9CglwdWJsaWMgZG91YmxlIHNxckxlbmd0aCgpIHsgcmV0dXJuIHgqeCArIHkqeTsgfQoJcHVibGljIGRvdWJsZSBkaXN0YW5jZVRvKFZlYzIgYikgeyByZXR1cm4gc3ViKGIpLmxlbmd0aCgpOyB9CglwdWJsaWMgZG91YmxlIHNxckRpc3RhbmNlVG8oVmVjMiBiKSB7IHJldHVybiBzdWIoYikuc3FyTGVuZ3RoKCk7IH0KCS8vIHB1YmxpYyBWZWMyIG5vcm1hbGl6ZWQoKSB7IHJldHVybiBtdWwoMSAvIGxlbmd0aCgpKTsgfQp9CgpjbGFzcyBTZWdtZW50IHsKCS8vINCf0LXRgNC10YHQtdC60LDRjtGC0YHRjyDQu9C4INC+0YLRgNC10LfQutC4IGEw4oCTYjAg0LggYTHigJNiMS4KCS8vIGh0dHBzOi8vd3d3LiBnZWVrc2ZvcmdlZWtzLm9yZy9jaGVjay1pZi10d28tZ2l2ZW4tbGluZS1zZWdtZW50cy1pbnRlcnNlY3QvCglwdWJsaWMgc3RhdGljIGJvb2xlYW4gaW50ZXJzZWN0cyhWZWMyIGEwLCBWZWMyIGIwLCBWZWMyIGExLCBWZWMyIGIxKSB7CgkJaW50IG8xID0gb3JpZW50YXRpb24oYTAsIGIwLCBhMSksCgkJCW8yID0gb3JpZW50YXRpb24oYTAsIGIwLCBiMSksCgkJCW8zID0gb3JpZW50YXRpb24oYTEsIGIxLCBhMCksCgkJCW80ID0gb3JpZW50YXRpb24oYTEsIGIxLCBiMCk7CgkJcmV0dXJuIG8xICE9IG8yICYmIG8zICE9IG80CgkJCXx8IG8xID09IDAgJiYgb25TZWdtZW50KGEwLCBhMSwgYjApCgkJCXx8IG8yID09IDAgJiYgb25TZWdtZW50KGEwLCBiMSwgYjApCgkJCXx8IG8zID09IDAgJiYgb25TZWdtZW50KGExLCBhMCwgYjEpCgkJCXx8IG80ID09IDAgJiYgb25TZWdtZW50KGExLCBiMCwgYjEpOwoJfQoKCXN0YXRpYyBkb3VibGUgY3Jvc3MwKFZlYzIgYSwgVmVjMiBiLCBWZWMyIGMpIHsKCQlyZXR1cm4gKGIueSAtIGEueSkgKiAoYy54IC0gYi54KSAtIChiLnggLSBhLngpICogKGMueSAtIGIueSk7Cgl9CgoJc3RhdGljIGludCBzaWduKGRvdWJsZSB4KSB7CgkJcmV0dXJuIHggPiAwID8gKzEgOiB4IDwgMCA/IC0xIDogMDsKCX0KCglzdGF0aWMgaW50IG9yaWVudGF0aW9uKFZlYzIgYSwgVmVjMiBiLCBWZWMyIGMpIHsKCQlyZXR1cm4gc2lnbihjcm9zczAoYSwgYiwgYykpOwoJfQoKCXN0YXRpYyBib29sZWFuIG9uU2VnbWVudDEoZG91YmxlIGEsIGRvdWJsZSB4LCBkb3VibGUgYikgewoJCXJldHVybiBhIDwgYiA/IHggPj0gYSAmJiB4IDw9IGIgOiB4ID49IGIgJiYgeCA8PSBhOwoJfQoKCXN0YXRpYyBib29sZWFuIG9uU2VnbWVudChWZWMyIGEsIFZlYzIgcCwgVmVjMiBiKSB7CgkJcmV0dXJuIG9uU2VnbWVudDEoYS54LCBwLngsIGIueCkgJiYgb25TZWdtZW50MShhLnksIHAueSwgYi55KTsKCX0KfQoKLy8g0J/RgNGP0LzQvtGD0LPQvtC70YzQvdC40Log0YHQviDRgdGC0L7RgNC+0L3QsNC80LgsINC/0LDRgNCw0LvQu9C10LvRjNC90YvQvNC4INC+0YHRj9C8INC60L7QvtGA0LTQuNC90LDRgi4g0JvQtdCz0LrQviDQstC+0LrRgNGD0LMg0YfQtdCz0L4t0L3QuNCx0YPQtNGMINC+0L/QuNGB0LDRgtGMLgovLyBhIOKAlCDQvNC40L3QuNC80LDQu9GM0L3QsNGPINGC0L7Rh9C60LAsIGIg4oCUINC80LDQutGB0LjQvNCw0LvRjNC90LDRjy4KY2xhc3MgQUFCQiB7CglwdWJsaWMgZmluYWwgVmVjMiBhLCBiOwoJcHVibGljIEFBQkIoVmVjMiBhLCBWZWMyIGIpIHsgdGhpcy5hID0gYTsgdGhpcy5iID0gYjsgfQoJcHVibGljIHN0YXRpYyBBQUJCIEFBQkIoVmVjMiBhLCBWZWMyIGIpIHsgcmV0dXJuIG5ldyBBQUJCKGEsIGIpOyB9CglAT3ZlcnJpZGUgcHVibGljIFN0cmluZyB0b1N0cmluZygpIHsgcmV0dXJuIFN0cmluZy5mb3JtYXQoIkEgPSAlcywgQiA9ICVzIiwgYSwgYik7IH0KCglwdWJsaWMgc3RhdGljIEFBQkIgYm91bmQoQUFCQiBiMCwgQUFCQiBiMSkgewoJCXJldHVybiBBQUJCKGIwLmEubWluKGIxLmEpLCBiMC5iLm1heChiMS5iKSk7Cgl9CgoJLypwdWJsaWMgc3RhdGljIEFBQkIgYm91bmQoaW50IGNvdW50LCBJbnRGdW5jdGlvbjxBQUJCPiBpdGhCQikgewoJCWlmIChjb3VudCA8PSAwKSB0aHJvdyBVdGlsLmJhZEFyZ2YoIkFBQkIuYm91bmQ6IGNvdW50ID0gJWQuIiwgY291bnQpOwoJCXZhciBiYiA9IGl0aEJCLmFwcGx5KDApOwoJCVZlYzIgYSA9IGJiLmEsIGIgPSBiYi5iOwoJCWZvciAoaW50IGkgPSAxOyBpIDwgY291bnQ7IGkrKykgewoJCQliYiA9IGl0aEJCLmFwcGx5KGkpOwoJCQlhID0gYS5taW4oYmIuYSk7CgkJCWIgPSBiLm1heChiYi5iKTsKCQl9CgkJcmV0dXJuIEFBQkIoYSwgYik7Cgl9Ki8KCglwdWJsaWMgYm9vbGVhbiBjb250YWlucyhWZWMyIHApIHsgcmV0dXJuIHAueCA+PSBhLnggJiYgcC54IDw9IGIueCAmJiBwLnkgPj0gYS55ICYmIHAueSA8PSBiLnk7IH0KCgkvLyDQntGB0YLQsNC70YzQvdGL0LUgMiDRgtC+0YfQutC4INC/0YDRj9C80L7Rg9Cz0L7Qu9GM0L3QuNC60LAuCglwdWJsaWMgVmVjMiBieGF5KCkgeyByZXR1cm4gVmVjMi5WZWMyKGIueCwgYS55KTsgfQoJcHVibGljIFZlYzIgYXhieSgpIHsgcmV0dXJuIFZlYzIuVmVjMihhLngsIGIueSk7IH0KCglwdWJsaWMgYm9vbGVhbiBpbnRlcnNlY3RzU2VnbWVudChWZWMyIGEsIFZlYzIgYikgewoJCVZlYzIgYnhheSA9IHRoaXMuYnhheSgpLCBheGJ5ID0gdGhpcy5heGJ5KCk7CgkJcmV0dXJuIGNvbnRhaW5zKGEpIHx8IGNvbnRhaW5zKGIpCgkJCS8vINCV0YHQu9C4INC90Lgg0L3QsNGH0LDQu9C+LCDQvdC4INC60L7QvdC10YYg0L7RgtGA0LXQt9C60LAg0L3QtSDQu9C10LbQsNGCINCy0L3Rg9GC0YDQuCBBQUJCLAoJCQkvLyDRgtC+INC+0YLRgNC10LfQvtC6LCDQv9C10YDQtdGB0LXQutCw0Y7RidC40LkgQUFCQiwg0L/QtdGA0LXRgdC10LrQsNC10YIg0LzQuNC90LjQvNGD0LwgMiDQtdCz0L4g0YHRgtC+0YDQvtC90YssCgkJCS8vINC/0L7RjdGC0L7QvNGDINC90LAg0YHQsNC80L7QvCDQtNC10LvQtSDQtNC+0YHRgtCw0YLQvtGH0L3QviDQv9GA0L7QstC10YDQuNGC0Ywg0LrQsNC60LjQtS3QvdC40LHRg9C00YwgMywg0LAg0L3QtSDQstGB0LUgNC4KCQkJfHwgU2VnbWVudC5pbnRlcnNlY3RzKHRoaXMuYSwgYXhieSwgYSwgYikKCQkJfHwgU2VnbWVudC5pbnRlcnNlY3RzKHRoaXMuYSwgYnhheSwgYSwgYikKCQkJfHwgU2VnbWVudC5pbnRlcnNlY3RzKGF4YnksIHRoaXMuYiwgYSwgYikKCQkJfHwgU2VnbWVudC5pbnRlcnNlY3RzKGJ4YXksIHRoaXMuYiwgYSwgYik7Cgl9CgoJcHVibGljIGRvdWJsZSBzcXJEaXN0YW5jZVRvKFZlYzIgcCkgewoJCXJldHVybiBwLnggPCBhLnggPwoJCQlwLnkgPCBhLnkgPyBwLnNxckRpc3RhbmNlVG8oYSkgOgoJCQlwLnkgPiBiLnkgPyBwLnNxckRpc3RhbmNlVG8oYXhieSgpKSA6CgkJCVV0aWwuc3FyKGEueCAtIHAueCkgOgoJCXAueCA+IGIueCA/CgkJCXAueSA8IGEueSA/IHAuc3FyRGlzdGFuY2VUbyhieGF5KCkpIDoKCQkJcC55ID4gYi55ID8gcC5zcXJEaXN0YW5jZVRvKGIpIDoKCQkJVXRpbC5zcXIocC54IC0gYi54KQoJCToKCQkJcC55IDwgYS55ID8gVXRpbC5zcXIoYS55IC0gcC55KSA6CgkJCXAueSA+IGIueSA/IFV0aWwuc3FyKHAueSAtIGIueSkgOgoJCQkwOwoJfQp9CgovLyDQlNC10YDQtdCy0L4g0L7Qv9C40YHQsNC90L3Ri9GFINC+0LHRitGR0LzQvtCyIChib3VuZGluZyB2b2x1bWUgaGllcmFyY2h5KS4KLy8g0J7QsdGK0LXQutGC0Ysg0L3QsNGB0LvQtdC00YPRjtGC0YHRjyDQvtGCIEl0ZW0sINC00L7QsdCw0LLQu9GP0Y7RgtGB0Y8g0LzQtdGC0L7QtNC+0LwgYWRkLgovLyDQlNC10YDQtdCy0L4g0L/QtdGA0LXRgdGC0YDQsNC40LLQsNC10YLRgdGPINCS0KDQo9Cn0J3Qo9CuINC80LXRgtC+0LTQvtC8IHJlYnVpbGQsCi8vINCe0LHRitC10LrRgtGLLCDQv9C10YDQtdGB0LXRh9GR0L3QvdGL0LUg0L/RgNC+0LjQt9Cy0L7Qu9GM0L3Ri9C8INC+0YLRgNC10LfQutC+0LwsINC30LDQv9GA0LDRiNC40LLQsNGO0YLRgdGPINC80LXRgtC+0LTQvtC8IGNhc3RTZWdtZW50LgpjbGFzcyBCVkggewoJcHVibGljIHN0YXRpYyBjbGFzcyBJdGVtIHsKCQlwdWJsaWMgZmluYWwgQUFCQiBiYjsKCQlJdGVtIG5leHQ7CgkJTm9kZSBub2RlOwoJCXB1YmxpYyBJdGVtKEFBQkIgYmIpIHsgdGhpcy5iYiA9IGJiOyB9Cgl9CgoJLy8g0KPQt9C70Ysg0YDQsNC30LHQuNCy0LDRjtGC0YHRjyDQtNC+INC90LUg0LHQvtC70LXQtSDRh9C10LwgTUlOX0xFQUZfTk9ERV9TSVpFINC+0LHRitC10LrRgtC+0LIuCgkvLyDQotCw0LrQttC1INGD0LfQtdC7INC+0LHRitC10LTQuNC90Y/QtdGC0YHRjyDRgSDRgNC+0LTQuNGC0LXQu9GM0YHQutC40LwsINC10YHQu9C4INC/0L7RgdC70LUg0YPQtNCw0LvQtdC90LjRjyDQvtCx0YrQtdC60YLQsAoJLy8g0LIg0L3RkdC8INC+0YHRgtCw0LvQvtGB0Ywg0LzQtdC90LXQtSBNSU5fTEVBRl9OT0RFX1NJWkUg0L7QsdGK0LXQutGC0L7Qsi4KCXN0YXRpYyBmaW5hbCBpbnQgTUlOX0xFQUZfTk9ERV9TSVpFID0gMzsKCgkvLyDQo9C30LXQuyDRgSDQvtC/0LjRgdCw0L3QvdGL0Lwg0L7QsdGK0ZHQvNC+0LwgYmIuCgkvLyDQntCx0YrQtdC60YLRiywg0L/QtdGA0LXQttC40LLRiNC40LUgcmVidWlsZCwg0YHQvtC00LXRgNC20LDRgtGB0Y8g0LIg0LvQuNGB0YLQvtCy0YvRhSDRg9C30LvQsNGFLAoJLy8g0LAg0LTQvtCx0LDQstC70LXQvdC90YvQtSDRgSDQv9C+0YHQu9C10LTQvdC10LPQviDQstGL0LfQvtCy0LAgcmVidWlsZCDigJQg0LIg0LrQvtGA0L3QtdCy0L7QvAoJLy8gKNGC0LDQuiDRh9GC0L4g0LIg0LfQsNC/0YDQvtGB0LDRhSDQsdGD0LTRg9GCINC/0YDQvtCy0LXRgNC10L3RiyDQsdC10LfRg9GB0LvQvtCy0L3QvikuCglzdGF0aWMgY2xhc3MgTm9kZSB7CgkJQUFCQiBiYjsKCQlCVkggYnZoOwoJCU5vZGUgbGVmdCwgcmlnaHQsIHBhcmVudDsKCQlJdGVtIGZpcnN0LCBsYXN0OwoJCWludCBuSXRlbXM7CgoJCS8vINCf0YDQvtGB0YLQviDQtNC+0LHQsNCy0LvRj9C10YIg0L7QsdGK0LXQutGCINCyINGB0L/QuNGB0L7Quiwg0L3QtSDRgNCw0YHRiNC40YDRj9GPINC+0L/QuNGB0LDQvdC90YvQuSDQvtCx0YrRkdC8LgoJCXZvaWQgYWRkVG9MaXN0KEl0ZW0gaXRlbSkgewoJCQlpZiAoaXRlbS5uZXh0ICE9IG51bGwpIHRocm93IFV0aWwuaW1wb3NzaWJsZSgpOwoJCQlpZiAoZmlyc3QgPT0gbnVsbCkgZmlyc3QgPSBpdGVtOyBlbHNlIGxhc3QubmV4dCA9IGl0ZW07CgkJCWxhc3QgPSBpdGVtOwoJCQlpdGVtLm5vZGUgPSB0aGlzOwoJCQluSXRlbXMrKzsKCQl9CgoJCS8vINCU0L7QsdCw0LLQu9GP0LXRgiDQstGB0LUg0L7QsdGK0LXQutGC0Ysg0LjQtyDRgdC/0LjRgdC60LAgb3RoZXIuCgkJdm9pZCBjb25zdW1lTGlzdChOb2RlIG90aGVyKSB7CgkJCWlmIChvdGhlci5maXJzdCA9PSBudWxsKSByZXR1cm47CgkJCWlmIChmaXJzdCA9PSBudWxsKSBmaXJzdCA9IG90aGVyLmZpcnN0OyBlbHNlIGxhc3QubmV4dCA9IG90aGVyLmZpcnN0OwoJCQlmb3IgKEl0ZW0gaXQgPSBvdGhlci5maXJzdDsgaXQgIT0gbnVsbDsgaXQgPSBpdC5uZXh0KSBpdC5ub2RlID0gdGhpczsKCQkJbGFzdCA9IG90aGVyLmxhc3Q7CgkJCW90aGVyLmZpcnN0ID0gbnVsbDsKCQkJb3RoZXIubGFzdCA9IG51bGw7CgkJCW5JdGVtcyArPSBvdGhlci5uSXRlbXM7CgkJCW90aGVyLm5JdGVtcyA9IDA7CgkJfQoKCQkvLyDQodC/0LvRjtGJ0LjQstCw0LXRgiDQsiBub2RlINGB0L7QtNC10YDQttC40LzQvtC1INGD0LfQu9CwIHRoaXMg0Lgg0LXQs9C+INGB0YvQvdC+0LLQtdC5LgoJCXZvaWQgY29sbGFwc2VUbyhOb2RlIG5vZGUpIHsKCQkJaWYgKHRoaXMgIT0gbm9kZSkgbm9kZS5jb25zdW1lTGlzdCh0aGlzKTsKCQkJaWYgKGxlZnQgIT0gbnVsbCkgeyBsZWZ0LmNvbGxhcHNlVG8obm9kZSk7IGxlZnQgPSBudWxsOyB9CgkJCWlmIChyaWdodCAhPSBudWxsKSB7IHJpZ2h0LmNvbGxhcHNlVG8obm9kZSk7IHJpZ2h0ID0gbnVsbDsgfQoJCX0KCgkJc3RhdGljIEFBQkIgYm91bmQoQUFCQiBiMCwgQUFCQiBiMSkgewoJCQlyZXR1cm4gYjAgPT0gbnVsbCA/IGIxIDogQUFCQi5ib3VuZChiMCwgYjEpOwoJCX0KCgkJLy8g0J/QtdGA0LXRgNCw0YHRgdGH0LjRgtGL0LLQsNC10YIg0L7Qv9C40YHQsNC90L3Ri9C5INC+0LHRitGR0LwuINCV0YHQu9C4INC10YHRgtGMINC00LXRgtC4LCDRgtC+INC/0YDQtdC00L/QvtC70LDQs9Cw0LXRgtGB0Y8sINGH0YLQviDQuNGFINC+0LHRitGR0LzRiyDRg9C20LUg0YDQsNGB0YHRh9C40YLQsNC90YsuCgkJdm9pZCByZWNhbGN1bGF0ZUJCKCkgewoJCQlBQUJCIGJiID0gbnVsbDsKCQkJZm9yIChJdGVtIGl0ID0gZmlyc3Q7IGl0ICE9IG51bGw7IGl0ID0gaXQubmV4dCkgYmIgPSBib3VuZChiYiwgaXQuYmIpOwoJCQlpZiAobGVmdCAhPSBudWxsKSBiYiA9IGJvdW5kKGJiLCBsZWZ0LmJiKTsKCQkJaWYgKHJpZ2h0ICE9IG51bGwpIGJiID0gYm91bmQoYmIsIHJpZ2h0LmJiKTsKCQkJaWYgKGJiID09IG51bGwpIHRocm93IFV0aWwuaW1wb3NzaWJsZSgpOwoJCQl0aGlzLmJiID0gYmI7CgkJfQoKCQkvLyDQn9C10YDQtdGA0LDQt9Cx0LjQstCw0LXRgiBub2RlLiDQn9GA0LXQtNC/0L7Qu9Cw0LPQsNC10YLRgdGPLCDRh9GC0L4g0L7QvSDQvdC1INC40LzQtdC10YIg0LTQtdGC0LXQuSwg0YLQvtC70YzQutC+INC+0LHRitC10LrRgtGLCgkJLy8gKNGB0L7RgdGC0L7Rj9C90LjQtSDQv9C+0YHQu9C1IGNvbGxhcHNlVG8g0LIg0YHQsNC80L7Qs9C+INGB0LXQsdGPKSwg0Lgg0YfRgtC+INC+0L/QuNGB0LDQvdC90YvQuSDQvtCx0YrRkdC8INGA0LDRgdGB0YfQuNGC0LDQvS4KCQl2b2lkIGRpdmlkZSgpIHsKCQkJaWYgKGxlZnQgIT0gbnVsbCB8fCByaWdodCAhPSBudWxsKSB0aHJvdyBVdGlsLmltcG9zc2libGUoKTsKCQkJLy8g0L/QvtGB0LvQtSDRgNCw0LfQsdC40LXQvdC40Y8g0LIg0LrQsNC20LTQvtC8INC40LcgMiDQtNC10YLQtdC5INC00L7Qu9C20L3QviDQvtGB0YLQsNGC0YzRgdGPIE1JTl9MRUFGX05PREVfU0laRSwg0YLQsNC6INGH0YLQvgoJCQkvLyAyICogTUlOX0xFQUZfTk9ERV9TSVpFIOKAlCDQsNCx0YHQvtC70Y7RgtC90YvQuSDQvNC40L3QuNC80YPQvC4KCQkJLy8g0J3QviDQttC10LvQsNGC0LXQu9GM0L3QviDQstC30Y/RgtGMINCx0L7Qu9GM0YjQtSwg0YfRgtC+0LHRiyDRg9C80LXQvdGM0YjQuNGC0Ywg0YjQsNC90YEg0LLRi9C/0L7Qu9C90LjRgtGMINC70LjRiNC90Y7RjiDRgNCw0LHQvtGC0YMgKNGB0LwuINC90LjQttC1KS4KCQkJaWYgKG5JdGVtcyA8IDMgKiBNSU5fTEVBRl9OT0RFX1NJWkUpIHJldHVybjsKCQkJLy8g0YDQsNC30LHQuNC10L3QuNC1INC/0L4g0LzQsNC60YHQuNC80LDQu9GM0L3QvtC80YMg0LjQt9C80LXRgNC10L3QuNGOCgkJCWludCBkaW0gPSBiYi5iLnkgLSBiYi5hLnkgPiBiYi5iLnggLSBiYi5hLnggPyAxIDogMDsKCQkJLy8g0LIg0LrQsNGH0LXRgdGC0LLQtSDRgtC+0YfQutC4INGA0LDQt9Cx0LjQtdC90LjRjyDQuNGB0L/QvtC70YzQt9GD0LXRgtGB0Y8g0YHRgNC10LTQvdC10LUg0LDRgNC40YTQvNC10YLQuNGH0LXRgdC60L7QtSDRhtC10L3RgtGA0L7QsgoJCQlkb3VibGUgZGl2cCA9IDA7CgkJCWZvciAoSXRlbSBpdCA9IGZpcnN0OyBpdCAhPSBudWxsOyBpdCA9IGl0Lm5leHQpCgkJCQlkaXZwICs9IGl0LmJiLmEuZ2V0KGRpbSkgKyBpdC5iYi5iLmdldChkaW0pOwoJCQlkaXZwIC89IDIgKiBuSXRlbXM7CgoJCQkvLyDRgNCw0LfQtNC10LvQtdC90LjQtSDQvdCwINC00LLQsCDRg9C30LvQsCDQv9C+INC/0L7Qu9C+0LbQtdC90LjRjiDRhtC10L3RgtGA0L7QsiDQvtGC0L3QvtGB0LjRgtC10LvRjNC90L4gZGl2cAoJCQlOb2RlIG5MID0gbmV3IE5vZGUoKSwgblIgPSBuZXcgTm9kZSgpOwoJCQlJdGVtIG5leHQgPSBudWxsOwoJCQlmb3IgKEl0ZW0gaXQgPSBmaXJzdDsgaXQgIT0gbnVsbDsgaXQgPSBuZXh0KSB7CgkJCQluZXh0ID0gaXQubmV4dDsKCQkJCWl0Lm5leHQgPSBudWxsOwoJCQkJaWYgKDAuNSAqIChpdC5iYi5hLmdldChkaW0pICsgaXQuYmIuYi5nZXQoZGltKSkgPCBkaXZwKQoJCQkJCW5MLmFkZFRvTGlzdChpdCk7CgkJCQllbHNlCgkJCQkJblIuYWRkVG9MaXN0KGl0KTsKCQkJfQoJCQl0aGlzLm5JdGVtcyA9IDA7CgkJCXRoaXMuZmlyc3QgPSBudWxsOwoJCQl0aGlzLmxhc3QgPSBudWxsOwoKCQkJLy8g0JXRgdC70Lgg0LLRgdGRINGF0L7RgNC+0YjQviwg0LfQsNCy0LXRgNGI0LjRgtGMINC40LfQvNC10L3QtdC90LjRjywg0L/QtdGA0LXRgdGH0LjRgtCw0YLRjCDQvtCx0YrRkdC80Ysg0LTQtdGC0LXQuQoJCQkvLyDQuCDRgNC10LrRg9GA0YHQuNCy0L3QviDQv9GA0L7QtNC+0LvQttC40YLRjCDRgNCw0LfQsdC40LXQvdC40LUuCgkJCWlmIChuTC5uSXRlbXMgPj0gTUlOX0xFQUZfTk9ERV9TSVpFICYmIG5SLm5JdGVtcyA+PSBNSU5fTEVBRl9OT0RFX1NJWkUpIHsKCQkJCXRoaXMubGVmdCA9IG5MOyBuTC5wYXJlbnQgPSB0aGlzOyBuTC5idmggPSBidmg7CgkJCQl0aGlzLnJpZ2h0ID0gblI7IG5SLnBhcmVudCA9IHRoaXM7IG5SLmJ2aCA9IGJ2aDsKCQkJCW5MLnJlY2FsY3VsYXRlQkIoKTsgbkwuZGl2aWRlKCk7CgkJCQluUi5yZWNhbGN1bGF0ZUJCKCk7IG5SLmRpdmlkZSgpOwoJCQl9IGVsc2UgewoJCQkJLy8g0JjQvdCw0YfQtSDRgNCw0LfQsdC40LXQvdC40LUg0LLRi9C/0L7Qu9C90LXQvdC+INGB0LvQuNGI0LrQvtC8INGB0LjQu9GM0L3QviAo0L3QviDQutGC0L4g0LYg0LfQvdCw0LspLAoJCQkJLy8g0LLQtdGA0L3Rg9GC0Ywg0L7QsdGK0LXQutGC0Ysg0LjQtyDQvdC10YHQvtGB0YLQvtGP0LLRiNC40YXRgdGPINC00LXRgtC10Lkg0LIgdGhpcy4KCQkJCWNvbnN1bWVMaXN0KG5MKTsKCQkJCWNvbnN1bWVMaXN0KG5SKTsKCQkJfQoJCX0KCgkJLy8g0JLRi9C30YvQstCw0LXRgiBjYiDQtNC70Y8g0L7QsdGK0LXQutGC0L7Qsiwg0L/QtdGA0LXRgdC10LrQsNGO0YnQuNGFINC+0YLRgNC10LfQvtC6IGHigJNiLgoJCXZvaWQgY2FzdFNlZ21lbnQoVmVjMiBhLCBWZWMyIGIsIENvbnN1bWVyPEl0ZW0+IGNiKSB7CgkJCWJ2aC5fYWFiYlhzZWdDYWxscysrOwoJCQlpZiAoIWJiLmludGVyc2VjdHNTZWdtZW50KGEsIGIpKSByZXR1cm47CgkJCWZvciAoSXRlbSBpdCA9IGZpcnN0OyBpdCAhPSBudWxsOyBpdCA9IGl0Lm5leHQpIHsKCQkJCWJ2aC5fYWFiYlhzZWdDYWxscysrOwoJCQkJaWYgKGl0LmJiLmludGVyc2VjdHNTZWdtZW50KGEsIGIpKSBjYi5hY2NlcHQoaXQpOwoJCQl9CgkJCWlmIChsZWZ0ICE9IG51bGwpIGxlZnQuY2FzdFNlZ21lbnQoYSwgYiwgY2IpOwoJCQlpZiAocmlnaHQgIT0gbnVsbCkgcmlnaHQuY2FzdFNlZ21lbnQoYSwgYiwgY2IpOwoJCX0KCX0KCglOb2RlIHJvb3Q7CglpbnQgX2FhYmJYc2VnQ2FsbHMsIF9tYXhQcVNpemU7CgoJLy8gYWRkINC/0YDQvtGB0YLQviDQtNC+0LHQsNCy0LvRj9C10YIg0L7QsdGK0LXQutGCINCyINC60L7RgNC10L3RjCwg0YLQsNC6INGH0YLQviDQvtC9INCx0YPQtNC10YIg0L/RgNC+0LLQtdGA0Y/RgtGM0YHRjyDQv9GA0Lgg0LvRjtCx0YvRhSDQt9Cw0L/RgNC+0YHQsNGFLgoJLy8g0J7QvSDQv9C+0L/QsNC00ZHRgiDQsiDQvNC10YHRgtC+INC/0L7Qu9GD0YfRiNC1INC/0L7RgdC70LUgcmVidWlsZC4KCXB1YmxpYyB2b2lkIGFkZChJdGVtIGl0ZW0pIHsKCQlpZiAoaXRlbS5ub2RlICE9IG51bGwpIHRocm93IFV0aWwuYmFkQXJnZigiJXMg0YPQttC1INC00L7QsdCw0LLQu9C10L0uIiwgaXRlbSk7CgkJaWYgKHJvb3QgPT0gbnVsbCkgewoJCQlyb290ID0gbmV3IE5vZGUoKTsKCQkJcm9vdC5iYiA9IGl0ZW0uYmI7CgkJCXJvb3QuYnZoID0gdGhpczsKCQl9IGVsc2UgewoJCQlyb290LmJiID0gQUFCQi5ib3VuZChyb290LmJiLCBpdGVtLmJiKTsKCQl9CgkJcm9vdC5hZGRUb0xpc3QoaXRlbSk7CgkJaXRlbS5ub2RlID0gcm9vdDsKCX0KCglwdWJsaWMgdm9pZCByZW1vdmUoSXRlbSBpdGVtKSB7CgkJTm9kZSBub2RlID0gaXRlbS5ub2RlOwoJCWlmIChub2RlID09IG51bGwgfHwgbm9kZS5idmggIT0gdGhpcykgdGhyb3cgVXRpbC5iYWRBcmdmKCIlcyDQvdC1ICVzLiIsIGl0ZW0sIG5vZGUgPT0gbnVsbCA/ICLQtNC+0LHQsNCy0LvQtdC9IiA6ICLQsiDRgtC+0Lwg0LTQtdGA0LXQstC1Iik7CgkJLy8g0KPQtNCw0LvQuNGC0YwgaXRlbSDQuNC3INGB0LLRj9C30L3QvtCz0L4g0YHQv9C40YHQutCwLCDRhdGA0LDQvdGP0YnQtdCz0L7RgdGPINCyIGl0ZW0ubm9kZS4g0JzQvtC20L3QviDQsdGL0LvQviDQsdGLINC90LUg0LjRgdC60LDRgtGMLCDQtdGB0LvQuCDQsdGLINC+0L0g0LHRi9C7INC00LLRg9GB0LLRj9C30L3Ri9C8Li4uCgkJZm9yIChJdGVtIGl0ID0gbm9kZS5maXJzdCwgcHJldiA9IG51bGw7IGl0ICE9IG51bGw7IHByZXYgPSBpdCwgaXQgPSBpdC5uZXh0KQoJCQlpZiAoaXRlbSA9PSBpdCkgewoJCQkJaWYgKHByZXYgPT0gbnVsbCkgbm9kZS5maXJzdCA9IGl0OyBlbHNlIHByZXYubmV4dCA9IGl0Lm5leHQ7CgkJCQlpZiAoaXQubmV4dCA9PSBudWxsKSBub2RlLmxhc3QgPSBwcmV2OwoJCQkJbm9kZS5uSXRlbXMtLTsKCQkJCWl0ZW0ubm9kZSA9IG51bGw7CgkJCQlicmVhazsKCQkJfQoJCWlmIChpdGVtLm5vZGUgIT0gbnVsbCkgdGhyb3cgVXRpbC5pbXBvc3NpYmxlKCLRg9C30LXQuyDQvdC1INC90LDQudC00LXQvSIpOwoKCQkvLyDQkiDQu9C40YHRgtC+0LLQvtC8INGD0LfQu9C1INGB0YLQsNC70L4g0YHQu9C40YjQutC+0Lwg0LzQsNC70L4g0L7QsdGK0LXQutGC0L7Qsj8g0KPQtNCw0LvQuNGC0Ywg0LXQs9C+LCDQv9C10YDQtdC00LDQsiDQvtGB0YLQsNCy0YjQuNC10YHRjyDQvtCx0YrQtdC60YLRiyDRgNC+0LTQuNGC0LXQu9GM0YHQutC+0LzRgy4KCQkvLyDQrdGC0L7RgiDQv9GA0L7RhtC10YHRgSDQvNC+0LbQtdGCINC/0L7QstGC0L7RgNC40YLRjNGB0Y8g0YDQtdC60YPRgNGB0LjQstC90L4uCgkJd2hpbGUgKG5vZGUubkl0ZW1zIDwgTUlOX0xFQUZfTk9ERV9TSVpFICYmIG5vZGUubGVmdCA9PSBudWxsICYmIG5vZGUucmlnaHQgPT0gbnVsbCkgewoJCQlpZiAobm9kZS5wYXJlbnQgPT0gbnVsbCkgewoJCQkJaWYgKG5vZGUubkl0ZW1zID09IDApIHsgbm9kZSA9IG51bGw7IHJvb3QgPSBudWxsOyB9CgkJCQlicmVhazsKCQkJfQoJCQlub2RlLnBhcmVudC5jb25zdW1lTGlzdChub2RlKTsKCQkJaWYgKG5vZGUgPT0gbm9kZS5wYXJlbnQubGVmdCkgbm9kZS5wYXJlbnQubGVmdCA9IG51bGw7CgkJCWVsc2UgaWYgKG5vZGUgPT0gbm9kZS5wYXJlbnQucmlnaHQpIG5vZGUucGFyZW50LnJpZ2h0ID0gbnVsbDsKCQkJZWxzZSB0aHJvdyBVdGlsLmltcG9zc2libGUoKTsKCQkJbm9kZSA9IG5vZGUucGFyZW50OwoJCX0KCgkJaWYgKG5vZGUgIT0gbnVsbCkgbm9kZS5yZWNhbGN1bGF0ZUJCKCk7Cgl9CgoJcHVibGljIHZvaWQgcmVidWlsZCgpIHsKCQlpZiAocm9vdCA9PSBudWxsKSByZXR1cm47CgkJcm9vdC5jb2xsYXBzZVRvKHJvb3QpOwoJCXJvb3QuZGl2aWRlKCk7Cgl9CgoJcHVibGljIHZvaWQgY2FzdFNlZ21lbnQoVmVjMiBhLCBWZWMyIGIsIENvbnN1bWVyPEl0ZW0+IGNiKSB7CgkJaWYgKHJvb3QgPT0gbnVsbCkgcmV0dXJuOwoJCXJvb3QuY2FzdFNlZ21lbnQoYSwgYiwgY2IpOwoJfQoKCXB1YmxpYyBMaXN0PEl0ZW0+IGNhc3RTZWdtZW50KFZlYzIgYSwgVmVjMiBiKSB7CgkJdmFyIHIgPSBuZXcgQXJyYXlMaXN0PEl0ZW0+KCk7CgkJY2FzdFNlZ21lbnQoYSwgYiwgaXQgLT4gci5hZGQoaXQpKTsKCQlyZXR1cm4gcjsKCX0KCgljbGFzcyBRdWVyeUFyb3VuZFVuaW9uIHsKCQlib29sZWFuIGlzTm9kZTsKCQlPYmplY3Qgb2JqOwoJCWRvdWJsZSBkaXN0OwoKCQlwdWJsaWMgUXVlcnlBcm91bmRVbmlvbihib29sZWFuIGlzTm9kZSwgT2JqZWN0IG9iaiwgZG91YmxlIGRpc3QpIHsKCQkJdGhpcy5pc05vZGUgPSBpc05vZGU7CgkJCXRoaXMub2JqID0gb2JqOwoJCQl0aGlzLmRpc3QgPSBkaXN0OwoJCX0KCQlwdWJsaWMgUXVlcnlBcm91bmRVbmlvbihJdGVtIGl0LCBWZWMyIHApIHsgdGhpcyhmYWxzZSwgaXQsIGl0LmJiLnNxckRpc3RhbmNlVG8ocCkpOyB9CgkJcHVibGljIFF1ZXJ5QXJvdW5kVW5pb24oTm9kZSBuLCBWZWMyIHApIHsgdGhpcyh0cnVlLCBuLCBuLmJiLnNxckRpc3RhbmNlVG8ocCkpOyB9Cgl9CgoJQEZ1bmN0aW9uYWxJbnRlcmZhY2UKCXB1YmxpYyBzdGF0aWMgaW50ZXJmYWNlIFF1ZXJ5QXJvdW5kU2hvZ2dvdGggewoJCWJvb2xlYW4gZmVlZChJdGVtIGl0LCBkb3VibGUgc3FyRGlzdCk7Cgl9CgoJdm9pZCBjaG9wQmFiaWVzKE5vZGUgbiwgUHJpb3JpdHlRdWV1ZTxRdWVyeUFyb3VuZFVuaW9uPiBxLCBWZWMyIHApIHsKCQlpZiAobi5sZWZ0ICE9IG51bGwpIHEuYWRkKG5ldyBRdWVyeUFyb3VuZFVuaW9uKG4ubGVmdCwgcCkpOwoJCWlmIChuLnJpZ2h0ICE9IG51bGwpIHEuYWRkKG5ldyBRdWVyeUFyb3VuZFVuaW9uKG4ucmlnaHQsIHApKTsKCQlmb3IgKEl0ZW0gaXQgPSBuLmZpcnN0OyBpdCAhPSBudWxsOyBpdCA9IGl0Lm5leHQpIHEuYWRkKG5ldyBRdWVyeUFyb3VuZFVuaW9uKGl0LCBwKSk7CgkJX21heFBxU2l6ZSA9IG1heChfbWF4UHFTaXplLCBxLnNpemUoKSk7Cgl9CgoJcHVibGljIHZvaWQgcXVlcnlBcm91bmQoVmVjMiBwLCBRdWVyeUFyb3VuZFNob2dnb3RoIHNob2dnb3RoKSB7CgkJdmFyIHEgPSBuZXcgUHJpb3JpdHlRdWV1ZTxRdWVyeUFyb3VuZFVuaW9uPihDb21wYXJhdG9yLmNvbXBhcmluZ0RvdWJsZShxdSAtPiBxdS5kaXN0KSk7CgkJaWYgKHJvb3QgIT0gbnVsbCkgY2hvcEJhYmllcyhyb290LCBxLCBwKTsKCQlmb3IgKFF1ZXJ5QXJvdW5kVW5pb24gcWl0ZW07IG51bGwgIT0gKHFpdGVtID0gcS5wb2xsKCkpOyApCgkJCWlmIChxaXRlbS5pc05vZGUpIGNob3BCYWJpZXMoKE5vZGUpIHFpdGVtLm9iaiwgcSwgcCk7CgkJCWVsc2UgaWYgKCFzaG9nZ290aC5mZWVkKChJdGVtKSBxaXRlbS5vYmosIHFpdGVtLmRpc3QpKSByZXR1cm47Cgl9CgoJcHVibGljIExpc3Q8SXRlbT4gcXVlcnlBcm91bmQoVmVjMiBwKSB7CgkJdmFyIHIgPSBuZXcgQXJyYXlMaXN0PEl0ZW0+KCk7CgkJcXVlcnlBcm91bmQocCwgKGl0LCBfc3FyRGlzdCkgLT4geyByLmFkZChpdCk7IHJldHVybiB0cnVlOyB9KTsKCQlyZXR1cm4gcjsKCX0KCglwdWJsaWMgU3RyaW5nIGR1bXAoKSB7CgkJaWYgKHJvb3QgPT0gbnVsbCkgcmV0dXJuICI8ZW1wdHk+IjsKCQl2YXIgc2IgPSBuZXcgU3RyaW5nQnVpbGRlcigpOwoJCXNiLmFwcGVuZCgicm9vdCA9IHtcbiIpOwoJCWR1bXAocm9vdCwgc2IsIDEpOwoJCXNiLmFwcGVuZCgifSIpOwoJCXJldHVybiBzYi50b1N0cmluZygpOwoJfQoKCXN0YXRpYyBTdHJpbmdCdWlsZGVyIHRhYihTdHJpbmdCdWlsZGVyIHNiLCBpbnQgZGVwdGgpIHsKCQlyZXR1cm4gc2IuYXBwZW5kKCIgIi5yZXBlYXQoMiAqIGRlcHRoKSk7Cgl9CgoJc3RhdGljIHZvaWQgZHVtcChOb2RlIG5vZGUsIFN0cmluZ0J1aWxkZXIgc2IsIGludCBkZXB0aCkgewoJCXRhYihzYiwgZGVwdGgpLmFwcGVuZChub2RlLmJiKS5hcHBlbmQoIlxuIik7CgkJaWYgKG5vZGUubkl0ZW1zICE9IDApIHsKCQkJdGFiKHNiLCBkZXB0aCkuYXBwZW5kKCJpdGVtcygiKS5hcHBlbmQobm9kZS5uSXRlbXMpLmFwcGVuZCgiKTogIik7CgkJCWZvciAoSXRlbSBpdCA9IG5vZGUuZmlyc3Q7IGl0ICE9IG51bGw7IGl0ID0gaXQubmV4dCkgewoJCQkJc2IuYXBwZW5kKGl0ICE9IG5vZGUuZmlyc3QgPyAiLCAiIDogIiIpLmFwcGVuZChpdCk7CgkJCX0KCQkJc2IuYXBwZW5kKCJcbiIpOwoJCX0KCQlmb3IgKGludCBjaGlsZCA9IDA7IGNoaWxkIDwgMjsgY2hpbGQrKykKCQkJaWYgKChjaGlsZCA9PSAwID8gbm9kZS5sZWZ0IDogbm9kZS5yaWdodCkgIT0gbnVsbCkgewoJCQkJdGFiKHNiLCBkZXB0aCkuYXBwZW5kKGNoaWxkID09IDAgPyAibGVmdCIgOiAicmlnaHQiKS5hcHBlbmQoIiA9IHtcbiIpOwoJCQkJZHVtcChjaGlsZCA9PSAwID8gbm9kZS5sZWZ0IDogbm9kZS5yaWdodCwgc2IsIGRlcHRoICsgMSk7CgkJCQl0YWIoc2IsIGRlcHRoKS5hcHBlbmQoIn1cbiIpOwoJCQl9Cgl9CgoJaW50IHJlc2V0QWFiYlhzZWdDYWxscygpIHsgaW50IHIgPSBfYWFiYlhzZWdDYWxsczsgX2FhYmJYc2VnQ2FsbHMgPSAwOyByZXR1cm4gcjsgfQoJaW50IHJlc2V0TWF4UHFTaXplKCkgeyBpbnQgciA9IF9tYXhQcVNpemU7IF9tYXhQcVNpemUgPSAwOyByZXR1cm4gcjsgfQp9CgpjbGFzcyBNeUl0ZW0gZXh0ZW5kcyBCVkguSXRlbSB7CglTdHJpbmcgbmFtZTsKCU15SXRlbShBQUJCIGJiLCBTdHJpbmcgbmFtZSkgeyBzdXBlcihiYik7IHRoaXMubmFtZSA9IG5hbWU7IH0KCUBPdmVycmlkZSBwdWJsaWMgU3RyaW5nIHRvU3RyaW5nKCkgeyByZXR1cm4gbmFtZTsgfQp9CgpjbGFzcyBJZGVvbmUKewoJc3RhdGljIGNoYXIgY3ljbGljTGV0dGVyKGludCBpbmRleCkgewoJCWZpbmFsIGludCBOX0xFVFRFUlMgPSAoaW50KSdaJyAtIChpbnQpJ0EnICsgMTsKCQlyZXR1cm4gKGNoYXIpICgoaW50KSdBJyArIChpbmRleCAlIE5fTEVUVEVSUyArIE5fTEVUVEVSUykgJSBOX0xFVFRFUlMpOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCBtYWluIChTdHJpbmdbXSBhcmdzKSB0aHJvd3MgamF2YS5sYW5nLkV4Y2VwdGlvbgoJewoJCXRyeSB7CgkJCXZhciBidmggPSBuZXcgQlZIKCk7CgkJCS8vIHZhciB0b1JlbW92ZSA9IG5ldyBBcnJheUxpc3Q8TXlJdGVtPigpOwoJCQlmb3IgKGludCB5ID0gMDsgeSA8IC8qMTAqLyAxMDA7IHkrKykgewoJCQkJZm9yIChpbnQgeCA9IDA7IHggPCAvKjEwKi8gMTAwOyB4KyspIHsKCQkJCQl2YXIgaXRlbSA9IG5ldyBNeUl0ZW0oCgkJCQkJCUFBQkIuQUFCQigKCQkJCQkJCVZlYzIuVmVjMih4KzAuMiwgeSswLjIpLAoJCQkJCQkJVmVjMi5WZWMyKHgrMC44LCB5KzAuOCkpLAoJCQkJCQkiIiArIGN5Y2xpY0xldHRlcih4KSArIGN5Y2xpY0xldHRlcih5KSk7CgkJCQkJLy8gaWYgKCh4ICsgeSkgJSAyIDwgMSkgdG9SZW1vdmUuYWRkKGl0ZW0pOwoJCQkJCWJ2aC5hZGQoaXRlbSk7CgkJCQl9CgkJCX0KCgkJCVZlYzIgc2VnQSA9IFZlYzIuVmVjMigyLCA0LjMpLCBzZWdCID0gVmVjMi5WZWMyKDcsIDMuMyk7CgoJCQlTeXN0ZW0ub3V0LnByaW50bG4oIjEuINCX0LDQv9GA0L7RgSDQvdCwINC90LXQv9C10YDQtdGB0YLRgNC+0LXQvdC90L7QvCDQtNC10YDQtdCy0LUuIik7CgkJCVN5c3RlbS5vdXQucHJpbnRmKCIlc1xuIiwgYnZoLmNhc3RTZWdtZW50KHNlZ0EsIHNlZ0IpKTsKCQkJU3lzdGVtLm91dC5wcmludGYoItCS0YvQt9C+0LLQvtCyIEFBQkIuaW50ZXJzZWN0c1NlZ21lbnQ6ICVkLlxuXG4iLCBidmgucmVzZXRBYWJiWHNlZ0NhbGxzKCkpOwoJCQkvLyBTdHJpbmcgdHJlZUR1bXAwID0gYnZoLmR1bXAoKTsKCgkJCWJ2aC5yZWJ1aWxkKCk7CgkJCVN5c3RlbS5vdXQucHJpbnRsbigiMi4g0JfQsNC/0YDQvtGBINC90LAg0L/QtdGA0LXRgdGC0YDQvtC10L3QvdC+0Lwg0LTQtdGA0LXQstC1LiIpOwoJCQlTeXN0ZW0ub3V0LnByaW50ZigiJXNcbiIsIGJ2aC5jYXN0U2VnbWVudChzZWdBLCBzZWdCKSk7CgkJCVN5c3RlbS5vdXQucHJpbnRmKCLQktGL0LfQvtCy0L7QsiBBQUJCLmludGVyc2VjdHNTZWdtZW50OiAlZC5cblxuIiwgYnZoLnJlc2V0QWFiYlhzZWdDYWxscygpKTsKCQkJLy8gU3RyaW5nIHRyZWVEdW1wMSA9IGJ2aC5kdW1wKCk7CgoJCQkvKmZvciAodmFyIHJlbTogdG9SZW1vdmUpIGJ2aC5yZW1vdmUocmVtKTsKCQkJU3lzdGVtLm91dC5wcmludGYoIjMuINCX0LDQv9GA0L7RgSDQv9C+0YHQu9C1INGA0LDQstC90L7QvNC10YDQvdC+0LPQviDRg9C00LDQu9C10L3QuNGPICVkINGN0LvQtdC80LXQvdGC0L7Qsi5cbiIsIHRvUmVtb3ZlLnNpemUoKSk7CgkJCVN5c3RlbS5vdXQucHJpbnRmKCIlc1xuIiwgYnZoLmNhc3RTZWdtZW50KHNlZ0EsIHNlZ0IpKTsKCQkJU3lzdGVtLm91dC5wcmludGYoItCS0YvQt9C+0LLQvtCyIEFBQkIuaW50ZXJzZWN0c1NlZ21lbnQ6ICVkLlxuXG4iLCBidmgucmVzZXRBYWJiWHNlZ0NhbGxzKCkpOyovCgoJCQlWZWMyIGVwaWNlbnRlciA9IFZlYzIuVmVjMig3LjUsIDYuNSk7IC8vIEhHCgkJCS8qU3lzdGVtLm91dC5wcmludGYoIjMuINCh0L7RgNGC0LjRgNC+0LLQutCwINC/0L4g0YDQsNGB0YHRgtC+0Y/QvdC40Y4g0L7RgiDRgtC+0YfQutC4ICVzLlxuIiwgZXBpY2VudGVyKTsKCQkJYnZoLnF1ZXJ5QXJvdW5kKGVwaWNlbnRlciwgKGl0LCBzcXJEaXN0KSAtPiB7CgkJCQlTeXN0ZW0ub3V0LnByaW50ZigiJXMgKCVzKVxuIiwgaXQsIFV0aWwuaHVtYW5GbG9hdChzcXJ0KHNxckRpc3QpKSk7CgkJCQlyZXR1cm4gdHJ1ZTsKCQkJfSk7CgkJCVN5c3RlbS5vdXQucHJpbnRmKCLQnNCw0LrRgdC40LzQsNC70YzQvdC+0LUg0YfQuNGB0LvQviDRjdC70LXQvNC10L3RgtC+0LIg0L7Rh9C10YDQtdC00Lg6ICVkLlxuXG4iLCBidmgucmVzZXRNYXhQcVNpemUoKSk7Ki8KCgkJCWRvdWJsZSByYWRpdXMgPSAzOwoJCQlTeXN0ZW0ub3V0LnByaW50ZigiMy4g0KHQvtGA0YLQuNGA0L7QstC60LAg0L/QviDRgNCw0YHRgdGC0L7Rj9C90LjRjiDQvtGCINGC0L7Rh9C60LggJXMsINC80LDQutGBLiDRgNCw0LTQuNGD0YEgJXMuXG4iLCBlcGljZW50ZXIsIFV0aWwuaHVtYW5GbG9hdChyYWRpdXMpKTsKCQkJYnZoLnF1ZXJ5QXJvdW5kKGVwaWNlbnRlciwgKGl0LCBzcXJEaXN0KSAtPiB7CgkJCQlpZiAoc3FyRGlzdCA+IHJhZGl1cyAqIHJhZGl1cykgcmV0dXJuIGZhbHNlOwoJCQkJU3lzdGVtLm91dC5wcmludGYoIiVzICglcylcbiIsIGl0LCBVdGlsLmh1bWFuRmxvYXQoc3FydChzcXJEaXN0KSkpOwoJCQkJcmV0dXJuIHRydWU7CgkJCX0pOwoJCQlTeXN0ZW0ub3V0LnByaW50Zigi0JzQsNC60YHQuNC80LDQu9GM0L3QvtC1INGH0LjRgdC70L4g0Y3Qu9C10LzQtdC90YLQvtCyINC+0YfQtdGA0LXQtNC4OiAlZC5cblxuIiwgYnZoLnJlc2V0TWF4UHFTaXplKCkpOwoKCQkJLypTeXN0ZW0ub3V0LnByaW50Zigi0JTQsNC80L8g0LTQtdGA0LXQstCwINC/0L7RgdC70LUg0YjQsNCz0LAgMTpcbiVzXG5cbiIsIHRyZWVEdW1wMCk7CgkJCVN5c3RlbS5vdXQucHJpbnRmKCLQlNCw0LzQvyDQtNC10YDQtdCy0LAg0L/QvtGB0LvQtSDRiNCw0LPQsCAyOlxuJXNcblxuIiwgdHJlZUR1bXAxKTsqLwoJCX0gY2F0Y2ggKEV4Y2VwdGlvbiBlKSB7CgkJCVN5c3RlbS5vdXQucHJpbnRsbihVdGlsLnRyYWNlKGUpKTsKCQl9Cgl9Cn0=