import java.util.*;
import java.util.stream.*;
import java.lang.*;
import java.io.*;
import static java.
lang.
Math.
*;
};
return sw.toString();
}
public static int rangecheck
(int x,
int min,
int max,
String what
) { if (x < min || x > max) throw BadArgf("Bad %s: %d, should be [%d; %d].", what, x, min, max);
return x;
}
}
class Bitfield2D {
int[] data;
int w, h;
static final int SANE_BITS_LIMIT = 1_000_000_000;
static final int SANE_DIMENSION_LIMIT = 100_000_000;
static final int BASE
= Integer.
SIZE; // BASE expected to be power of 2, then
// (bitno >> BASEP2) is faster version of (bitno / BASE), and
// (bitno & BASEMASK) is faster version of (bitno % BASE).
//
// Compiler cannot optimize these operations as effectively as programmer can,
// because they work differently for negative 'bitno' values, so it should check for negatives.
// And it's only we who know that 'bitno' is never negative.
//
// Not sure if there is no danger of BASEP2 formula NOT resulting in compile-time constant,
// but it appears to be OK.
static final int BASEP2
= 32 - (Integer.
numberOfLeadingZeros(BASE
) + 1); static final int BASEMASK = BASE - 1;
public Bitfield2D(int w, int h) {
validateSize(w, h);
this.data = new int[(w*h + (BASE - 1)) >> BASEP2];
this.w = w;
this.h = h;
}
static void validateSize(int w, int h) {
if (w < 0 || h < 0 || w > SANE_DIMENSION_LIMIT || h > SANE_DIMENSION_LIMIT
|| w > 0 && h > SANE_BITS_LIMIT / w)
throw Util.
BadArgf("Bad bitfield size: %d×%d.", w, h
); }
public static boolean validCoord(int w, int h, int x, int y) {
return x >= 0 && y >= 0 && x < w && y < h;
}
public static void validateCoord(int w, int h, int x, int y) {
if (!validCoord(w, h, x, y))
throw Util.
BadArgf("Bad coord of bitfield %d×%d: (%d, %d).", w, h, x, y
); }
static void validateRect(int fullw, int fullh, int x, int y, int w, int h) {
if (x < 0 || y < 0 || w < 0 || h < 0 || x + w > fullw || y + h > fullh)
throw Util.
BadArgf("Bad rect of bitfield %d×%d: (%d, %d) ~ (%d, %d).",
fullw, fullh, x, y, x+w, y+h);
}
boolean rawget(int index) { return (data[index >> BASEP2] & (1 << (index & BASEMASK))) != 0; }
void rawset(int index) { data[index >> BASEP2] |= 1 << (index & BASEMASK); }
void rawunset(int index) { data[index >> BASEP2] &= ~(1 << (index & BASEMASK)); }
void rawset(int index, boolean value) { if (value) rawset(index); else rawunset(index); }
public boolean get(int x, int y) { validateCoord(w, h, x, y); return rawget(y*w+x); }
public void set(int x, int y) { validateCoord(w, h, x, y); rawset(y*w+x); }
public void unset(int x, int y) { validateCoord(w, h, x, y); rawunset(y*w+x); }
public void set(int x, int y, boolean value) { validateCoord(w, h, x, y); rawset(y*w+x, value); }
public int width() { return w; }
public int height() { return h; }
// For functions like or() that apply one bitfield to another,
// reduces potentially out-of-borders input to its valid part.
static class CoordWarden {
int srcX, srcY, w, h, destX, destY;
CoordWarden(int destFullW, int destFullH, int srcFullW, int srcFullH,
int aSrcX, int aSrcY, int aW, int aH, int aDestX, int aDestY) {
validateRect(srcFullW, srcFullH, aSrcX, aSrcY, aW, aH);
srcX = aSrcX; srcY = aSrcY; w = aW; h = aH; destX = aDestX; destY = aDestY;
if (destX < 0) { srcX -= destX; w += destX; destX = 0; }
if (destX + w > destFullW) { w = destFullW - destX; }
if (destY < 0) { srcY -= destY; h += destY; destY = 0; }
if (destY + h > destFullH) { h = destFullH - destY; }
}
}
public void or(Bitfield2D b, int thisX, int thisY) {
or(b, 0, 0, b.w, b.h, thisX, thisY);
}
public void or(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w - ward.w, bOfs += b.w - ward.w)
for (int dx = 0; dx < ward.w; dx++, thisOfs++, bOfs++)
if (b.rawget(bOfs)) this.rawset(thisOfs);
}
public void orBATCH(Bitfield2D b, int thisX, int thisY) {
orBATCH(b, 0, 0, b.w, b.h, thisX, thisY);
}
public void orBATCH(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
apply(b, bx, by, w, h, thisX, thisY, (itemA, itemB) -> itemA | itemB);
}
public void andNot(Bitfield2D b, int thisX, int thisY) {
andNot(b, 0, 0, b.w, b.h, thisX, thisY);
}
public void andNot(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w - ward.w, bOfs += b.w - ward.w)
for (int dx = 0; dx < ward.w; dx++, thisOfs++, bOfs++)
if (b.rawget(bOfs)) this.rawunset(thisOfs);
}
public void andNotBATCH(Bitfield2D b, int thisX, int thisY) {
andNotBATCH(b, 0, 0, b.w, b.h, thisX, thisY);
}
public void andNotBATCH(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
apply(b, bx, by, w, h, thisX, thisY, (itemA, itemB) -> itemA & ~itemB);
}
@FunctionalInterface
abstract int apply(int a, int b);
}
void apply
(Bitfield2D b,
int bx,
int by,
int w,
int h,
int thisX,
int thisY,
Operation op
) { var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w, bOfs += b.w)
applyOp(this.data, thisOfs, b.data, bOfs, ward.w, op);
}
public Bitfield2D copyFrom(Bitfield2D b, int thisX, int thisY) {
return copyFrom(b, 0, 0, b.w, b.h, thisX, thisY);
}
public Bitfield2D copyFrom(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w, bOfs += b.w)
copyBits(b.data, bOfs, this.data, thisOfs, ward.w);
return this;
}
static int readBitsSmall(int[] src, int srcPos, int count) {
assert count > 0 && count < BASE;
// 0 1 2 3 4 5 6
// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
// ^srcPos=5
// ^srcPos+count=11
int bits = src[srcPos >> BASEP2] >>> (srcPos & BASEMASK); // FGH
if (count > BASE - (srcPos & BASEMASK))
bits |= src[(srcPos >> BASEP2) + 1] << (BASE - (srcPos & BASEMASK)); // FGH | 000IJKLMNOP = FGHIJKLMNOP
return bits & ((1 << count) - 1); // FGHIJK
}
static void writeBits1(int bits, int[] dst, int dstPos, int count) {
assert count > 0 && count < BASE - (dstPos & BASEMASK) && bits < 1 << count;
// bits = qwer
// 0 1 2 3 4 5 6
// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
// ^dstPos=3
// ^dstPos+count=7
dst[dstPos >> BASEP2] = dst[dstPos >> BASEP2] // ABCDEFGH
& ~(((1 << count) - 1) << (dstPos & BASEMASK)) // ABC0000H
| (bits << (dstPos & BASEMASK)); // ABCqwerH
}
static void copyBits(int[] src, int srcPos, int[] dst, int dstPos, int count) {
// Imagine BASE=8 (as if byte[] instead of int[]), srcPos = 3, dstPos = 2, count=25.
//
// 0 1 2 3 4 5 6
// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
// ^srcPos=3 ^srcPos+count=28
// dst = ...............................................
// 0 ^dstPos=2 2 3 4 5
//
// First we align dstPos to the multiple of BASE by reading
// first BASE - dstPos%BASE bits with readBitsSmall() and writing them
// into the first cell of dst[] with writeBits1.
//
// We'll get:
// 0 1 2 3 4 5 6
// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
// ^srcPos=9 ^srcPos+count=28
// dst = ..DEFGHI.......................................
// 0 1 2 3 4 5
// ^dstPos=8
if ((dstPos & BASEMASK) != 0) {
int n = min(count, BASE - (dstPos & BASEMASK));
if (n > 0) writeBits1(readBitsSmall(src, srcPos, n), dst, dstPos, n);
srcPos += n;
dstPos += n;
count -= n;
}
// Now we fill whole cells while possible.
// We'll get:
// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
// ^srcPos=25
// ^srcPos+count=28
// dst = ..DEFGHIJKLMNOPQRSTUVWXY.......................
// 0 1 2 3 4 5
// ^dstPos=24
if (count >= BASE) {
if ((srcPos & BASEMASK) == 0) {
// Aligned case. This is not even an optimization, because 'int' shifts are restricted to 0..31.
System.
arraycopy(src, srcPos
>> BASEP2, dst, dstPos
>> BASEP2, count
>> BASEP2
); } else {
// Unaligned case.
// src: ...ABCDE FGH.....
// ^srcPos
// dst: ........
for (int iSrc = srcPos >> BASEP2, iDst = dstPos >> BASEP2, iDstEd = iDst + (count >> BASEP2); iDst < iDstEd; iDst++, iSrc++)
dst[iDst] =
(src[iSrc] >>> (srcPos & BASEMASK)) // ABCDE
| (src[iSrc + 1] << (BASE - (srcPos & BASEMASK))); // ABCDE | 00000FGH = ABCDEFGH
}
srcPos += count - (count & BASEMASK);
dstPos += count - (count & BASEMASK);
count &= BASEMASK;
}
// Append the tail.
if (count > 0)
writeBits1(readBitsSmall(src, srcPos, count), dst, dstPos, count);
}
// Same as copyBits() but with custom operation instead of copying.
// Argument order changed both to emphasize modifying 'dst' and
// to match the order in op.apply: (dst, src) that matters for noncommutative operations.
// Speaking of it, the only reason why copyBits() has (src, srcPos, dst,
// dstPos, count) order is consistency with System.arraycopy.
static void applyOp
(int[] dst,
int dstPos,
int[] src,
int srcPos,
int count,
Operation op
) { if ((dstPos & BASEMASK) != 0) {
int n = min(count, BASE - (dstPos & BASEMASK));
if (n > 0) writeBits1(op.apply(readBitsSmall(dst, dstPos, n), readBitsSmall(src, srcPos, n)), dst, dstPos, n);
srcPos += n; dstPos += n; count -= n;
}
if (count >= BASE) {
for (int iSrc = srcPos >> BASEP2, iDst = dstPos >> BASEP2, iDstEd = iDst + (count >> BASEP2); iDst < iDstEd; iDst++, iSrc++)
dst[iDst] = op.apply(dst[iDst], (srcPos & BASEMASK) == 0 ? src[iSrc] : (src[iSrc] >>> (srcPos & BASEMASK)) | (src[iSrc + 1] << (BASE - (srcPos & BASEMASK))));
srcPos += count - (count & BASEMASK); dstPos += count - (count & BASEMASK); count &= BASEMASK;
}
if (count > 0)
writeBits1(op.apply(readBitsSmall(dst, dstPos, count), readBitsSmall(src, srcPos, count)), dst, dstPos, count);
}
@Override
return toString("##", "..");
}
var sb = new StringBuilder();
for (int ofs = 0, y = 0; y < h; y++) {
if (y > 0) sb.append("\n");
for (int x = 0; x < w; x++, ofs++)
sb.append(rawget(ofs) ? one : zero);
}
return sb.toString();
}
static Bitfield2D parse
(String src
) { return parse
(src,
"##"); }
if (one.
isEmpty()) throw Util.
BadArgf("'one' can't be empty."); if (one.
indexOf('\n') >= 0) throw Util.
BadArgf("Bad 'one': \"%s\". Shouldn't contain EOL.", one
); // Prepass for size.
int w = 0, h = 0;
for (int pos = 0; pos < src.length(); ) {
int eolp = src.indexOf("\n", pos);
if (eolp < 0) eolp = src.length();
if ((eolp - pos) % one.length() != 0)
throw Util.
BadArgf("Line %d:\n%s\ncontains %d character%s, which is not multiple of len(%s) = %d.",
h, src.substring(pos, eolp), eolp - pos, eolp - pos != 1 ? "s" : "", one, one.length());
w = max(w, (eolp - pos) / one.length());
h++;
pos = eolp + "\n".length();
}
var r = new Bitfield2D(w, h);
for (int y = 0, x = 0, ofs = 0, pos = 0; pos < src.length(); )
if (src.charAt(pos) == '\n') {
ofs += r.w - x;
x = 0; y++;
pos += "\n".length();
} else {
if (src.startsWith(one, pos)) r.rawset(ofs);
pos += one.length();
x++; ofs++;
}
return r;
}
public boolean[][] toBool2D() { return toBool2D(0, 0, w, h); }
public boolean[][] toBool2D(int atx, int aty, int w, int h) {
validateRect(this.w, this.h, atx, aty, w, h);
boolean[][] r = Bool2D.create(w, h);
for (int y = 0, ofs = aty * this.w + atx; y < h; y++, ofs += this.w - w)
for (int x = 0; x < w; x++, ofs++)
r[y][x] = rawget(ofs);
return r;
}
}
class Bool2D {
public static boolean[][] create(int w, int h) {
Bitfield2D.validateSize(w, h);
if (h
== 0) throw Util.
BadArgf("boolean[][] can't have zero height."); return new boolean[h][w];
}
public static void or(boolean[][] it, boolean[][] b, int itX, int itY) {
or(it, b, 0, 0, b[0].length, b.length, itX, itY);
}
public static void or(boolean[][] it, boolean[][] b, int bx, int by, int w, int h, int itX, int itY) {
var ward = new Bitfield2D.CoordWarden(it[0].length, it.length, b[0].length, b.length, bx, by, w, h, itX, itY);
for (int dy = 0, iItY = ward.destY, iBy = ward.srcY; dy < ward.h; dy++, iItY++, iBy++)
for (int dx = 0, iItX = ward.destX, iBx = ward.srcX; dx < ward.w; dx++, iItX++, iBx++)
it[iItY][iItX] |= b[iBy][iBx];
}
public static void andNot(boolean[][] it, boolean[][] b, int itX, int itY) {
andNot(it, b, 0, 0, b[0].length, b.length, itX, itY);
}
public static void andNot(boolean[][] it, boolean[][] b, int bx, int by, int w, int h, int itX, int itY) {
var ward = new Bitfield2D.CoordWarden(it[0].length, it.length, b[0].length, b.length, bx, by, w, h, itX, itY);
for (int dy = 0, iItY = ward.destY, iBy = ward.srcY; dy < ward.h; dy++, iItY++, iBy++)
for (int dx = 0, iItX = ward.destX, iBx = ward.srcX; dx < ward.w; dx++, iItX++, iBx++)
it[iItY][iItX] &= !b[iBy][iBx];
}
public static void copyFrom(boolean[][] it, boolean[][] b, int itX, int itY) {
copyFrom(it, b, 0, 0, b[0].length, b.length, itX, itY);
}
public static void copyFrom(boolean[][] it, boolean[][] b, int bx, int by, int w, int h, int itX, int itY) {
var ward = new Bitfield2D.CoordWarden(it[0].length, it.length, b[0].length, b.length, bx, by, w, h, itX, itY);
if (ward.w <= 0) return;
for (int dy = 0, iItY = ward.destY, iBy = ward.srcY; dy < ward.h; dy++, iItY++, iBy++)
System.
arraycopy(b
[iBy
], ward.
srcX, it
[iItY
], ward.
destX, ward.
w); }
public static String toString
(boolean[][] it
) { return toString(it, "##", "..");
}
return toBitfield2D(it).toString(one, zero);
}
public static boolean[][] parse
(String src
) { return parse
(src,
"##"); } return Bitfield2D.parse(src, one).toBool2D();
}
public static Bitfield2D toBitfield2D(boolean[][] it) { return toBitfield2D(it, 0, 0, it[0].length, it.length); }
public static Bitfield2D toBitfield2D(boolean[][] it, int atx, int aty, int w, int h) {
Bitfield2D.validateRect(it[0].length, it.length, atx, aty, w, h);
var r = new Bitfield2D(w, h);
for (int y = 0, rOfs = 0; y < h; y++)
for (int x = 0; x < w; x++, rOfs++)
if (it[y][x]) r.rawset(rOfs);
return r;
}
}
class Ideone
{
// Auto-growing bitfield for procedural generation when resulting size is impractical to predict.
static class ProcBitfield2D {
Bitfield2D b = new Bitfield2D(0, 0);
int ax, ay, bx, by, boundAx, boundAy, boundBx, boundBy;
void set(int x, int y) {
if (boundAx == boundBx) {
ax = x; ay = y; bx = x; by = y;
boundAx = x; boundAy = y; boundBx = x; boundBy = y;
}
if (x < ax || y < ay || x >= bx || y >= by)
grow(
x < ax ? growStgy(ax - x, bx - ax) : 0,
y < ay ? growStgy(ay - y, by - ay) : 0,
x >= bx ? growStgy(x - bx + 1, bx - ax) : 0,
y >= by ? growStgy(y - by + 1, by - ay) : 0);
if (x < boundAx) boundAx = x;
if (x >= boundBx) boundBx = x + 1;
if (y < boundAy) boundAy = y;
if (y >= boundBy) boundBy = y + 1;
b.set(x-ax, y-ay);
}
void unset(int x, int y) {
if (x >= boundAx && x < boundBx && x >= boundAy && x < boundBy)
b.unset(x-ax, y-ay);
}
void set(int x, int y, boolean value) { if (value) set(x, y); else unset(x, y); }
static int growStgy(int atLeast, int present) {
return max(max(8, present / 2), atLeast);
}
void grow(int Lx, int Ly, int Rx, int Ry) {
this.b = new Bitfield2D((bx - ax) + Lx + Rx, (by - ay) + Ly + Ry).copyFrom(
this.b, boundAx - ax, boundAy - ay, boundBx - boundAx, boundBy - boundAy, (boundAx - ax) + Lx, (boundAy - ay) + Ly);
ax -= Lx; ay -= Ly; bx += Rx; by += Ry;
}
Bitfield2D bake() {
return new Bitfield2D(boundBx - boundAx, boundBy - boundAy).copyFrom(
this.b, boundAx - ax, boundAy - ay, boundBx - boundAx, boundBy - boundAy, 0, 0);
}
}
static Bitfield2D makeSpruce(int size) {
Util.
rangecheck(size,
1,
20,
"size"); var r = new ProcBitfield2D();
int nSegs = 3+size/4, trunkWidth = 1+size/3*2;
int y = 0;
for (int iSeg = 0; iSeg < nSegs; iSeg++) {
int maxSegWidth = trunkWidth + 2 * (size + iSeg * size / 2);
int segWidth = iSeg == 0 ? 1 : trunkWidth + (size + iSeg)/3*2;
for (; segWidth < maxSegWidth; segWidth += 2) {
for (int x = -segWidth/2; x < (segWidth+1)/2; x++)
r.set(x, y);
y += 1;
}
}
for (int yTrunk = 0; yTrunk < size; yTrunk++) {
for (int x = -trunkWidth/2; x < (trunkWidth + 1)/2; x++)
r.set(x, y);
y++;
}
return r.bake();
}
static Bitfield2D moon = Bitfield2D.parse(
"....######..........\n" +
"..####..............\n" +
"####................\n" +
"####................\n" +
"####................\n" +
"####..............##\n" +
"######..........####\n" +
"..################..\n" +
"....############", "##");
static Bitfield2D paintBitfield2D() {
var r = new Bitfield2D(50, 40);
r.or(moon, 11, 4);
r.or(makeSpruce(4), -1, 15);
r.or(makeSpruce(3), 29, 17);
r.or(makeSpruce(2), 18, 18);
r.or(makeSpruce(5), 32, 16);
for (int iSnowflake = 0; iSnowflake < 25; iSnowflake++) {
int x = rand.nextInt(r.width()),
y = min(r.height() - 1, (int) (r.height() * pow(rand.nextFloat(), 2.0)));
r.set(x, y, !r.get(x, y));
}
return r;
}
static boolean[][] paintBool2D() {
var r = Bool2D.create(50, 40);
Bool2D.or(r, moon.toBool2D(), 11, 4);
Bool2D.or(r, makeSpruce(4).toBool2D(), -1, 15);
Bool2D.or(r, makeSpruce(3).toBool2D(), 29, 17);
Bool2D.or(r, makeSpruce(2).toBool2D(), 18, 18);
Bool2D.or(r, makeSpruce(5).toBool2D(), 32, 16);
for (int iSnowflake = 0; iSnowflake < 25; iSnowflake++) {
int x = rand.nextInt(r[0].length),
y = min(r.length - 1, (int) (r.length * pow(rand.nextFloat(), 2.0)));
r[y][x] = !r[y][x];
}
return r;
}
static Bitfield2D makeRandomBitfield
(int w,
int h,
Random rand
) { var r = new Bitfield2D(w, h);
for (int i = 0; i < w*h/5; i++) {
r.set(rand.nextInt(w), rand.nextInt(h));
}
return r;
}
static enum Op { OR, AND_NOT, COPY }
static class PlanItem {
Op op;
int rectIndex;
int rectX, rectY, w, h, destX, destY;
static PlanItem random
(Random rand, Bitfield2D canvas, Bitfield2D
[] rects
) { var r = new PlanItem();
r.op = Op.values()[rand.nextInt(Op.values().length)];
r.rectIndex = rand.nextInt(rects.length);
var rect = rects[r.rectIndex];
if (rand.nextInt(2) == 0) {
r.rectX = rand.nextInt(rect.width());
r.rectY = rand.nextInt(rect.height());
r.w = 1 + rand.nextInt(rect.width() - r.rectX);
r.h = 1 + rand.nextInt(rect.height() - r.rectY);
r.destX = -rect.width() + rand.nextInt(canvas.width() + rect.width() + 1);
r.destY = -rect.height() + rand.nextInt(canvas.height() + rect.height() + 1);
} else {
r.rectX = 0;
r.rectY = 0;
r.w = rect.width();
r.h = rect.height();
r.destX = rand.nextInt(canvas.width() - rect.width());
r.destY = rand.nextInt(canvas.height() - rect.height());
}
return r;
}
}
static void fusedBenchmarkTest() {
Bitfield2D bitCanvas = new Bitfield2D(1000, 1000);
boolean[][] boolCanvas = Bool2D.create(bitCanvas.width(), bitCanvas.height());
Bitfield2D bitCanvasBATCH = new Bitfield2D(bitCanvas.width(), bitCanvas.height());
Bitfield2D[] bitRects = new Bitfield2D[1000];
boolean[] [][] boolRects = new boolean[bitRects.length][][];
for (int i = 0; i < bitRects.length; i++) {
bitRects[i] = makeRandomBitfield(1 + rand.nextInt(bitCanvas.width()/10), 1 + rand.nextInt(bitCanvas.height()/10), rand);
boolRects[i] = bitRects[i].toBool2D();
}
PlanItem[] plan = new PlanItem[600000];
for (int i = 0; i < plan.length; i++)
plan[i] = PlanItem.random(rand, bitCanvas, bitRects);
final int TRIALS = 3;
for (int iTrial = 0; iTrial < TRIALS; iTrial++) {
System.
out.
printf("%s--- TRIAL %d/%d ---\n", iTrial
> 0 ? "\n" : "",
1 + iTrial, TRIALS
); double refTime = benchmark("boolean[][]", () -> {
for (int i = 0; i < plan.length; i++) {
switch (plan[i].op) {
case OR: Bool2D.or(boolCanvas, boolRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
case AND_NOT: Bool2D.andNot(boolCanvas, boolRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
case COPY: default: Bool2D.copyFrom(boolCanvas, boolRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
}
}
return boolCanvas;
});
benchmark("Bitfield2D (per-bit)", () -> {
for (int i = 0; i < plan.length; i++) {
switch (plan[i].op) {
case OR: bitCanvas.or(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
case AND_NOT: bitCanvas.andNot(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
case COPY: default: bitCanvas.copyFrom(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
}
}
return bitCanvas;
}, refTime);
benchmark("Bitfield2D (bitsizeof(int) batches)", () -> {
for (int i = 0; i < plan.length; i++) {
switch (plan[i].op) {
case OR: bitCanvasBATCH.orBATCH(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
case AND_NOT: bitCanvasBATCH.andNotBATCH(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
case COPY: default: bitCanvasBATCH.copyFrom(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
}
}
return bitCanvasBATCH;
}, refTime);
if (iTrial == 0) {
String ref
= Bool2D.
toString(boolCanvas
); if (!bitCanvas.toString().equals(ref))
if (!bitCanvasBATCH.toString().equals(ref))
throw new RuntimeException("Bitfield2D(bitsizeof(int) batches) and boolean[][] do differ."); }
}
}
@FunctionalInterface
interface BenchmarkPayload {
}
static double benchmark
(String title, BenchmarkPayload payload
) { return benchmark(title, payload, -1);
}
static double benchmark
(String title, BenchmarkPayload payload,
double refTime
) { long start
= System.
nanoTime(); Object keepAlive
= payload.
run(); double time
= (System.
nanoTime() - start
) * 1e
-9
; "%s: %.2f s%s",
title, time, refTime
>= 0 ? String.
format(" (%s)", judgement
(time, refTime
)) : "", keepAlive
)); return time;
}
static String judgement
(double time,
double refTime
) { final double absThreshold = 0.3, relThreshold = .1;
return time <= absThreshold || refTime <= absThreshold ?
String.
format("can't judge, min. required time is %.1f s", absThreshold
) : Math.
max(time, refTime
) > (1 + relThreshold
) * Math.
min(time, refTime
) ? Math.
abs((time
< refTime
? refTime
/ time
: time
/ refTime
) - 1) * 100,
time < refTime ? "faster" : "slower") :
String.
format("no observable difference, min. %.0f%% required", relThreshold
* 100); }
{
try {
var image = paintBitfield2D();
var im2 = paintBool2D();
if (!image.toString().equals(Bool2D.toString(im2))) {
}
fusedBenchmarkTest();
System.
out.
printf("\n%s\n\n", image
); }
}
}
aW1wb3J0IGphdmEudXRpbC4qOwppbXBvcnQgamF2YS51dGlsLnN0cmVhbS4qOwppbXBvcnQgamF2YS5sYW5nLio7CmltcG9ydCBqYXZhLmlvLio7CmltcG9ydCBzdGF0aWMgamF2YS5sYW5nLk1hdGguKjsKCmNsYXNzIFV0aWwgewoJcHVibGljIHN0YXRpYyBJbGxlZ2FsQXJndW1lbnRFeGNlcHRpb24gQmFkQXJnZihTdHJpbmcgZm10LCBPYmplY3QuLi4gYXJncykgewoJCXJldHVybiBuZXcgSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uKFN0cmluZy5mb3JtYXQoZm10LCBhcmdzKSk7Cgl9OwoKCXB1YmxpYyBzdGF0aWMgU3RyaW5nIHRyYWNlKEV4Y2VwdGlvbiBlKSB7CgkJU3RyaW5nV3JpdGVyIHN3ID0gbmV3IFN0cmluZ1dyaXRlcigpOwoJCWUucHJpbnRTdGFja1RyYWNlKG5ldyBQcmludFdyaXRlcihzdykpOwoJCXJldHVybiBzdy50b1N0cmluZygpOwoJfQoKCXB1YmxpYyBzdGF0aWMgaW50IHJhbmdlY2hlY2soaW50IHgsIGludCBtaW4sIGludCBtYXgsIFN0cmluZyB3aGF0KSB7CgkJaWYgKHggPCBtaW4gfHwgeCA+IG1heCkgdGhyb3cgQmFkQXJnZigiQmFkICVzOiAlZCwgc2hvdWxkIGJlIFslZDsgJWRdLiIsIHdoYXQsIHgsIG1pbiwgbWF4KTsKCQlyZXR1cm4geDsKCX0KfQoKY2xhc3MgQml0ZmllbGQyRCB7CglpbnRbXSBkYXRhOwoJaW50IHcsIGg7CglzdGF0aWMgZmluYWwgaW50IFNBTkVfQklUU19MSU1JVCA9IDFfMDAwXzAwMF8wMDA7CglzdGF0aWMgZmluYWwgaW50IFNBTkVfRElNRU5TSU9OX0xJTUlUID0gMTAwXzAwMF8wMDA7CglzdGF0aWMgZmluYWwgaW50IEJBU0UgPSBJbnRlZ2VyLlNJWkU7IAoJLy8gQkFTRSBleHBlY3RlZCB0byBiZSBwb3dlciBvZiAyLCB0aGVuCgkvLyAoYml0bm8gPj4gQkFTRVAyKSBpcyBmYXN0ZXIgdmVyc2lvbiBvZiAoYml0bm8gLyBCQVNFKSwgYW5kCgkvLyAoYml0bm8gJiBCQVNFTUFTSykgaXMgZmFzdGVyIHZlcnNpb24gb2YgKGJpdG5vICUgQkFTRSkuCgkvLwoJLy8gQ29tcGlsZXIgY2Fubm90IG9wdGltaXplIHRoZXNlIG9wZXJhdGlvbnMgYXMgZWZmZWN0aXZlbHkgYXMgcHJvZ3JhbW1lciBjYW4sCgkvLyBiZWNhdXNlIHRoZXkgd29yayBkaWZmZXJlbnRseSBmb3IgbmVnYXRpdmUgJ2JpdG5vJyB2YWx1ZXMsIHNvIGl0IHNob3VsZCBjaGVjayBmb3IgbmVnYXRpdmVzLgoJLy8gQW5kIGl0J3Mgb25seSB3ZSB3aG8ga25vdyB0aGF0ICdiaXRubycgaXMgbmV2ZXIgbmVnYXRpdmUuCgkvLwoJLy8gTm90IHN1cmUgaWYgdGhlcmUgaXMgbm8gZGFuZ2VyIG9mIEJBU0VQMiBmb3JtdWxhIE5PVCByZXN1bHRpbmcgaW4gY29tcGlsZS10aW1lIGNvbnN0YW50LAoJLy8gYnV0IGl0IGFwcGVhcnMgdG8gYmUgT0suCglzdGF0aWMgZmluYWwgaW50IEJBU0VQMiA9IDMyIC0gKEludGVnZXIubnVtYmVyT2ZMZWFkaW5nWmVyb3MoQkFTRSkgKyAxKTsKCXN0YXRpYyBmaW5hbCBpbnQgQkFTRU1BU0sgPSBCQVNFIC0gMTsKCglwdWJsaWMgQml0ZmllbGQyRChpbnQgdywgaW50IGgpIHsKCQl2YWxpZGF0ZVNpemUodywgaCk7CgkJdGhpcy5kYXRhID0gbmV3IGludFsodypoICsgKEJBU0UgLSAxKSkgPj4gQkFTRVAyXTsKCQl0aGlzLncgPSB3OwoJCXRoaXMuaCA9IGg7Cgl9CgoJc3RhdGljIHZvaWQgdmFsaWRhdGVTaXplKGludCB3LCBpbnQgaCkgewoJCWlmICh3IDwgMCB8fCBoIDwgMCB8fCB3ID4gU0FORV9ESU1FTlNJT05fTElNSVQgfHwgaCA+IFNBTkVfRElNRU5TSU9OX0xJTUlUCgkJCXx8IHcgPiAwICYmIGggPiBTQU5FX0JJVFNfTElNSVQgLyB3KQoJCQl0aHJvdyBVdGlsLkJhZEFyZ2YoIkJhZCBiaXRmaWVsZCBzaXplOiAlZMOXJWQuIiwgdywgaCk7Cgl9CgoJcHVibGljIHN0YXRpYyBib29sZWFuIHZhbGlkQ29vcmQoaW50IHcsIGludCBoLCBpbnQgeCwgaW50IHkpIHsKCQlyZXR1cm4geCA+PSAwICYmIHkgPj0gMCAmJiB4IDwgdyAmJiB5IDwgaDsKCX0KCglwdWJsaWMgc3RhdGljIHZvaWQgdmFsaWRhdGVDb29yZChpbnQgdywgaW50IGgsIGludCB4LCBpbnQgeSkgewoJCWlmICghdmFsaWRDb29yZCh3LCBoLCB4LCB5KSkKCQkJdGhyb3cgVXRpbC5CYWRBcmdmKCJCYWQgY29vcmQgb2YgYml0ZmllbGQgJWTDlyVkOiAoJWQsICVkKS4iLCB3LCBoLCB4LCB5KTsKCX0KCglzdGF0aWMgdm9pZCB2YWxpZGF0ZVJlY3QoaW50IGZ1bGx3LCBpbnQgZnVsbGgsIGludCB4LCBpbnQgeSwgaW50IHcsIGludCBoKSB7CgkJaWYgKHggPCAwIHx8IHkgPCAwIHx8IHcgPCAwIHx8IGggPCAwIHx8IHggKyB3ID4gZnVsbHcgfHwgeSArIGggPiBmdWxsaCkKCQkJdGhyb3cgVXRpbC5CYWRBcmdmKCJCYWQgcmVjdCBvZiBiaXRmaWVsZCAlZMOXJWQ6ICglZCwgJWQpIH4gKCVkLCAlZCkuIiwKCQkJCWZ1bGx3LCBmdWxsaCwgeCwgeSwgeCt3LCB5K2gpOwoJfQoKCWJvb2xlYW4gcmF3Z2V0KGludCBpbmRleCkgeyByZXR1cm4gKGRhdGFbaW5kZXggPj4gQkFTRVAyXSAmICgxIDw8IChpbmRleCAmIEJBU0VNQVNLKSkpICE9IDA7IH0KCXZvaWQgcmF3c2V0KGludCBpbmRleCkgeyBkYXRhW2luZGV4ID4+IEJBU0VQMl0gfD0gMSA8PCAoaW5kZXggJiBCQVNFTUFTSyk7IH0KCXZvaWQgcmF3dW5zZXQoaW50IGluZGV4KSB7IGRhdGFbaW5kZXggPj4gQkFTRVAyXSAmPSB+KDEgPDwgKGluZGV4ICYgQkFTRU1BU0spKTsgfQoJdm9pZCByYXdzZXQoaW50IGluZGV4LCBib29sZWFuIHZhbHVlKSB7IGlmICh2YWx1ZSkgcmF3c2V0KGluZGV4KTsgZWxzZSByYXd1bnNldChpbmRleCk7IH0KCglwdWJsaWMgYm9vbGVhbiBnZXQoaW50IHgsIGludCB5KSB7IHZhbGlkYXRlQ29vcmQodywgaCwgeCwgeSk7IHJldHVybiByYXdnZXQoeSp3K3gpOyB9CglwdWJsaWMgdm9pZCBzZXQoaW50IHgsIGludCB5KSB7IHZhbGlkYXRlQ29vcmQodywgaCwgeCwgeSk7IHJhd3NldCh5KncreCk7IH0KCXB1YmxpYyB2b2lkIHVuc2V0KGludCB4LCBpbnQgeSkgeyB2YWxpZGF0ZUNvb3JkKHcsIGgsIHgsIHkpOyByYXd1bnNldCh5KncreCk7IH0KCXB1YmxpYyB2b2lkIHNldChpbnQgeCwgaW50IHksIGJvb2xlYW4gdmFsdWUpIHsgdmFsaWRhdGVDb29yZCh3LCBoLCB4LCB5KTsgcmF3c2V0KHkqdyt4LCB2YWx1ZSk7IH0KCXB1YmxpYyBpbnQgd2lkdGgoKSB7IHJldHVybiB3OyB9CglwdWJsaWMgaW50IGhlaWdodCgpIHsgcmV0dXJuIGg7IH0KCgkvLyBGb3IgZnVuY3Rpb25zIGxpa2Ugb3IoKSB0aGF0IGFwcGx5IG9uZSBiaXRmaWVsZCB0byBhbm90aGVyLAoJLy8gcmVkdWNlcyBwb3RlbnRpYWxseSBvdXQtb2YtYm9yZGVycyBpbnB1dCB0byBpdHMgdmFsaWQgcGFydC4KCXN0YXRpYyBjbGFzcyBDb29yZFdhcmRlbiB7CgkJaW50IHNyY1gsIHNyY1ksIHcsIGgsIGRlc3RYLCBkZXN0WTsKCgkJQ29vcmRXYXJkZW4oaW50IGRlc3RGdWxsVywgaW50IGRlc3RGdWxsSCwgaW50IHNyY0Z1bGxXLCBpbnQgc3JjRnVsbEgsCgkJCWludCBhU3JjWCwgaW50IGFTcmNZLCBpbnQgYVcsIGludCBhSCwgaW50IGFEZXN0WCwgaW50IGFEZXN0WSkgewoKCQkJdmFsaWRhdGVSZWN0KHNyY0Z1bGxXLCBzcmNGdWxsSCwgYVNyY1gsIGFTcmNZLCBhVywgYUgpOwoJCQlzcmNYID0gYVNyY1g7IHNyY1kgPSBhU3JjWTsgdyA9IGFXOyBoID0gYUg7IGRlc3RYID0gYURlc3RYOyBkZXN0WSA9IGFEZXN0WTsKCQkJaWYgKGRlc3RYIDwgMCkgeyBzcmNYIC09IGRlc3RYOyB3ICs9IGRlc3RYOyBkZXN0WCA9IDA7IH0KCQkJaWYgKGRlc3RYICsgdyA+IGRlc3RGdWxsVykgeyB3ID0gZGVzdEZ1bGxXIC0gZGVzdFg7IH0KCQkJaWYgKGRlc3RZIDwgMCkgeyBzcmNZIC09IGRlc3RZOyBoICs9IGRlc3RZOyBkZXN0WSA9IDA7IH0KCQkJaWYgKGRlc3RZICsgaCA+IGRlc3RGdWxsSCkgeyBoID0gZGVzdEZ1bGxIIC0gZGVzdFk7IH0KCQl9Cgl9CgoJcHVibGljIHZvaWQgb3IoQml0ZmllbGQyRCBiLCBpbnQgdGhpc1gsIGludCB0aGlzWSkgewoJCW9yKGIsIDAsIDAsIGIudywgYi5oLCB0aGlzWCwgdGhpc1kpOwoJfQoKCXB1YmxpYyB2b2lkIG9yKEJpdGZpZWxkMkQgYiwgaW50IGJ4LCBpbnQgYnksIGludCB3LCBpbnQgaCwgaW50IHRoaXNYLCBpbnQgdGhpc1kpIHsKCQl2YXIgd2FyZCA9IG5ldyBDb29yZFdhcmRlbih0aGlzLncsIHRoaXMuaCwgYi53LCBiLmgsIGJ4LCBieSwgdywgaCwgdGhpc1gsIHRoaXNZKTsKCQlpbnQgdGhpc09mcyA9IHdhcmQuZGVzdFkqdGhpcy53ICsgd2FyZC5kZXN0WCwgYk9mcyA9IHdhcmQuc3JjWSpiLncgKyB3YXJkLnNyY1g7CgkJZm9yIChpbnQgZHkgPSAwOyBkeSA8IHdhcmQuaDsgZHkrKywgdGhpc09mcyArPSB0aGlzLncgLSB3YXJkLncsIGJPZnMgKz0gYi53IC0gd2FyZC53KQoJCQlmb3IgKGludCBkeCA9IDA7IGR4IDwgd2FyZC53OyBkeCsrLCB0aGlzT2ZzKyssIGJPZnMrKykKCQkJCWlmIChiLnJhd2dldChiT2ZzKSkgdGhpcy5yYXdzZXQodGhpc09mcyk7Cgl9CgoJcHVibGljIHZvaWQgb3JCQVRDSChCaXRmaWVsZDJEIGIsIGludCB0aGlzWCwgaW50IHRoaXNZKSB7CgkJb3JCQVRDSChiLCAwLCAwLCBiLncsIGIuaCwgdGhpc1gsIHRoaXNZKTsKCX0KCglwdWJsaWMgdm9pZCBvckJBVENIKEJpdGZpZWxkMkQgYiwgaW50IGJ4LCBpbnQgYnksIGludCB3LCBpbnQgaCwgaW50IHRoaXNYLCBpbnQgdGhpc1kpIHsKCQlhcHBseShiLCBieCwgYnksIHcsIGgsIHRoaXNYLCB0aGlzWSwgKGl0ZW1BLCBpdGVtQikgLT4gaXRlbUEgfCBpdGVtQik7Cgl9CgoJcHVibGljIHZvaWQgYW5kTm90KEJpdGZpZWxkMkQgYiwgaW50IHRoaXNYLCBpbnQgdGhpc1kpIHsKCQlhbmROb3QoYiwgMCwgMCwgYi53LCBiLmgsIHRoaXNYLCB0aGlzWSk7Cgl9CgoJcHVibGljIHZvaWQgYW5kTm90KEJpdGZpZWxkMkQgYiwgaW50IGJ4LCBpbnQgYnksIGludCB3LCBpbnQgaCwgaW50IHRoaXNYLCBpbnQgdGhpc1kpIHsKCQl2YXIgd2FyZCA9IG5ldyBDb29yZFdhcmRlbih0aGlzLncsIHRoaXMuaCwgYi53LCBiLmgsIGJ4LCBieSwgdywgaCwgdGhpc1gsIHRoaXNZKTsKCQlpbnQgdGhpc09mcyA9IHdhcmQuZGVzdFkqdGhpcy53ICsgd2FyZC5kZXN0WCwgYk9mcyA9IHdhcmQuc3JjWSpiLncgKyB3YXJkLnNyY1g7CgkJZm9yIChpbnQgZHkgPSAwOyBkeSA8IHdhcmQuaDsgZHkrKywgdGhpc09mcyArPSB0aGlzLncgLSB3YXJkLncsIGJPZnMgKz0gYi53IC0gd2FyZC53KQoJCQlmb3IgKGludCBkeCA9IDA7IGR4IDwgd2FyZC53OyBkeCsrLCB0aGlzT2ZzKyssIGJPZnMrKykKCQkJCWlmIChiLnJhd2dldChiT2ZzKSkgdGhpcy5yYXd1bnNldCh0aGlzT2ZzKTsKCX0KCglwdWJsaWMgdm9pZCBhbmROb3RCQVRDSChCaXRmaWVsZDJEIGIsIGludCB0aGlzWCwgaW50IHRoaXNZKSB7CgkJYW5kTm90QkFUQ0goYiwgMCwgMCwgYi53LCBiLmgsIHRoaXNYLCB0aGlzWSk7Cgl9CgoJcHVibGljIHZvaWQgYW5kTm90QkFUQ0goQml0ZmllbGQyRCBiLCBpbnQgYngsIGludCBieSwgaW50IHcsIGludCBoLCBpbnQgdGhpc1gsIGludCB0aGlzWSkgewoJCWFwcGx5KGIsIGJ4LCBieSwgdywgaCwgdGhpc1gsIHRoaXNZLCAoaXRlbUEsIGl0ZW1CKSAtPiBpdGVtQSAmIH5pdGVtQik7Cgl9CgoJQEZ1bmN0aW9uYWxJbnRlcmZhY2UKCWludGVyZmFjZSBPcGVyYXRpb24gewoJCWFic3RyYWN0IGludCBhcHBseShpbnQgYSwgaW50IGIpOwoJfQoKCXZvaWQgYXBwbHkoQml0ZmllbGQyRCBiLCBpbnQgYngsIGludCBieSwgaW50IHcsIGludCBoLCBpbnQgdGhpc1gsIGludCB0aGlzWSwgT3BlcmF0aW9uIG9wKSB7CgkJdmFyIHdhcmQgPSBuZXcgQ29vcmRXYXJkZW4odGhpcy53LCB0aGlzLmgsIGIudywgYi5oLCBieCwgYnksIHcsIGgsIHRoaXNYLCB0aGlzWSk7CgkJaW50IHRoaXNPZnMgPSB3YXJkLmRlc3RZKnRoaXMudyArIHdhcmQuZGVzdFgsIGJPZnMgPSB3YXJkLnNyY1kqYi53ICsgd2FyZC5zcmNYOwoJCWZvciAoaW50IGR5ID0gMDsgZHkgPCB3YXJkLmg7IGR5KyssIHRoaXNPZnMgKz0gdGhpcy53LCBiT2ZzICs9IGIudykKCQkJYXBwbHlPcCh0aGlzLmRhdGEsIHRoaXNPZnMsIGIuZGF0YSwgYk9mcywgd2FyZC53LCBvcCk7Cgl9CgoJcHVibGljIEJpdGZpZWxkMkQgY29weUZyb20oQml0ZmllbGQyRCBiLCBpbnQgdGhpc1gsIGludCB0aGlzWSkgewoJCXJldHVybiBjb3B5RnJvbShiLCAwLCAwLCBiLncsIGIuaCwgdGhpc1gsIHRoaXNZKTsKCX0KCglwdWJsaWMgQml0ZmllbGQyRCBjb3B5RnJvbShCaXRmaWVsZDJEIGIsIGludCBieCwgaW50IGJ5LCBpbnQgdywgaW50IGgsIGludCB0aGlzWCwgaW50IHRoaXNZKSB7CgkJdmFyIHdhcmQgPSBuZXcgQ29vcmRXYXJkZW4odGhpcy53LCB0aGlzLmgsIGIudywgYi5oLCBieCwgYnksIHcsIGgsIHRoaXNYLCB0aGlzWSk7CgkJaW50IHRoaXNPZnMgPSB3YXJkLmRlc3RZKnRoaXMudyArIHdhcmQuZGVzdFgsIGJPZnMgPSB3YXJkLnNyY1kqYi53ICsgd2FyZC5zcmNYOwoJCWZvciAoaW50IGR5ID0gMDsgZHkgPCB3YXJkLmg7IGR5KyssIHRoaXNPZnMgKz0gdGhpcy53LCBiT2ZzICs9IGIudykKCQkJY29weUJpdHMoYi5kYXRhLCBiT2ZzLCB0aGlzLmRhdGEsIHRoaXNPZnMsIHdhcmQudyk7CgkJcmV0dXJuIHRoaXM7Cgl9CgoJc3RhdGljIGludCByZWFkQml0c1NtYWxsKGludFtdIHNyYywgaW50IHNyY1BvcywgaW50IGNvdW50KSB7CgkJYXNzZXJ0IGNvdW50ID4gMCAmJiBjb3VudCA8IEJBU0U7CgkJLy8gICAgICAgMCAgICAgICAxICAgICAgIDIgICAgICAgMyAgICAgICA0ICAgICAgIDUgICAgICAgNgoJCS8vIHNyYyA9IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKCQkvLyAgICAgICAgICAgIF5zcmNQb3M9NQoJCS8vICAgICAgICAgICAgICAgICAgXnNyY1Bvcytjb3VudD0xMQoJCWludCBiaXRzID0gc3JjW3NyY1BvcyA+PiBCQVNFUDJdID4+PiAoc3JjUG9zICYgQkFTRU1BU0spOyAvLyBGR0gKCQlpZiAoY291bnQgPiBCQVNFIC0gKHNyY1BvcyAmIEJBU0VNQVNLKSkKCQkJYml0cyB8PSBzcmNbKHNyY1BvcyA+PiBCQVNFUDIpICsgMV0gPDwgKEJBU0UgLSAoc3JjUG9zICYgQkFTRU1BU0spKTsgLy8gRkdIIHwgMDAwSUpLTE1OT1AgPSBGR0hJSktMTU5PUAoJCXJldHVybiBiaXRzICYgKCgxIDw8IGNvdW50KSAtIDEpOyAvLyBGR0hJSksKCX0KCglzdGF0aWMgdm9pZCB3cml0ZUJpdHMxKGludCBiaXRzLCBpbnRbXSBkc3QsIGludCBkc3RQb3MsIGludCBjb3VudCkgewoJCWFzc2VydCBjb3VudCA+IDAgJiYgY291bnQgPCBCQVNFIC0gKGRzdFBvcyAmIEJBU0VNQVNLKSAmJiBiaXRzIDwgMSA8PCBjb3VudDsKCQkvLyBiaXRzID0gcXdlcgoJCS8vICAgICAgIDAgICAgICAgMSAgICAgICAyICAgICAgIDMgICAgICAgNCAgICAgICA1ICAgICAgIDYKCQkvLyBzcmMgPSBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CgkJLy8gICAgICAgICAgXmRzdFBvcz0zCgkJLy8gICAgICAgICAgICAgIF5kc3RQb3MrY291bnQ9NwoJCWRzdFtkc3RQb3MgPj4gQkFTRVAyXSA9IGRzdFtkc3RQb3MgPj4gQkFTRVAyXSAvLyBBQkNERUZHSAoJCQkmIH4oKCgxIDw8IGNvdW50KSAtIDEpIDw8IChkc3RQb3MgJiBCQVNFTUFTSykpIC8vIEFCQzAwMDBICgkJCXwgKGJpdHMgPDwgKGRzdFBvcyAmIEJBU0VNQVNLKSk7IC8vIEFCQ3F3ZXJICgl9CgoJc3RhdGljIHZvaWQgY29weUJpdHMoaW50W10gc3JjLCBpbnQgc3JjUG9zLCBpbnRbXSBkc3QsIGludCBkc3RQb3MsIGludCBjb3VudCkgewoJCS8vIEltYWdpbmUgQkFTRT04IChhcyBpZiBieXRlW10gaW5zdGVhZCBvZiBpbnRbXSksIHNyY1BvcyA9IDMsIGRzdFBvcyA9IDIsIGNvdW50PTI1LgoJCS8vCgkJLy8gICAgICAgMCAgICAgICAxICAgICAgIDIgICAgICAgMyAgICAgICA0ICAgICAgIDUgICAgICAgNgoJCS8vIHNyYyA9IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKCQkvLyAgICAgICAgICBec3JjUG9zPTMgICAgICAgICAgICAgICAgXnNyY1Bvcytjb3VudD0yOAoJCS8vIGRzdCA9IC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uCgkJLy8gICAgICAgMCBeZHN0UG9zPTIgICAgIDIgICAgICAgMyAgICAgICA0ICAgICAgIDUKCQkvLwoJCS8vIEZpcnN0IHdlIGFsaWduIGRzdFBvcyB0byB0aGUgbXVsdGlwbGUgb2YgQkFTRSBieSByZWFkaW5nCgkJLy8gZmlyc3QgQkFTRSAtIGRzdFBvcyVCQVNFIGJpdHMgd2l0aCByZWFkQml0c1NtYWxsKCkgYW5kIHdyaXRpbmcgdGhlbQoJCS8vIGludG8gdGhlIGZpcnN0IGNlbGwgb2YgZHN0W10gd2l0aCB3cml0ZUJpdHMxLgoJCS8vCgkJLy8gV2UnbGwgZ2V0OgoJCS8vICAgICAgIDAgICAgICAgMSAgICAgICAyICAgICAgIDMgICAgICAgNCAgICAgICA1ICAgICAgIDYKCQkvLyBzcmMgPSBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CgkJLy8gICAgICAgICAgICAgICAgXnNyY1Bvcz05ICAgICAgICAgIF5zcmNQb3MrY291bnQ9MjgKCQkvLyBkc3QgPSAuLkRFRkdISS4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLgoJCS8vICAgICAgIDAgICAgICAgMSAgICAgICAyICAgICAgIDMgICAgICAgNCAgICAgICA1CgkJLy8gICAgICAgICAgICAgICBeZHN0UG9zPTgKCQlpZiAoKGRzdFBvcyAmIEJBU0VNQVNLKSAhPSAwKSB7CgkJCWludCBuID0gbWluKGNvdW50LCBCQVNFIC0gKGRzdFBvcyAmIEJBU0VNQVNLKSk7CgkJCWlmIChuID4gMCkgd3JpdGVCaXRzMShyZWFkQml0c1NtYWxsKHNyYywgc3JjUG9zLCBuKSwgZHN0LCBkc3RQb3MsIG4pOwoJCQlzcmNQb3MgKz0gbjsKCQkJZHN0UG9zICs9IG47CgkJCWNvdW50IC09IG47CgkJfQoKCQkvLyBOb3cgd2UgZmlsbCB3aG9sZSBjZWxscyB3aGlsZSBwb3NzaWJsZS4KCQkvLyBXZSdsbCBnZXQ6CgkJLy8gc3JjID0gQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egoJCS8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBec3JjUG9zPTI1CgkJLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF5zcmNQb3MrY291bnQ9MjgKCQkvLyBkc3QgPSAuLkRFRkdISUpLTE1OT1BRUlNUVVZXWFkuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLgoJCS8vICAgICAgIDAgICAgICAgMSAgICAgICAyICAgICAgIDMgICAgICAgNCAgICAgICA1CgkJLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXmRzdFBvcz0yNAoJCWlmIChjb3VudCA+PSBCQVNFKSB7CgkJCWlmICgoc3JjUG9zICYgQkFTRU1BU0spID09IDApIHsKCQkJCS8vIEFsaWduZWQgY2FzZS4gVGhpcyBpcyBub3QgZXZlbiBhbiBvcHRpbWl6YXRpb24sIGJlY2F1c2UgJ2ludCcgc2hpZnRzIGFyZSByZXN0cmljdGVkIHRvIDAuLjMxLgoJCQkJU3lzdGVtLmFycmF5Y29weShzcmMsIHNyY1BvcyA+PiBCQVNFUDIsIGRzdCwgZHN0UG9zID4+IEJBU0VQMiwgY291bnQgPj4gQkFTRVAyKTsKCQkJfSBlbHNlIHsKCQkJCS8vIFVuYWxpZ25lZCBjYXNlLgoJCQkJLy8gc3JjOiAuLi5BQkNERSBGR0guLi4uLgoJCQkJLy8gICAgICAgICBec3JjUG9zCgkJCQkvLyBkc3Q6IC4uLi4uLi4uCgkJCQlmb3IgKGludCBpU3JjID0gc3JjUG9zID4+IEJBU0VQMiwgaURzdCA9IGRzdFBvcyA+PiBCQVNFUDIsIGlEc3RFZCA9IGlEc3QgKyAoY291bnQgPj4gQkFTRVAyKTsgaURzdCA8IGlEc3RFZDsgaURzdCsrLCBpU3JjKyspCgkJCQkJZHN0W2lEc3RdID0KCQkJCQkJKHNyY1tpU3JjXSA+Pj4gKHNyY1BvcyAmIEJBU0VNQVNLKSkgLy8gQUJDREUKCQkJCQkJfCAoc3JjW2lTcmMgKyAxXSA8PCAoQkFTRSAtIChzcmNQb3MgJiBCQVNFTUFTSykpKTsgLy8gQUJDREUgfCAwMDAwMEZHSCA9IEFCQ0RFRkdICgkJCX0KCQkJc3JjUG9zICs9IGNvdW50IC0gKGNvdW50ICYgQkFTRU1BU0spOwoJCQlkc3RQb3MgKz0gY291bnQgLSAoY291bnQgJiBCQVNFTUFTSyk7CgkJCWNvdW50ICY9IEJBU0VNQVNLOwoJCX0KCgkJLy8gQXBwZW5kIHRoZSB0YWlsLgoJCWlmIChjb3VudCA+IDApCgkJCXdyaXRlQml0czEocmVhZEJpdHNTbWFsbChzcmMsIHNyY1BvcywgY291bnQpLCBkc3QsIGRzdFBvcywgY291bnQpOwoJfQoKCS8vIFNhbWUgYXMgY29weUJpdHMoKSBidXQgd2l0aCBjdXN0b20gb3BlcmF0aW9uIGluc3RlYWQgb2YgY29weWluZy4KCS8vIEFyZ3VtZW50IG9yZGVyIGNoYW5nZWQgYm90aCB0byBlbXBoYXNpemUgbW9kaWZ5aW5nICdkc3QnIGFuZAoJLy8gdG8gbWF0Y2ggdGhlIG9yZGVyIGluIG9wLmFwcGx5OiAoZHN0LCBzcmMpIHRoYXQgbWF0dGVycyBmb3Igbm9uY29tbXV0YXRpdmUgb3BlcmF0aW9ucy4KCS8vIFNwZWFraW5nIG9mIGl0LCB0aGUgb25seSByZWFzb24gd2h5IGNvcHlCaXRzKCkgaGFzIChzcmMsIHNyY1BvcywgZHN0LAoJLy8gZHN0UG9zLCBjb3VudCkgb3JkZXIgaXMgY29uc2lzdGVuY3kgd2l0aCBTeXN0ZW0uYXJyYXljb3B5LgoJc3RhdGljIHZvaWQgYXBwbHlPcChpbnRbXSBkc3QsIGludCBkc3RQb3MsIGludFtdIHNyYywgaW50IHNyY1BvcywgaW50IGNvdW50LCBPcGVyYXRpb24gb3ApIHsKCQlpZiAoKGRzdFBvcyAmIEJBU0VNQVNLKSAhPSAwKSB7CgkJCWludCBuID0gbWluKGNvdW50LCBCQVNFIC0gKGRzdFBvcyAmIEJBU0VNQVNLKSk7CgkJCWlmIChuID4gMCkgd3JpdGVCaXRzMShvcC5hcHBseShyZWFkQml0c1NtYWxsKGRzdCwgZHN0UG9zLCBuKSwgcmVhZEJpdHNTbWFsbChzcmMsIHNyY1BvcywgbikpLCBkc3QsIGRzdFBvcywgbik7CgkJCXNyY1BvcyArPSBuOyBkc3RQb3MgKz0gbjsgY291bnQgLT0gbjsKCQl9CgkJaWYgKGNvdW50ID49IEJBU0UpIHsKCQkJZm9yIChpbnQgaVNyYyA9IHNyY1BvcyA+PiBCQVNFUDIsIGlEc3QgPSBkc3RQb3MgPj4gQkFTRVAyLCBpRHN0RWQgPSBpRHN0ICsgKGNvdW50ID4+IEJBU0VQMik7IGlEc3QgPCBpRHN0RWQ7IGlEc3QrKywgaVNyYysrKQoJCQkJZHN0W2lEc3RdID0gb3AuYXBwbHkoZHN0W2lEc3RdLCAoc3JjUG9zICYgQkFTRU1BU0spID09IDAgPyBzcmNbaVNyY10gOiAoc3JjW2lTcmNdID4+PiAoc3JjUG9zICYgQkFTRU1BU0spKSB8IChzcmNbaVNyYyArIDFdIDw8IChCQVNFIC0gKHNyY1BvcyAmIEJBU0VNQVNLKSkpKTsKCQkJc3JjUG9zICs9IGNvdW50IC0gKGNvdW50ICYgQkFTRU1BU0spOyBkc3RQb3MgKz0gY291bnQgLSAoY291bnQgJiBCQVNFTUFTSyk7IGNvdW50ICY9IEJBU0VNQVNLOwoJCX0KCQlpZiAoY291bnQgPiAwKQoJCQl3cml0ZUJpdHMxKG9wLmFwcGx5KHJlYWRCaXRzU21hbGwoZHN0LCBkc3RQb3MsIGNvdW50KSwgcmVhZEJpdHNTbWFsbChzcmMsIHNyY1BvcywgY291bnQpKSwgZHN0LCBkc3RQb3MsIGNvdW50KTsKCX0KCglAT3ZlcnJpZGUKCXB1YmxpYyBTdHJpbmcgdG9TdHJpbmcoKSB7CgkJcmV0dXJuIHRvU3RyaW5nKCIjIyIsICIuLiIpOwoJfQoKCXB1YmxpYyBTdHJpbmcgdG9TdHJpbmcoU3RyaW5nIG9uZSwgU3RyaW5nIHplcm8pIHsKCQl2YXIgc2IgPSBuZXcgU3RyaW5nQnVpbGRlcigpOwoJCWZvciAoaW50IG9mcyA9IDAsIHkgPSAwOyB5IDwgaDsgeSsrKSB7CgkJCWlmICh5ID4gMCkgc2IuYXBwZW5kKCJcbiIpOwoJCQlmb3IgKGludCB4ID0gMDsgeCA8IHc7IHgrKywgb2ZzKyspCgkJCQlzYi5hcHBlbmQocmF3Z2V0KG9mcykgPyBvbmUgOiB6ZXJvKTsKCQl9CgkJcmV0dXJuIHNiLnRvU3RyaW5nKCk7Cgl9CgoJc3RhdGljIEJpdGZpZWxkMkQgcGFyc2UoU3RyaW5nIHNyYykgeyByZXR1cm4gcGFyc2Uoc3JjLCAiIyMiKTsgfQoKCXN0YXRpYyBCaXRmaWVsZDJEIHBhcnNlKFN0cmluZyBzcmMsIFN0cmluZyBvbmUpIHsKCQlpZiAob25lLmlzRW1wdHkoKSkgdGhyb3cgVXRpbC5CYWRBcmdmKCInb25lJyBjYW4ndCBiZSBlbXB0eS4iKTsKCQlpZiAob25lLmluZGV4T2YoJ1xuJykgPj0gMCkgdGhyb3cgVXRpbC5CYWRBcmdmKCJCYWQgJ29uZSc6IFwiJXNcIi4gU2hvdWxkbid0IGNvbnRhaW4gRU9MLiIsIG9uZSk7CgkJLy8gUHJlcGFzcyBmb3Igc2l6ZS4KCQlpbnQgdyA9IDAsIGggPSAwOwoJCWZvciAoaW50IHBvcyA9IDA7IHBvcyA8IHNyYy5sZW5ndGgoKTsgKSB7CgkJCWludCBlb2xwID0gc3JjLmluZGV4T2YoIlxuIiwgcG9zKTsKCQkJaWYgKGVvbHAgPCAwKSBlb2xwID0gc3JjLmxlbmd0aCgpOwoJCQlpZiAoKGVvbHAgLSBwb3MpICUgb25lLmxlbmd0aCgpICE9IDApCgkJCQl0aHJvdyBVdGlsLkJhZEFyZ2YoIkxpbmUgJWQ6XG4lc1xuY29udGFpbnMgJWQgY2hhcmFjdGVyJXMsIHdoaWNoIGlzIG5vdCBtdWx0aXBsZSBvZiBsZW4oJXMpID0gJWQuIiwKCQkJCQloLCBzcmMuc3Vic3RyaW5nKHBvcywgZW9scCksIGVvbHAgLSBwb3MsIGVvbHAgLSBwb3MgIT0gMSA/ICJzIiA6ICIiLCBvbmUsIG9uZS5sZW5ndGgoKSk7CgkJCXcgPSBtYXgodywgKGVvbHAgLSBwb3MpIC8gb25lLmxlbmd0aCgpKTsKCQkJaCsrOwoJCQlwb3MgPSBlb2xwICsgIlxuIi5sZW5ndGgoKTsKCQl9CgkJdmFyIHIgPSBuZXcgQml0ZmllbGQyRCh3LCBoKTsKCgkJZm9yIChpbnQgeSA9IDAsIHggPSAwLCBvZnMgPSAwLCBwb3MgPSAwOyBwb3MgPCBzcmMubGVuZ3RoKCk7ICkKCQkJaWYgKHNyYy5jaGFyQXQocG9zKSA9PSAnXG4nKSB7CgkJCQlvZnMgKz0gci53IC0geDsKCQkJCXggPSAwOyB5Kys7CgkJCQlwb3MgKz0gIlxuIi5sZW5ndGgoKTsKCQkJfSBlbHNlIHsKCQkJCWlmIChzcmMuc3RhcnRzV2l0aChvbmUsIHBvcykpIHIucmF3c2V0KG9mcyk7CgkJCQlwb3MgKz0gb25lLmxlbmd0aCgpOwoJCQkJeCsrOyBvZnMrKzsKCQkJfQoJCXJldHVybiByOwoJfQoKCXB1YmxpYyBib29sZWFuW11bXSB0b0Jvb2wyRCgpIHsgcmV0dXJuIHRvQm9vbDJEKDAsIDAsIHcsIGgpOyB9CglwdWJsaWMgYm9vbGVhbltdW10gdG9Cb29sMkQoaW50IGF0eCwgaW50IGF0eSwgaW50IHcsIGludCBoKSB7CgkJdmFsaWRhdGVSZWN0KHRoaXMudywgdGhpcy5oLCBhdHgsIGF0eSwgdywgaCk7CgkJYm9vbGVhbltdW10gciA9IEJvb2wyRC5jcmVhdGUodywgaCk7CgkJZm9yIChpbnQgeSA9IDAsIG9mcyA9IGF0eSAqIHRoaXMudyArIGF0eDsgeSA8IGg7IHkrKywgb2ZzICs9IHRoaXMudyAtIHcpCgkJCWZvciAoaW50IHggPSAwOyB4IDwgdzsgeCsrLCBvZnMrKykKCQkJCXJbeV1beF0gPSByYXdnZXQob2ZzKTsKCQlyZXR1cm4gcjsKCX0KfQoKY2xhc3MgQm9vbDJEIHsKCXB1YmxpYyBzdGF0aWMgYm9vbGVhbltdW10gY3JlYXRlKGludCB3LCBpbnQgaCkgewoJCUJpdGZpZWxkMkQudmFsaWRhdGVTaXplKHcsIGgpOwoJCWlmIChoID09IDApIHRocm93IFV0aWwuQmFkQXJnZigiYm9vbGVhbltdW10gY2FuJ3QgaGF2ZSB6ZXJvIGhlaWdodC4iKTsKCQlyZXR1cm4gbmV3IGJvb2xlYW5baF1bd107Cgl9CgoJcHVibGljIHN0YXRpYyB2b2lkIG9yKGJvb2xlYW5bXVtdIGl0LCBib29sZWFuW11bXSBiLCBpbnQgaXRYLCBpbnQgaXRZKSB7CgkJb3IoaXQsIGIsIDAsIDAsIGJbMF0ubGVuZ3RoLCBiLmxlbmd0aCwgaXRYLCBpdFkpOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCBvcihib29sZWFuW11bXSBpdCwgYm9vbGVhbltdW10gYiwgaW50IGJ4LCBpbnQgYnksIGludCB3LCBpbnQgaCwgaW50IGl0WCwgaW50IGl0WSkgewoJCXZhciB3YXJkID0gbmV3IEJpdGZpZWxkMkQuQ29vcmRXYXJkZW4oaXRbMF0ubGVuZ3RoLCBpdC5sZW5ndGgsIGJbMF0ubGVuZ3RoLCBiLmxlbmd0aCwgYngsIGJ5LCB3LCBoLCBpdFgsIGl0WSk7CgkJZm9yIChpbnQgZHkgPSAwLCBpSXRZID0gd2FyZC5kZXN0WSwgaUJ5ID0gd2FyZC5zcmNZOyBkeSA8IHdhcmQuaDsgZHkrKywgaUl0WSsrLCBpQnkrKykKCQkJZm9yIChpbnQgZHggPSAwLCBpSXRYID0gd2FyZC5kZXN0WCwgaUJ4ID0gd2FyZC5zcmNYOyBkeCA8IHdhcmQudzsgZHgrKywgaUl0WCsrLCBpQngrKykKCQkJCWl0W2lJdFldW2lJdFhdIHw9IGJbaUJ5XVtpQnhdOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCBhbmROb3QoYm9vbGVhbltdW10gaXQsIGJvb2xlYW5bXVtdIGIsIGludCBpdFgsIGludCBpdFkpIHsKCQlhbmROb3QoaXQsIGIsIDAsIDAsIGJbMF0ubGVuZ3RoLCBiLmxlbmd0aCwgaXRYLCBpdFkpOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCBhbmROb3QoYm9vbGVhbltdW10gaXQsIGJvb2xlYW5bXVtdIGIsIGludCBieCwgaW50IGJ5LCBpbnQgdywgaW50IGgsIGludCBpdFgsIGludCBpdFkpIHsKCQl2YXIgd2FyZCA9IG5ldyBCaXRmaWVsZDJELkNvb3JkV2FyZGVuKGl0WzBdLmxlbmd0aCwgaXQubGVuZ3RoLCBiWzBdLmxlbmd0aCwgYi5sZW5ndGgsIGJ4LCBieSwgdywgaCwgaXRYLCBpdFkpOwoJCWZvciAoaW50IGR5ID0gMCwgaUl0WSA9IHdhcmQuZGVzdFksIGlCeSA9IHdhcmQuc3JjWTsgZHkgPCB3YXJkLmg7IGR5KyssIGlJdFkrKywgaUJ5KyspCgkJCWZvciAoaW50IGR4ID0gMCwgaUl0WCA9IHdhcmQuZGVzdFgsIGlCeCA9IHdhcmQuc3JjWDsgZHggPCB3YXJkLnc7IGR4KyssIGlJdFgrKywgaUJ4KyspCgkJCQlpdFtpSXRZXVtpSXRYXSAmPSAhYltpQnldW2lCeF07Cgl9CgoJcHVibGljIHN0YXRpYyB2b2lkIGNvcHlGcm9tKGJvb2xlYW5bXVtdIGl0LCBib29sZWFuW11bXSBiLCBpbnQgaXRYLCBpbnQgaXRZKSB7CgkJY29weUZyb20oaXQsIGIsIDAsIDAsIGJbMF0ubGVuZ3RoLCBiLmxlbmd0aCwgaXRYLCBpdFkpOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCBjb3B5RnJvbShib29sZWFuW11bXSBpdCwgYm9vbGVhbltdW10gYiwgaW50IGJ4LCBpbnQgYnksIGludCB3LCBpbnQgaCwgaW50IGl0WCwgaW50IGl0WSkgewoJCXZhciB3YXJkID0gbmV3IEJpdGZpZWxkMkQuQ29vcmRXYXJkZW4oaXRbMF0ubGVuZ3RoLCBpdC5sZW5ndGgsIGJbMF0ubGVuZ3RoLCBiLmxlbmd0aCwgYngsIGJ5LCB3LCBoLCBpdFgsIGl0WSk7CgkJaWYgKHdhcmQudyA8PSAwKSByZXR1cm47CgkJZm9yIChpbnQgZHkgPSAwLCBpSXRZID0gd2FyZC5kZXN0WSwgaUJ5ID0gd2FyZC5zcmNZOyBkeSA8IHdhcmQuaDsgZHkrKywgaUl0WSsrLCBpQnkrKykKCQkJU3lzdGVtLmFycmF5Y29weShiW2lCeV0sIHdhcmQuc3JjWCwgaXRbaUl0WV0sIHdhcmQuZGVzdFgsIHdhcmQudyk7Cgl9CgoJcHVibGljIHN0YXRpYyBTdHJpbmcgdG9TdHJpbmcoYm9vbGVhbltdW10gaXQpIHsKCQlyZXR1cm4gdG9TdHJpbmcoaXQsICIjIyIsICIuLiIpOwoJfQoKCXB1YmxpYyBzdGF0aWMgU3RyaW5nIHRvU3RyaW5nKGJvb2xlYW5bXVtdIGl0LCBTdHJpbmcgb25lLCBTdHJpbmcgemVybykgewoJCXJldHVybiB0b0JpdGZpZWxkMkQoaXQpLnRvU3RyaW5nKG9uZSwgemVybyk7Cgl9CgoJcHVibGljIHN0YXRpYyBib29sZWFuW11bXSBwYXJzZShTdHJpbmcgc3JjKSB7IHJldHVybiBwYXJzZShzcmMsICIjIyIpOyB9CglwdWJsaWMgc3RhdGljIGJvb2xlYW5bXVtdIHBhcnNlKFN0cmluZyBzcmMsIFN0cmluZyBvbmUpIHsKCQlyZXR1cm4gQml0ZmllbGQyRC5wYXJzZShzcmMsIG9uZSkudG9Cb29sMkQoKTsKCX0KCglwdWJsaWMgc3RhdGljIEJpdGZpZWxkMkQgdG9CaXRmaWVsZDJEKGJvb2xlYW5bXVtdIGl0KSB7IHJldHVybiB0b0JpdGZpZWxkMkQoaXQsIDAsIDAsIGl0WzBdLmxlbmd0aCwgaXQubGVuZ3RoKTsgfQoJcHVibGljIHN0YXRpYyBCaXRmaWVsZDJEIHRvQml0ZmllbGQyRChib29sZWFuW11bXSBpdCwgaW50IGF0eCwgaW50IGF0eSwgaW50IHcsIGludCBoKSB7CgkJQml0ZmllbGQyRC52YWxpZGF0ZVJlY3QoaXRbMF0ubGVuZ3RoLCBpdC5sZW5ndGgsIGF0eCwgYXR5LCB3LCBoKTsKCQl2YXIgciA9IG5ldyBCaXRmaWVsZDJEKHcsIGgpOwoJCWZvciAoaW50IHkgPSAwLCByT2ZzID0gMDsgeSA8IGg7IHkrKykKCQkJZm9yIChpbnQgeCA9IDA7IHggPCB3OyB4KyssIHJPZnMrKykKCQkJCWlmIChpdFt5XVt4XSkgci5yYXdzZXQock9mcyk7CgkJcmV0dXJuIHI7Cgl9Cn0KCmNsYXNzIElkZW9uZQp7CgkvLyBBdXRvLWdyb3dpbmcgYml0ZmllbGQgZm9yIHByb2NlZHVyYWwgZ2VuZXJhdGlvbiB3aGVuIHJlc3VsdGluZyBzaXplIGlzIGltcHJhY3RpY2FsIHRvIHByZWRpY3QuCglzdGF0aWMgY2xhc3MgUHJvY0JpdGZpZWxkMkQgewoJCUJpdGZpZWxkMkQgYiA9IG5ldyBCaXRmaWVsZDJEKDAsIDApOwoJCWludCBheCwgYXksIGJ4LCBieSwgYm91bmRBeCwgYm91bmRBeSwgYm91bmRCeCwgYm91bmRCeTsKCgkJdm9pZCBzZXQoaW50IHgsIGludCB5KSB7CgkJCWlmIChib3VuZEF4ID09IGJvdW5kQngpIHsKCQkJCWF4ID0geDsgYXkgPSB5OyBieCA9IHg7IGJ5ID0geTsKCQkJCWJvdW5kQXggPSB4OyBib3VuZEF5ID0geTsgYm91bmRCeCA9IHg7IGJvdW5kQnkgPSB5OwoJCQl9CgkJCWlmICh4IDwgYXggfHwgeSA8IGF5IHx8IHggPj0gYnggfHwgeSA+PSBieSkKCQkJCWdyb3coCgkJCQkJeCA8IGF4ID8gZ3Jvd1N0Z3koYXggLSB4LCBieCAtIGF4KSA6IDAsCgkJCQkJeSA8IGF5ID8gZ3Jvd1N0Z3koYXkgLSB5LCBieSAtIGF5KSA6IDAsCgkJCQkJeCA+PSBieCA/IGdyb3dTdGd5KHggLSBieCArIDEsIGJ4IC0gYXgpIDogMCwKCQkJCQl5ID49IGJ5ID8gZ3Jvd1N0Z3koeSAtIGJ5ICsgMSwgYnkgLSBheSkgOiAwKTsKCQkJaWYgKHggPCBib3VuZEF4KSBib3VuZEF4ID0geDsKCQkJaWYgKHggPj0gYm91bmRCeCkgYm91bmRCeCA9IHggKyAxOwoJCQlpZiAoeSA8IGJvdW5kQXkpIGJvdW5kQXkgPSB5OwoJCQlpZiAoeSA+PSBib3VuZEJ5KSBib3VuZEJ5ID0geSArIDE7CgkJCWIuc2V0KHgtYXgsIHktYXkpOwoJCX0KCgkJdm9pZCB1bnNldChpbnQgeCwgaW50IHkpIHsKCQkJaWYgKHggPj0gYm91bmRBeCAmJiB4IDwgYm91bmRCeCAmJiB4ID49IGJvdW5kQXkgJiYgeCA8IGJvdW5kQnkpCgkJCQliLnVuc2V0KHgtYXgsIHktYXkpOwoJCX0KCgkJdm9pZCBzZXQoaW50IHgsIGludCB5LCBib29sZWFuIHZhbHVlKSB7IGlmICh2YWx1ZSkgc2V0KHgsIHkpOyBlbHNlIHVuc2V0KHgsIHkpOyB9CgoJCXN0YXRpYyBpbnQgZ3Jvd1N0Z3koaW50IGF0TGVhc3QsIGludCBwcmVzZW50KSB7CgkJCXJldHVybiBtYXgobWF4KDgsIHByZXNlbnQgLyAyKSwgYXRMZWFzdCk7CgkJfQoKCQl2b2lkIGdyb3coaW50IEx4LCBpbnQgTHksIGludCBSeCwgaW50IFJ5KSB7CgkJCXRoaXMuYiA9IG5ldyBCaXRmaWVsZDJEKChieCAtIGF4KSArIEx4ICsgUngsIChieSAtIGF5KSArIEx5ICsgUnkpLmNvcHlGcm9tKAoJCQkJdGhpcy5iLCBib3VuZEF4IC0gYXgsIGJvdW5kQXkgLSBheSwgYm91bmRCeCAtIGJvdW5kQXgsIGJvdW5kQnkgLSBib3VuZEF5LCAoYm91bmRBeCAtIGF4KSArIEx4LCAoYm91bmRBeSAtIGF5KSArIEx5KTsKCQkJYXggLT0gTHg7IGF5IC09IEx5OyBieCArPSBSeDsgYnkgKz0gUnk7CgkJfQoKCQlCaXRmaWVsZDJEIGJha2UoKSB7CgkJCXJldHVybiBuZXcgQml0ZmllbGQyRChib3VuZEJ4IC0gYm91bmRBeCwgYm91bmRCeSAtIGJvdW5kQXkpLmNvcHlGcm9tKAoJCQkJdGhpcy5iLCBib3VuZEF4IC0gYXgsIGJvdW5kQXkgLSBheSwgYm91bmRCeCAtIGJvdW5kQXgsIGJvdW5kQnkgLSBib3VuZEF5LCAwLCAwKTsKCQl9Cgl9CgoJc3RhdGljIEJpdGZpZWxkMkQgbWFrZVNwcnVjZShpbnQgc2l6ZSkgewoJCVV0aWwucmFuZ2VjaGVjayhzaXplLCAxLCAyMCwgInNpemUiKTsKCQl2YXIgciA9IG5ldyBQcm9jQml0ZmllbGQyRCgpOwoJCWludCBuU2VncyA9IDMrc2l6ZS80LCB0cnVua1dpZHRoID0gMStzaXplLzMqMjsKCQlpbnQgeSA9IDA7CgkJZm9yIChpbnQgaVNlZyA9IDA7IGlTZWcgPCBuU2VnczsgaVNlZysrKSB7CgkJCWludCBtYXhTZWdXaWR0aCA9IHRydW5rV2lkdGggKyAyICogKHNpemUgKyBpU2VnICogc2l6ZSAvIDIpOwoJCQlpbnQgc2VnV2lkdGggPSBpU2VnID09IDAgPyAxIDogdHJ1bmtXaWR0aCArIChzaXplICsgaVNlZykvMyoyOwoJCQlmb3IgKDsgc2VnV2lkdGggPCBtYXhTZWdXaWR0aDsgc2VnV2lkdGggKz0gMikgewoJCQkJZm9yIChpbnQgeCA9IC1zZWdXaWR0aC8yOyB4IDwgKHNlZ1dpZHRoKzEpLzI7IHgrKykKCQkJCQlyLnNldCh4LCB5KTsKCQkJCXkgKz0gMTsKCQkJfQoJCX0KCQlmb3IgKGludCB5VHJ1bmsgPSAwOyB5VHJ1bmsgPCBzaXplOyB5VHJ1bmsrKykgewoJCQlmb3IgKGludCB4ID0gLXRydW5rV2lkdGgvMjsgeCA8ICh0cnVua1dpZHRoICsgMSkvMjsgeCsrKQoJCQkJci5zZXQoeCwgeSk7CgkJCXkrKzsKCQl9CgkJcmV0dXJuIHIuYmFrZSgpOwoJfQoKCXN0YXRpYyBCaXRmaWVsZDJEIG1vb24gPSBCaXRmaWVsZDJELnBhcnNlKAoJCSIuLi4uIyMjIyMjLi4uLi4uLi4uLlxuIiArCgkJIi4uIyMjIy4uLi4uLi4uLi4uLi4uXG4iICsKCQkiIyMjIy4uLi4uLi4uLi4uLi4uLi5cbiIgKwoJCSIjIyMjLi4uLi4uLi4uLi4uLi4uLlxuIiArCgkJIiMjIyMuLi4uLi4uLi4uLi4uLi4uXG4iICsKCQkiIyMjIy4uLi4uLi4uLi4uLi4uIyNcbiIgKwoJCSIjIyMjIyMuLi4uLi4uLi4uIyMjI1xuIiArCgkJIi4uIyMjIyMjIyMjIyMjIyMjIy4uXG4iICsKCQkiLi4uLiMjIyMjIyMjIyMjIyIsICIjIyIpOwoKCXN0YXRpYyBCaXRmaWVsZDJEIHBhaW50Qml0ZmllbGQyRCgpIHsKCQl2YXIgciA9IG5ldyBCaXRmaWVsZDJEKDUwLCA0MCk7CgkJci5vcihtb29uLCAxMSwgNCk7CgkJci5vcihtYWtlU3BydWNlKDQpLCAtMSwgMTUpOwoJCXIub3IobWFrZVNwcnVjZSgzKSwgMjksIDE3KTsKCQlyLm9yKG1ha2VTcHJ1Y2UoMiksIDE4LCAxOCk7CgkJci5vcihtYWtlU3BydWNlKDUpLCAzMiwgMTYpOwoJCXZhciByYW5kID0gbmV3IFJhbmRvbSg3KTsKCQlmb3IgKGludCBpU25vd2ZsYWtlID0gMDsgaVNub3dmbGFrZSA8IDI1OyBpU25vd2ZsYWtlKyspIHsKCQkJaW50IHggPSByYW5kLm5leHRJbnQoci53aWR0aCgpKSwKCQkJCXkgPSBtaW4oci5oZWlnaHQoKSAtIDEsIChpbnQpIChyLmhlaWdodCgpICogcG93KHJhbmQubmV4dEZsb2F0KCksIDIuMCkpKTsKCQkJci5zZXQoeCwgeSwgIXIuZ2V0KHgsIHkpKTsKCQl9CgkJcmV0dXJuIHI7Cgl9CgoJc3RhdGljIGJvb2xlYW5bXVtdIHBhaW50Qm9vbDJEKCkgewoJCXZhciByID0gQm9vbDJELmNyZWF0ZSg1MCwgNDApOwoJCUJvb2wyRC5vcihyLCBtb29uLnRvQm9vbDJEKCksIDExLCA0KTsKCQlCb29sMkQub3IociwgbWFrZVNwcnVjZSg0KS50b0Jvb2wyRCgpLCAtMSwgMTUpOwoJCUJvb2wyRC5vcihyLCBtYWtlU3BydWNlKDMpLnRvQm9vbDJEKCksIDI5LCAxNyk7CgkJQm9vbDJELm9yKHIsIG1ha2VTcHJ1Y2UoMikudG9Cb29sMkQoKSwgMTgsIDE4KTsKCQlCb29sMkQub3IociwgbWFrZVNwcnVjZSg1KS50b0Jvb2wyRCgpLCAzMiwgMTYpOwoJCXZhciByYW5kID0gbmV3IFJhbmRvbSg3KTsKCQlmb3IgKGludCBpU25vd2ZsYWtlID0gMDsgaVNub3dmbGFrZSA8IDI1OyBpU25vd2ZsYWtlKyspIHsKCQkJaW50IHggPSByYW5kLm5leHRJbnQoclswXS5sZW5ndGgpLAoJCQkJeSA9IG1pbihyLmxlbmd0aCAtIDEsIChpbnQpIChyLmxlbmd0aCAqIHBvdyhyYW5kLm5leHRGbG9hdCgpLCAyLjApKSk7CgkJCXJbeV1beF0gPSAhclt5XVt4XTsKCQl9CgkJcmV0dXJuIHI7Cgl9CgoJc3RhdGljIEJpdGZpZWxkMkQgbWFrZVJhbmRvbUJpdGZpZWxkKGludCB3LCBpbnQgaCwgUmFuZG9tIHJhbmQpIHsKCQl2YXIgciA9IG5ldyBCaXRmaWVsZDJEKHcsIGgpOwoJCWZvciAoaW50IGkgPSAwOyBpIDwgdypoLzU7IGkrKykgewoJCQlyLnNldChyYW5kLm5leHRJbnQodyksIHJhbmQubmV4dEludChoKSk7CgkJfQoJCXJldHVybiByOwoJfQoKCXN0YXRpYyBlbnVtIE9wIHsgT1IsIEFORF9OT1QsIENPUFkgfQoKCXN0YXRpYyBjbGFzcyBQbGFuSXRlbSB7CgkJT3Agb3A7CgkJaW50IHJlY3RJbmRleDsKCQlpbnQgcmVjdFgsIHJlY3RZLCB3LCBoLCBkZXN0WCwgZGVzdFk7CgoJCXN0YXRpYyBQbGFuSXRlbSByYW5kb20oUmFuZG9tIHJhbmQsIEJpdGZpZWxkMkQgY2FudmFzLCBCaXRmaWVsZDJEW10gcmVjdHMpIHsKCQkJdmFyIHIgPSBuZXcgUGxhbkl0ZW0oKTsKCQkJci5vcCA9IE9wLnZhbHVlcygpW3JhbmQubmV4dEludChPcC52YWx1ZXMoKS5sZW5ndGgpXTsKCgkJCXIucmVjdEluZGV4ID0gcmFuZC5uZXh0SW50KHJlY3RzLmxlbmd0aCk7CgkJCXZhciByZWN0ID0gcmVjdHNbci5yZWN0SW5kZXhdOwoJCQlpZiAocmFuZC5uZXh0SW50KDIpID09IDApIHsKCQkJCXIucmVjdFggPSByYW5kLm5leHRJbnQocmVjdC53aWR0aCgpKTsKCQkJCXIucmVjdFkgPSByYW5kLm5leHRJbnQocmVjdC5oZWlnaHQoKSk7CgkJCQlyLncgPSAxICsgcmFuZC5uZXh0SW50KHJlY3Qud2lkdGgoKSAtIHIucmVjdFgpOwoJCQkJci5oID0gMSArIHJhbmQubmV4dEludChyZWN0LmhlaWdodCgpIC0gci5yZWN0WSk7CgkJCQlyLmRlc3RYID0gLXJlY3Qud2lkdGgoKSArIHJhbmQubmV4dEludChjYW52YXMud2lkdGgoKSArIHJlY3Qud2lkdGgoKSArIDEpOwoJCQkJci5kZXN0WSA9IC1yZWN0LmhlaWdodCgpICsgcmFuZC5uZXh0SW50KGNhbnZhcy5oZWlnaHQoKSArIHJlY3QuaGVpZ2h0KCkgKyAxKTsKCQkJfSBlbHNlIHsKCQkJCXIucmVjdFggPSAwOwoJCQkJci5yZWN0WSA9IDA7CgkJCQlyLncgPSByZWN0LndpZHRoKCk7CgkJCQlyLmggPSByZWN0LmhlaWdodCgpOwoJCQkJci5kZXN0WCA9IHJhbmQubmV4dEludChjYW52YXMud2lkdGgoKSAtIHJlY3Qud2lkdGgoKSk7CgkJCQlyLmRlc3RZID0gcmFuZC5uZXh0SW50KGNhbnZhcy5oZWlnaHQoKSAtIHJlY3QuaGVpZ2h0KCkpOwoJCQl9CgkJCXJldHVybiByOwoJCX0KCX0KCglzdGF0aWMgdm9pZCBmdXNlZEJlbmNobWFya1Rlc3QoKSB7CgkJUmFuZG9tIHJhbmQgPSBuZXcgUmFuZG9tKDApOwoJCUJpdGZpZWxkMkQgYml0Q2FudmFzID0gbmV3IEJpdGZpZWxkMkQoMTAwMCwgMTAwMCk7CgkJYm9vbGVhbltdW10gYm9vbENhbnZhcyA9IEJvb2wyRC5jcmVhdGUoYml0Q2FudmFzLndpZHRoKCksIGJpdENhbnZhcy5oZWlnaHQoKSk7CgkJQml0ZmllbGQyRCBiaXRDYW52YXNCQVRDSCA9IG5ldyBCaXRmaWVsZDJEKGJpdENhbnZhcy53aWR0aCgpLCBiaXRDYW52YXMuaGVpZ2h0KCkpOwoKCQlCaXRmaWVsZDJEW10gYml0UmVjdHMgPSBuZXcgQml0ZmllbGQyRFsxMDAwXTsKCQlib29sZWFuW10gW11bXSBib29sUmVjdHMgPSBuZXcgYm9vbGVhbltiaXRSZWN0cy5sZW5ndGhdW11bXTsKCQlmb3IgKGludCBpID0gMDsgaSA8IGJpdFJlY3RzLmxlbmd0aDsgaSsrKSB7CgkJCWJpdFJlY3RzW2ldID0gbWFrZVJhbmRvbUJpdGZpZWxkKDEgKyByYW5kLm5leHRJbnQoYml0Q2FudmFzLndpZHRoKCkvMTApLCAxICsgcmFuZC5uZXh0SW50KGJpdENhbnZhcy5oZWlnaHQoKS8xMCksIHJhbmQpOwoJCQlib29sUmVjdHNbaV0gPSBiaXRSZWN0c1tpXS50b0Jvb2wyRCgpOwoJCX0KCgkJUGxhbkl0ZW1bXSBwbGFuID0gbmV3IFBsYW5JdGVtWzYwMDAwMF07CgkJZm9yIChpbnQgaSA9IDA7IGkgPCBwbGFuLmxlbmd0aDsgaSsrKQoJCQlwbGFuW2ldID0gUGxhbkl0ZW0ucmFuZG9tKHJhbmQsIGJpdENhbnZhcywgYml0UmVjdHMpOwoKCQlmaW5hbCBpbnQgVFJJQUxTID0gMzsKCQlmb3IgKGludCBpVHJpYWwgPSAwOyBpVHJpYWwgPCBUUklBTFM7IGlUcmlhbCsrKSB7CgkJCVN5c3RlbS5vdXQucHJpbnRmKCIlcy0tLSBUUklBTCAlZC8lZCAtLS1cbiIsIGlUcmlhbCA+IDAgPyAiXG4iIDogIiIsIDEgKyBpVHJpYWwsIFRSSUFMUyk7CgkJCWRvdWJsZSByZWZUaW1lID0gYmVuY2htYXJrKCJib29sZWFuW11bXSIsICgpIC0+IHsKCQkJCQlmb3IgKGludCBpID0gMDsgaSA8IHBsYW4ubGVuZ3RoOyBpKyspIHsKCQkJCQkJc3dpdGNoIChwbGFuW2ldLm9wKSB7CgkJCQkJCQljYXNlIE9SOiBCb29sMkQub3IoYm9vbENhbnZhcywgYm9vbFJlY3RzW3BsYW5baV0ucmVjdEluZGV4XSwgcGxhbltpXS5yZWN0WCwgcGxhbltpXS5yZWN0WSwgcGxhbltpXS53LCBwbGFuW2ldLmgsIHBsYW5baV0uZGVzdFgsIHBsYW5baV0uZGVzdFkpOyBicmVhazsKCQkJCQkJCWNhc2UgQU5EX05PVDogQm9vbDJELmFuZE5vdChib29sQ2FudmFzLCBib29sUmVjdHNbcGxhbltpXS5yZWN0SW5kZXhdLCBwbGFuW2ldLnJlY3RYLCBwbGFuW2ldLnJlY3RZLCBwbGFuW2ldLncsIHBsYW5baV0uaCwgcGxhbltpXS5kZXN0WCwgcGxhbltpXS5kZXN0WSk7IGJyZWFrOwoJCQkJCQkJY2FzZSBDT1BZOiBkZWZhdWx0OiBCb29sMkQuY29weUZyb20oYm9vbENhbnZhcywgYm9vbFJlY3RzW3BsYW5baV0ucmVjdEluZGV4XSwgcGxhbltpXS5yZWN0WCwgcGxhbltpXS5yZWN0WSwgcGxhbltpXS53LCBwbGFuW2ldLmgsIHBsYW5baV0uZGVzdFgsIHBsYW5baV0uZGVzdFkpOyBicmVhazsKCQkJCQkJfQoJCQkJCX0KCQkJCQlyZXR1cm4gYm9vbENhbnZhczsKCQkJCX0pOwoKCQkJYmVuY2htYXJrKCJCaXRmaWVsZDJEIChwZXItYml0KSIsICgpIC0+IHsKCQkJCWZvciAoaW50IGkgPSAwOyBpIDwgcGxhbi5sZW5ndGg7IGkrKykgewoJCQkJCXN3aXRjaCAocGxhbltpXS5vcCkgewoJCQkJCQljYXNlIE9SOiBiaXRDYW52YXMub3IoYml0UmVjdHNbcGxhbltpXS5yZWN0SW5kZXhdLCBwbGFuW2ldLnJlY3RYLCBwbGFuW2ldLnJlY3RZLCBwbGFuW2ldLncsIHBsYW5baV0uaCwgcGxhbltpXS5kZXN0WCwgcGxhbltpXS5kZXN0WSk7IGJyZWFrOwoJCQkJCQljYXNlIEFORF9OT1Q6IGJpdENhbnZhcy5hbmROb3QoYml0UmVjdHNbcGxhbltpXS5yZWN0SW5kZXhdLCBwbGFuW2ldLnJlY3RYLCBwbGFuW2ldLnJlY3RZLCBwbGFuW2ldLncsIHBsYW5baV0uaCwgcGxhbltpXS5kZXN0WCwgcGxhbltpXS5kZXN0WSk7IGJyZWFrOwoJCQkJCQljYXNlIENPUFk6IGRlZmF1bHQ6IGJpdENhbnZhcy5jb3B5RnJvbShiaXRSZWN0c1twbGFuW2ldLnJlY3RJbmRleF0sIHBsYW5baV0ucmVjdFgsIHBsYW5baV0ucmVjdFksIHBsYW5baV0udywgcGxhbltpXS5oLCBwbGFuW2ldLmRlc3RYLCBwbGFuW2ldLmRlc3RZKTsgYnJlYWs7CgkJCQkJfQoJCQkJfQoJCQkJcmV0dXJuIGJpdENhbnZhczsKCQkJfSwgcmVmVGltZSk7CgoJCQliZW5jaG1hcmsoIkJpdGZpZWxkMkQgKGJpdHNpemVvZihpbnQpIGJhdGNoZXMpIiwgKCkgLT4gewoJCQkJZm9yIChpbnQgaSA9IDA7IGkgPCBwbGFuLmxlbmd0aDsgaSsrKSB7CgkJCQkJc3dpdGNoIChwbGFuW2ldLm9wKSB7CgkJCQkJCWNhc2UgT1I6IGJpdENhbnZhc0JBVENILm9yQkFUQ0goYml0UmVjdHNbcGxhbltpXS5yZWN0SW5kZXhdLCBwbGFuW2ldLnJlY3RYLCBwbGFuW2ldLnJlY3RZLCBwbGFuW2ldLncsIHBsYW5baV0uaCwgcGxhbltpXS5kZXN0WCwgcGxhbltpXS5kZXN0WSk7IGJyZWFrOwoJCQkJCQljYXNlIEFORF9OT1Q6IGJpdENhbnZhc0JBVENILmFuZE5vdEJBVENIKGJpdFJlY3RzW3BsYW5baV0ucmVjdEluZGV4XSwgcGxhbltpXS5yZWN0WCwgcGxhbltpXS5yZWN0WSwgcGxhbltpXS53LCBwbGFuW2ldLmgsIHBsYW5baV0uZGVzdFgsIHBsYW5baV0uZGVzdFkpOyBicmVhazsKCQkJCQkJY2FzZSBDT1BZOiBkZWZhdWx0OiBiaXRDYW52YXNCQVRDSC5jb3B5RnJvbShiaXRSZWN0c1twbGFuW2ldLnJlY3RJbmRleF0sIHBsYW5baV0ucmVjdFgsIHBsYW5baV0ucmVjdFksIHBsYW5baV0udywgcGxhbltpXS5oLCBwbGFuW2ldLmRlc3RYLCBwbGFuW2ldLmRlc3RZKTsgYnJlYWs7CgkJCQkJfQoJCQkJfQoJCQkJcmV0dXJuIGJpdENhbnZhc0JBVENIOwoJCQl9LCByZWZUaW1lKTsKCgkJCWlmIChpVHJpYWwgPT0gMCkgewoJCQkJU3RyaW5nIHJlZiA9IEJvb2wyRC50b1N0cmluZyhib29sQ2FudmFzKTsKCQkJCWlmICghYml0Q2FudmFzLnRvU3RyaW5nKCkuZXF1YWxzKHJlZikpCgkJCQkJdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oIkJpdGZpZWxkMkQocGVyLWJpdCkgYW5kIGJvb2xlYW5bXVtdIGRvIGRpZmZlci4iKTsKCQkJCWlmICghYml0Q2FudmFzQkFUQ0gudG9TdHJpbmcoKS5lcXVhbHMocmVmKSkKCQkJCQl0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbigiQml0ZmllbGQyRChiaXRzaXplb2YoaW50KSBiYXRjaGVzKSBhbmQgYm9vbGVhbltdW10gZG8gZGlmZmVyLiIpOwoJCQl9CgkJfQoJfQoKCUBGdW5jdGlvbmFsSW50ZXJmYWNlCglpbnRlcmZhY2UgQmVuY2htYXJrUGF5bG9hZCB7CgkJYWJzdHJhY3QgT2JqZWN0IHJ1bigpOwoJfQoKCXN0YXRpYyBkb3VibGUgYmVuY2htYXJrKFN0cmluZyB0aXRsZSwgQmVuY2htYXJrUGF5bG9hZCBwYXlsb2FkKSB7CgkJcmV0dXJuIGJlbmNobWFyayh0aXRsZSwgcGF5bG9hZCwgLTEpOwoJfQoKCXN0YXRpYyBkb3VibGUgYmVuY2htYXJrKFN0cmluZyB0aXRsZSwgQmVuY2htYXJrUGF5bG9hZCBwYXlsb2FkLCBkb3VibGUgcmVmVGltZSkgewoJCWxvbmcgc3RhcnQgPSBTeXN0ZW0ubmFub1RpbWUoKTsKCQlPYmplY3Qga2VlcEFsaXZlID0gcGF5bG9hZC5ydW4oKTsKCQlkb3VibGUgdGltZSA9IChTeXN0ZW0ubmFub1RpbWUoKSAtIHN0YXJ0KSAqIDFlLTk7CgkJU3lzdGVtLm91dC5wcmludGxuKFN0cmluZy5mb3JtYXQoCgkJCSIlczogJS4yZiBzJXMiLAoJCQl0aXRsZSwgdGltZSwgcmVmVGltZSA+PSAwID8gU3RyaW5nLmZvcm1hdCgiICglcykiLCBqdWRnZW1lbnQodGltZSwgcmVmVGltZSkpIDogIiIsIGtlZXBBbGl2ZSkpOwoJCXJldHVybiB0aW1lOwoJfQoKCXN0YXRpYyBTdHJpbmcganVkZ2VtZW50KGRvdWJsZSB0aW1lLCBkb3VibGUgcmVmVGltZSkgewoJCWZpbmFsIGRvdWJsZSBhYnNUaHJlc2hvbGQgPSAwLjMsIHJlbFRocmVzaG9sZCA9IC4xOwoKCQlyZXR1cm4gdGltZSA8PSBhYnNUaHJlc2hvbGQgfHwgcmVmVGltZSA8PSBhYnNUaHJlc2hvbGQgPwoJCQlTdHJpbmcuZm9ybWF0KCJjYW4ndCBqdWRnZSwgbWluLiByZXF1aXJlZCB0aW1lIGlzICUuMWYgcyIsIGFic1RocmVzaG9sZCkgOgoJCQlNYXRoLm1heCh0aW1lLCByZWZUaW1lKSA+ICgxICsgcmVsVGhyZXNob2xkKSAqIE1hdGgubWluKHRpbWUsIHJlZlRpbWUpID8KCQkJU3RyaW5nLmZvcm1hdCgiJS4wZiUlICVzIiwKCQkJCU1hdGguYWJzKCh0aW1lIDwgcmVmVGltZSA/IHJlZlRpbWUgLyB0aW1lIDogdGltZSAvIHJlZlRpbWUpIC0gMSkgKiAxMDAsCgkJCQl0aW1lIDwgcmVmVGltZSA/ICJmYXN0ZXIiIDogInNsb3dlciIpIDoKCQkJU3RyaW5nLmZvcm1hdCgibm8gb2JzZXJ2YWJsZSBkaWZmZXJlbmNlLCBtaW4uICUuMGYlJSByZXF1aXJlZCIsIHJlbFRocmVzaG9sZCAqIDEwMCk7Cgl9CgoJcHVibGljIHN0YXRpYyB2b2lkIG1haW4gKFN0cmluZ1tdIGFyZ3MpIHRocm93cyBqYXZhLmxhbmcuRXhjZXB0aW9uCgl7CgkJdHJ5IHsKCQkJdmFyIGltYWdlID0gcGFpbnRCaXRmaWVsZDJEKCk7CgkJCXZhciBpbTIgPSBwYWludEJvb2wyRCgpOwoJCQlpZiAoIWltYWdlLnRvU3RyaW5nKCkuZXF1YWxzKEJvb2wyRC50b1N0cmluZyhpbTIpKSkgewoJCQkJdGhyb3cgbmV3IFJ1bnRpbWVFeGNlcHRpb24oU3RyaW5nLmZvcm1hdCgicGFpbnRCb29sMkQgPVxuJXNcblxueWV0IHBhaW50Qml0ZmllbGQyRCA9XG4lcyIsIGltMiwgaW1hZ2UpKTsKCQkJfQoJCQlmdXNlZEJlbmNobWFya1Rlc3QoKTsKCQkJU3lzdGVtLm91dC5wcmludGYoIlxuJXNcblxuIiwgaW1hZ2UpOwoJCX0gY2F0Y2ggKEV4Y2VwdGlvbiBlKSB7CgkJCVN5c3RlbS5vdXQucHJpbnRsbihVdGlsLnRyYWNlKGUpKTsKCQl9Cgl9Cn0=