import java.util.* ;
import java.lang.* ;
import java.io.* ;
class Throw {
}
}
class Matrix {
public final int w, h;
public final int [ ] raw;
public Matrix( int w, int h) {
validateSize( w, h) ;
this .w = w;
this .h = h;
raw = new int [ w * h] ;
}
public static Matrix explicit( int w, int h, int ... cells ) {
if ( cells.length != w * h) Throw .badArg ( "Expected %d values of %d×%d matrix, got %d." , w* h, w, h, cells.length ) ;
Matrix r = new Matrix( w, h) ;
System .
arraycopy ( cells,
0 , r.
raw ,
0 , cells.
length ) ; return r;
}
@FunctionalInterface
public interface ValueGetter {
int getValue( int x, int y) ;
}
public Matrix( int w, int h, ValueGetter getValue) {
this ( w, h) ;
fill( getValue) ;
}
int maxLen = 0 ;
for ( int i = 0 ; i < reprs.length ; i++ ) {
reprs
[ i
] = Integer .
toString ( raw
[ i
] ) ; maxLen
= Math .
max ( maxLen, reprs
[ i
] .
length ( ) ) ; }
String fmt
= "%" + maxLen
+ "s" ;
StringBuilder rsb = new StringBuilder( ) ;
for ( int y = 0 , ai = 0 ; y < h; y++ )
for ( int x = 0 ; x < w; x++ , ai++ )
rsb.
append ( String .
format ( fmt, reprs
[ ai
] ) ) .
append ( x
+ 1 < w
? " " : y
+ 1 < h
? "\n " : "" ) ; return rsb.toString ( ) ;
}
public void fill( ValueGetter getValue)
{
for ( int y = 0 , ai = 0 ; y < h; y++ )
for ( int x = 0 ; x < w; x++ , ai++ )
raw[ ai] = getValue.getValue ( x, y) ;
}
private static void moveSubmatrix( Matrix a, int ax, int ay, Matrix b, int bx, int by, int w, int h) {
for ( int y = 0 , aAi = a.absIdx ( ax, ay) , bAi = b.absIdx ( bx, by) ; y < h; y++ , aAi += a.w , bAi += b.w )
System .
arraycopy ( a.
raw , aAi, b.
raw , bAi, w
) ; }
public Matrix submatrix( int sx, int sy, int sw, int sh) {
validateSubmatrix( this .w , this .h , sx, sy, sw, sh) ;
Matrix r = new Matrix( sw, sh) ;
moveSubmatrix( this , sx, sy, r, 0 , 0 , sw, sh) ;
return r;
}
public Matrix expand( int filler, int left, int right, int top, int bottom) {
if ( left < 0 || right < 0 || top < 0 || bottom < 0 )
Throw .badArg ( "Bad expand(): (%d, %d, %d, %d)." , left, right, top, bottom) ;
Matrix r = new Matrix( w + left + right, h + top + bottom) ;
for ( int y = 0 , rAi = 0 , thisAi = 0 ; y < r.h ; y++ )
for ( int x = 0 ; x < r.w ; x++ , rAi++ )
r.raw [ rAi] = x < left || x >= left + w || y < top || y >= top + h ? filler : raw[ thisAi++ ] ;
return r;
}
int absIdx( int x, int y) {
return y * w + x;
}
public static void validateSize( int w, int h) {
if ( w <= 0 || h <= 0 ) Throw .badArg ( "Bad matrix size: %d×%d." , w, h) ;
}
public static void validateSubmatrix( int thisw, int thish, int x, int y, int w, int h) {
validateSize( w, h) ;
if ( x < 0 || y < 0 || x + w > thisw || y + h > thish)
Throw .badArg ( "Bad submatrix of %d×%d: (%d, %d) + %d×%d." , thisw, thish, x, y, w, h) ;
}
public static void validateAddLike
( int aw,
int ah,
int bw,
int bh,
int rw,
int rh,
String op
) { if ( aw != bw || ah != bh)
Throw .badArg ( "Bad matrices for %s: %d×%d and %d×%d, size must be equal." , op, aw, ah, bw, bh) ;
if ( rw != aw || rh != ah)
Throw .badArg ( "Bad %s result size: %d×%d, expected %d×%d." , op, rw, rh, aw, ah) ;
}
public static void add(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
validateSubmatrix( a.w , a.h , ax, ay, aw, ah) ;
validateSubmatrix( b.w , b.h , bx, by, bw, bh) ;
validateSubmatrix( r.w , r.h , rx, ry, rw, rh) ;
validateAddLike( aw, ah, bw, bh, rw, rh, "addition" ) ;
trustedAdd( a, ax, ay, b, bx, by, r, rx, ry, rw, rh) ;
}
static void trustedAdd(
Matrix a, int ax, int ay,
Matrix b, int bx, int by,
Matrix r, int rx, int ry, int w, int h) {
int aAi = a.absIdx ( ax, ay) , bAi = b.absIdx ( bx, by) , rAi = r.absIdx ( rx, ry) ;
for ( int y = 0 ; y < h; y++ , aAi += a.w - w, bAi += b.w - w, rAi += r.w - w)
for ( int x = 0 ; x < w; x++ , aAi++ , bAi++ , rAi++ )
r.raw [ rAi] = a.raw [ aAi] + b.raw [ bAi] ;
}
public Matrix plus( Matrix b) {
Matrix r = new Matrix( w, h) ;
add( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ;
return r;
}
// Mutatis mutandis of add()
public static void subtract(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
validateSubmatrix( a.w , a.h , ax, ay, aw, ah) ;
validateSubmatrix( b.w , b.h , bx, by, bw, bh) ;
validateSubmatrix( r.w , r.h , rx, ry, rw, rh) ;
validateAddLike( aw, ah, bw, bh, rw, rh, "subtraction" ) ;
trustedSubtract( a, ax, ay, b, bx, by, r, rx, ry, rw, rh) ;
}
static void trustedSubtract(
Matrix a, int ax, int ay,
Matrix b, int bx, int by,
Matrix r, int rx, int ry, int w, int h) {
int aAi = a.absIdx ( ax, ay) , bAi = b.absIdx ( bx, by) , rAi = r.absIdx ( rx, ry) ;
for ( int y = 0 ; y < h; y++ , aAi += a.w - w, bAi += b.w - w, rAi += r.w - w)
for ( int x = 0 ; x < w; x++ , aAi++ , bAi++ , rAi++ )
r.raw [ rAi] = a.raw [ aAi] - b.raw [ bAi] ;
}
public Matrix minus( Matrix b) {
Matrix r = new Matrix( w, h) ;
subtract( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ;
return r;
}
public static void validateMultiply( int aw, int ah, int bw, int bh, int rw, int rh) {
if ( aw != bh)
Throw .badArg ( "Matrices of %d×%d and %d×%d cannot be multiplied: number of columns of the first must match the number of rows of the second." , aw, ah, bw, bh) ;
if ( rw != bw || rh != ah)
Throw .badArg ( "Bad resulting matrix size: expected %d×%d, got %d×%d." , bw, ah, rw, rh) ;
}
public static void multiplyPlain(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
validateSubmatrix( a.w , a.h , ax, ay, aw, ah) ;
validateSubmatrix( b.w , b.h , bx, by, bw, bh) ;
validateSubmatrix( r.w , r.h , rx, ry, rw, rh) ;
validateMultiply( aw, ah, bw, bh, rw, rh) ;
int aRowAi = a.absIdx ( ax, ay) , bColAi = b.absIdx ( bx, by) , rAi = r.absIdx ( rx, ry) ;
for ( int y = 0 ; y < rh; y++ , aRowAi += a.w , bColAi -= rw, rAi += r.w - rw)
for ( int x = 0 ; x < rw; x++ , rAi++ , bColAi++ ) {
int dot = 0 ;
for ( int i = 0 , aAi = aRowAi, bAi = bColAi; i < aw; i++ , aAi++ , bAi += b.w )
dot += a.raw [ aAi] * b.raw [ bAi] ;
r.raw [ rAi] = dot;
}
}
public Matrix multiplyPlain( Matrix b) {
Matrix r = new Matrix( b.w , h) ;
multiplyPlain( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ;
return r;
}
public int get( int x, int y) {
return raw[ y * w + x] ;
}
public void set( int x, int y, int value) {
raw[ y * w + x] = value;
}
private Matrix strassenPaddedBlock( int sx, int sy, int rw, int rh, int takew, int takeh) {
Matrix r = new Matrix( rw, rh) ;
moveSubmatrix( this , sx, sy, r, 0 , 0 , takew, takeh) ;
return r;
}
private static boolean shouldResortToPlainMultiply( int bw, int ah, int aw) {
final int THRESHOLD = 8 ;
return bw <= THRESHOLD || ah <= THRESHOLD || aw <= THRESHOLD;
}
public static void multiplyStrassen_StraightVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
if ( shouldResortToPlainMultiply( bw, ah, aw) ) {
multiplyPlain( a, ax, ay, aw, ah, b, bx, by, bw, bh, r, rx, ry, rw, rh) ;
return ;
}
validateSubmatrix( a.w , a.h , ax, ay, aw, ah) ;
validateSubmatrix( b.w , b.h , bx, by, bw, bh) ;
validateSubmatrix( r.w , r.h , rx, ry, rw, rh) ;
validateMultiply( aw, ah, bw, bh, rw, rh) ;
int halfAw = ( aw + 1 ) / 2 , halfAh = ( ah + 1 ) / 2 ,
halfBw = ( bw + 1 ) / 2 , halfBh = ( bh + 1 ) / 2 ,
halfRw = ( rw + 1 ) / 2 , halfRh = ( rh + 1 ) / 2 ;
Matrix
a11 = a.submatrix ( ax, ay, halfAw, halfAh) ,
a21 = a.strassenPaddedBlock ( ax, ay + halfAh, halfAw, halfAh, halfAw, ah - halfAh) ,
a12 = a.strassenPaddedBlock ( ax + halfAw, ay, halfAw, halfAh, aw - halfAw, halfAh) ,
a22 = a.strassenPaddedBlock ( ax + halfAw, ay + halfAh, halfAw, halfAh, aw - halfAw, ah - halfAh) ,
b11 = b.submatrix ( bx, by, halfBw, halfBh) ,
b21 = b.strassenPaddedBlock ( bx, by + halfBh, halfBw, halfBh, halfBw, bh - halfBh) ,
b12 = b.strassenPaddedBlock ( bx + halfBw, by, halfBw, halfBh, bw - halfBw, halfBh) ,
b22 = b.strassenPaddedBlock ( bx + halfBw, by + halfBh, halfBw, halfBh, bw - halfBw, bh - halfBh) ,
m1 = a11.plus ( a22) .multiplyStrassen_StraightVer ( b11.plus ( b22) ) ,
m2 = a21.plus ( a22) .multiplyStrassen_StraightVer ( b11) ,
m3 = a11.multiplyStrassen_StraightVer ( b12.minus ( b22) ) ,
m4 = a22.multiplyStrassen_StraightVer ( b21.minus ( b11) ) ,
m5 = a11.plus ( a12) .multiplyStrassen_StraightVer ( b22) ,
m6 = a21.minus ( a11) .multiplyStrassen_StraightVer ( b11.plus ( b12) ) ,
m7 = a12.minus ( a22) .multiplyStrassen_StraightVer ( b21.plus ( b22) ) ,
c11 = m1.plus ( m4) .minus ( m5) .plus ( m7) ,
c12 = m3.plus ( m5) ,
c21 = m2.plus ( m4) ,
c22 = m1.minus ( m2) .plus ( m3) .plus ( m6) ;
moveSubmatrix( c11, 0 , 0 , r, rx, ry, halfRw, halfRh) ;
moveSubmatrix( c21, 0 , 0 , r, rx, ry + halfRh, halfRw, rh - halfRh) ;
moveSubmatrix( c12, 0 , 0 , r, rx + halfRw, ry, rw - halfRw, halfRh) ;
moveSubmatrix( c22, 0 , 0 , r, rx + halfRw, ry + halfRh, rw - halfRw, rh - halfRh) ;
}
public Matrix multiplyStrassen_StraightVer( Matrix b) {
Matrix r = new Matrix( b.w , h) ;
multiplyStrassen_StraightVer( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ;
return r;
}
private static class MatrixView {
public Matrix m;
public int x, y, w, h;
public MatrixView( int w, int h) {
this ( new Matrix( w, h) , 0 , 0 , w, h) ;
}
public MatrixView( Matrix m) {
this ( m, 0 , 0 , m.w , m.h ) ;
}
public MatrixView( Matrix m, int x, int y, int w, int h) {
this .m = m;
this .x = x;
this .y = y;
this .w = w;
this .h = h;
}
public void add( MatrixView b, MatrixView r) {
Matrix.add ( m, x, y, w, h, b.m , b.x , b.y , b.w , b.h , r.m , r.x , r.y , r.w , r.h ) ;
}
public MatrixView plus( MatrixView b) {
MatrixView r = new MatrixView( w, h) ;
add( b, r) ;
return r;
}
public void subtract( MatrixView b, MatrixView r) {
Matrix.subtract ( m, x, y, w, h, b.m , b.x , b.y , b.w , b.h , r.m , r.x , r.y , r.w , r.h ) ;
}
public MatrixView minus( MatrixView b) {
MatrixView r = new MatrixView( w, h) ;
subtract( b, r) ;
return r;
}
public void multiplyStrassen_ViewVer( MatrixView b, MatrixView r) {
Matrix.multiplyStrassen_ViewVer ( m, x, y, w, h, b.m , b.x , b.y , b.w , b.h , r.m , r.x , r.y , r.w , r.h ) ;
}
public MatrixView multiplyStrassen_ViewVer( MatrixView b) {
MatrixView r = new MatrixView( b.w , h) ;
multiplyStrassen_ViewVer( b, r) ;
return r;
}
public void multiplyStrassen_Pow2ViewVer( MatrixView b, MatrixView r) {
Matrix.multiplyStrassen_Pow2ViewVer ( m, x, y, w, h, b.m , b.x , b.y , b.w , b.h , r.m , r.x , r.y , r.w , r.h ) ;
}
public MatrixView multiplyStrassen_Pow2ViewVer( MatrixView b) {
MatrixView r = new MatrixView( b.w , h) ;
multiplyStrassen_Pow2ViewVer( b, r) ;
return r;
}
}
private MatrixView strassenPaddedBlockView( int x, int y, int w, int h) {
return x + w <= this .w && y + h <= this .h ?
new MatrixView( this , x, y, w, h) :
new MatrixView( this .strassenPaddedBlock (
x, y, w, h,
Math .
min ( w,
this .
w - x
) ,
Math .
min ( h,
this .
h - y
) ) ) ; }
private MatrixView strassenTargetBlockView( int x, int y, int w, int h) {
return x + w <= this .w && y + h <= this .h ?
new MatrixView( this , x, y, w, h) :
new MatrixView( w, h) ;
}
public static void multiplyStrassen_ViewVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
if ( shouldResortToPlainMultiply( bw, ah, aw) ) {
multiplyPlain( a, ax, ay, aw, ah, b, bx, by, bw, bh, r, rx, ry, rw, rh) ;
return ;
}
validateSubmatrix( a.w , a.h , ax, ay, aw, ah) ;
validateSubmatrix( b.w , b.h , bx, by, bw, bh) ;
validateSubmatrix( r.w , r.h , rx, ry, rw, rh) ;
validateMultiply( aw, ah, bw, bh, rw, rh) ;
int halfAw = ( aw + 1 ) / 2 , halfAh = ( ah + 1 ) / 2 ,
halfBw = ( bw + 1 ) / 2 , halfBh = ( bh + 1 ) / 2 ,
halfRw = ( rw + 1 ) / 2 , halfRh = ( rh + 1 ) / 2 ;
MatrixView
a11 = a.strassenPaddedBlockView ( ax, ay, halfAw, halfAh) ,
a21 = a.strassenPaddedBlockView ( ax, ay + halfAh, halfAw, halfAh) ,
a12 = a.strassenPaddedBlockView ( ax + halfAw, ay, halfAw, halfAh) ,
a22 = a.strassenPaddedBlockView ( ax + halfAw, ay + halfAh, halfAw, halfAh) ,
b11 = b.strassenPaddedBlockView ( bx, by, halfBw, halfBh) ,
b21 = b.strassenPaddedBlockView ( bx, by + halfBh, halfBw, halfBh) ,
b12 = b.strassenPaddedBlockView ( bx + halfBw, by, halfBw, halfBh) ,
b22 = b.strassenPaddedBlockView ( bx + halfBw, by + halfBh, halfBw, halfBh) ,
m1 = a11.plus ( a22) .multiplyStrassen_ViewVer ( b11.plus ( b22) ) ,
m2 = a21.plus ( a22) .multiplyStrassen_ViewVer ( b11) ,
m3 = a11.multiplyStrassen_ViewVer ( b12.minus ( b22) ) ,
m4 = a22.multiplyStrassen_ViewVer ( b21.minus ( b11) ) ,
m5 = a11.plus ( a12) .multiplyStrassen_ViewVer ( b22) ,
m6 = a21.minus ( a11) .multiplyStrassen_ViewVer ( b11.plus ( b12) ) ,
m7 = a12.minus ( a22) .multiplyStrassen_ViewVer ( b21.plus ( b22) ) ,
c11 = new MatrixView( r, rx, ry, halfRw, halfRh) ,
c21 = r.strassenTargetBlockView ( rx, ry + halfRh, halfRw, halfRh) ,
c12 = r.strassenTargetBlockView ( rx + halfRw, ry, halfRw, halfRh) ,
c22 = r.strassenTargetBlockView ( rx + halfRw, ry + halfRh, halfRw, halfRh) ;
m1.add ( m4, c11) ; c11.subtract ( m5, c11) ; c11.add ( m7, c11) ;
m3.add ( m5, c12) ;
m2.add ( m4, c21) ;
m1.subtract ( m2, c22) ; c22.add ( m3, c22) ; c22.add ( m6, c22) ;
if ( c21.m != r) moveSubmatrix( c21.m , 0 , 0 , r, rx, ry + halfRh, halfRw, rh - halfRh) ;
if ( c12.m != r) moveSubmatrix( c12.m , 0 , 0 , r, rx + halfRw, ry, rw - halfRw, halfRh) ;
if ( c22.m != r) moveSubmatrix( c22.m , 0 , 0 , r, rx + halfRw, ry + halfRh, rw - halfRw, rh - halfRh) ;
}
public Matrix multiplyStrassen_ViewVer( Matrix b) {
Matrix r = new Matrix( b.w , h) ;
multiplyStrassen_ViewVer( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ;
return r;
}
static void trustedMultiplyStrassen_Pow2UnrolledVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
if ( shouldResortToPlainMultiply( bw, ah, aw) ) {
multiplyPlain( a, ax, ay, aw, ah, b, bx, by, bw, bh, r, rx, ry, rw, rh) ;
return ;
}
int halfAw = aw / 2 , halfAh = ah / 2 ,
halfBw = bw / 2 , halfBh = bh / 2 ,
halfRw = rw / 2 , halfRh = rh / 2 ;
Matrix
tsuma = new Matrix( halfAw, halfAh) ,
tsumb = new Matrix( halfBw, halfBh) ,
m1 = new Matrix( halfRw, halfRh) ,
m2 = new Matrix( halfRw, halfRh) ,
m3 = new Matrix( halfRw, halfRh) ,
m4 = new Matrix( halfRw, halfRh) ,
m5 = new Matrix( halfRw, halfRh) ,
m6 = new Matrix( halfRw, halfRh) ,
m7 = new Matrix( halfRw, halfRh) ;
// tsuma = a11 + a22, tsumb = b11 + b22, m1 = (a11 + a22) * (b11 + b22)
trustedAdd(
a, ax, ay,
a, ax + halfAw, ay + halfAh,
tsuma, 0 , 0 , halfAw, halfAh) ;
trustedAdd(
b, bx, by,
b, bx + halfBw, by + halfBh,
tsumb, 0 , 0 , halfBw, halfBh) ;
trustedMultiplyStrassen_Pow2UnrolledVer(
tsuma, 0 , 0 , halfAw, halfAh,
tsumb, 0 , 0 , halfBw, halfBh,
m1, 0 , 0 , halfRw, halfRh) ;
// tsuma = a21 + a22, m2 = (a21 + a22) * b11
trustedAdd(
a, ax, ay + halfAh,
a, ax + halfAw, ay + halfAh,
tsuma, 0 , 0 , halfAw, halfAh) ;
trustedMultiplyStrassen_Pow2UnrolledVer(
tsuma, 0 , 0 , halfAw, halfAh,
b, bx, by, halfBw, halfBh,
m2, 0 , 0 , halfRw, halfRh) ;
// tsumb = b12 - b22, m3 = a11 * (b12 - b22)
trustedSubtract(
b, bx + halfBw, by,
b, bx + halfBw, by + halfBh,
tsumb, 0 , 0 , halfBw, halfBh) ;
trustedMultiplyStrassen_Pow2UnrolledVer(
a, ax, ay, halfAw, halfAh,
tsumb, 0 , 0 , halfBw, halfBh,
m3, 0 , 0 , halfRw, halfRh) ;
// tsumb = b21 - b11, m4 = a22 * (b21 - b11)
trustedSubtract(
b, bx, by + halfBh,
b, bx, by,
tsumb, 0 , 0 , halfBw, halfBh) ;
trustedMultiplyStrassen_Pow2UnrolledVer(
a, ax + halfAw, ay + halfAh, halfAw, halfAh,
tsumb, 0 , 0 , halfBw, halfBh,
m4, 0 , 0 , halfRw, halfRh) ;
// tsuma = a11 + a12, m5 = (a11 + a12) * b22
trustedAdd(
a, ax, ay,
a, ax + halfAw, ay,
tsuma, 0 , 0 , halfAw, halfAh) ;
trustedMultiplyStrassen_Pow2UnrolledVer(
tsuma, 0 , 0 , halfAw, halfAh,
b, bx + halfBw, by + halfBh, halfBw, halfBh,
m5, 0 , 0 , halfRw, halfRh) ;
// tsuma = a21 - a11, tsumb = b11 + b12, m6 = (a21 - a11) * (b11 + b12)
trustedSubtract(
a, ax, ay + halfAh,
a, ax, ay,
tsuma, 0 , 0 , halfAw, halfAh) ;
trustedAdd(
b, bx, by,
b, bx + halfBw, by,
tsumb, 0 , 0 , halfBw, halfBh) ;
trustedMultiplyStrassen_Pow2UnrolledVer(
tsuma, 0 , 0 , halfAw, halfAh,
tsumb, 0 , 0 , halfBw, halfBh,
m6, 0 , 0 , halfRw, halfRh) ;
// tsuma = a12 - a22, tsumb = b21 + b22, m7 = (a12 - a22) * (b21 + b22)
trustedSubtract(
a, ax + halfAw, ay,
a, ax + halfAw, ay + halfAh,
tsuma, 0 , 0 , halfAw, halfAh) ;
trustedAdd(
b, bx, by + halfBh,
b, bx + halfBw, by + halfBh,
tsumb, 0 , 0 , halfBw, halfBh) ;
trustedMultiplyStrassen_Pow2UnrolledVer(
tsuma, 0 , 0 , halfAw, halfAh,
tsumb, 0 , 0 , halfBw, halfBh,
m7, 0 , 0 , halfRw, halfRh) ;
// c11 = m1 + m4 - m5 + m7
trustedAdd(
m1, 0 , 0 ,
m4, 0 , 0 ,
r, rx, ry, halfRw, halfRh) ;
trustedSubtract(
r, rx, ry,
m5, 0 , 0 ,
r, rx, ry, halfRw, halfRh) ;
trustedAdd(
r, rx, ry,
m7, 0 , 0 ,
r, rx, ry, halfRw, halfRh) ;
// c12 = m3 + m5
trustedAdd(
m3, 0 , 0 ,
m5, 0 , 0 ,
r, rx + halfRw, ry, halfRw, halfRh) ;
// c21 = m2 + m4
trustedAdd(
m2, 0 , 0 ,
m4, 0 , 0 ,
r, rx, ry + halfRh, halfRw, halfRh) ;
// c22 = m1 - m2 + m3 + m6
trustedSubtract(
m1, 0 , 0 ,
m2, 0 , 0 ,
r, rx + halfRw, ry + halfRh, halfRw, halfRh) ;
trustedAdd(
r, rx + halfRw, ry + halfRh,
m3, 0 , 0 ,
r, rx + halfRw, ry + halfRh, halfRw, halfRh) ;
trustedAdd(
r, rx + halfRw, ry + halfRh,
m6, 0 , 0 ,
r, rx + halfRw, ry + halfRh, halfRw, halfRh) ;
}
@FunctionalInterface
interface GenericMultiply {
void multiply(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) ;
}
static boolean isPow2( int x) {
if ( x <= 0 ) Throw .badArg ( "isPow2 requires positive value, got %d." , x) ;
return ( x & ( x - 1 ) ) == 0 ;
}
static int ceilPow2( int x) {
if ( x <= 0 ) Throw .badArg ( "ceilPow2 requires positive value, got %d." , x) ;
x -= 1 ;
for ( int shlp = 0 ; shlp <= 4 ; shlp++ )
x |= x >>> ( 1 << shlp) ;
return x + 1 ;
}
public static void adaptTrustedMultiplyPow2(
GenericMultiply multiplyPow2,
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
validateSubmatrix( a.w , a.h , ax, ay, aw, ah) ;
validateSubmatrix( b.w , b.h , bx, by, bw, bh) ;
validateSubmatrix( r.w , r.h , rx, ry, rw, rh) ;
validateMultiply( aw, ah, bw, bh, rw, rh) ;
boolean altered = false ;
if ( ! isPow2( aw) || ! isPow2( ah) ) {
Matrix na = new Matrix( ceilPow2( aw) , ceilPow2( ah) ) ;
moveSubmatrix( a, ax, ay, na, 0 , 0 , aw, ah) ;
a = na; ax = 0 ; ay = 0 ; aw = na.w ; ah = na.h ; altered = true ;
}
if ( ! isPow2( bw) || ! isPow2( bh) ) {
Matrix nb = new Matrix( ceilPow2( bw) , ceilPow2( bh) ) ;
moveSubmatrix( b, bx, by, nb, 0 , 0 , bw, bh) ;
b = nb; bx = 0 ; by = 0 ; bw = nb.w ; bh = nb.h ; altered = true ;
}
int origRx = rx, origRy = ry, origRw = rw, origRh = rh;
Matrix origR = r;
if ( altered) {
r = new Matrix( bw, ah) ;
rx = 0 ; ry = 0 ; rw = r.w ; rh = r.h ;
}
multiplyPow2.multiply ( a, ax, ay, aw, ah, b, bx, by, bw, bh, r, rx, ry, rw, rh) ;
if ( altered) {
moveSubmatrix( r, 0 , 0 , origR, origRx, origRy, origRw, origRh) ;
}
}
public static void multiplyStrassen_Pow2UnrolledVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
adaptTrustedMultiplyPow2( Matrix:: trustedMultiplyStrassen_Pow2UnrolledVer,
a, ax, ay, aw, ah, b, bx, by, bw, bh, r, rx, ry, rw, rh) ;
}
public Matrix multiplyStrassen_Pow2UnrolledVer( Matrix b) {
Matrix r = new Matrix( b.w , h) ;
multiplyStrassen_Pow2UnrolledVer( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ;
return r;
}
static void trustedMultiplyStrassen_Pow2ViewVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
if ( shouldResortToPlainMultiply( bw, ah, aw) ) {
multiplyPlain( a, ax, ay, aw, ah, b, bx, by, bw, bh, r, rx, ry, rw, rh) ;
return ;
}
int halfAw = aw / 2 , halfAh = ah / 2 ,
halfBw = bw / 2 , halfBh = bh / 2 ,
halfRw = rw / 2 , halfRh = rh / 2 ;
MatrixView
a11 = new MatrixView( a, ax, ay, halfAw, halfAh) ,
a21 = new MatrixView( a, ax, ay + halfAh, halfAw, halfAh) ,
a12 = new MatrixView( a, ax + halfAw, ay, halfAw, halfAh) ,
a22 = new MatrixView( a, ax + halfAw, ay + halfAh, halfAw, halfAh) ,
b11 = new MatrixView( b, bx, by, halfBw, halfBh) ,
b21 = new MatrixView( b, bx, by + halfBh, halfBw, halfBh) ,
b12 = new MatrixView( b, bx + halfBw, by, halfBw, halfBh) ,
b22 = new MatrixView( b, bx + halfBw, by + halfBh, halfBw, halfBh) ,
m1 = a11.plus ( a22) .multiplyStrassen_Pow2ViewVer ( b11.plus ( b22) ) ,
m2 = a21.plus ( a22) .multiplyStrassen_Pow2ViewVer ( b11) ,
m3 = a11.multiplyStrassen_Pow2ViewVer ( b12.minus ( b22) ) ,
m4 = a22.multiplyStrassen_Pow2ViewVer ( b21.minus ( b11) ) ,
m5 = a11.plus ( a12) .multiplyStrassen_Pow2ViewVer ( b22) ,
m6 = a21.minus ( a11) .multiplyStrassen_Pow2ViewVer ( b11.plus ( b12) ) ,
m7 = a12.minus ( a22) .multiplyStrassen_Pow2ViewVer ( b21.plus ( b22) ) ,
c11 = new MatrixView( r, rx, ry, halfRw, halfRh) ,
c21 = new MatrixView( r, rx, ry + halfRh, halfRw, halfRh) ,
c12 = new MatrixView( r, rx + halfRw, ry, halfRw, halfRh) ,
c22 = new MatrixView( r, rx + halfRw, ry + halfRh, halfRw, halfRh) ;
m1.add ( m4, c11) ; c11.subtract ( m5, c11) ; c11.add ( m7, c11) ;
m3.add ( m5, c12) ;
m2.add ( m4, c21) ;
m1.subtract ( m2, c22) ; c22.add ( m3, c22) ; c22.add ( m6, c22) ;
}
public static void multiplyStrassen_Pow2ViewVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r, int rx, int ry, int rw, int rh) {
adaptTrustedMultiplyPow2( Matrix:: trustedMultiplyStrassen_Pow2ViewVer,
a, ax, ay, aw, ah, b, bx, by, bw, bh, r, rx, ry, rw, rh) ;
}
public Matrix multiplyStrassen_Pow2ViewVer( Matrix b) {
Matrix r = new Matrix( b.w , h) ;
multiplyStrassen_Pow2ViewVer( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ;
return r;
}
@Override
public boolean equals
( Object other
) { if ( this == other) return true ;
if ( this .getClass ( ) != other.getClass ( ) ) return false ;
Matrix om = ( Matrix) other;
return w
== om.
w && h
== om.
h && Arrays .
equals ( raw, om.
raw ) ; }
}
class Ideone {
static final int FILLER = 9 ;
static void testAddSubtract( ) {
Matrix a = Matrix.explicit ( 4 , 2 ,
1 , 2 , 3 , 4 ,
5 , 6 , 7 , 8 ) ;
Matrix b = Matrix.explicit ( 4 , 2 ,
9 , 11 , 13 , 15 ,
17 , 19 , 21 , 23 ) ;
Matrix expectedSum = Matrix.explicit ( 4 , 2 ,
10 , 13 , 16 , 19 ,
22 , 25 , 28 , 31 ) ;
Matrix expectedDif = Matrix.explicit ( 4 , 2 ,
- 8 , - 9 , - 10 , - 11 ,
- 12 , - 13 , - 14 , - 15 ) ;
final int F = FILLER;
expectEqual( "Matrix.expand" , a.expand ( FILLER, 1 , 4 , 2 , 3 ) ,
Matrix.explicit ( 9 , 7 ,
F, F, F, F, F, F, F, F, F,
F, F, F, F, F, F, F, F, F,
F, 1 , 2 , 3 , 4 , F, F, F, F,
F, 5 , 6 , 7 , 8 , F, F, F, F,
F, F, F, F, F, F, F, F, F,
F, F, F, F, F, F, F, F, F,
F, F, F, F, F, F, F, F, F) ) ;
Matrix result = new Matrix( expectedSum.w + 5 , expectedSum.h + 5 , ( x, y) -> FILLER) ;
Matrix.add (
a.expand ( FILLER, 1 , 2 , 3 , 4 ) , 1 , 3 , a.w , a.h ,
b.expand ( FILLER, 5 , 6 , 7 , 8 ) , 5 , 7 , b.w , b.h ,
result, 1 , 2 , expectedSum.w , expectedSum.h ) ;
expectEqual( "Matrix.add" , result, expectedSum.expand ( FILLER, 1 , 4 , 2 , 3 ) ) ;
result.fill ( ( x, y) -> FILLER) ;
Matrix.subtract (
a.expand ( FILLER, 1 , 2 , 3 , 4 ) , 1 , 3 , a.w , a.h ,
b.expand ( FILLER, 5 , 6 , 7 , 8 ) , 5 , 7 , b.w , b.h ,
result, 1 , 2 , expectedDif.w , expectedDif.h ) ;
expectEqual( "Matrix.subtract" , result, expectedDif.expand ( FILLER, 1 , 4 , 2 , 3 ) ) ;
expectEqual( "ceilPow2" , Matrix.ceilPow2 ( 1 ) , 1 ) ;
expectEqual( "ceilPow2" , Matrix.ceilPow2 ( 6 ) , 8 ) ;
}
static void testPlainMultiply( ) {
// www.mathwarehouse.com/algebra/matrix/images/product-matrix2.png
Matrix a = Matrix.explicit ( 4 , 2 ,
1 , 4 , 6 , 10 ,
2 , 7 , 5 , 3 ) ;
Matrix b = Matrix.explicit ( 3 , 4 ,
1 , 4 , 6 ,
2 , 7 , 5 ,
9 , 0 , 11 ,
3 , 1 , 0 ) ;
Matrix expected = Matrix.explicit ( 3 , 2 ,
93 , 42 , 92 ,
70 , 60 , 102 ) ;
Matrix result = new Matrix( expected.w + 5 , expected.h + 5 , ( x, y) -> FILLER) ;
Matrix.multiplyPlain (
a.expand ( FILLER, 1 , 2 , 3 , 4 ) , 1 , 3 , a.w , a.h ,
b.expand ( FILLER, 5 , 6 , 7 , 8 ) , 5 , 7 , b.w , b.h ,
result, 1 , 2 , expected.w , expected.h ) ;
expectEqual( "Matrix.multiplyPlain" , result, expected.expand ( FILLER, 1 , 4 , 2 , 3 ) ) ;
}
static void testStrassenMultiply( ) {
Matrix a = new Matrix( 97 , 71 , ( x, y) -> - 10 + rng.nextInt ( 21 ) ) ;
Matrix b = new Matrix( 91 , 97 , ( x, y) -> - 10 + rng.nextInt ( 21 ) ) ;
Matrix abProdRef = a.multiplyPlain ( b) ;
expectEqual( "Matrix.multiplyStrassen_StraightVer" , a.multiplyStrassen_StraightVer ( b) , abProdRef) ;
expectEqual( "Matrix.multiplyStrassen_ViewVer" , a.multiplyStrassen_ViewVer ( b) , abProdRef) ;
expectEqual( "Matrix.multiplyStrassen_Pow2UnrolledVer" , a.multiplyStrassen_Pow2UnrolledVer ( b) , abProdRef) ;
expectEqual( "Matrix.multiplyStrassen_Pow2ViewVer" , a.multiplyStrassen_Pow2ViewVer ( b) , abProdRef) ;
}
if ( ! a.equals ( b) )
}
static void benchmark( ) {
final int TRIALS = 2 ;
for ( int trial = 0 ; trial < TRIALS; trial++ ) {
trial > 0 ? "\n " : "" , 1 + trial, TRIALS, trial == 0 ? " — HEATING UP" : "" ) ) ;
final int AW = 850 , AH = 650 , BW = 750 ;
// final int AW = 512, AH = 1024, BW = 768;
Matrix a = new Matrix( AW, AH) ;
Matrix b = new Matrix( BW, AW) ;
Matrix r = new Matrix( BW, AH) ;
double refTime = benchmark( "Straightforward O(N^3) multiply" ,
( ) -> { Matrix.multiplyPlain ( a, 0 , 0 , a.w , a.h , b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ; return r; } ) ;
benchmark( "Strassen multiply with many intermediate matrix allocations" ,
( ) -> { Matrix.multiplyStrassen_StraightVer ( a, 0 , 0 , a.w , a.h , b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ; return r; } , refTime) ;
benchmark( "Strassen multiply with submatrix views" ,
( ) -> { Matrix.multiplyStrassen_ViewVer ( a, 0 , 0 , a.w , a.h , b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ; return r; } , refTime) ;
benchmark( "Strassen multiply with forceful 2^n size and unrolled calculations" ,
( ) -> { Matrix.multiplyStrassen_Pow2UnrolledVer ( a, 0 , 0 , a.w , a.h , b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ; return r; } , refTime) ;
benchmark( "Strassen multiply with forceful 2^n size and submatrix views" ,
( ) -> { Matrix.multiplyStrassen_Pow2ViewVer ( a, 0 , 0 , a.w , a.h , b, 0 , 0 , b.w , b.h , r, 0 , 0 , r.w , r.h ) ; return r; } , refTime) ;
}
}
@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.1 , relThreshold = .05;
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
) ? String .
format ( "%.0f%% %s" ,
Math .
abs ( time
/ refTime
- 1 ) * 100 , time
< refTime
? "faster" : "slower" ) : String .
format ( "no observable difference, min. %.0f%% required" , relThreshold
* 100 ) ; }
public static void main
( String [ ] args
) { try {
testAddSubtract( ) ;
testPlainMultiply( ) ;
testStrassenMultiply( ) ;
benchmark( ) ;
System .
out .
println ( sw.
toString ( ) ) ; }
}
}
aW1wb3J0IGphdmEudXRpbC4qOwppbXBvcnQgamF2YS5sYW5nLio7CmltcG9ydCBqYXZhLmlvLio7CgpjbGFzcyBUaHJvdyB7CglwdWJsaWMgc3RhdGljIHZvaWQgYmFkQXJnKFN0cmluZyBmbXQsIE9iamVjdC4uLiBhcmdzKSB7CgkJdGhyb3cgbmV3IElsbGVnYWxBcmd1bWVudEV4Y2VwdGlvbihTdHJpbmcuZm9ybWF0KGZtdCwgYXJncykpOwoJfQp9CgpjbGFzcyBNYXRyaXggewoJcHVibGljIGZpbmFsIGludCB3LCBoOwoJcHVibGljIGZpbmFsIGludFtdIHJhdzsKCglwdWJsaWMgTWF0cml4KGludCB3LCBpbnQgaCkgewoJCXZhbGlkYXRlU2l6ZSh3LCBoKTsKCQl0aGlzLncgPSB3OwoJCXRoaXMuaCA9IGg7CgkJcmF3ID0gbmV3IGludFt3ICogaF07Cgl9CgoJcHVibGljIHN0YXRpYyBNYXRyaXggZXhwbGljaXQoaW50IHcsIGludCBoLCBpbnQuLi4gY2VsbHMpIHsKCQlpZiAoY2VsbHMubGVuZ3RoICE9IHcgKiBoKSBUaHJvdy5iYWRBcmcoIkV4cGVjdGVkICVkIHZhbHVlcyBvZiAlZMOXJWQgbWF0cml4LCBnb3QgJWQuIiwgdypoLCB3LCBoLCBjZWxscy5sZW5ndGgpOwoJCU1hdHJpeCByID0gbmV3IE1hdHJpeCh3LCBoKTsKCQlTeXN0ZW0uYXJyYXljb3B5KGNlbGxzLCAwLCByLnJhdywgMCwgY2VsbHMubGVuZ3RoKTsKCQlyZXR1cm4gcjsKCX0KCglARnVuY3Rpb25hbEludGVyZmFjZQoJcHVibGljIGludGVyZmFjZSBWYWx1ZUdldHRlciB7CgkJaW50IGdldFZhbHVlKGludCB4LCBpbnQgeSk7Cgl9CgoJcHVibGljIE1hdHJpeChpbnQgdywgaW50IGgsIFZhbHVlR2V0dGVyIGdldFZhbHVlKSB7CgkJdGhpcyh3LCBoKTsKCQlmaWxsKGdldFZhbHVlKTsKCX0KCglwdWJsaWMgU3RyaW5nIHRvU3RyaW5nKCkgewoJCVN0cmluZ1tdIHJlcHJzID0gbmV3IFN0cmluZ1tyYXcubGVuZ3RoXTsKCQlpbnQgbWF4TGVuID0gMDsKCQlmb3IgKGludCBpID0gMDsgaSA8IHJlcHJzLmxlbmd0aDsgaSsrKSB7CgkJCXJlcHJzW2ldID0gSW50ZWdlci50b1N0cmluZyhyYXdbaV0pOwoJCQltYXhMZW4gPSBNYXRoLm1heChtYXhMZW4sIHJlcHJzW2ldLmxlbmd0aCgpKTsKCQl9CgkJU3RyaW5nIGZtdCA9ICIlIiArIG1heExlbiArICJzIjsKCgkJU3RyaW5nQnVpbGRlciByc2IgPSBuZXcgU3RyaW5nQnVpbGRlcigpOwoJCWZvciAoaW50IHkgPSAwLCBhaSA9IDA7IHkgPCBoOyB5KyspCgkJCWZvciAoaW50IHggPSAwOyB4IDwgdzsgeCsrLCBhaSsrKQoJCQkJcnNiLmFwcGVuZChTdHJpbmcuZm9ybWF0KGZtdCwgcmVwcnNbYWldKSkuYXBwZW5kKHggKyAxIDwgdyA/ICIgIiA6IHkgKyAxIDwgaCA/ICJcbiIgOiAiIik7CgkJcmV0dXJuIHJzYi50b1N0cmluZygpOwoJfQoKCXB1YmxpYyB2b2lkIGZpbGwoVmFsdWVHZXR0ZXIgZ2V0VmFsdWUpCgl7CgkJZm9yIChpbnQgeSA9IDAsIGFpID0gMDsgeSA8IGg7IHkrKykKCQkJZm9yIChpbnQgeCA9IDA7IHggPCB3OyB4KyssIGFpKyspCgkJCQlyYXdbYWldID0gZ2V0VmFsdWUuZ2V0VmFsdWUoeCwgeSk7Cgl9CgoJcHJpdmF0ZSBzdGF0aWMgdm9pZCBtb3ZlU3VibWF0cml4KE1hdHJpeCBhLCBpbnQgYXgsIGludCBheSwgTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgdywgaW50IGgpIHsKCQlmb3IgKGludCB5ID0gMCwgYUFpID0gYS5hYnNJZHgoYXgsIGF5KSwgYkFpID0gYi5hYnNJZHgoYngsIGJ5KTsgeSA8IGg7IHkrKywgYUFpICs9IGEudywgYkFpICs9IGIudykKCQkJU3lzdGVtLmFycmF5Y29weShhLnJhdywgYUFpLCBiLnJhdywgYkFpLCB3KTsKCX0KCglwdWJsaWMgTWF0cml4IHN1Ym1hdHJpeChpbnQgc3gsIGludCBzeSwgaW50IHN3LCBpbnQgc2gpIHsKCQl2YWxpZGF0ZVN1Ym1hdHJpeCh0aGlzLncsIHRoaXMuaCwgc3gsIHN5LCBzdywgc2gpOwoJCU1hdHJpeCByID0gbmV3IE1hdHJpeChzdywgc2gpOwoJCW1vdmVTdWJtYXRyaXgodGhpcywgc3gsIHN5LCByLCAwLCAwLCBzdywgc2gpOwoJCXJldHVybiByOwoJfQoKCXB1YmxpYyBNYXRyaXggZXhwYW5kKGludCBmaWxsZXIsIGludCBsZWZ0LCBpbnQgcmlnaHQsIGludCB0b3AsIGludCBib3R0b20pIHsKCQlpZiAobGVmdCA8IDAgfHwgcmlnaHQgPCAwIHx8IHRvcCA8IDAgfHwgYm90dG9tIDwgMCkKCQkJVGhyb3cuYmFkQXJnKCJCYWQgZXhwYW5kKCk6ICglZCwgJWQsICVkLCAlZCkuIiwgbGVmdCwgcmlnaHQsIHRvcCwgYm90dG9tKTsKCgkJTWF0cml4IHIgPSBuZXcgTWF0cml4KHcgKyBsZWZ0ICsgcmlnaHQsIGggKyB0b3AgKyBib3R0b20pOwoJCWZvciAoaW50IHkgPSAwLCByQWkgPSAwLCB0aGlzQWkgPSAwOyB5IDwgci5oOyB5KyspCgkJCWZvciAoaW50IHggPSAwOyB4IDwgci53OyB4KyssIHJBaSsrKQoJCQkJci5yYXdbckFpXSA9IHggPCBsZWZ0IHx8IHggPj0gbGVmdCArIHcgfHwgeSA8IHRvcCB8fCB5ID49IHRvcCArIGggPyBmaWxsZXIgOiByYXdbdGhpc0FpKytdOwoJCXJldHVybiByOwoJfQoKCWludCBhYnNJZHgoaW50IHgsIGludCB5KSB7CgkJcmV0dXJuIHkgKiB3ICsgeDsKCX0KCglwdWJsaWMgc3RhdGljIHZvaWQgdmFsaWRhdGVTaXplKGludCB3LCBpbnQgaCkgewoJCWlmICh3IDw9IDAgfHwgaCA8PSAwKSBUaHJvdy5iYWRBcmcoIkJhZCBtYXRyaXggc2l6ZTogJWTDlyVkLiIsIHcsIGgpOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCB2YWxpZGF0ZVN1Ym1hdHJpeChpbnQgdGhpc3csIGludCB0aGlzaCwgaW50IHgsIGludCB5LCBpbnQgdywgaW50IGgpIHsKCQl2YWxpZGF0ZVNpemUodywgaCk7CgkJaWYgKHggPCAwIHx8IHkgPCAwIHx8IHggKyB3ID4gdGhpc3cgfHwgeSArIGggPiB0aGlzaCkKCQkJVGhyb3cuYmFkQXJnKCJCYWQgc3VibWF0cml4IG9mICVkw5clZDogKCVkLCAlZCkgKyAlZMOXJWQuIiwgdGhpc3csIHRoaXNoLCB4LCB5LCB3LCBoKTsKCX0KCglwdWJsaWMgc3RhdGljIHZvaWQgdmFsaWRhdGVBZGRMaWtlKGludCBhdywgaW50IGFoLCBpbnQgYncsIGludCBiaCwgaW50IHJ3LCBpbnQgcmgsIFN0cmluZyBvcCkgewoJCWlmIChhdyAhPSBidyB8fCBhaCAhPSBiaCkKCQkJVGhyb3cuYmFkQXJnKCJCYWQgbWF0cmljZXMgZm9yICVzOiAlZMOXJWQgYW5kICVkw5clZCwgc2l6ZSBtdXN0IGJlIGVxdWFsLiIsIG9wLCBhdywgYWgsIGJ3LCBiaCk7CgkJaWYgKHJ3ICE9IGF3IHx8IHJoICE9IGFoKQoJCQlUaHJvdy5iYWRBcmcoIkJhZCAlcyByZXN1bHQgc2l6ZTogJWTDlyVkLCBleHBlY3RlZCAlZMOXJWQuIiwgb3AsIHJ3LCByaCwgYXcsIGFoKTsKCX0KCglwdWJsaWMgc3RhdGljIHZvaWQgYWRkKAoJCU1hdHJpeCBhLCBpbnQgYXgsIGludCBheSwgaW50IGF3LCBpbnQgYWgsCgkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgYncsIGludCBiaCwKCQlNYXRyaXggciwgaW50IHJ4LCBpbnQgcnksIGludCBydywgaW50IHJoKSB7CgoJCXZhbGlkYXRlU3VibWF0cml4KGEudywgYS5oLCBheCwgYXksIGF3LCBhaCk7CgkJdmFsaWRhdGVTdWJtYXRyaXgoYi53LCBiLmgsIGJ4LCBieSwgYncsIGJoKTsKCQl2YWxpZGF0ZVN1Ym1hdHJpeChyLncsIHIuaCwgcngsIHJ5LCBydywgcmgpOwoJCXZhbGlkYXRlQWRkTGlrZShhdywgYWgsIGJ3LCBiaCwgcncsIHJoLCAiYWRkaXRpb24iKTsKCQl0cnVzdGVkQWRkKGEsIGF4LCBheSwgYiwgYngsIGJ5LCByLCByeCwgcnksIHJ3LCByaCk7Cgl9CgoJc3RhdGljIHZvaWQgdHJ1c3RlZEFkZCgKCQlNYXRyaXggYSwgaW50IGF4LCBpbnQgYXksCgkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LAoJCU1hdHJpeCByLCBpbnQgcngsIGludCByeSwgaW50IHcsIGludCBoKSB7CgoJCWludCBhQWkgPSBhLmFic0lkeChheCwgYXkpLCBiQWkgPSBiLmFic0lkeChieCwgYnkpLCByQWkgPSByLmFic0lkeChyeCwgcnkpOwoJCWZvciAoaW50IHkgPSAwOyB5IDwgaDsgeSsrLCBhQWkgKz0gYS53IC0gdywgYkFpICs9IGIudyAtIHcsIHJBaSArPSByLncgLSB3KQoJCQlmb3IgKGludCB4ID0gMDsgeCA8IHc7IHgrKywgYUFpKyssIGJBaSsrLCByQWkrKykKCQkJCXIucmF3W3JBaV0gPSBhLnJhd1thQWldICsgYi5yYXdbYkFpXTsKCX0KCglwdWJsaWMgTWF0cml4IHBsdXMoTWF0cml4IGIpIHsKCQlNYXRyaXggciA9IG5ldyBNYXRyaXgodywgaCk7CgkJYWRkKHRoaXMsIDAsIDAsIHcsIGgsIGIsIDAsIDAsIGIudywgYi5oLCByLCAwLCAwLCByLncsIHIuaCk7CgkJcmV0dXJuIHI7Cgl9CgoJLy8gTXV0YXRpcyBtdXRhbmRpcyBvZiBhZGQoKQoJcHVibGljIHN0YXRpYyB2b2lkIHN1YnRyYWN0KAoJCU1hdHJpeCBhLCBpbnQgYXgsIGludCBheSwgaW50IGF3LCBpbnQgYWgsCgkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgYncsIGludCBiaCwKCQlNYXRyaXggciwgaW50IHJ4LCBpbnQgcnksIGludCBydywgaW50IHJoKSB7CgoJCXZhbGlkYXRlU3VibWF0cml4KGEudywgYS5oLCBheCwgYXksIGF3LCBhaCk7CgkJdmFsaWRhdGVTdWJtYXRyaXgoYi53LCBiLmgsIGJ4LCBieSwgYncsIGJoKTsKCQl2YWxpZGF0ZVN1Ym1hdHJpeChyLncsIHIuaCwgcngsIHJ5LCBydywgcmgpOwoJCXZhbGlkYXRlQWRkTGlrZShhdywgYWgsIGJ3LCBiaCwgcncsIHJoLCAic3VidHJhY3Rpb24iKTsKCQl0cnVzdGVkU3VidHJhY3QoYSwgYXgsIGF5LCBiLCBieCwgYnksIHIsIHJ4LCByeSwgcncsIHJoKTsKCX0KCglzdGF0aWMgdm9pZCB0cnVzdGVkU3VidHJhY3QoCgkJTWF0cml4IGEsIGludCBheCwgaW50IGF5LAoJCU1hdHJpeCBiLCBpbnQgYngsIGludCBieSwKCQlNYXRyaXggciwgaW50IHJ4LCBpbnQgcnksIGludCB3LCBpbnQgaCkgewoKCQlpbnQgYUFpID0gYS5hYnNJZHgoYXgsIGF5KSwgYkFpID0gYi5hYnNJZHgoYngsIGJ5KSwgckFpID0gci5hYnNJZHgocngsIHJ5KTsKCQlmb3IgKGludCB5ID0gMDsgeSA8IGg7IHkrKywgYUFpICs9IGEudyAtIHcsIGJBaSArPSBiLncgLSB3LCByQWkgKz0gci53IC0gdykKCQkJZm9yIChpbnQgeCA9IDA7IHggPCB3OyB4KyssIGFBaSsrLCBiQWkrKywgckFpKyspCgkJCQlyLnJhd1tyQWldID0gYS5yYXdbYUFpXSAtIGIucmF3W2JBaV07Cgl9CgoJcHVibGljIE1hdHJpeCBtaW51cyhNYXRyaXggYikgewoJCU1hdHJpeCByID0gbmV3IE1hdHJpeCh3LCBoKTsKCQlzdWJ0cmFjdCh0aGlzLCAwLCAwLCB3LCBoLCBiLCAwLCAwLCBiLncsIGIuaCwgciwgMCwgMCwgci53LCByLmgpOwoJCXJldHVybiByOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCB2YWxpZGF0ZU11bHRpcGx5KGludCBhdywgaW50IGFoLCBpbnQgYncsIGludCBiaCwgaW50IHJ3LCBpbnQgcmgpIHsKCQlpZiAoYXcgIT0gYmgpCgkJCVRocm93LmJhZEFyZygiTWF0cmljZXMgb2YgJWTDlyVkIGFuZCAlZMOXJWQgY2Fubm90IGJlIG11bHRpcGxpZWQ6IG51bWJlciBvZiBjb2x1bW5zIG9mIHRoZSBmaXJzdCBtdXN0IG1hdGNoIHRoZSBudW1iZXIgb2Ygcm93cyBvZiB0aGUgc2Vjb25kLiIsIGF3LCBhaCwgYncsIGJoKTsKCgkJaWYgKHJ3ICE9IGJ3IHx8IHJoICE9IGFoKQoJCQlUaHJvdy5iYWRBcmcoIkJhZCByZXN1bHRpbmcgbWF0cml4IHNpemU6IGV4cGVjdGVkICVkw5clZCwgZ290ICVkw5clZC4iLCBidywgYWgsIHJ3LCByaCk7Cgl9CgoJcHVibGljIHN0YXRpYyB2b2lkIG11bHRpcGx5UGxhaW4oCgkJTWF0cml4IGEsIGludCBheCwgaW50IGF5LCBpbnQgYXcsIGludCBhaCwKCQlNYXRyaXggYiwgaW50IGJ4LCBpbnQgYnksIGludCBidywgaW50IGJoLAoJCU1hdHJpeCByLCBpbnQgcngsIGludCByeSwgaW50IHJ3LCBpbnQgcmgpIHsKCgkJdmFsaWRhdGVTdWJtYXRyaXgoYS53LCBhLmgsIGF4LCBheSwgYXcsIGFoKTsKCQl2YWxpZGF0ZVN1Ym1hdHJpeChiLncsIGIuaCwgYngsIGJ5LCBidywgYmgpOwoJCXZhbGlkYXRlU3VibWF0cml4KHIudywgci5oLCByeCwgcnksIHJ3LCByaCk7CgkJdmFsaWRhdGVNdWx0aXBseShhdywgYWgsIGJ3LCBiaCwgcncsIHJoKTsKCgkJaW50IGFSb3dBaSA9IGEuYWJzSWR4KGF4LCBheSksIGJDb2xBaSA9IGIuYWJzSWR4KGJ4LCBieSksIHJBaSA9IHIuYWJzSWR4KHJ4LCByeSk7CgkJZm9yIChpbnQgeSA9IDA7IHkgPCByaDsgeSsrLCBhUm93QWkgKz0gYS53LCBiQ29sQWkgLT0gcncsIHJBaSArPSByLncgLSBydykKCQkJZm9yIChpbnQgeCA9IDA7IHggPCBydzsgeCsrLCByQWkrKywgYkNvbEFpKyspIHsKCQkJCWludCBkb3QgPSAwOwoJCQkJZm9yIChpbnQgaSA9IDAsIGFBaSA9IGFSb3dBaSwgYkFpID0gYkNvbEFpOyBpIDwgYXc7IGkrKywgYUFpKyssIGJBaSArPSBiLncpCgkJCQkJZG90ICs9IGEucmF3W2FBaV0gKiBiLnJhd1tiQWldOwoJCQkJci5yYXdbckFpXSA9IGRvdDsKCQkJfQoJfQoKCXB1YmxpYyBNYXRyaXggbXVsdGlwbHlQbGFpbihNYXRyaXggYikgewoJCU1hdHJpeCByID0gbmV3IE1hdHJpeChiLncsIGgpOwoJCW11bHRpcGx5UGxhaW4odGhpcywgMCwgMCwgdywgaCwgYiwgMCwgMCwgYi53LCBiLmgsIHIsIDAsIDAsIHIudywgci5oKTsKCQlyZXR1cm4gcjsKCX0KCglwdWJsaWMgaW50IGdldChpbnQgeCwgaW50IHkpIHsKCQlyZXR1cm4gcmF3W3kgKiB3ICsgeF07Cgl9CgoJcHVibGljIHZvaWQgc2V0KGludCB4LCBpbnQgeSwgaW50IHZhbHVlKSB7CgkJcmF3W3kgKiB3ICsgeF0gPSB2YWx1ZTsKCX0KCglwcml2YXRlIE1hdHJpeCBzdHJhc3NlblBhZGRlZEJsb2NrKGludCBzeCwgaW50IHN5LCBpbnQgcncsIGludCByaCwgaW50IHRha2V3LCBpbnQgdGFrZWgpIHsKCQlNYXRyaXggciA9IG5ldyBNYXRyaXgocncsIHJoKTsKCQltb3ZlU3VibWF0cml4KHRoaXMsIHN4LCBzeSwgciwgMCwgMCwgdGFrZXcsIHRha2VoKTsKCQlyZXR1cm4gcjsKCX0KCglwcml2YXRlIHN0YXRpYyBib29sZWFuIHNob3VsZFJlc29ydFRvUGxhaW5NdWx0aXBseShpbnQgYncsIGludCBhaCwgaW50IGF3KSB7CgkJZmluYWwgaW50IFRIUkVTSE9MRCA9IDg7CgkJcmV0dXJuIGJ3IDw9IFRIUkVTSE9MRCB8fCBhaCA8PSBUSFJFU0hPTEQgfHwgYXcgPD0gVEhSRVNIT0xEOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCBtdWx0aXBseVN0cmFzc2VuX1N0cmFpZ2h0VmVyKAoJCU1hdHJpeCBhLCBpbnQgYXgsIGludCBheSwgaW50IGF3LCBpbnQgYWgsCgkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgYncsIGludCBiaCwKCQlNYXRyaXggciwgaW50IHJ4LCBpbnQgcnksIGludCBydywgaW50IHJoKSB7CgoJCWlmIChzaG91bGRSZXNvcnRUb1BsYWluTXVsdGlwbHkoYncsIGFoLCBhdykpIHsKCQkJbXVsdGlwbHlQbGFpbihhLCBheCwgYXksIGF3LCBhaCwgYiwgYngsIGJ5LCBidywgYmgsIHIsIHJ4LCByeSwgcncsIHJoKTsKCQkJcmV0dXJuOwoJCX0KCgkJdmFsaWRhdGVTdWJtYXRyaXgoYS53LCBhLmgsIGF4LCBheSwgYXcsIGFoKTsKCQl2YWxpZGF0ZVN1Ym1hdHJpeChiLncsIGIuaCwgYngsIGJ5LCBidywgYmgpOwoJCXZhbGlkYXRlU3VibWF0cml4KHIudywgci5oLCByeCwgcnksIHJ3LCByaCk7CgkJdmFsaWRhdGVNdWx0aXBseShhdywgYWgsIGJ3LCBiaCwgcncsIHJoKTsKCgkJaW50IGhhbGZBdyA9IChhdyArIDEpIC8gMiwgaGFsZkFoID0gKGFoICsgMSkgLyAyLAoJCQloYWxmQncgPSAoYncgKyAxKSAvIDIsIGhhbGZCaCA9IChiaCArIDEpIC8gMiwKCQkJaGFsZlJ3ID0gKHJ3ICsgMSkgLyAyLCBoYWxmUmggPSAocmggKyAxKSAvIDI7CgoJCU1hdHJpeAoJCQlhMTEgPSBhLnN1Ym1hdHJpeChheCwgYXksIGhhbGZBdywgaGFsZkFoKSwKCQkJYTIxID0gYS5zdHJhc3NlblBhZGRlZEJsb2NrKGF4LCBheSArIGhhbGZBaCwgaGFsZkF3LCBoYWxmQWgsIGhhbGZBdywgYWggLSBoYWxmQWgpLAoJCQlhMTIgPSBhLnN0cmFzc2VuUGFkZGVkQmxvY2soYXggKyBoYWxmQXcsIGF5LCBoYWxmQXcsIGhhbGZBaCwgYXcgLSBoYWxmQXcsIGhhbGZBaCksCgkJCWEyMiA9IGEuc3RyYXNzZW5QYWRkZWRCbG9jayhheCArIGhhbGZBdywgYXkgKyBoYWxmQWgsIGhhbGZBdywgaGFsZkFoLCBhdyAtIGhhbGZBdywgYWggLSBoYWxmQWgpLAoKCQkJYjExID0gYi5zdWJtYXRyaXgoYngsIGJ5LCBoYWxmQncsIGhhbGZCaCksCgkJCWIyMSA9IGIuc3RyYXNzZW5QYWRkZWRCbG9jayhieCwgYnkgKyBoYWxmQmgsIGhhbGZCdywgaGFsZkJoLCBoYWxmQncsIGJoIC0gaGFsZkJoKSwKCQkJYjEyID0gYi5zdHJhc3NlblBhZGRlZEJsb2NrKGJ4ICsgaGFsZkJ3LCBieSwgaGFsZkJ3LCBoYWxmQmgsIGJ3IC0gaGFsZkJ3LCBoYWxmQmgpLAoJCQliMjIgPSBiLnN0cmFzc2VuUGFkZGVkQmxvY2soYnggKyBoYWxmQncsIGJ5ICsgaGFsZkJoLCBoYWxmQncsIGhhbGZCaCwgYncgLSBoYWxmQncsIGJoIC0gaGFsZkJoKSwKCgkJCW0xID0gYTExLnBsdXMoYTIyKS5tdWx0aXBseVN0cmFzc2VuX1N0cmFpZ2h0VmVyKGIxMS5wbHVzKGIyMikpLAoJCQltMiA9IGEyMS5wbHVzKGEyMikubXVsdGlwbHlTdHJhc3Nlbl9TdHJhaWdodFZlcihiMTEpLAoJCQltMyA9IGExMS5tdWx0aXBseVN0cmFzc2VuX1N0cmFpZ2h0VmVyKGIxMi5taW51cyhiMjIpKSwKCQkJbTQgPSBhMjIubXVsdGlwbHlTdHJhc3Nlbl9TdHJhaWdodFZlcihiMjEubWludXMoYjExKSksCgkJCW01ID0gYTExLnBsdXMoYTEyKS5tdWx0aXBseVN0cmFzc2VuX1N0cmFpZ2h0VmVyKGIyMiksCgkJCW02ID0gYTIxLm1pbnVzKGExMSkubXVsdGlwbHlTdHJhc3Nlbl9TdHJhaWdodFZlcihiMTEucGx1cyhiMTIpKSwKCQkJbTcgPSBhMTIubWludXMoYTIyKS5tdWx0aXBseVN0cmFzc2VuX1N0cmFpZ2h0VmVyKGIyMS5wbHVzKGIyMikpLAoKCQkJYzExID0gbTEucGx1cyhtNCkubWludXMobTUpLnBsdXMobTcpLAoJCQljMTIgPSBtMy5wbHVzKG01KSwKCQkJYzIxID0gbTIucGx1cyhtNCksCgkJCWMyMiA9IG0xLm1pbnVzKG0yKS5wbHVzKG0zKS5wbHVzKG02KTsKCgkJCW1vdmVTdWJtYXRyaXgoYzExLCAwLCAwLCByLCByeCwgcnksIGhhbGZSdywgaGFsZlJoKTsKCQkJbW92ZVN1Ym1hdHJpeChjMjEsIDAsIDAsIHIsIHJ4LCByeSArIGhhbGZSaCwgaGFsZlJ3LCByaCAtIGhhbGZSaCk7CgkJCW1vdmVTdWJtYXRyaXgoYzEyLCAwLCAwLCByLCByeCArIGhhbGZSdywgcnksIHJ3IC0gaGFsZlJ3LCBoYWxmUmgpOwoJCQltb3ZlU3VibWF0cml4KGMyMiwgMCwgMCwgciwgcnggKyBoYWxmUncsIHJ5ICsgaGFsZlJoLCBydyAtIGhhbGZSdywgcmggLSBoYWxmUmgpOwoJfQoKCXB1YmxpYyBNYXRyaXggbXVsdGlwbHlTdHJhc3Nlbl9TdHJhaWdodFZlcihNYXRyaXggYikgewoJCU1hdHJpeCByID0gbmV3IE1hdHJpeChiLncsIGgpOwoJCW11bHRpcGx5U3RyYXNzZW5fU3RyYWlnaHRWZXIodGhpcywgMCwgMCwgdywgaCwgYiwgMCwgMCwgYi53LCBiLmgsIHIsIDAsIDAsIHIudywgci5oKTsKCQlyZXR1cm4gcjsKCX0KCglwcml2YXRlIHN0YXRpYyBjbGFzcyBNYXRyaXhWaWV3IHsKCQlwdWJsaWMgTWF0cml4IG07CgkJcHVibGljIGludCB4LCB5LCB3LCBoOwoKCQlwdWJsaWMgTWF0cml4VmlldyhpbnQgdywgaW50IGgpIHsKCQkJdGhpcyhuZXcgTWF0cml4KHcsIGgpLCAwLCAwLCB3LCBoKTsKCQl9CgoJCXB1YmxpYyBNYXRyaXhWaWV3KE1hdHJpeCBtKSB7CgkJCXRoaXMobSwgMCwgMCwgbS53LCBtLmgpOwoJCX0KCgkJcHVibGljIE1hdHJpeFZpZXcoTWF0cml4IG0sIGludCB4LCBpbnQgeSwgaW50IHcsIGludCBoKSB7CgkJCXRoaXMubSA9IG07CgkJCXRoaXMueCA9IHg7CgkJCXRoaXMueSA9IHk7CgkJCXRoaXMudyA9IHc7CgkJCXRoaXMuaCA9IGg7CgkJfQoKCQlwdWJsaWMgdm9pZCBhZGQoTWF0cml4VmlldyBiLCBNYXRyaXhWaWV3IHIpIHsKCQkJTWF0cml4LmFkZChtLCB4LCB5LCB3LCBoLCBiLm0sIGIueCwgYi55LCBiLncsIGIuaCwgci5tLCByLngsIHIueSwgci53LCByLmgpOwoJCX0KCgkJcHVibGljIE1hdHJpeFZpZXcgcGx1cyhNYXRyaXhWaWV3IGIpIHsKCQkJTWF0cml4VmlldyByID0gbmV3IE1hdHJpeFZpZXcodywgaCk7CgkJCWFkZChiLCByKTsKCQkJcmV0dXJuIHI7CgkJfQoKCQlwdWJsaWMgdm9pZCBzdWJ0cmFjdChNYXRyaXhWaWV3IGIsIE1hdHJpeFZpZXcgcikgewoJCQlNYXRyaXguc3VidHJhY3QobSwgeCwgeSwgdywgaCwgYi5tLCBiLngsIGIueSwgYi53LCBiLmgsIHIubSwgci54LCByLnksIHIudywgci5oKTsKCQl9CgoJCXB1YmxpYyBNYXRyaXhWaWV3IG1pbnVzKE1hdHJpeFZpZXcgYikgewoJCQlNYXRyaXhWaWV3IHIgPSBuZXcgTWF0cml4Vmlldyh3LCBoKTsKCQkJc3VidHJhY3QoYiwgcik7CgkJCXJldHVybiByOwoJCX0KCgkJcHVibGljIHZvaWQgbXVsdGlwbHlTdHJhc3Nlbl9WaWV3VmVyKE1hdHJpeFZpZXcgYiwgTWF0cml4VmlldyByKSB7CgkJCU1hdHJpeC5tdWx0aXBseVN0cmFzc2VuX1ZpZXdWZXIobSwgeCwgeSwgdywgaCwgYi5tLCBiLngsIGIueSwgYi53LCBiLmgsIHIubSwgci54LCByLnksIHIudywgci5oKTsKCQl9CgoJCXB1YmxpYyBNYXRyaXhWaWV3IG11bHRpcGx5U3RyYXNzZW5fVmlld1ZlcihNYXRyaXhWaWV3IGIpIHsKCQkJTWF0cml4VmlldyByID0gbmV3IE1hdHJpeFZpZXcoYi53LCBoKTsKCQkJbXVsdGlwbHlTdHJhc3Nlbl9WaWV3VmVyKGIsIHIpOwoJCQlyZXR1cm4gcjsKCQl9CgoJCXB1YmxpYyB2b2lkIG11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoTWF0cml4VmlldyBiLCBNYXRyaXhWaWV3IHIpIHsKCQkJTWF0cml4Lm11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIobSwgeCwgeSwgdywgaCwgYi5tLCBiLngsIGIueSwgYi53LCBiLmgsIHIubSwgci54LCByLnksIHIudywgci5oKTsKCQl9CgoJCXB1YmxpYyBNYXRyaXhWaWV3IG11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoTWF0cml4VmlldyBiKSB7CgkJCU1hdHJpeFZpZXcgciA9IG5ldyBNYXRyaXhWaWV3KGIudywgaCk7CgkJCW11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoYiwgcik7CgkJCXJldHVybiByOwoJCX0KCX0KCglwcml2YXRlIE1hdHJpeFZpZXcgc3RyYXNzZW5QYWRkZWRCbG9ja1ZpZXcoaW50IHgsIGludCB5LCBpbnQgdywgaW50IGgpIHsKCQlyZXR1cm4geCArIHcgPD0gdGhpcy53ICYmIHkgKyBoIDw9IHRoaXMuaCA/CgkJCW5ldyBNYXRyaXhWaWV3KHRoaXMsIHgsIHksIHcsIGgpIDoKCQkJbmV3IE1hdHJpeFZpZXcodGhpcy5zdHJhc3NlblBhZGRlZEJsb2NrKAoJCQkJeCwgeSwgdywgaCwgTWF0aC5taW4odywgdGhpcy53IC0geCksIE1hdGgubWluKGgsIHRoaXMuaCAtIHkpKSk7Cgl9CgoJcHJpdmF0ZSBNYXRyaXhWaWV3IHN0cmFzc2VuVGFyZ2V0QmxvY2tWaWV3KGludCB4LCBpbnQgeSwgaW50IHcsIGludCBoKSB7CgkJcmV0dXJuIHggKyB3IDw9IHRoaXMudyAmJiB5ICsgaCA8PSB0aGlzLmggPwoJCQluZXcgTWF0cml4Vmlldyh0aGlzLCB4LCB5LCB3LCBoKSA6CgkJCW5ldyBNYXRyaXhWaWV3KHcsIGgpOwoJfQoKCXB1YmxpYyBzdGF0aWMgdm9pZCBtdWx0aXBseVN0cmFzc2VuX1ZpZXdWZXIoCgkJTWF0cml4IGEsIGludCBheCwgaW50IGF5LCBpbnQgYXcsIGludCBhaCwKCQlNYXRyaXggYiwgaW50IGJ4LCBpbnQgYnksIGludCBidywgaW50IGJoLAoJCU1hdHJpeCByLCBpbnQgcngsIGludCByeSwgaW50IHJ3LCBpbnQgcmgpIHsKCgkJaWYgKHNob3VsZFJlc29ydFRvUGxhaW5NdWx0aXBseShidywgYWgsIGF3KSkgewoJCQltdWx0aXBseVBsYWluKGEsIGF4LCBheSwgYXcsIGFoLCBiLCBieCwgYnksIGJ3LCBiaCwgciwgcngsIHJ5LCBydywgcmgpOwoJCQlyZXR1cm47CgkJfQoKCQl2YWxpZGF0ZVN1Ym1hdHJpeChhLncsIGEuaCwgYXgsIGF5LCBhdywgYWgpOwoJCXZhbGlkYXRlU3VibWF0cml4KGIudywgYi5oLCBieCwgYnksIGJ3LCBiaCk7CgkJdmFsaWRhdGVTdWJtYXRyaXgoci53LCByLmgsIHJ4LCByeSwgcncsIHJoKTsKCQl2YWxpZGF0ZU11bHRpcGx5KGF3LCBhaCwgYncsIGJoLCBydywgcmgpOwoKCQlpbnQgaGFsZkF3ID0gKGF3ICsgMSkgLyAyLCBoYWxmQWggPSAoYWggKyAxKSAvIDIsCgkJCWhhbGZCdyA9IChidyArIDEpIC8gMiwgaGFsZkJoID0gKGJoICsgMSkgLyAyLAoJCQloYWxmUncgPSAocncgKyAxKSAvIDIsIGhhbGZSaCA9IChyaCArIDEpIC8gMjsKCgkJTWF0cml4VmlldwoJCQlhMTEgPSBhLnN0cmFzc2VuUGFkZGVkQmxvY2tWaWV3KGF4LCBheSwgaGFsZkF3LCBoYWxmQWgpLAoJCQlhMjEgPSBhLnN0cmFzc2VuUGFkZGVkQmxvY2tWaWV3KGF4LCBheSArIGhhbGZBaCwgaGFsZkF3LCBoYWxmQWgpLAoJCQlhMTIgPSBhLnN0cmFzc2VuUGFkZGVkQmxvY2tWaWV3KGF4ICsgaGFsZkF3LCBheSwgaGFsZkF3LCBoYWxmQWgpLAoJCQlhMjIgPSBhLnN0cmFzc2VuUGFkZGVkQmxvY2tWaWV3KGF4ICsgaGFsZkF3LCBheSArIGhhbGZBaCwgaGFsZkF3LCBoYWxmQWgpLAoKCQkJYjExID0gYi5zdHJhc3NlblBhZGRlZEJsb2NrVmlldyhieCwgYnksIGhhbGZCdywgaGFsZkJoKSwKCQkJYjIxID0gYi5zdHJhc3NlblBhZGRlZEJsb2NrVmlldyhieCwgYnkgKyBoYWxmQmgsIGhhbGZCdywgaGFsZkJoKSwKCQkJYjEyID0gYi5zdHJhc3NlblBhZGRlZEJsb2NrVmlldyhieCArIGhhbGZCdywgYnksIGhhbGZCdywgaGFsZkJoKSwKCQkJYjIyID0gYi5zdHJhc3NlblBhZGRlZEJsb2NrVmlldyhieCArIGhhbGZCdywgYnkgKyBoYWxmQmgsIGhhbGZCdywgaGFsZkJoKSwKCgkJCW0xID0gYTExLnBsdXMoYTIyKS5tdWx0aXBseVN0cmFzc2VuX1ZpZXdWZXIoYjExLnBsdXMoYjIyKSksCgkJCW0yID0gYTIxLnBsdXMoYTIyKS5tdWx0aXBseVN0cmFzc2VuX1ZpZXdWZXIoYjExKSwKCQkJbTMgPSBhMTEubXVsdGlwbHlTdHJhc3Nlbl9WaWV3VmVyKGIxMi5taW51cyhiMjIpKSwKCQkJbTQgPSBhMjIubXVsdGlwbHlTdHJhc3Nlbl9WaWV3VmVyKGIyMS5taW51cyhiMTEpKSwKCQkJbTUgPSBhMTEucGx1cyhhMTIpLm11bHRpcGx5U3RyYXNzZW5fVmlld1ZlcihiMjIpLAoJCQltNiA9IGEyMS5taW51cyhhMTEpLm11bHRpcGx5U3RyYXNzZW5fVmlld1ZlcihiMTEucGx1cyhiMTIpKSwKCQkJbTcgPSBhMTIubWludXMoYTIyKS5tdWx0aXBseVN0cmFzc2VuX1ZpZXdWZXIoYjIxLnBsdXMoYjIyKSksCgkJCQoJCQljMTEgPSBuZXcgTWF0cml4VmlldyhyLCByeCwgcnksIGhhbGZSdywgaGFsZlJoKSwKCQkJYzIxID0gci5zdHJhc3NlblRhcmdldEJsb2NrVmlldyhyeCwgcnkgKyBoYWxmUmgsIGhhbGZSdywgaGFsZlJoKSwKCQkJYzEyID0gci5zdHJhc3NlblRhcmdldEJsb2NrVmlldyhyeCArIGhhbGZSdywgcnksIGhhbGZSdywgaGFsZlJoKSwKCQkJYzIyID0gci5zdHJhc3NlblRhcmdldEJsb2NrVmlldyhyeCArIGhhbGZSdywgcnkgKyBoYWxmUmgsIGhhbGZSdywgaGFsZlJoKTsKCgkJbTEuYWRkKG00LCBjMTEpOyBjMTEuc3VidHJhY3QobTUsIGMxMSk7IGMxMS5hZGQobTcsIGMxMSk7CgkJbTMuYWRkKG01LCBjMTIpOwoJCW0yLmFkZChtNCwgYzIxKTsKCQltMS5zdWJ0cmFjdChtMiwgYzIyKTsgYzIyLmFkZChtMywgYzIyKTsgYzIyLmFkZChtNiwgYzIyKTsKCgkJaWYgKGMyMS5tICE9IHIpIG1vdmVTdWJtYXRyaXgoYzIxLm0sIDAsIDAsIHIsIHJ4LCByeSArIGhhbGZSaCwgaGFsZlJ3LCByaCAtIGhhbGZSaCk7CgkJaWYgKGMxMi5tICE9IHIpIG1vdmVTdWJtYXRyaXgoYzEyLm0sIDAsIDAsIHIsIHJ4ICsgaGFsZlJ3LCByeSwgcncgLSBoYWxmUncsIGhhbGZSaCk7CgkJaWYgKGMyMi5tICE9IHIpIG1vdmVTdWJtYXRyaXgoYzIyLm0sIDAsIDAsIHIsIHJ4ICsgaGFsZlJ3LCByeSArIGhhbGZSaCwgcncgLSBoYWxmUncsIHJoIC0gaGFsZlJoKTsKCX0KCglwdWJsaWMgTWF0cml4IG11bHRpcGx5U3RyYXNzZW5fVmlld1ZlcihNYXRyaXggYikgewoJCU1hdHJpeCByID0gbmV3IE1hdHJpeChiLncsIGgpOwoJCW11bHRpcGx5U3RyYXNzZW5fVmlld1Zlcih0aGlzLCAwLCAwLCB3LCBoLCBiLCAwLCAwLCBiLncsIGIuaCwgciwgMCwgMCwgci53LCByLmgpOwoJCXJldHVybiByOwoJfQoKCXN0YXRpYyB2b2lkIHRydXN0ZWRNdWx0aXBseVN0cmFzc2VuX1BvdzJVbnJvbGxlZFZlcigKCQlNYXRyaXggYSwgaW50IGF4LCBpbnQgYXksIGludCBhdywgaW50IGFoLAoJCU1hdHJpeCBiLCBpbnQgYngsIGludCBieSwgaW50IGJ3LCBpbnQgYmgsCgkJTWF0cml4IHIsIGludCByeCwgaW50IHJ5LCBpbnQgcncsIGludCByaCkgewoKCQlpZiAoc2hvdWxkUmVzb3J0VG9QbGFpbk11bHRpcGx5KGJ3LCBhaCwgYXcpKSB7CgkJCW11bHRpcGx5UGxhaW4oYSwgYXgsIGF5LCBhdywgYWgsIGIsIGJ4LCBieSwgYncsIGJoLCByLCByeCwgcnksIHJ3LCByaCk7CgkJCXJldHVybjsKCQl9CgoJCWludCBoYWxmQXcgPSBhdyAvIDIsIGhhbGZBaCA9IGFoIC8gMiwKCQkJaGFsZkJ3ID0gYncgLyAyLCBoYWxmQmggPSBiaCAvIDIsCgkJCWhhbGZSdyA9IHJ3IC8gMiwgaGFsZlJoID0gcmggLyAyOwoKCQlNYXRyaXgKCQkJdHN1bWEgPSBuZXcgTWF0cml4KGhhbGZBdywgaGFsZkFoKSwKCQkJdHN1bWIgPSBuZXcgTWF0cml4KGhhbGZCdywgaGFsZkJoKSwKCQkJbTEgPSBuZXcgTWF0cml4KGhhbGZSdywgaGFsZlJoKSwKCQkJbTIgPSBuZXcgTWF0cml4KGhhbGZSdywgaGFsZlJoKSwKCQkJbTMgPSBuZXcgTWF0cml4KGhhbGZSdywgaGFsZlJoKSwKCQkJbTQgPSBuZXcgTWF0cml4KGhhbGZSdywgaGFsZlJoKSwKCQkJbTUgPSBuZXcgTWF0cml4KGhhbGZSdywgaGFsZlJoKSwKCQkJbTYgPSBuZXcgTWF0cml4KGhhbGZSdywgaGFsZlJoKSwKCQkJbTcgPSBuZXcgTWF0cml4KGhhbGZSdywgaGFsZlJoKTsKCgkJLy8gdHN1bWEgPSBhMTEgKyBhMjIsIHRzdW1iID0gYjExICsgYjIyLCBtMSA9IChhMTEgKyBhMjIpICogKGIxMSArIGIyMikKCQl0cnVzdGVkQWRkKAoJCQlhLCBheCwgYXksCgkJCWEsIGF4ICsgaGFsZkF3LCBheSArIGhhbGZBaCwKCQkJdHN1bWEsIDAsIDAsIGhhbGZBdywgaGFsZkFoKTsKCQl0cnVzdGVkQWRkKAoJCQliLCBieCwgYnksCgkJCWIsIGJ4ICsgaGFsZkJ3LCBieSArIGhhbGZCaCwKCQkJdHN1bWIsIDAsIDAsIGhhbGZCdywgaGFsZkJoKTsKCQl0cnVzdGVkTXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVW5yb2xsZWRWZXIoCgkJCXRzdW1hLCAwLCAwLCBoYWxmQXcsIGhhbGZBaCwKCQkJdHN1bWIsIDAsIDAsIGhhbGZCdywgaGFsZkJoLAoJCQltMSwgMCwgMCwgaGFsZlJ3LCBoYWxmUmgpOwoKCQkvLyB0c3VtYSA9IGEyMSArIGEyMiwgbTIgPSAoYTIxICsgYTIyKSAqIGIxMQoJCXRydXN0ZWRBZGQoCgkJCWEsIGF4LCBheSArIGhhbGZBaCwKCQkJYSwgYXggKyBoYWxmQXcsIGF5ICsgaGFsZkFoLAoJCQl0c3VtYSwgMCwgMCwgaGFsZkF3LCBoYWxmQWgpOwoJCXRydXN0ZWRNdWx0aXBseVN0cmFzc2VuX1BvdzJVbnJvbGxlZFZlcigKCQkJdHN1bWEsIDAsIDAsIGhhbGZBdywgaGFsZkFoLAoJCQliLCBieCwgYnksIGhhbGZCdywgaGFsZkJoLAoJCQltMiwgMCwgMCwgaGFsZlJ3LCBoYWxmUmgpOwoKCQkvLyB0c3VtYiA9IGIxMiAtIGIyMiwgbTMgPSBhMTEgKiAoYjEyIC0gYjIyKQoJCXRydXN0ZWRTdWJ0cmFjdCgKCQkJYiwgYnggKyBoYWxmQncsIGJ5LAoJCQliLCBieCArIGhhbGZCdywgYnkgKyBoYWxmQmgsCgkJCXRzdW1iLCAwLCAwLCBoYWxmQncsIGhhbGZCaCk7CgkJdHJ1c3RlZE11bHRpcGx5U3RyYXNzZW5fUG93MlVucm9sbGVkVmVyKAoJCQlhLCBheCwgYXksIGhhbGZBdywgaGFsZkFoLAoJCQl0c3VtYiwgMCwgMCwgaGFsZkJ3LCBoYWxmQmgsCgkJCW0zLCAwLCAwLCBoYWxmUncsIGhhbGZSaCk7CgoJCS8vIHRzdW1iID0gYjIxIC0gYjExLCBtNCA9IGEyMiAqIChiMjEgLSBiMTEpCgkJdHJ1c3RlZFN1YnRyYWN0KAoJCQliLCBieCwgYnkgKyBoYWxmQmgsCgkJCWIsIGJ4LCBieSwKCQkJdHN1bWIsIDAsIDAsIGhhbGZCdywgaGFsZkJoKTsKCQl0cnVzdGVkTXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVW5yb2xsZWRWZXIoCgkJCWEsIGF4ICsgaGFsZkF3LCBheSArIGhhbGZBaCwgaGFsZkF3LCBoYWxmQWgsCgkJCXRzdW1iLCAwLCAwLCBoYWxmQncsIGhhbGZCaCwKCQkJbTQsIDAsIDAsIGhhbGZSdywgaGFsZlJoKTsKCgkJLy8gdHN1bWEgPSBhMTEgKyBhMTIsIG01ID0gKGExMSArIGExMikgKiBiMjIKCQl0cnVzdGVkQWRkKAoJCQlhLCBheCwgYXksCgkJCWEsIGF4ICsgaGFsZkF3LCBheSwKCQkJdHN1bWEsIDAsIDAsIGhhbGZBdywgaGFsZkFoKTsKCQl0cnVzdGVkTXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVW5yb2xsZWRWZXIoCgkJCXRzdW1hLCAwLCAwLCBoYWxmQXcsIGhhbGZBaCwKCQkJYiwgYnggKyBoYWxmQncsIGJ5ICsgaGFsZkJoLCBoYWxmQncsIGhhbGZCaCwKCQkJbTUsIDAsIDAsIGhhbGZSdywgaGFsZlJoKTsKCgkJLy8gdHN1bWEgPSBhMjEgLSBhMTEsIHRzdW1iID0gYjExICsgYjEyLCBtNiA9IChhMjEgLSBhMTEpICogKGIxMSArIGIxMikKCQl0cnVzdGVkU3VidHJhY3QoCgkJCWEsIGF4LCBheSArIGhhbGZBaCwKCQkJYSwgYXgsIGF5LAoJCQl0c3VtYSwgMCwgMCwgaGFsZkF3LCBoYWxmQWgpOwoJCXRydXN0ZWRBZGQoCgkJCWIsIGJ4LCBieSwKCQkJYiwgYnggKyBoYWxmQncsIGJ5LAoJCQl0c3VtYiwgMCwgMCwgaGFsZkJ3LCBoYWxmQmgpOwoJCXRydXN0ZWRNdWx0aXBseVN0cmFzc2VuX1BvdzJVbnJvbGxlZFZlcigKCQkJdHN1bWEsIDAsIDAsIGhhbGZBdywgaGFsZkFoLAoJCQl0c3VtYiwgMCwgMCwgaGFsZkJ3LCBoYWxmQmgsCgkJCW02LCAwLCAwLCBoYWxmUncsIGhhbGZSaCk7CgoJCS8vIHRzdW1hID0gYTEyIC0gYTIyLCB0c3VtYiA9IGIyMSArIGIyMiwgbTcgPSAoYTEyIC0gYTIyKSAqIChiMjEgKyBiMjIpCgkJdHJ1c3RlZFN1YnRyYWN0KAoJCQlhLCBheCArIGhhbGZBdywgYXksCgkJCWEsIGF4ICsgaGFsZkF3LCBheSArIGhhbGZBaCwKCQkJdHN1bWEsIDAsIDAsIGhhbGZBdywgaGFsZkFoKTsKCQl0cnVzdGVkQWRkKAoJCQliLCBieCwgYnkgKyBoYWxmQmgsCgkJCWIsIGJ4ICsgaGFsZkJ3LCBieSArIGhhbGZCaCwKCQkJdHN1bWIsIDAsIDAsIGhhbGZCdywgaGFsZkJoKTsKCQl0cnVzdGVkTXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVW5yb2xsZWRWZXIoCgkJCXRzdW1hLCAwLCAwLCBoYWxmQXcsIGhhbGZBaCwKCQkJdHN1bWIsIDAsIDAsIGhhbGZCdywgaGFsZkJoLAoJCQltNywgMCwgMCwgaGFsZlJ3LCBoYWxmUmgpOwoKCQkvLyBjMTEgPSBtMSArIG00IC0gbTUgKyBtNwoJCXRydXN0ZWRBZGQoCgkJCW0xLCAwLCAwLAoJCQltNCwgMCwgMCwKCQkJciwgcngsIHJ5LCBoYWxmUncsIGhhbGZSaCk7CgkJdHJ1c3RlZFN1YnRyYWN0KAoJCQlyLCByeCwgcnksCgkJCW01LCAwLCAwLAoJCQlyLCByeCwgcnksIGhhbGZSdywgaGFsZlJoKTsKCQl0cnVzdGVkQWRkKAoJCQlyLCByeCwgcnksCgkJCW03LCAwLCAwLAoJCQlyLCByeCwgcnksIGhhbGZSdywgaGFsZlJoKTsKCgkJLy8gYzEyID0gbTMgKyBtNQoJCXRydXN0ZWRBZGQoCgkJCW0zLCAwLCAwLAoJCQltNSwgMCwgMCwKCQkJciwgcnggKyBoYWxmUncsIHJ5LCBoYWxmUncsIGhhbGZSaCk7CgoJCS8vIGMyMSA9IG0yICsgbTQKCQl0cnVzdGVkQWRkKAoJCQltMiwgMCwgMCwKCQkJbTQsIDAsIDAsCgkJCXIsIHJ4LCByeSArIGhhbGZSaCwgaGFsZlJ3LCBoYWxmUmgpOwoKCQkvLyBjMjIgPSBtMSAtIG0yICsgbTMgKyBtNgoJCXRydXN0ZWRTdWJ0cmFjdCgKCQkJbTEsIDAsIDAsCgkJCW0yLCAwLCAwLAoJCQlyLCByeCArIGhhbGZSdywgcnkgKyBoYWxmUmgsIGhhbGZSdywgaGFsZlJoKTsKCQl0cnVzdGVkQWRkKAoJCQlyLCByeCArIGhhbGZSdywgcnkgKyBoYWxmUmgsCgkJCW0zLCAwLCAwLAoJCQlyLCByeCArIGhhbGZSdywgcnkgKyBoYWxmUmgsIGhhbGZSdywgaGFsZlJoKTsKCQl0cnVzdGVkQWRkKAoJCQlyLCByeCArIGhhbGZSdywgcnkgKyBoYWxmUmgsCgkJCW02LCAwLCAwLAoJCQlyLCByeCArIGhhbGZSdywgcnkgKyBoYWxmUmgsIGhhbGZSdywgaGFsZlJoKTsKCX0KCglARnVuY3Rpb25hbEludGVyZmFjZQoJaW50ZXJmYWNlIEdlbmVyaWNNdWx0aXBseSB7CgkJdm9pZCBtdWx0aXBseSgKCQkJTWF0cml4IGEsIGludCBheCwgaW50IGF5LCBpbnQgYXcsIGludCBhaCwKCQkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgYncsIGludCBiaCwKCQkJTWF0cml4IHIsIGludCByeCwgaW50IHJ5LCBpbnQgcncsIGludCByaCk7Cgl9CgoJc3RhdGljIGJvb2xlYW4gaXNQb3cyKGludCB4KSB7CgkJaWYgKHggPD0gMCkgVGhyb3cuYmFkQXJnKCJpc1BvdzIgcmVxdWlyZXMgcG9zaXRpdmUgdmFsdWUsIGdvdCAlZC4iLCB4KTsKCQlyZXR1cm4gKHggJiAoeCAtIDEpKSA9PSAwOwoJfQoKCXN0YXRpYyBpbnQgY2VpbFBvdzIoaW50IHgpIHsKCQlpZiAoeCA8PSAwKSBUaHJvdy5iYWRBcmcoImNlaWxQb3cyIHJlcXVpcmVzIHBvc2l0aXZlIHZhbHVlLCBnb3QgJWQuIiwgeCk7CgkJeCAtPSAxOwoJCWZvciAoaW50IHNobHAgPSAwOyBzaGxwIDw9IDQ7IHNobHArKykKCQkJeCB8PSB4ID4+PiAoMSA8PCBzaGxwKTsKCQlyZXR1cm4geCArIDE7Cgl9CgoJcHVibGljIHN0YXRpYyB2b2lkIGFkYXB0VHJ1c3RlZE11bHRpcGx5UG93MigKCQlHZW5lcmljTXVsdGlwbHkgbXVsdGlwbHlQb3cyLAoJCU1hdHJpeCBhLCBpbnQgYXgsIGludCBheSwgaW50IGF3LCBpbnQgYWgsCgkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgYncsIGludCBiaCwKCQlNYXRyaXggciwgaW50IHJ4LCBpbnQgcnksIGludCBydywgaW50IHJoKSB7CgoJCXZhbGlkYXRlU3VibWF0cml4KGEudywgYS5oLCBheCwgYXksIGF3LCBhaCk7CgkJdmFsaWRhdGVTdWJtYXRyaXgoYi53LCBiLmgsIGJ4LCBieSwgYncsIGJoKTsKCQl2YWxpZGF0ZVN1Ym1hdHJpeChyLncsIHIuaCwgcngsIHJ5LCBydywgcmgpOwoJCXZhbGlkYXRlTXVsdGlwbHkoYXcsIGFoLCBidywgYmgsIHJ3LCByaCk7CgoJCWJvb2xlYW4gYWx0ZXJlZCA9IGZhbHNlOwoKCQlpZiAoIWlzUG93MihhdykgfHwgIWlzUG93MihhaCkpIHsKCQkJTWF0cml4IG5hID0gbmV3IE1hdHJpeChjZWlsUG93MihhdyksIGNlaWxQb3cyKGFoKSk7CgkJCW1vdmVTdWJtYXRyaXgoYSwgYXgsIGF5LCBuYSwgMCwgMCwgYXcsIGFoKTsKCQkJYSA9IG5hOyBheCA9IDA7IGF5ID0gMDsgYXcgPSBuYS53OyBhaCA9IG5hLmg7IGFsdGVyZWQgPSB0cnVlOwoJCX0KCgkJaWYgKCFpc1BvdzIoYncpIHx8ICFpc1BvdzIoYmgpKSB7CgkJCU1hdHJpeCBuYiA9IG5ldyBNYXRyaXgoY2VpbFBvdzIoYncpLCBjZWlsUG93MihiaCkpOwoJCQltb3ZlU3VibWF0cml4KGIsIGJ4LCBieSwgbmIsIDAsIDAsIGJ3LCBiaCk7CgkJCWIgPSBuYjsgYnggPSAwOyBieSA9IDA7IGJ3ID0gbmIudzsgYmggPSBuYi5oOyBhbHRlcmVkID0gdHJ1ZTsKCQl9CgoJCWludCBvcmlnUnggPSByeCwgb3JpZ1J5ID0gcnksIG9yaWdSdyA9IHJ3LCBvcmlnUmggPSByaDsKCQlNYXRyaXggb3JpZ1IgPSByOwoJCWlmIChhbHRlcmVkKSB7CgkJCXIgPSBuZXcgTWF0cml4KGJ3LCBhaCk7CgkJCXJ4ID0gMDsgcnkgPSAwOyBydyA9IHIudzsgcmggPSByLmg7CgkJfQoKCQltdWx0aXBseVBvdzIubXVsdGlwbHkoYSwgYXgsIGF5LCBhdywgYWgsIGIsIGJ4LCBieSwgYncsIGJoLCByLCByeCwgcnksIHJ3LCByaCk7CgkJaWYgKGFsdGVyZWQpIHsKCQkJbW92ZVN1Ym1hdHJpeChyLCAwLCAwLCBvcmlnUiwgb3JpZ1J4LCBvcmlnUnksIG9yaWdSdywgb3JpZ1JoKTsKCQl9Cgl9CgoJcHVibGljIHN0YXRpYyB2b2lkIG11bHRpcGx5U3RyYXNzZW5fUG93MlVucm9sbGVkVmVyKAoJCU1hdHJpeCBhLCBpbnQgYXgsIGludCBheSwgaW50IGF3LCBpbnQgYWgsCgkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgYncsIGludCBiaCwKCQlNYXRyaXggciwgaW50IHJ4LCBpbnQgcnksIGludCBydywgaW50IHJoKSB7CgoJCWFkYXB0VHJ1c3RlZE11bHRpcGx5UG93MihNYXRyaXg6OnRydXN0ZWRNdWx0aXBseVN0cmFzc2VuX1BvdzJVbnJvbGxlZFZlciwKCQkJYSwgYXgsIGF5LCBhdywgYWgsIGIsIGJ4LCBieSwgYncsIGJoLCByLCByeCwgcnksIHJ3LCByaCk7Cgl9CgoJcHVibGljIE1hdHJpeCBtdWx0aXBseVN0cmFzc2VuX1BvdzJVbnJvbGxlZFZlcihNYXRyaXggYikgewoJCU1hdHJpeCByID0gbmV3IE1hdHJpeChiLncsIGgpOwoJCW11bHRpcGx5U3RyYXNzZW5fUG93MlVucm9sbGVkVmVyKHRoaXMsIDAsIDAsIHcsIGgsIGIsIDAsIDAsIGIudywgYi5oLCByLCAwLCAwLCByLncsIHIuaCk7CgkJcmV0dXJuIHI7Cgl9CgoJc3RhdGljIHZvaWQgdHJ1c3RlZE11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoCgkJTWF0cml4IGEsIGludCBheCwgaW50IGF5LCBpbnQgYXcsIGludCBhaCwKCQlNYXRyaXggYiwgaW50IGJ4LCBpbnQgYnksIGludCBidywgaW50IGJoLAoJCU1hdHJpeCByLCBpbnQgcngsIGludCByeSwgaW50IHJ3LCBpbnQgcmgpIHsKCgkJaWYgKHNob3VsZFJlc29ydFRvUGxhaW5NdWx0aXBseShidywgYWgsIGF3KSkgewoJCQltdWx0aXBseVBsYWluKGEsIGF4LCBheSwgYXcsIGFoLCBiLCBieCwgYnksIGJ3LCBiaCwgciwgcngsIHJ5LCBydywgcmgpOwoJCQlyZXR1cm47CgkJfQoKCQlpbnQgaGFsZkF3ID0gYXcgLyAyLCBoYWxmQWggPSBhaCAvIDIsCgkJCWhhbGZCdyA9IGJ3IC8gMiwgaGFsZkJoID0gYmggLyAyLAoJCQloYWxmUncgPSBydyAvIDIsIGhhbGZSaCA9IHJoIC8gMjsKCgkJTWF0cml4VmlldwoJCQlhMTEgPSBuZXcgTWF0cml4VmlldyhhLCBheCwgYXksIGhhbGZBdywgaGFsZkFoKSwKCQkJYTIxID0gbmV3IE1hdHJpeFZpZXcoYSwgYXgsIGF5ICsgaGFsZkFoLCBoYWxmQXcsIGhhbGZBaCksCgkJCWExMiA9IG5ldyBNYXRyaXhWaWV3KGEsIGF4ICsgaGFsZkF3LCBheSwgaGFsZkF3LCBoYWxmQWgpLAoJCQlhMjIgPSBuZXcgTWF0cml4VmlldyhhLCBheCArIGhhbGZBdywgYXkgKyBoYWxmQWgsIGhhbGZBdywgaGFsZkFoKSwKCgkJCWIxMSA9IG5ldyBNYXRyaXhWaWV3KGIsIGJ4LCBieSwgaGFsZkJ3LCBoYWxmQmgpLAoJCQliMjEgPSBuZXcgTWF0cml4VmlldyhiLCBieCwgYnkgKyBoYWxmQmgsIGhhbGZCdywgaGFsZkJoKSwKCQkJYjEyID0gbmV3IE1hdHJpeFZpZXcoYiwgYnggKyBoYWxmQncsIGJ5LCBoYWxmQncsIGhhbGZCaCksCgkJCWIyMiA9IG5ldyBNYXRyaXhWaWV3KGIsIGJ4ICsgaGFsZkJ3LCBieSArIGhhbGZCaCwgaGFsZkJ3LCBoYWxmQmgpLAoKCQkJbTEgPSBhMTEucGx1cyhhMjIpLm11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoYjExLnBsdXMoYjIyKSksCgkJCW0yID0gYTIxLnBsdXMoYTIyKS5tdWx0aXBseVN0cmFzc2VuX1BvdzJWaWV3VmVyKGIxMSksCgkJCW0zID0gYTExLm11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoYjEyLm1pbnVzKGIyMikpLAoJCQltNCA9IGEyMi5tdWx0aXBseVN0cmFzc2VuX1BvdzJWaWV3VmVyKGIyMS5taW51cyhiMTEpKSwKCQkJbTUgPSBhMTEucGx1cyhhMTIpLm11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoYjIyKSwKCQkJbTYgPSBhMjEubWludXMoYTExKS5tdWx0aXBseVN0cmFzc2VuX1BvdzJWaWV3VmVyKGIxMS5wbHVzKGIxMikpLAoJCQltNyA9IGExMi5taW51cyhhMjIpLm11bHRpcGx5U3RyYXNzZW5fUG93MlZpZXdWZXIoYjIxLnBsdXMoYjIyKSksCgoJCQljMTEgPSBuZXcgTWF0cml4VmlldyhyLCByeCwgcnksIGhhbGZSdywgaGFsZlJoKSwKCQkJYzIxID0gbmV3IE1hdHJpeFZpZXcociwgcngsIHJ5ICsgaGFsZlJoLCBoYWxmUncsIGhhbGZSaCksCgkJCWMxMiA9IG5ldyBNYXRyaXhWaWV3KHIsIHJ4ICsgaGFsZlJ3LCByeSwgaGFsZlJ3LCBoYWxmUmgpLAoJCQljMjIgPSBuZXcgTWF0cml4VmlldyhyLCByeCArIGhhbGZSdywgcnkgKyBoYWxmUmgsIGhhbGZSdywgaGFsZlJoKTsKCgkJbTEuYWRkKG00LCBjMTEpOyBjMTEuc3VidHJhY3QobTUsIGMxMSk7IGMxMS5hZGQobTcsIGMxMSk7CgkJbTMuYWRkKG01LCBjMTIpOwoJCW0yLmFkZChtNCwgYzIxKTsKCQltMS5zdWJ0cmFjdChtMiwgYzIyKTsgYzIyLmFkZChtMywgYzIyKTsgYzIyLmFkZChtNiwgYzIyKTsKCX0KCglwdWJsaWMgc3RhdGljIHZvaWQgbXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVmlld1ZlcigKCQlNYXRyaXggYSwgaW50IGF4LCBpbnQgYXksIGludCBhdywgaW50IGFoLAoJCU1hdHJpeCBiLCBpbnQgYngsIGludCBieSwgaW50IGJ3LCBpbnQgYmgsCgkJTWF0cml4IHIsIGludCByeCwgaW50IHJ5LCBpbnQgcncsIGludCByaCkgewoKCQlhZGFwdFRydXN0ZWRNdWx0aXBseVBvdzIoTWF0cml4Ojp0cnVzdGVkTXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVmlld1ZlciwKCQkJYSwgYXgsIGF5LCBhdywgYWgsIGIsIGJ4LCBieSwgYncsIGJoLCByLCByeCwgcnksIHJ3LCByaCk7Cgl9CgoJcHVibGljIE1hdHJpeCBtdWx0aXBseVN0cmFzc2VuX1BvdzJWaWV3VmVyKE1hdHJpeCBiKSB7CgkJTWF0cml4IHIgPSBuZXcgTWF0cml4KGIudywgaCk7CgkJbXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVmlld1Zlcih0aGlzLCAwLCAwLCB3LCBoLCBiLCAwLCAwLCBiLncsIGIuaCwgciwgMCwgMCwgci53LCByLmgpOwoJCXJldHVybiByOwoJfQoKCUBPdmVycmlkZQoJcHVibGljIGJvb2xlYW4gZXF1YWxzKE9iamVjdCBvdGhlcikgewoJCWlmICh0aGlzID09IG90aGVyKSByZXR1cm4gdHJ1ZTsKCQlpZiAodGhpcy5nZXRDbGFzcygpICE9IG90aGVyLmdldENsYXNzKCkpIHJldHVybiBmYWxzZTsKCQlNYXRyaXggb20gPSAoTWF0cml4KSBvdGhlcjsKCQlyZXR1cm4gdyA9PSBvbS53ICYmIGggPT0gb20uaCAmJiBBcnJheXMuZXF1YWxzKHJhdywgb20ucmF3KTsKCX0KfQoKY2xhc3MgSWRlb25lIHsKCXN0YXRpYyBmaW5hbCBpbnQgRklMTEVSID0gOTsKCglzdGF0aWMgdm9pZCB0ZXN0QWRkU3VidHJhY3QoKSB7CgkJTWF0cml4IGEgPSBNYXRyaXguZXhwbGljaXQoNCwgMiwKCQkJMSwgMiwgMywgNCwKCQkJNSwgNiwgNywgOCk7CgoJCU1hdHJpeCBiID0gTWF0cml4LmV4cGxpY2l0KDQsIDIsCgkJCTksIDExLCAxMywgMTUsCgkJCTE3LCAxOSwgMjEsIDIzKTsKCgkJTWF0cml4IGV4cGVjdGVkU3VtID0gTWF0cml4LmV4cGxpY2l0KDQsIDIsCgkJCTEwLCAxMywgMTYsIDE5LAoJCQkyMiwgMjUsIDI4LCAzMSk7CgoJCU1hdHJpeCBleHBlY3RlZERpZiA9IE1hdHJpeC5leHBsaWNpdCg0LCAyLAoJCQktOCwgLTksIC0xMCwgLTExLAoJCQktMTIsIC0xMywgLTE0LCAtMTUpOwoKCQlmaW5hbCBpbnQgRiA9IEZJTExFUjsKCQlleHBlY3RFcXVhbCgiTWF0cml4LmV4cGFuZCIsIGEuZXhwYW5kKEZJTExFUiwgMSwgNCwgMiwgMyksCgkJCU1hdHJpeC5leHBsaWNpdCg5LCA3LAoJCQkJRiwgRiwgRiwgRiwgRiwgRiwgRiwgRiwgRiwKCQkJCUYsIEYsIEYsIEYsIEYsIEYsIEYsIEYsIEYsCgkJCQlGLCAxLCAyLCAzLCA0LCBGLCBGLCBGLCBGLAoJCQkJRiwgNSwgNiwgNywgOCwgRiwgRiwgRiwgRiwKCQkJCUYsIEYsIEYsIEYsIEYsIEYsIEYsIEYsIEYsCgkJCQlGLCBGLCBGLCBGLCBGLCBGLCBGLCBGLCBGLAoJCQkJRiwgRiwgRiwgRiwgRiwgRiwgRiwgRiwgRikpOwoKCQlNYXRyaXggcmVzdWx0ID0gbmV3IE1hdHJpeChleHBlY3RlZFN1bS53ICsgNSwgZXhwZWN0ZWRTdW0uaCArIDUsICh4LCB5KSAtPiBGSUxMRVIpOwoJCU1hdHJpeC5hZGQoCgkJCWEuZXhwYW5kKEZJTExFUiwgMSwgMiwgMywgNCksIDEsIDMsIGEudywgYS5oLAoJCQliLmV4cGFuZChGSUxMRVIsIDUsIDYsIDcsIDgpLCA1LCA3LCBiLncsIGIuaCwKCQkJcmVzdWx0LCAxLCAyLCBleHBlY3RlZFN1bS53LCBleHBlY3RlZFN1bS5oKTsKCQlleHBlY3RFcXVhbCgiTWF0cml4LmFkZCIsIHJlc3VsdCwgZXhwZWN0ZWRTdW0uZXhwYW5kKEZJTExFUiwgMSwgNCwgMiwgMykpOwoKCQlyZXN1bHQuZmlsbCgoeCwgeSkgLT4gRklMTEVSKTsKCQlNYXRyaXguc3VidHJhY3QoCgkJCWEuZXhwYW5kKEZJTExFUiwgMSwgMiwgMywgNCksIDEsIDMsIGEudywgYS5oLAoJCQliLmV4cGFuZChGSUxMRVIsIDUsIDYsIDcsIDgpLCA1LCA3LCBiLncsIGIuaCwKCQkJcmVzdWx0LCAxLCAyLCBleHBlY3RlZERpZi53LCBleHBlY3RlZERpZi5oKTsKCQlleHBlY3RFcXVhbCgiTWF0cml4LnN1YnRyYWN0IiwgcmVzdWx0LCBleHBlY3RlZERpZi5leHBhbmQoRklMTEVSLCAxLCA0LCAyLCAzKSk7CgoJCWV4cGVjdEVxdWFsKCJjZWlsUG93MiIsIE1hdHJpeC5jZWlsUG93MigxKSwgMSk7CgkJZXhwZWN0RXF1YWwoImNlaWxQb3cyIiwgTWF0cml4LmNlaWxQb3cyKDYpLCA4KTsKCX0KCglzdGF0aWMgdm9pZCB0ZXN0UGxhaW5NdWx0aXBseSgpIHsKCQkvLyB3d3cubWF0aHdhcmVob3VzZS5jb20vYWxnZWJyYS9tYXRyaXgvaW1hZ2VzL3Byb2R1Y3QtbWF0cml4Mi5wbmcKCQlNYXRyaXggYSA9IE1hdHJpeC5leHBsaWNpdCg0LCAyLAoJCQkxLCA0LCA2LCAxMCwKCQkJMiwgNywgNSwgMyk7CgoJCU1hdHJpeCBiID0gTWF0cml4LmV4cGxpY2l0KDMsIDQsCgkJCTEsIDQsIDYsCgkJCTIsIDcsIDUsCgkJCTksIDAsIDExLAoJCQkzLCAxLCAwKTsKCgkJTWF0cml4IGV4cGVjdGVkID0gTWF0cml4LmV4cGxpY2l0KDMsIDIsCgkJCTkzLCA0MiwgOTIsCgkJCTcwLCA2MCwgMTAyKTsKCgkJTWF0cml4IHJlc3VsdCA9IG5ldyBNYXRyaXgoZXhwZWN0ZWQudyArIDUsIGV4cGVjdGVkLmggKyA1LCAoeCwgeSkgLT4gRklMTEVSKTsKCQlNYXRyaXgubXVsdGlwbHlQbGFpbigKCQkJYS5leHBhbmQoRklMTEVSLCAxLCAyLCAzLCA0KSwgMSwgMywgYS53LCBhLmgsCgkJCWIuZXhwYW5kKEZJTExFUiwgNSwgNiwgNywgOCksIDUsIDcsIGIudywgYi5oLAoJCQlyZXN1bHQsIDEsIDIsIGV4cGVjdGVkLncsIGV4cGVjdGVkLmgpOwoJCWV4cGVjdEVxdWFsKCJNYXRyaXgubXVsdGlwbHlQbGFpbiIsIHJlc3VsdCwgZXhwZWN0ZWQuZXhwYW5kKEZJTExFUiwgMSwgNCwgMiwgMykpOwoJfQoKCXN0YXRpYyB2b2lkIHRlc3RTdHJhc3Nlbk11bHRpcGx5KCkgewoJCVJhbmRvbSBybmcgPSBuZXcgUmFuZG9tKDEpOwoJCU1hdHJpeCBhID0gbmV3IE1hdHJpeCg5NywgNzEsICh4LCB5KSAtPiAtMTAgKyBybmcubmV4dEludCgyMSkpOwoJCU1hdHJpeCBiID0gbmV3IE1hdHJpeCg5MSwgOTcsICh4LCB5KSAtPiAtMTAgKyBybmcubmV4dEludCgyMSkpOwoJCU1hdHJpeCBhYlByb2RSZWYgPSBhLm11bHRpcGx5UGxhaW4oYik7CgkJZXhwZWN0RXF1YWwoIk1hdHJpeC5tdWx0aXBseVN0cmFzc2VuX1N0cmFpZ2h0VmVyIiwgYS5tdWx0aXBseVN0cmFzc2VuX1N0cmFpZ2h0VmVyKGIpLCBhYlByb2RSZWYpOwoJCWV4cGVjdEVxdWFsKCJNYXRyaXgubXVsdGlwbHlTdHJhc3Nlbl9WaWV3VmVyIiwgYS5tdWx0aXBseVN0cmFzc2VuX1ZpZXdWZXIoYiksIGFiUHJvZFJlZik7CgkJZXhwZWN0RXF1YWwoIk1hdHJpeC5tdWx0aXBseVN0cmFzc2VuX1BvdzJVbnJvbGxlZFZlciIsIGEubXVsdGlwbHlTdHJhc3Nlbl9Qb3cyVW5yb2xsZWRWZXIoYiksIGFiUHJvZFJlZik7CgkJZXhwZWN0RXF1YWwoIk1hdHJpeC5tdWx0aXBseVN0cmFzc2VuX1BvdzJWaWV3VmVyIiwgYS5tdWx0aXBseVN0cmFzc2VuX1BvdzJWaWV3VmVyKGIpLCBhYlByb2RSZWYpOwoJfQoKCXN0YXRpYyB2b2lkIGV4cGVjdEVxdWFsKFN0cmluZyB3aGF0LCBPYmplY3QgYSwgT2JqZWN0IGIpIHsKCQlpZiAoIWEuZXF1YWxzKGIpKQoJCQl0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbihTdHJpbmcuZm9ybWF0KCJiYWQgJXM6IGdvdFxuJXNcbmV4cGVjdGVkXG4lcy4iLCB3aGF0LCBhLCBiKSk7Cgl9CgoJc3RhdGljIHZvaWQgYmVuY2htYXJrKCkgewoJCWZpbmFsIGludCBUUklBTFMgPSAyOwoJCWZvciAoaW50IHRyaWFsID0gMDsgdHJpYWwgPCBUUklBTFM7IHRyaWFsKyspIHsKCQkJU3lzdGVtLm91dC5wcmludGxuKFN0cmluZy5mb3JtYXQoIiVzLS0tIFRSSUFMICVkLyVkJXMgLS0tIiwKCQkJCXRyaWFsID4gMD8gIlxuIiA6ICIiLCAxICsgdHJpYWwsIFRSSUFMUywgdHJpYWwgPT0gMCA/ICIg4oCUIEhFQVRJTkcgVVAiIDogIiIpKTsKCQkJZmluYWwgaW50IEFXID0gODUwLCBBSCA9IDY1MCwgQlcgPSA3NTA7CgkJCS8vIGZpbmFsIGludCBBVyA9IDUxMiwgQUggPSAxMDI0LCBCVyA9IDc2ODsKCQkJTWF0cml4IGEgPSBuZXcgTWF0cml4KEFXLCBBSCk7CgkJCU1hdHJpeCBiID0gbmV3IE1hdHJpeChCVywgQVcpOwoJCQlNYXRyaXggciA9IG5ldyBNYXRyaXgoQlcsIEFIKTsKCgkJCWRvdWJsZSByZWZUaW1lID0gYmVuY2htYXJrKCJTdHJhaWdodGZvcndhcmQgTyhOXjMpIG11bHRpcGx5IiwKCQkJCSgpIC0+IHsgTWF0cml4Lm11bHRpcGx5UGxhaW4oYSwgMCwgMCwgYS53LCBhLmgsIGIsIDAsIDAsIGIudywgYi5oLCByLCAwLCAwLCByLncsIHIuaCk7IHJldHVybiByOyB9KTsKCgkJCWJlbmNobWFyaygiU3RyYXNzZW4gbXVsdGlwbHkgd2l0aCBtYW55IGludGVybWVkaWF0ZSBtYXRyaXggYWxsb2NhdGlvbnMiLAoJCQkJKCkgLT4geyBNYXRyaXgubXVsdGlwbHlTdHJhc3Nlbl9TdHJhaWdodFZlcihhLCAwLCAwLCBhLncsIGEuaCwgYiwgMCwgMCwgYi53LCBiLmgsIHIsIDAsIDAsIHIudywgci5oKTsgcmV0dXJuIHI7IH0sIHJlZlRpbWUpOwoKCQkJYmVuY2htYXJrKCJTdHJhc3NlbiBtdWx0aXBseSB3aXRoIHN1Ym1hdHJpeCB2aWV3cyIsCgkJCQkoKSAtPiB7IE1hdHJpeC5tdWx0aXBseVN0cmFzc2VuX1ZpZXdWZXIoYSwgMCwgMCwgYS53LCBhLmgsIGIsIDAsIDAsIGIudywgYi5oLCByLCAwLCAwLCByLncsIHIuaCk7IHJldHVybiByOyB9LCByZWZUaW1lKTsKCgkJCWJlbmNobWFyaygiU3RyYXNzZW4gbXVsdGlwbHkgd2l0aCBmb3JjZWZ1bCAyXm4gc2l6ZSBhbmQgdW5yb2xsZWQgY2FsY3VsYXRpb25zIiwKCQkJCSgpIC0+IHsgTWF0cml4Lm11bHRpcGx5U3RyYXNzZW5fUG93MlVucm9sbGVkVmVyKGEsIDAsIDAsIGEudywgYS5oLCBiLCAwLCAwLCBiLncsIGIuaCwgciwgMCwgMCwgci53LCByLmgpOyByZXR1cm4gcjsgfSwgcmVmVGltZSk7CgoJCQliZW5jaG1hcmsoIlN0cmFzc2VuIG11bHRpcGx5IHdpdGggZm9yY2VmdWwgMl5uIHNpemUgYW5kIHN1Ym1hdHJpeCB2aWV3cyIsCgkJCQkoKSAtPiB7IE1hdHJpeC5tdWx0aXBseVN0cmFzc2VuX1BvdzJWaWV3VmVyKGEsIDAsIDAsIGEudywgYS5oLCBiLCAwLCAwLCBiLncsIGIuaCwgciwgMCwgMCwgci53LCByLmgpOyByZXR1cm4gcjsgfSwgcmVmVGltZSk7CgkJfQoJfQoKCUBGdW5jdGlvbmFsSW50ZXJmYWNlCglpbnRlcmZhY2UgQmVuY2htYXJrUGF5bG9hZCB7CgkJYWJzdHJhY3QgT2JqZWN0IHJ1bigpOwoJfQoKCXN0YXRpYyBkb3VibGUgYmVuY2htYXJrKFN0cmluZyB0aXRsZSwgQmVuY2htYXJrUGF5bG9hZCBwYXlsb2FkKSB7CgkJcmV0dXJuIGJlbmNobWFyayh0aXRsZSwgcGF5bG9hZCwgLTEpOwoJfQoKCXN0YXRpYyBkb3VibGUgYmVuY2htYXJrKFN0cmluZyB0aXRsZSwgQmVuY2htYXJrUGF5bG9hZCBwYXlsb2FkLCBkb3VibGUgcmVmVGltZSkgewoJCWxvbmcgc3RhcnQgPSBTeXN0ZW0ubmFub1RpbWUoKTsKCQlPYmplY3Qga2VlcEFsaXZlID0gcGF5bG9hZC5ydW4oKTsKCQlkb3VibGUgdGltZSA9IChTeXN0ZW0ubmFub1RpbWUoKSAtIHN0YXJ0KSAqIDFlLTk7CgkJU3lzdGVtLm91dC5wcmludGxuKFN0cmluZy5mb3JtYXQoCgkJCSIlczogJS4yZiBzJXMiLAoJCQl0aXRsZSwgdGltZSwgcmVmVGltZSA+PSAwID8gU3RyaW5nLmZvcm1hdCgiICglcykiLCBqdWRnZW1lbnQodGltZSwgcmVmVGltZSkpIDogIiIsIGtlZXBBbGl2ZSkpOwoJCXJldHVybiB0aW1lOwoJfQoKCXN0YXRpYyBTdHJpbmcganVkZ2VtZW50KGRvdWJsZSB0aW1lLCBkb3VibGUgcmVmVGltZSkgewoJCWZpbmFsIGRvdWJsZSBhYnNUaHJlc2hvbGQgPSAwLjEsIHJlbFRocmVzaG9sZCA9IC4wNTsKCgkJcmV0dXJuIHRpbWUgPD0gYWJzVGhyZXNob2xkIHx8IHJlZlRpbWUgPD0gYWJzVGhyZXNob2xkID8KCQkJU3RyaW5nLmZvcm1hdCgiY2FuJ3QganVkZ2UsIG1pbi4gcmVxdWlyZWQgdGltZSBpcyAlLjFmIHMiLCBhYnNUaHJlc2hvbGQpIDoKCQkJTWF0aC5tYXgodGltZSwgcmVmVGltZSkgPiAoMSArIHJlbFRocmVzaG9sZCkgKiBNYXRoLm1pbih0aW1lLCByZWZUaW1lKSA/CgkJCVN0cmluZy5mb3JtYXQoIiUuMGYlJSAlcyIsIE1hdGguYWJzKHRpbWUgLyByZWZUaW1lIC0gMSkgKiAxMDAsIHRpbWUgPCByZWZUaW1lID8gImZhc3RlciIgOiAic2xvd2VyIikgOgoJCQlTdHJpbmcuZm9ybWF0KCJubyBvYnNlcnZhYmxlIGRpZmZlcmVuY2UsIG1pbi4gJS4wZiUlIHJlcXVpcmVkIiwgcmVsVGhyZXNob2xkICogMTAwKTsKCX0KCglwdWJsaWMgc3RhdGljIHZvaWQgbWFpbiAoU3RyaW5nW10gYXJncykgewoJCXRyeSB7CgkJCXRlc3RBZGRTdWJ0cmFjdCgpOwoJCQl0ZXN0UGxhaW5NdWx0aXBseSgpOwoJCQl0ZXN0U3RyYXNzZW5NdWx0aXBseSgpOwoJCQliZW5jaG1hcmsoKTsKCQl9IGNhdGNoIChFeGNlcHRpb24gZSkgewoJCQlTdHJpbmdXcml0ZXIgc3cgPSBuZXcgU3RyaW5nV3JpdGVyKCk7CgkJCWUucHJpbnRTdGFja1RyYWNlKG5ldyBQcmludFdyaXRlcihzdykpOwoJCQlTeXN0ZW0ub3V0LnByaW50bG4oc3cudG9TdHJpbmcoKSk7CgkJfQoJfQp9