import java.util.* ;
import java.lang.* ;
import java.io.* ;
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)
Matrix r = new Matrix( w, h) ;
System .
arraycopy ( cells,
0 , r.
raw ,
0 , cells.
length ) ; return r;
}
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 , absIdx = 0 ; y < h; y++ ) {
for ( int x = 0 ; x < w; x++ , absIdx++ ) {
rsb.
append ( String .
format ( fmt, reprs
[ absIdx
] ) ) .
append ( x
+ 1 < w
? " " : y
+ 1 < h
? "\n " : "" ) ; }
}
return rsb.toString ( ) ;
}
public static void validateSize( int w, int h) {
if ( w <= 0 || h <= 0 )
}
public void validateSubmatrix( int x, int y, int w, int h) {
validateSize( w, h) ;
if ( x < 0 || y < 0 || x + w > this .w || y + h > this .h )
}
public int absoluteIndex( int x, int y) {
if ( x < 0 || y < 0 || x >= w || y >= h)
return y * w + x;
}
static Matrix prepareMultiply(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r) {
a.validateSubmatrix ( ax, ay, aw, ah) ;
b.validateSubmatrix ( bx, by, bw, bh) ;
if ( aw != bh)
throw new IllegalArgumentException ( String .
format ( "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 ( r == null ) {
r = new Matrix( bw, ah) ;
} else if ( r.w != bw || r.h != ah) {
}
return r;
}
public static Matrix multiply_AiVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r) {
r = prepareMultiply( a, ax, ay, aw, ah, b, bx, by, bw, bh, r) ;
int aRowStartAbsIdx = a.absoluteIndex ( ax, ay) ; // points to the beginning of the current row of A, i. e. A[0, y].
int bColStartAbsIdx = b.absoluteIndex ( bx, by) ; // points to the beginning of the current column of B, i. e. B[x, 0].
// result cell (x, y) (pointed by rAbsIdx) gets the dot product of current row of A and current column of B.
for ( int y = 0 , rAbsIdx = 0 ; y < r.h ; y++ , aRowStartAbsIdx += a.w , bColStartAbsIdx -= r.w ) {
for ( int x = 0 ; x < r.w ; x++ , rAbsIdx++ , bColStartAbsIdx++ ) {
int dot = 0 ;
for ( int i = 0 , aAbsIdx = aRowStartAbsIdx, bAbsIdx = bColStartAbsIdx; i < aw; i++ , aAbsIdx++ , bAbsIdx += b.w ) {
dot += a.raw [ aAbsIdx] * b.raw [ bAbsIdx] ;
}
r.raw [ rAbsIdx] = dot;
}
}
return r;
}
public Matrix multiply_AiVer( Matrix b) {
return multiply_AiVer( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , null ) ;
}
int get( int x, int y) {
return raw[ y * w + x] ;
}
void set( int x, int y, int value) {
raw[ y * w + x] = value;
}
public static Matrix multiply_GsVer(
Matrix a, int ax, int ay, int aw, int ah,
Matrix b, int bx, int by, int bw, int bh,
Matrix r) {
r = prepareMultiply( a, ax, ay, aw, ah, b, bx, by, bw, bh, r) ;
for ( int y = 0 ; y < r.h ; y++ ) {
for ( int x = 0 ; x < r.w ; x++ ) {
int dot = 0 ;
for ( int i = 0 ; i < aw; i++ ) {
dot += a.get ( i, y) * b.get ( x, i) ;
}
r.set ( x, y, dot) ;
}
}
return r;
}
public Matrix multiply_GsVer( Matrix b) {
return multiply_GsVer( this , 0 , 0 , w, h, b, 0 , 0 , b.w , b.h , null ) ;
}
@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 ) ; }
// just for resulting matrices to not be subjected to dead store elimination.
public int elementsSum( ) {
int sum = 0 ;
for ( int i = 0 ; i < raw.length ; i++ ) {
sum += raw[ i] ;
}
return sum;
}
}
class Ideone
{
static void test( ) {
// www.mathwarehouse.com/algebra/matrix/images/product-matrix2.png
Matrix
a = Matrix.explicit ( 4 , 2 ,
1 , 4 , 6 , 10 ,
2 , 7 , 5 , 3 ) ,
b = Matrix.explicit ( 3 , 4 ,
1 , 4 , 6 ,
2 , 7 , 5 ,
9 , 0 , 11 ,
3 , 1 , 0 ) ,
expected = Matrix.explicit ( 3 , 2 ,
93 , 42 , 92 ,
70 , 60 , 102 ) ,
abProd_AiVer = a.multiply_AiVer ( b) ,
abProd_GsVer = a.multiply_GsVer ( b) ;
if ( ! abProd_AiVer.
equals ( expected
) ) throw new RuntimeException ( String .
format ( "bad Matrix.multiply_AiVer: got\n %s\n expected\n %s." , abProd_AiVer, expected
) ) ; if ( ! abProd_GsVer.
equals ( expected
) ) throw new RuntimeException ( String .
format ( "bad Matrix.multiply_GsVer: got\n %s\n expected\n %s." , abProd_GsVer, expected
) ) ; }
static void benchmark( ) {
Matrix a = new Matrix( 1000 , 500 ) ;
Matrix b = new Matrix( 600 , 1000 ) ;
Matrix r = new Matrix( 600 , 500 ) ; // so allocation won't happen during time measurement
long start_AiVer
= System .
nanoTime ( ) ; Matrix.multiply_AiVer ( a, 0 , 0 , a.w , a.h , b, 0 , 0 , b.w , b.h , r) ;
double time_AiVer
= ( System .
nanoTime ( ) - start_AiVer
) * 1e
- 9
; System .
out .
println ( String .
format ( "Multiplication using absolute indices: %.2f s" , time_AiVer, r.
elementsSum ( ) ) ) ;
long start_GsVer
= System .
nanoTime ( ) ; Matrix.multiply_GsVer ( a, 0 , 0 , a.w , a.h , b, 0 , 0 , b.w , b.h , r) ;
double time_GsVer
= ( System .
nanoTime ( ) - start_GsVer
) * 1e
- 9
; System .
out .
println ( String .
format ( "Multiplication using .get()/.set(): %.2f s (%s)" , time_GsVer, judgement
( time_GsVer, time_AiVer
) , r.
elementsSum ( ) ) ) ; }
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 {
test( ) ;
benchmark( ) ;
}
}
}
aW1wb3J0IGphdmEudXRpbC4qOwppbXBvcnQgamF2YS5sYW5nLio7CmltcG9ydCBqYXZhLmlvLio7CgpjbGFzcyBNYXRyaXgKewoJcHVibGljIGZpbmFsIGludCB3LCBoOwoJcHVibGljIGZpbmFsIGludFtdIHJhdzsKCglwdWJsaWMgTWF0cml4KGludCB3LCBpbnQgaCkgewoJCXZhbGlkYXRlU2l6ZSh3LCBoKTsKCQl0aGlzLncgPSB3OwoJCXRoaXMuaCA9IGg7CgkJcmF3ID0gbmV3IGludFt3ICogaF07Cgl9CgoJcHVibGljIHN0YXRpYyBNYXRyaXggZXhwbGljaXQoaW50IHcsIGludCBoLCBpbnQuLi4gY2VsbHMpIHsKCQlpZiAoY2VsbHMubGVuZ3RoICE9IHcgKiBoKQoJCQl0aHJvdyBuZXcgSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uKFN0cmluZy5mb3JtYXQoIkV4cGVjdGVkICVkIHZhbHVlcyBvZiAlZMOXJWQgbWF0cml4LCBnb3QgJWQuIiwgdypoLCB3LCBoLCBjZWxscy5sZW5ndGgpKTsKCQlNYXRyaXggciA9IG5ldyBNYXRyaXgodywgaCk7CgkJU3lzdGVtLmFycmF5Y29weShjZWxscywgMCwgci5yYXcsIDAsIGNlbGxzLmxlbmd0aCk7CgkJcmV0dXJuIHI7Cgl9CgoJcHVibGljIFN0cmluZyB0b1N0cmluZygpIHsKCQlTdHJpbmdbXSByZXBycyA9IG5ldyBTdHJpbmdbcmF3Lmxlbmd0aF07CgkJaW50IG1heExlbiA9IDA7CgkJZm9yIChpbnQgaSA9IDA7IGkgPCByZXBycy5sZW5ndGg7IGkrKykgewoJCQlyZXByc1tpXSA9IEludGVnZXIudG9TdHJpbmcocmF3W2ldKTsKCQkJbWF4TGVuID0gTWF0aC5tYXgobWF4TGVuLCByZXByc1tpXS5sZW5ndGgoKSk7CgkJfQoJCVN0cmluZyBmbXQgPSAiJSIgKyBtYXhMZW4gKyAicyI7CgoJCVN0cmluZ0J1aWxkZXIgcnNiID0gbmV3IFN0cmluZ0J1aWxkZXIoKTsKCQlmb3IgKGludCB5ID0gMCwgYWJzSWR4ID0gMDsgeSA8IGg7IHkrKykgewoJCQlmb3IgKGludCB4ID0gMDsgeCA8IHc7IHgrKywgYWJzSWR4KyspIHsKCQkJCXJzYi5hcHBlbmQoU3RyaW5nLmZvcm1hdChmbXQsIHJlcHJzW2Fic0lkeF0pKS5hcHBlbmQoeCArIDEgPCB3ID8gIiAiIDogeSArIDEgPCBoID8gIlxuIiA6ICIiKTsKCQkJfQoJCX0KCQlyZXR1cm4gcnNiLnRvU3RyaW5nKCk7Cgl9CgoJcHVibGljIHN0YXRpYyB2b2lkIHZhbGlkYXRlU2l6ZShpbnQgdywgaW50IGgpIHsKCQlpZiAodyA8PSAwIHx8IGggPD0gMCkKCQkJdGhyb3cgbmV3IElsbGVnYWxBcmd1bWVudEV4Y2VwdGlvbihTdHJpbmcuZm9ybWF0KCJCYWQgbWF0cml4IHNpemU6ICVkw5clZC4iLCB3LCBoKSk7Cgl9CgoJcHVibGljIHZvaWQgdmFsaWRhdGVTdWJtYXRyaXgoaW50IHgsIGludCB5LCBpbnQgdywgaW50IGgpIHsKCQl2YWxpZGF0ZVNpemUodywgaCk7CgkJaWYgKHggPCAwIHx8IHkgPCAwIHx8IHggKyB3ID4gdGhpcy53IHx8IHkgKyBoID4gdGhpcy5oKQoJCQl0aHJvdyBuZXcgSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uKFN0cmluZy5mb3JtYXQoIkJhZCBzdWJtYXRyaXggb2YgJWTDlyVkOiAoJWQsICVkKSArICVkw5clZC4iLCB0aGlzLncsIHRoaXMuaCwgeCwgeSwgdywgaCkpOwoJfQoKCXB1YmxpYyBpbnQgYWJzb2x1dGVJbmRleChpbnQgeCwgaW50IHkpIHsKCQlpZiAoeCA8IDAgfHwgeSA8IDAgfHwgeCA+PSB3IHx8IHkgPj0gaCkKCQkJdGhyb3cgbmV3IElsbGVnYWxBcmd1bWVudEV4Y2VwdGlvbihTdHJpbmcuZm9ybWF0KCJCYWQgY2VsbCBvZiAlZMOXJWQ6ICglZCwgJWQpLiIsIHcsIGgsIHgsIHkpKTsKCQlyZXR1cm4geSAqIHcgKyB4OwoJfQoKCXN0YXRpYyBNYXRyaXggcHJlcGFyZU11bHRpcGx5KAoJCU1hdHJpeCBhLCBpbnQgYXgsIGludCBheSwgaW50IGF3LCBpbnQgYWgsCgkJTWF0cml4IGIsIGludCBieCwgaW50IGJ5LCBpbnQgYncsIGludCBiaCwKCQlNYXRyaXggcikgewoKCQlhLnZhbGlkYXRlU3VibWF0cml4KGF4LCBheSwgYXcsIGFoKTsKCQliLnZhbGlkYXRlU3VibWF0cml4KGJ4LCBieSwgYncsIGJoKTsKCQlpZiAoYXcgIT0gYmgpCgkJCXRocm93IG5ldyBJbGxlZ2FsQXJndW1lbnRFeGNlcHRpb24oU3RyaW5nLmZvcm1hdCgiTWF0cmljZXMgb2YgJWTDlyVkIGFuZCAlZMOXJWQgY2Fubm90IGJlIG11bHRpcGxpZWQ6IG51bWJlciBvZiBjb2x1bW5zIG9mIHRoZSBmaXJzdCBtdXN0IG1hdGNoIHRoZSBudW1iZXIgb2Ygcm93cyBvZiB0aGUgc2Vjb25kLiIsIGF3LCBhaCwgYncsIGJoKSk7CgoJCWlmIChyID09IG51bGwpIHsKCQkJciA9IG5ldyBNYXRyaXgoYncsIGFoKTsKCQl9IGVsc2UgaWYgKHIudyAhPSBidyB8fCByLmggIT0gYWgpIHsKCQkJdGhyb3cgbmV3IElsbGVnYWxBcmd1bWVudEV4Y2VwdGlvbihTdHJpbmcuZm9ybWF0KCJCYWQgcmVzdWx0aW5nIG1hdHJpeCBzaXplOiBleHBlY3RlZCAlZMOXJWQsIGdvdCAlZMOXJWQuIiwgYncsIGFoLCByLncsIHIuaCkpOwoJCX0KCQlyZXR1cm4gcjsKCX0KCglwdWJsaWMgc3RhdGljIE1hdHJpeCBtdWx0aXBseV9BaVZlcigKCQlNYXRyaXggYSwgaW50IGF4LCBpbnQgYXksIGludCBhdywgaW50IGFoLAoJCU1hdHJpeCBiLCBpbnQgYngsIGludCBieSwgaW50IGJ3LCBpbnQgYmgsCgkJTWF0cml4IHIpIHsKCgkJciA9IHByZXBhcmVNdWx0aXBseShhLCBheCwgYXksIGF3LCBhaCwgYiwgYngsIGJ5LCBidywgYmgsIHIpOwoKCQlpbnQgYVJvd1N0YXJ0QWJzSWR4ID0gYS5hYnNvbHV0ZUluZGV4KGF4LCBheSk7IC8vIHBvaW50cyB0byB0aGUgYmVnaW5uaW5nIG9mIHRoZSBjdXJyZW50IHJvdyBvZiBBLCBpLiBlLiBBWzAsIHldLgoJCWludCBiQ29sU3RhcnRBYnNJZHggPSBiLmFic29sdXRlSW5kZXgoYngsIGJ5KTsgLy8gcG9pbnRzIHRvIHRoZSBiZWdpbm5pbmcgb2YgdGhlIGN1cnJlbnQgY29sdW1uIG9mIEIsIGkuIGUuIEJbeCwgMF0uCgoJCS8vIHJlc3VsdCBjZWxsICh4LCB5KSAocG9pbnRlZCBieSByQWJzSWR4KSBnZXRzIHRoZSBkb3QgcHJvZHVjdCBvZiBjdXJyZW50IHJvdyBvZiBBIGFuZCBjdXJyZW50IGNvbHVtbiBvZiBCLgoJCWZvciAoaW50IHkgPSAwLCByQWJzSWR4ID0gMDsgeSA8IHIuaDsgeSsrLCBhUm93U3RhcnRBYnNJZHggKz0gYS53LCBiQ29sU3RhcnRBYnNJZHggLT0gci53KSB7CgkJCWZvciAoaW50IHggPSAwOyB4IDwgci53OyB4KyssIHJBYnNJZHgrKywgYkNvbFN0YXJ0QWJzSWR4KyspIHsKCQkJCWludCBkb3QgPSAwOwoJCQkJZm9yIChpbnQgaSA9IDAsIGFBYnNJZHggPSBhUm93U3RhcnRBYnNJZHgsIGJBYnNJZHggPSBiQ29sU3RhcnRBYnNJZHg7IGkgPCBhdzsgaSsrLCBhQWJzSWR4KyssIGJBYnNJZHggKz0gYi53KSB7CgkJCQkJZG90ICs9IGEucmF3W2FBYnNJZHhdICogYi5yYXdbYkFic0lkeF07CgkJCQl9CgkJCQlyLnJhd1tyQWJzSWR4XSA9IGRvdDsKCQkJfQoJCX0KCQlyZXR1cm4gcjsKCX0KCglwdWJsaWMgTWF0cml4IG11bHRpcGx5X0FpVmVyKE1hdHJpeCBiKSB7CgkJcmV0dXJuIG11bHRpcGx5X0FpVmVyKHRoaXMsIDAsIDAsIHcsIGgsIGIsIDAsIDAsIGIudywgYi5oLCBudWxsKTsKCX0KCglpbnQgZ2V0KGludCB4LCBpbnQgeSkgewoJCXJldHVybiByYXdbeSAqIHcgKyB4XTsKCX0KCgl2b2lkIHNldChpbnQgeCwgaW50IHksIGludCB2YWx1ZSkgewoJCXJhd1t5ICogdyArIHhdID0gdmFsdWU7Cgl9CgoJcHVibGljIHN0YXRpYyBNYXRyaXggbXVsdGlwbHlfR3NWZXIoCgkJTWF0cml4IGEsIGludCBheCwgaW50IGF5LCBpbnQgYXcsIGludCBhaCwKCQlNYXRyaXggYiwgaW50IGJ4LCBpbnQgYnksIGludCBidywgaW50IGJoLAoJCU1hdHJpeCByKSB7CgoJCXIgPSBwcmVwYXJlTXVsdGlwbHkoYSwgYXgsIGF5LCBhdywgYWgsIGIsIGJ4LCBieSwgYncsIGJoLCByKTsKCgkJZm9yIChpbnQgeSA9IDA7IHkgPCByLmg7IHkrKykgewoJCQlmb3IgKGludCB4ID0gMDsgeCA8IHIudzsgeCsrKSB7CgkJCQlpbnQgZG90ID0gMDsKCQkJCWZvciAoaW50IGkgPSAwOyBpIDwgYXc7IGkrKykgewoJCQkJCWRvdCArPSBhLmdldChpLCB5KSAqIGIuZ2V0KHgsIGkpOwoJCQkJfQoJCQkJci5zZXQoeCwgeSwgZG90KTsKCQkJfQoJCX0KCQlyZXR1cm4gcjsKCX0KCglwdWJsaWMgTWF0cml4IG11bHRpcGx5X0dzVmVyKE1hdHJpeCBiKSB7CgkJcmV0dXJuIG11bHRpcGx5X0dzVmVyKHRoaXMsIDAsIDAsIHcsIGgsIGIsIDAsIDAsIGIudywgYi5oLCBudWxsKTsKCX0KCglAT3ZlcnJpZGUKCXB1YmxpYyBib29sZWFuIGVxdWFscyhPYmplY3Qgb3RoZXIpIHsKCQlpZiAodGhpcyA9PSBvdGhlcikgcmV0dXJuIHRydWU7CgkJaWYgKHRoaXMuZ2V0Q2xhc3MoKSAhPSBvdGhlci5nZXRDbGFzcygpKSByZXR1cm4gZmFsc2U7CgkJTWF0cml4IG9tID0gKE1hdHJpeCkgb3RoZXI7CgkJcmV0dXJuIHcgPT0gb20udyAmJiBoID09IG9tLmggJiYgQXJyYXlzLmVxdWFscyhyYXcsIG9tLnJhdyk7Cgl9CgoJLy8ganVzdCBmb3IgcmVzdWx0aW5nIG1hdHJpY2VzIHRvIG5vdCBiZSBzdWJqZWN0ZWQgdG8gZGVhZCBzdG9yZSBlbGltaW5hdGlvbi4KCXB1YmxpYyBpbnQgZWxlbWVudHNTdW0oKSB7CgkJaW50IHN1bSA9IDA7CgkJZm9yIChpbnQgaSA9IDA7IGkgPCByYXcubGVuZ3RoOyBpKyspIHsKCQkJc3VtICs9IHJhd1tpXTsKCQl9CgkJcmV0dXJuIHN1bTsKCX0KfQoKY2xhc3MgSWRlb25lCnsKCXN0YXRpYyB2b2lkIHRlc3QoKSB7CgkJLy8gd3d3Lm1hdGh3YXJlaG91c2UuY29tL2FsZ2VicmEvbWF0cml4L2ltYWdlcy9wcm9kdWN0LW1hdHJpeDIucG5nCgkJTWF0cml4CgkJCWEgPSBNYXRyaXguZXhwbGljaXQoNCwgMiwKCQkJCTEsIDQsIDYsIDEwLAoJCQkJMiwgNywgNSwgMyksCgoJCQliID0gTWF0cml4LmV4cGxpY2l0KDMsIDQsCgkJCQkxLCA0LCA2LAoJCQkJMiwgNywgNSwKCQkJCTksIDAsIDExLAoJCQkJMywgMSwgMCksCgoJCQlleHBlY3RlZCA9IE1hdHJpeC5leHBsaWNpdCgzLCAyLAoJCQkJOTMsIDQyLCA5MiwKCQkJCTcwLCA2MCwgMTAyKSwKCQkJCQoJCQlhYlByb2RfQWlWZXIgPSBhLm11bHRpcGx5X0FpVmVyKGIpLAoJCQlhYlByb2RfR3NWZXIgPSBhLm11bHRpcGx5X0dzVmVyKGIpOwoKCQlpZiAoIWFiUHJvZF9BaVZlci5lcXVhbHMoZXhwZWN0ZWQpKSB0aHJvdyBuZXcgUnVudGltZUV4Y2VwdGlvbihTdHJpbmcuZm9ybWF0KCJiYWQgTWF0cml4Lm11bHRpcGx5X0FpVmVyOiBnb3RcbiVzXG5leHBlY3RlZFxuJXMuIiwgYWJQcm9kX0FpVmVyLCBleHBlY3RlZCkpOwoJCWlmICghYWJQcm9kX0dzVmVyLmVxdWFscyhleHBlY3RlZCkpIHRocm93IG5ldyBSdW50aW1lRXhjZXB0aW9uKFN0cmluZy5mb3JtYXQoImJhZCBNYXRyaXgubXVsdGlwbHlfR3NWZXI6IGdvdFxuJXNcbmV4cGVjdGVkXG4lcy4iLCBhYlByb2RfR3NWZXIsIGV4cGVjdGVkKSk7Cgl9CgoJc3RhdGljIHZvaWQgYmVuY2htYXJrKCkgewoJCU1hdHJpeCBhID0gbmV3IE1hdHJpeCgxMDAwLCA1MDApOwoJCU1hdHJpeCBiID0gbmV3IE1hdHJpeCg2MDAsIDEwMDApOwoJCU1hdHJpeCByID0gbmV3IE1hdHJpeCg2MDAsIDUwMCk7IC8vIHNvIGFsbG9jYXRpb24gd29uJ3QgaGFwcGVuIGR1cmluZyB0aW1lIG1lYXN1cmVtZW50CgoJCWxvbmcgc3RhcnRfQWlWZXIgPSBTeXN0ZW0ubmFub1RpbWUoKTsKCQlNYXRyaXgubXVsdGlwbHlfQWlWZXIoYSwgMCwgMCwgYS53LCBhLmgsIGIsIDAsIDAsIGIudywgYi5oLCByKTsKCQlkb3VibGUgdGltZV9BaVZlciA9IChTeXN0ZW0ubmFub1RpbWUoKSAtIHN0YXJ0X0FpVmVyKSAqIDFlLTk7CgkJU3lzdGVtLm91dC5wcmludGxuKFN0cmluZy5mb3JtYXQoIk11bHRpcGxpY2F0aW9uIHVzaW5nIGFic29sdXRlIGluZGljZXM6ICUuMmYgcyIsIHRpbWVfQWlWZXIsIHIuZWxlbWVudHNTdW0oKSkpOwoKCQlsb25nIHN0YXJ0X0dzVmVyID0gU3lzdGVtLm5hbm9UaW1lKCk7CgkJTWF0cml4Lm11bHRpcGx5X0dzVmVyKGEsIDAsIDAsIGEudywgYS5oLCBiLCAwLCAwLCBiLncsIGIuaCwgcik7CgkJZG91YmxlIHRpbWVfR3NWZXIgPSAoU3lzdGVtLm5hbm9UaW1lKCkgLSBzdGFydF9Hc1ZlcikgKiAxZS05OwoJCVN5c3RlbS5vdXQucHJpbnRsbihTdHJpbmcuZm9ybWF0KCJNdWx0aXBsaWNhdGlvbiB1c2luZyAuZ2V0KCkvLnNldCgpOiAlLjJmIHMgKCVzKSIsIHRpbWVfR3NWZXIsIGp1ZGdlbWVudCh0aW1lX0dzVmVyLCB0aW1lX0FpVmVyKSwgci5lbGVtZW50c1N1bSgpKSk7Cgl9CgkKCXN0YXRpYyBTdHJpbmcganVkZ2VtZW50KGRvdWJsZSB0aW1lLCBkb3VibGUgcmVmVGltZSkgewoJCWZpbmFsIGRvdWJsZSBhYnNUaHJlc2hvbGQgPSAwLjEsIHJlbFRocmVzaG9sZCA9IC4wNTsKCgkJcmV0dXJuIHRpbWUgPD0gYWJzVGhyZXNob2xkIHx8IHJlZlRpbWUgPD0gYWJzVGhyZXNob2xkID8KCQkJU3RyaW5nLmZvcm1hdCgiY2FuJ3QganVkZ2UsIG1pbi4gcmVxdWlyZWQgdGltZSBpcyAlLjFmIHMiLCBhYnNUaHJlc2hvbGQpIDoKCQkJTWF0aC5tYXgodGltZSwgcmVmVGltZSkgPiAoMSArIHJlbFRocmVzaG9sZCkgKiBNYXRoLm1pbih0aW1lLCByZWZUaW1lKSA/CgkJCVN0cmluZy5mb3JtYXQoIiUuMGYlJSAlcyIsIE1hdGguYWJzKHRpbWUgLyByZWZUaW1lIC0gMSkgKiAxMDAsIHRpbWUgPCByZWZUaW1lID8gImZhc3RlciIgOiAic2xvd2VyIikgOgoJCQlTdHJpbmcuZm9ybWF0KCJubyBvYnNlcnZhYmxlIGRpZmZlcmVuY2UsIG1pbi4gJS4wZiUlIHJlcXVpcmVkIiwgcmVsVGhyZXNob2xkICogMTAwKTsKCX0KCglwdWJsaWMgc3RhdGljIHZvaWQgbWFpbiAoU3RyaW5nW10gYXJncykKCXsKCQl0cnkgewoJCQl0ZXN0KCk7CgkJCWJlbmNobWFyaygpOwoJCX0gY2F0Y2ggKEV4Y2VwdGlvbiBlKSB7CgkJCVN5c3RlbS5vdXQucHJpbnRsbihlKTsKCQl9Cgl9Cn0=