#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <utility>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <numeric>
#include <list>
#include <time.h>
#include <string.h>
#include <assert.h>
using namespace std;
typedef long long ll;
template < class T> bool INRANGE( T x,T a,T b) { return a<= x&& x<= b; }
#define NG (-1)
#define BIG (987654321)
#define SZ(a) ((int)(a).size())
// 最大流 Dinic O(EV^2)だけど、実際もっとはやい。
class Dinic
{
public :
Dinic( int input_maxv) : maxv( input_maxv)
{
G.resize ( input_maxv) ;
level.resize ( input_maxv) ;
iter.resize ( input_maxv) ;
}
void add_edge_both( int from, int to, int cap)
{
if ( cap> 0 )
{
const int rev_from = SZ( G[ from] ) ;
const int rev_to = SZ( G[ to] ) ;
G[ from] .push_back ( edge( to,cap,rev_to) ) ;
G[ to] .push_back ( edge( from,cap,rev_from) ) ;
}
}
void add_edge( int from, int to, int cap)
{
if ( cap> 0 )
{
const int rev_from = SZ( G[ from] ) ;
const int rev_to = SZ( G[ to] ) ;
G[ from] .push_back ( edge( to,cap,rev_to) ) ;
G[ to] .push_back ( edge( from,0 ,rev_from) ) ;
}
}
// sからtへの最大流を求める
int max_flow( int s, int t)
{
int flow = 0 ;
for ( ;; )
{
bfs( s) ;
if ( level[ t] < 0 ) break ;
fill( iter.begin ( ) ,iter.end ( ) ,0 ) ;
int f;
while ( ( f= dfs( s,t,DINIC_INF) ) > 0 )
{
flow + = f;
}
}
return flow;
}
// ノードsから辿れる範囲を求める(これ以上流せないところcap=0は、リンクがなくなる)
// (流し終わったあとsourceからたどれる範囲が、最小カット時のs側。たどれない法がt側。その境界がカットするところ。)
vector < bool > get_nodes_in_group( int s)
{
vector < bool > ret( maxv) ;
queue< int > que;
que.push ( s) ;
while ( ! que.empty ( ) )
{
int v = que.front ( ) ;
que.pop ( ) ;
ret[ v] = true ;
for ( int i= 0 ; i< SZ( G[ v] ) ; i++ )
{
edge & e = G[ v] [ i] ;
if ( e.cap > 0 && ! ret[ e.to ] )
{
que.push ( e.to ) ;
}
}
}
return ret;
}
void disp( )
{
for ( int v = 0 ; v < maxv; v++ )
{
printf ( "%d:" ,v) ;
for ( int i= 0 ; i< SZ( G[ v] ) ; i++ )
{
if ( G[ v] [ i] .init_cap > 0 )
{
printf ( "->%d(%d)," ,G[ v] [ i] .to ,G[ v] [ i] .init_cap ) ;
}
}
printf ( "\n " ) ;
}
}
private :
// sからの最短距離をBFSで計算する
void bfs( int s)
{
fill( level.begin ( ) ,level.end ( ) ,NG) ;
queue< int > que;
level[ s] = 0 ;
que.push ( s) ;
while ( ! que.empty ( ) )
{
int v = que.front ( ) ;
que.pop ( ) ;
for ( int i= 0 ; i< SZ( G[ v] ) ; i++ )
{
edge & e = G[ v] [ i] ;
if ( e.cap > 0 && level[ e.to ] < 0 )
{
level[ e.to ] = level[ v] + 1 ;
que.push ( e.to ) ;
}
}
}
}
// 増加パスをDFSで探す
int dfs( int v, int t, int f)
{
if ( v== t) return f;
for ( int & i= iter[ v] ; i< SZ( G[ v] ) ; i++ )
{
edge& e = G[ v] [ i] ;
if ( e.cap > 0 && level[ v] < level[ e.to ] )
{
int d = dfs( e.to , t, min( f, e.cap ) ) ;
if ( d> 0 )
{
e.cap - = d;
G[ e.to ] [ e.rev ] .cap + = d;
return d;
}
}
}
return 0 ;
}
static const int DINIC_INF = INT_MAX ; // 容量をllにしたいときは、ここも変える
struct edge
{
edge( int input_to, int input_cap, int input_rev) : to( input_to) , cap( input_cap) , rev( input_rev) , init_cap( input_cap) { }
int to; // 行先
int cap; // 容量
int rev; // 逆辺
int init_cap; // 初期容量(デバッグ用)
} ;
int maxv;
vector < vector < edge> > G; // グラフの隣接リスト
vector < int > level; // sからの距離
vector < int > iter; // どこまで調べ終わったか
} ;
class SurroundingGame {
public :
int maxScore( vector < string> cost, vector < string> benefit) {
// BEGIN CUT HERE
// 1. 制約条件から読むこと
// 2. 問題が理解できたら、Exampleで理解があってるか確認
// 3. 解法が分からないときは、とにかく小さいケースの、「答え」を書き出す。プログラムも使って可
// 4. それでも分からないときはグラフやx-y図を描く
// MODの問題はlong longで。=0, =1, ="0001", ="0357"とか危険
// END CUT HERE
H = SZ( cost) ;
W = SZ( cost[ 0 ] ) ;
const int S = H* W* 2 ;
const int T = S+ 1 ;
const int V = T+ 1 ;
Dinic* dinic = new Dinic( V) ;
int totalDefaultProfit = 0 ;
for ( int y = 0 ; y < H; y++ )
{
for ( int x = 0 ; x < W; x++ )
{
//---------- 置く or 置かない ----------
// 置く profit = benefit-cost
// 置かない profit = 0
{
const int bc = getDecode( benefit[ y] [ x] ) - getDecode( cost[ y] [ x] ) ;
int capS;
int capT;
if ( bc> 0 )
{
// benefit-cost>0のとき
// defaultProfit = benefit-cost
// 置く cap = defaultProfit - profit = 0
// 置かない cap = defaultProfit - profit = benefit-cost
capS = 0 ; // 置く
capT = bc; // 置かない
totalDefaultProfit + = bc;
}
else
{
// benefit-cost<0のとき
// defaultProfit = 0
// 置く cap = defaultProfit - profit = cost-benefit;
// 置かない cap = defaultProfit - profit = 0;
capS = - bc; // 置く
capT = 0 ; // 置かない
}
// 隣り合う条件を使うときに、二部グラフを利用。S側とT側を逆にする。
if ( ( x+ y) & 1 )
{
swap( capS,capT) ;
}
dinic- > add_edge( S, oku( y,x) , capS) ;
dinic- > add_edge( oku( y,x) , T, capT) ;
}
//---------- 囲まれない or 囲まれる ----------
{
const int b = getDecode( benefit[ y] [ x] ) ;
// 囲まれない profit = 0
// 囲まれる profit = benefit
// この問題ではbenefitは常に0以上
// defaultProfit = b
// 囲まない cap = defaultProfit - profit = benefit;
// 囲む cap = defaultProfit - profit = 0;
totalDefaultProfit + = b;
int capS = b;
int capT = 0 ;
// 隣り合う条件を使うときに、二部グラフを利用。S側とT側を逆にする。
if ( ( x+ y) & 1 )
{
swap( capS,capT) ;
}
dinic- > add_edge( S, kakomu( y,x) , capS) ;
dinic- > add_edge( kakomu( y,x) , T, capT) ;
}
//----- 同じ場所で 置く && 囲まれる は同時に成立禁止 -----
// 問題の条件より、置くか囲まれると、利益bもらえる。
// このままだと、置いてかつ囲まれると、利益が2bもらえてしまう。
// 置く && 囲む は同時に成立しても、グラフが切れないようにする。
if ( ( x+ y) & 1 )
{
dinic- > add_edge( oku( y,x) , kakomu( y,x) , BIG) ;
}
else
{
dinic- > add_edge( kakomu( y,x) , oku( y,x) , BIG) ;
}
//----- 囲まれる条件 -----
// 囲まれるときは、隣の場所はすべて置く必要がある。
// 言い換えると、囲まれたマスのまわりに、1個でも置かないマスがあったら、グラフが切れないようにする。
const static int dy[ ] = { - 1 , 0 , 1 , 0 } ; // U,R,D,L
const static int dx[ ] = { 0 , 1 , 0 ,- 1 } ;
for ( int d = 0 ; d < 4 ; d++ )
{
const int ny = y+ dy[ d] ;
const int nx = x+ dx[ d] ;
if ( INRANGE( ny,0 ,H- 1 ) && INRANGE( nx,0 ,W- 1 ) )
{
if ( ( x+ y) & 1 )
{
dinic- > add_edge( oku( ny,nx) , kakomu( y,x) , BIG) ;
}
else
{
dinic- > add_edge( kakomu( y,x) , oku( ny,nx) , BIG) ;
}
}
}
}
}
const int result = totalDefaultProfit - dinic- > max_flow( S,T) ;
delete dinic;
return result;
}
private :
int getDecode( const char c)
{
if ( isdigit ( c) )
{
return c- '0' ;
}
else if ( islower ( c) )
{
return c- 'a' + 10 ;
}
else if ( isupper ( c) )
{
return c- 'A' + 36 ;
}
return NG;
}
int oku( int y, int x)
{
return y* W+ x;
}
int kakomu( int y, int x)
{
return y* W+ x+ H* W;
}
int H;
int W;
private :
template < typename T> string print_array( const vector< T> & V) { ostringstream os; os << "{ " ; for ( typename vector< T> :: const_iterator iter = V.begin ( ) ; iter ! = V.end ( ) ; ++ iter) os << '\" ' << * iter << "\" ," ; os << " }" ; return os.str ( ) ; }
void verify_case( int Case, const int & Expected, const int & Received) { cerr << "Test Case #" << Case << "..." ; if ( Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\t Expected: \" " << Expected << '\" ' << endl; cerr << "\t Received: \" " << Received << '\" ' << endl; } }
public :
void run_test( int Case) {
int n = 0 ;
// test_case_0
if ( ( Case == - 1 ) || ( Case == n) ) {
string Arr0[ ] = { "21" ,"12" } ;
string Arr1[ ] = { "21" ,"12" } ;
int Arg2 = 4 ;
vector < string> Arg0( Arr0, Arr0 + ( sizeof ( Arr0) / sizeof ( Arr0[ 0 ] ) ) ) ;
vector < string> Arg1( Arr1, Arr1 + ( sizeof ( Arr1) / sizeof ( Arr1[ 0 ] ) ) ) ;
verify_case( n, Arg2, maxScore( Arg0, Arg1) ) ;
}
n++ ;
// test_case_1
if ( ( Case == - 1 ) || ( Case == n) ) {
string Arr0[ ] = { "ZZ" ,"ZZ" } ;
string Arr1[ ] = { "11" ,"11" } ;
int Arg2 = 0 ;
vector < string> Arg0( Arr0, Arr0 + ( sizeof ( Arr0) / sizeof ( Arr0[ 0 ] ) ) ) ;
vector < string> Arg1( Arr1, Arr1 + ( sizeof ( Arr1) / sizeof ( Arr1[ 0 ] ) ) ) ;
verify_case( n, Arg2, maxScore( Arg0, Arg1) ) ;
}
n++ ;
// test_case_2
if ( ( Case == - 1 ) || ( Case == n) ) {
string Arr0[ ] = { "XXX" ,"XXX" ,"XXX" } ;
string Arr1[ ] = { "aaa" ,"aZa" ,"aaa" } ;
int Arg2 = 2 ;
vector < string> Arg0( Arr0, Arr0 + ( sizeof ( Arr0) / sizeof ( Arr0[ 0 ] ) ) ) ;
vector < string> Arg1( Arr1, Arr1 + ( sizeof ( Arr1) / sizeof ( Arr1[ 0 ] ) ) ) ;
verify_case( n, Arg2, maxScore( Arg0, Arg1) ) ;
}
n++ ;
// test_case_3
if ( ( Case == - 1 ) || ( Case == n) ) {
string Arr0[ ] = { "asam" ,"atik" } ;
string Arr1[ ] = { "123A" ,"45BC" } ;
int Arg2 = 71 ;
vector < string> Arg0( Arr0, Arr0 + ( sizeof ( Arr0) / sizeof ( Arr0[ 0 ] ) ) ) ;
vector < string> Arg1( Arr1, Arr1 + ( sizeof ( Arr1) / sizeof ( Arr1[ 0 ] ) ) ) ;
verify_case( n, Arg2, maxScore( Arg0, Arg1) ) ;
}
n++ ;
// test_case_4
if ( ( Case == - 1 ) || ( Case == n) ) {
string Arr0[ ] = { "IIIIIIII" ,
"IIWWWWII" ,
"IIWIIIII" ,
"IIWIIIII" ,
"IIWWWWII" ,
"IIIIIWII" ,
"IIIIIWII" ,
"IIWWWWII" ,
"IIIIIIII" }
;
string Arr1[ ] = { "IIIIIIII" ,
"II0000II" ,
"II0II0II" ,
"II0II0II" ,
"II0000II" ,
"II0II0II" ,
"II0II0II" ,
"II0000II" ,
"IIIIIIII" }
;
int Arg2 = 606 ;
vector < string> Arg0( Arr0, Arr0 + ( sizeof ( Arr0) / sizeof ( Arr0[ 0 ] ) ) ) ;
vector < string> Arg1( Arr1, Arr1 + ( sizeof ( Arr1) / sizeof ( Arr1[ 0 ] ) ) ) ;
verify_case( n, Arg2, maxScore( Arg0, Arg1) ) ;
}
n++ ;
}
} ;
int main( ) {
SurroundingGame* ___test = new SurroundingGame( ) ;
___test- > run_test( - 1 ) ;
delete ___test;
}
I2luY2x1ZGUgPGNzdGRpbz4KI2luY2x1ZGUgPGNzdGRsaWI+CiNpbmNsdWRlIDxjbWF0aD4KI2luY2x1ZGUgPGNsaW1pdHM+CiNpbmNsdWRlIDxjZmxvYXQ+CiNpbmNsdWRlIDxtYXA+CiNpbmNsdWRlIDx1dGlsaXR5PgojaW5jbHVkZSA8c2V0PgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDxtZW1vcnk+CiNpbmNsdWRlIDxzdHJpbmc+CiNpbmNsdWRlIDx2ZWN0b3I+CiNpbmNsdWRlIDxhbGdvcml0aG0+CiNpbmNsdWRlIDxmdW5jdGlvbmFsPgojaW5jbHVkZSA8c3N0cmVhbT4KI2luY2x1ZGUgPGNvbXBsZXg+CiNpbmNsdWRlIDxzdGFjaz4KI2luY2x1ZGUgPHF1ZXVlPgojaW5jbHVkZSA8bnVtZXJpYz4KI2luY2x1ZGUgPGxpc3Q+CiNpbmNsdWRlIDx0aW1lLmg+CiNpbmNsdWRlIDxzdHJpbmcuaD4KI2luY2x1ZGUgPGFzc2VydC5oPgp1c2luZyBuYW1lc3BhY2Ugc3RkOwp0eXBlZGVmIGxvbmcgbG9uZyBsbDsKCnRlbXBsYXRlPGNsYXNzIFQ+IGJvb2wgSU5SQU5HRShUIHgsVCBhLFQgYikgeyByZXR1cm4gYTw9eCYmeDw9YjsgfQojZGVmaW5lIE5HICgtMSkKI2RlZmluZSBCSUcgKDk4NzY1NDMyMSkKI2RlZmluZSBTWihhKSAoKGludCkoYSkuc2l6ZSgpKSAKCi8vIOacgOWkp+a1gSBEaW5pYyBPKEVWXjIp44Gg44GR44Gp44CB5a6f6Zqb44KC44Gj44Go44Gv44KE44GE44CCCmNsYXNzIERpbmljCnsKcHVibGljOgoJRGluaWMoaW50IGlucHV0X21heHYpIDogbWF4dihpbnB1dF9tYXh2KQoJewoJCUcucmVzaXplKGlucHV0X21heHYpOwoJCWxldmVsLnJlc2l6ZShpbnB1dF9tYXh2KTsKCQlpdGVyLnJlc2l6ZShpbnB1dF9tYXh2KTsKCX0KCgl2b2lkIGFkZF9lZGdlX2JvdGgoaW50IGZyb20sIGludCB0bywgaW50IGNhcCkKCXsKCQlpZihjYXA+MCkKCQl7CgkJCWNvbnN0IGludCByZXZfZnJvbQk9IFNaKEdbZnJvbV0pOwoJCQljb25zdCBpbnQgcmV2X3RvCT0gU1ooR1t0b10pOwoJCQlHW2Zyb21dLnB1c2hfYmFjayhlZGdlKHRvLGNhcCxyZXZfdG8pKTsKCQkJR1t0b10ucHVzaF9iYWNrKGVkZ2UoZnJvbSxjYXAscmV2X2Zyb20pKTsKCQl9Cgl9CgoJdm9pZCBhZGRfZWRnZShpbnQgZnJvbSwgaW50IHRvLCBpbnQgY2FwKQoJewoJCWlmKGNhcD4wKQoJCXsKCQkJY29uc3QgaW50IHJldl9mcm9tCT0gU1ooR1tmcm9tXSk7CgkJCWNvbnN0IGludCByZXZfdG8JPSBTWihHW3RvXSk7CgkJCUdbZnJvbV0ucHVzaF9iYWNrKGVkZ2UodG8sY2FwLHJldl90bykpOwoJCQlHW3RvXS5wdXNoX2JhY2soZWRnZShmcm9tLDAscmV2X2Zyb20pKTsKCQl9Cgl9CgoJLy8gc+OBi+OCiXTjgbjjga7mnIDlpKfmtYHjgpLmsYLjgoHjgosKCWludCBtYXhfZmxvdyhpbnQgcywgaW50IHQpCgl7CgkJaW50IGZsb3cgPSAwOwoJCWZvcig7OykKCQl7CgkJCWJmcyhzKTsKCQkJaWYobGV2ZWxbdF08MCkgYnJlYWs7CgkJCWZpbGwoaXRlci5iZWdpbigpLGl0ZXIuZW5kKCksMCk7CgkJCWludCBmOwoJCQl3aGlsZSggKGY9ZGZzKHMsdCxESU5JQ19JTkYpKT4wKQoJCQl7CgkJCQlmbG93ICs9IGY7CgkJCX0KCQl9CgoJCXJldHVybiBmbG93OwoJfQoKCS8vICDjg47jg7zjg4lz44GL44KJ6L6/44KM44KL56+E5Zuy44KS5rGC44KB44KL77yI44GT44KM5Lul5LiK5rWB44Gb44Gq44GE44Go44GT44KNY2FwPTDjga/jgIHjg6rjg7Pjgq/jgYzjgarjgY/jgarjgovvvIkKCS8vIO+8iOa1geOBl+e1guOCj+OBo+OBn+OBguOBqHNvdXJjZeOBi+OCieOBn+OBqeOCjOOCi+evhOWbsuOBjOOAgeacgOWwj+OCq+ODg+ODiOaZguOBrnPlgbTjgILjgZ/jganjgozjgarjgYTms5XjgYx05YG044CC44Gd44Gu5aKD55WM44GM44Kr44OD44OI44GZ44KL44Go44GT44KN44CC77yJCgl2ZWN0b3IgPGJvb2w+IGdldF9ub2Rlc19pbl9ncm91cChpbnQgcykKCXsKCQl2ZWN0b3IgPGJvb2w+IHJldChtYXh2KTsKCgkJcXVldWU8aW50PiBxdWU7CgkJcXVlLnB1c2gocyk7CgkJd2hpbGUoIXF1ZS5lbXB0eSgpKQoJCXsKCQkJaW50IHYgPSBxdWUuZnJvbnQoKTsKCQkJcXVlLnBvcCgpOwoJCQlyZXRbdl09dHJ1ZTsKCgkJCWZvcihpbnQgaT0wO2k8U1ooR1t2XSk7aSsrKQoJCQl7CgkJCQllZGdlICZlID0gR1t2XVtpXTsKCQkJCWlmKGUuY2FwPjAgJiYgIXJldFtlLnRvXSkKCQkJCXsKCQkJCQlxdWUucHVzaChlLnRvKTsKCQkJCX0KCQkJfQoJCX0KCQlyZXR1cm4gcmV0OwoJfQoKCXZvaWQgZGlzcCgpCgl7CgkJZm9yIChpbnQgdiA9IDA7IHYgPCBtYXh2OyB2KyspCgkJewoJCQlwcmludGYoIiVkOiIsdik7CgkJCWZvcihpbnQgaT0wO2k8U1ooR1t2XSk7aSsrKQoJCQl7CgkJCQlpZihHW3ZdW2ldLmluaXRfY2FwPjApCgkJCQl7CgkJCQkJcHJpbnRmKCItPiVkKCVkKSwiLEdbdl1baV0udG8sR1t2XVtpXS5pbml0X2NhcCk7CgkJCQl9CgkJCX0KCQkJcHJpbnRmKCJcbiIpOwoJCX0KCX0KCnByaXZhdGU6CgkvLyBz44GL44KJ44Gu5pyA55+t6Led6Zui44KSQkZT44Gn6KiI566X44GZ44KLCgl2b2lkIGJmcyhpbnQgcykKCXsKCQlmaWxsKGxldmVsLmJlZ2luKCksbGV2ZWwuZW5kKCksTkcpOwoJCXF1ZXVlPGludD4gcXVlOwoJCWxldmVsW3NdPTA7CgkJcXVlLnB1c2gocyk7CgkJd2hpbGUoIXF1ZS5lbXB0eSgpKQoJCXsKCQkJaW50IHYgPSBxdWUuZnJvbnQoKTsKCQkJcXVlLnBvcCgpOwoJCQlmb3IoaW50IGk9MDtpPFNaKEdbdl0pO2krKykKCQkJewoJCQkJZWRnZSAmZSA9IEdbdl1baV07CgkJCQlpZihlLmNhcD4wICYmIGxldmVsW2UudG9dPDApCgkJCQl7CgkJCQkJbGV2ZWxbZS50b10gPSBsZXZlbFt2XSArIDE7CgkJCQkJcXVlLnB1c2goZS50byk7CgkJCQl9CgkJCX0KCQl9Cgl9CgoJLy8g5aKX5Yqg44OR44K544KSREZT44Gn5o6i44GZCglpbnQgZGZzKGludCB2LCBpbnQgdCwgaW50IGYpCgl7CgkJaWYodj09dCkgcmV0dXJuIGY7CgkJZm9yIChpbnQgJmk9aXRlclt2XTtpPFNaKEdbdl0pO2krKykKCQl7CgkJCWVkZ2UmIGUgPSBHW3ZdW2ldOwoJCQlpZihlLmNhcD4wICYmIGxldmVsW3ZdPGxldmVsW2UudG9dKQoJCQl7CgkJCQlpbnQgZCA9IGRmcyhlLnRvLCB0LCBtaW4oZiwgZS5jYXApKTsKCQkJCWlmKGQ+MCkKCQkJCXsKCQkJCQllLmNhcCAtPSBkOwoJCQkJCUdbZS50b11bZS5yZXZdLmNhcCArPSBkOwoJCQkJCXJldHVybiBkOwoJCQkJfQoJCQl9CgkJfQoJCXJldHVybiAwOwoJfQoKCXN0YXRpYyBjb25zdCBpbnQgRElOSUNfSU5GID0gSU5UX01BWDsgLy8g5a656YeP44KSbGzjgavjgZfjgZ/jgYTjgajjgY3jga/jgIHjgZPjgZPjgoLlpInjgYjjgosKCglzdHJ1Y3QgZWRnZSAKCXsKCQllZGdlKGludCBpbnB1dF90bywgaW50IGlucHV0X2NhcCwgaW50IGlucHV0X3JldikgOiB0byhpbnB1dF90byksIGNhcChpbnB1dF9jYXApLCByZXYoaW5wdXRfcmV2KSwgaW5pdF9jYXAoaW5wdXRfY2FwKSB7fQoJCWludCB0bzsJCS8vIOihjOWFiAoJCWludCBjYXA7CS8vIOWuuemHjwoJCWludCByZXY7CS8vIOmAhui+ugoJCWludCBpbml0X2NhcDsgLy8g5Yid5pyf5a656YeP77yI44OH44OQ44OD44Kw55So77yJCgl9OwoKCWludAltYXh2OwoJdmVjdG9yIDwgdmVjdG9yIDxlZGdlPiA+IEc7CS8vIOOCsOODqeODleOBrumao+aOpeODquOCueODiAoJdmVjdG9yIDwgaW50ID4gbGV2ZWw7CQkvLyBz44GL44KJ44Gu6Led6ZuiCgl2ZWN0b3IgPCBpbnQgPiBpdGVyOwkJLy8g44Gp44GT44G+44Gn6Kq/44G557WC44KP44Gj44Gf44GLCn07CgoKY2xhc3MgU3Vycm91bmRpbmdHYW1lIHsKcHVibGljOgoKCglpbnQgbWF4U2NvcmUodmVjdG9yIDxzdHJpbmc+IGNvc3QsIHZlY3RvciA8c3RyaW5nPiBiZW5lZml0KSB7Ci8vIEJFR0lOIENVVCBIRVJFCgkJLy8gMS4g5Yi257SE5p2h5Lu244GL44KJ6Kqt44KA44GT44GoCgkJLy8gMi4g5ZWP6aGM44GM55CG6Kej44Gn44GN44Gf44KJ44CBRXhhbXBsZeOBp+eQhuino+OBjOOBguOBo+OBpuOCi+OBi+eiuuiqjQoJCS8vIDMuIOino+azleOBjOWIhuOBi+OCieOBquOBhOOBqOOBjeOBr+OAgeOBqOOBq+OBi+OBj+Wwj+OBleOBhOOCseODvOOCueOBruOAgeOAjOetlOOBiOOAjeOCkuabuOOBjeWHuuOBmeOAguODl+ODreOCsOODqeODoOOCguS9v+OBo+OBpuWPrwoJCS8vIDQuIOOBneOCjOOBp+OCguWIhuOBi+OCieOBquOBhOOBqOOBjeOBr+OCsOODqeODleOChHgteeWbs+OCkuaPj+OBjwoJCS8vIE1PROOBruWVj+mhjOOBr2xvbmcgbG9uZ+OBp+OAgj0wLCA9MSwgPSIwMDAxIiwgPSIwMzU3IuOBqOOBi+WNsemZugovLyBFTkQgQ1VUIEhFUkUKCQlIID0gU1ooY29zdCk7CgkJVyA9IFNaKGNvc3RbMF0pOwoJCWNvbnN0IGludCBTID0gSCpXKjI7CgkJY29uc3QgaW50IFQgPSBTKzE7CgkJY29uc3QgaW50IFYgPSBUKzE7CgkJRGluaWMqCWRpbmljID0gbmV3IERpbmljKFYpOwoKCQlpbnQgdG90YWxEZWZhdWx0UHJvZml0ID0gMDsKCQlmb3IgKGludCB5ID0gMDsgeSA8IEg7IHkrKykKCQl7CgkJCWZvciAoaW50IHggPSAwOyB4IDwgVzsgeCsrKQoJCQl7CgoJCQkJLy8tLS0tLS0tLS0tIOe9ruOBjyBvciDnva7jgYvjgarjgYQgLS0tLS0tLS0tLQoJCQkJLy8g572u44GPICAgICBwcm9maXQgPSBiZW5lZml0LWNvc3QKCQkJCS8vIOe9ruOBi+OBquOBhCBwcm9maXQgPSAwICAgICAgIAoJCQkJewoJCQkJCWNvbnN0IGludCBiYyA9IGdldERlY29kZShiZW5lZml0W3ldW3hdKS1nZXREZWNvZGUoY29zdFt5XVt4XSk7CgkJCQkJaW50IGNhcFM7CgkJCQkJaW50IGNhcFQ7CgoJCQkJCWlmKGJjPjApCgkJCQkJewoJCQkJCQkvLyBiZW5lZml0LWNvc3Q+MOOBruOBqOOBjQoJCQkJCQkvLyBkZWZhdWx0UHJvZml0ID0gYmVuZWZpdC1jb3N0CgkJCQkJCS8vIOe9ruOBjyAgICAgY2FwID0gZGVmYXVsdFByb2ZpdCAtIHByb2ZpdCA9IDAKCQkJCQkJLy8g572u44GL44Gq44GEIGNhcCA9IGRlZmF1bHRQcm9maXQgLSBwcm9maXQgPSBiZW5lZml0LWNvc3QKCQkJCQkJY2FwUyA9IDA7CS8vIOe9ruOBjwoJCQkJCQljYXBUID0gYmM7CS8vIOe9ruOBi+OBquOBhAoJCQkJCQl0b3RhbERlZmF1bHRQcm9maXQgKz0gYmM7CgkJCQkJfQoJCQkJCWVsc2UKCQkJCQl7CgkJCQkJCS8vIGJlbmVmaXQtY29zdDww44Gu44Go44GNCgkJCQkJCS8vIGRlZmF1bHRQcm9maXQgPSAwCgkJCQkJCS8vIOe9ruOBjyAgICAgY2FwID0gZGVmYXVsdFByb2ZpdCAtIHByb2ZpdCA9IGNvc3QtYmVuZWZpdDsKCQkJCQkJLy8g572u44GL44Gq44GEIGNhcCA9IGRlZmF1bHRQcm9maXQgLSBwcm9maXQgPSAwOwoJCQkJCQljYXBTID0gLWJjOwkvLyDnva7jgY8KCQkJCQkJY2FwVCA9IDA7CS8vIOe9ruOBi+OBquOBhAoJCQkJCX0KCgkJCQkJLy8g6Zqj44KK5ZCI44GG5p2h5Lu244KS5L2/44GG44Go44GN44Gr44CB5LqM6YOo44Kw44Op44OV44KS5Yip55So44CCU+WBtOOBqFTlgbTjgpLpgIbjgavjgZnjgovjgIIKCQkJCQlpZigoeCt5KSYxKQoJCQkJCXsKCQkJCQkJc3dhcChjYXBTLGNhcFQpOwoJCQkJCX0KCgkJCQkJZGluaWMtPmFkZF9lZGdlKFMsIG9rdSh5LHgpLCBjYXBTKTsKCQkJCQlkaW5pYy0+YWRkX2VkZ2Uob2t1KHkseCksIFQsIGNhcFQpOwoJCQkJfQoKCQkJCS8vLS0tLS0tLS0tLSDlm7Ljgb7jgozjgarjgYQgb3Ig5Zuy44G+44KM44KLIC0tLS0tLS0tLS0KCQkJCXsKCQkJCQljb25zdCBpbnQgYiA9IGdldERlY29kZShiZW5lZml0W3ldW3hdKTsKCQkJCQkvLyDlm7Ljgb7jgozjgarjgYQgcHJvZml0ID0gMAoJCQkJCS8vIOWbsuOBvuOCjOOCiyAgIHByb2ZpdCA9IGJlbmVmaXQKCgkJCQkJLy8g44GT44Gu5ZWP6aGM44Gn44GvYmVuZWZpdOOBr+W4uOOBqzDku6XkuIoKCQkJCQkvLyBkZWZhdWx0UHJvZml0ID0gYgoJCQkJCS8vIOWbsuOBvuOBquOBhCBjYXAgPSBkZWZhdWx0UHJvZml0IC0gcHJvZml0ID0gYmVuZWZpdDsKCQkJCQkvLyDlm7LjgoAgICAgIGNhcCA9IGRlZmF1bHRQcm9maXQgLSBwcm9maXQgPSAwOwoJCQkJCXRvdGFsRGVmYXVsdFByb2ZpdCArPSBiOwoJCQkJCWludCBjYXBTID0gYjsKCQkJCQlpbnQgY2FwVCA9IDA7CgoJCQkJCS8vIOmao+OCiuWQiOOBhuadoeS7tuOCkuS9v+OBhuOBqOOBjeOBq+OAgeS6jOmDqOOCsOODqeODleOCkuWIqeeUqOOAglPlgbTjgahU5YG044KS6YCG44Gr44GZ44KL44CCCgkJCQkJaWYoKHgreSkmMSkKCQkJCQl7CgkJCQkJCXN3YXAoY2FwUyxjYXBUKTsKCQkJCQl9CgoJCQkJCWRpbmljLT5hZGRfZWRnZShTLCBrYWtvbXUoeSx4KSwgY2FwUyk7CgkJCQkJZGluaWMtPmFkZF9lZGdlKGtha29tdSh5LHgpLCBULCBjYXBUKTsKCQkJCX0KCgkJCQkvLy0tLS0tIOWQjOOBmOWgtOaJgOOBpyDnva7jgY8gJiYg5Zuy44G+44KM44KLIOOBr+WQjOaZguOBq+aIkOeri+emgeatoiAtLS0tLSAKCQkJCS8vIOWVj+mhjOOBruadoeS7tuOCiOOCiuOAgee9ruOBj+OBi+WbsuOBvuOCjOOCi+OBqOOAgeWIqeebimLjgoLjgonjgYjjgovjgIIKCQkJCS8vIOOBk+OBruOBvuOBvuOBoOOBqOOAgee9ruOBhOOBpuOBi+OBpOWbsuOBvuOCjOOCi+OBqOOAgeWIqeebiuOBjDJi44KC44KJ44GI44Gm44GX44G+44GG44CCCgkJCQkvLyDnva7jgY8gJiYg5Zuy44KAIOOBr+WQjOaZguOBq+aIkOeri+OBl+OBpuOCguOAgeOCsOODqeODleOBjOWIh+OCjOOBquOBhOOCiOOBhuOBq+OBmeOCi+OAggoJCQkJaWYoKHgreSkmMSkKCQkJCXsKCQkJCQlkaW5pYy0+YWRkX2VkZ2Uob2t1KHkseCksIGtha29tdSh5LHgpLCBCSUcpOwoJCQkJfQoJCQkJZWxzZQoJCQkJewoJCQkJCWRpbmljLT5hZGRfZWRnZShrYWtvbXUoeSx4KSwgb2t1KHkseCksIEJJRyk7CgkJCQl9CgoJCQkJLy8tLS0tLSAg5Zuy44G+44KM44KL5p2h5Lu2IC0tLS0tIAoJCQkJLy8g5Zuy44G+44KM44KL44Go44GN44Gv44CB6Zqj44Gu5aC05omA44Gv44GZ44G544Gm572u44GP5b+F6KaB44GM44GC44KL44CCCgkJCQkvLyDoqIDjgYTmj5vjgYjjgovjgajjgIHlm7Ljgb7jgozjgZ/jg57jgrnjga7jgb7jgo/jgorjgavjgIHvvJHlgIvjgafjgoLnva7jgYvjgarjgYTjg57jgrnjgYzjgYLjgaPjgZ/jgonjgIHjgrDjg6njg5XjgYzliIfjgozjgarjgYTjgojjgYbjgavjgZnjgovjgIIKCQkJCWNvbnN0IHN0YXRpYyBpbnQgZHlbXSA9IHstMSwgMCwgMSwgMH07IC8vIFUsUixELEwKCQkJCWNvbnN0IHN0YXRpYyBpbnQgZHhbXSA9IHsgMCwgMSwgMCwtMX07CgkJCQlmb3IoaW50IGQgPSAwOyBkIDwgNDsgZCsrKQoJCQkJewoJCQkJCWNvbnN0IGludCBueSA9IHkrZHlbZF07IAoJCQkJCWNvbnN0IGludCBueCA9IHgrZHhbZF07CgkJCQkJaWYoSU5SQU5HRShueSwwLEgtMSkmJklOUkFOR0UobngsMCxXLTEpKQoJCQkJCXsKCQkJCQkJaWYoKHgreSkmMSkKCQkJCQkJewoJCQkJCQkJZGluaWMtPmFkZF9lZGdlKG9rdShueSxueCksIGtha29tdSh5LHgpLCBCSUcpOwoJCQkJCQl9CgkJCQkJCWVsc2UKCQkJCQkJewoJCQkJCQkJZGluaWMtPmFkZF9lZGdlKGtha29tdSh5LHgpLCBva3UobnksbngpLCBCSUcpOwoJCQkJCQl9CgkJCQkJfQoJCQkJfQoJCQl9CgkJfQoKCQljb25zdCBpbnQgcmVzdWx0ID0gdG90YWxEZWZhdWx0UHJvZml0IC0gZGluaWMtPm1heF9mbG93KFMsVCk7CgoJCWRlbGV0ZSBkaW5pYzsKCgkJcmV0dXJuIHJlc3VsdDsKCX0KCnByaXZhdGU6CglpbnQgZ2V0RGVjb2RlKGNvbnN0IGNoYXIgYykKCXsKCQlpZihpc2RpZ2l0KGMpKQoJCXsKCQkJcmV0dXJuIGMtJzAnOwoJCX0KCQllbHNlIGlmIChpc2xvd2VyKGMpKQoJCXsKCQkJcmV0dXJuIGMtJ2EnKzEwOwoJCX0KCQllbHNlIGlmIChpc3VwcGVyKGMpKQoJCXsKCQkJcmV0dXJuIGMtJ0EnKzM2OwoJCX0KCgkJcmV0dXJuIE5HOwoJfQoKCWludCBva3UoaW50IHksIGludCB4KQoJewoJCXJldHVybiB5KlcreDsKCX0KCWludCBrYWtvbXUoaW50IHksIGludCB4KQoJewoJCXJldHVybiB5KlcreCtIKlc7Cgl9CgoJaW50IEg7CglpbnQgVzsKCnByaXZhdGU6Cgl0ZW1wbGF0ZSA8dHlwZW5hbWUgVD4gc3RyaW5nIHByaW50X2FycmF5KGNvbnN0IHZlY3RvcjxUPiAmVikgeyBvc3RyaW5nc3RyZWFtIG9zOyBvcyA8PCAieyAiOyBmb3IgKHR5cGVuYW1lIHZlY3RvcjxUPjo6Y29uc3RfaXRlcmF0b3IgaXRlciA9IFYuYmVnaW4oKTsgaXRlciAhPSBWLmVuZCgpOyArK2l0ZXIpIG9zIDw8ICdcIicgPDwgKml0ZXIgPDwgIlwiLCI7IG9zIDw8ICIgfSI7IHJldHVybiBvcy5zdHIoKTsgfQoKCXZvaWQgdmVyaWZ5X2Nhc2UoaW50IENhc2UsIGNvbnN0IGludCAmRXhwZWN0ZWQsIGNvbnN0IGludCAmUmVjZWl2ZWQpIHsgY2VyciA8PCAiVGVzdCBDYXNlICMiIDw8IENhc2UgPDwgIi4uLiI7IGlmIChFeHBlY3RlZCA9PSBSZWNlaXZlZCkgY2VyciA8PCAiUEFTU0VEIiA8PCBlbmRsOyBlbHNlIHsgY2VyciA8PCAiRkFJTEVEIiA8PCBlbmRsOyBjZXJyIDw8ICJcdEV4cGVjdGVkOiBcIiIgPDwgRXhwZWN0ZWQgPDwgJ1wiJyA8PCBlbmRsOyBjZXJyIDw8ICJcdFJlY2VpdmVkOiBcIiIgPDwgUmVjZWl2ZWQgPDwgJ1wiJyA8PCBlbmRsOyB9IH0KCnB1YmxpYzoKCXZvaWQgcnVuX3Rlc3QoaW50IENhc2UpIHsgCgkJaW50IG4gPSAwOwoKCQkvLyB0ZXN0X2Nhc2VfMAoJCWlmICgoQ2FzZSA9PSAtMSkgfHwgKENhc2UgPT0gbikpewoJCQlzdHJpbmcgQXJyMFtdID0geyIyMSIsIjEyIn07CgkJCXN0cmluZyBBcnIxW10gPSB7IjIxIiwiMTIifTsKCQkJaW50IEFyZzIgPSA0OwoKCQkJdmVjdG9yIDxzdHJpbmc+IEFyZzAoQXJyMCwgQXJyMCArIChzaXplb2YoQXJyMCkgLyBzaXplb2YoQXJyMFswXSkpKTsKCQkJdmVjdG9yIDxzdHJpbmc+IEFyZzEoQXJyMSwgQXJyMSArIChzaXplb2YoQXJyMSkgLyBzaXplb2YoQXJyMVswXSkpKTsKCQkJdmVyaWZ5X2Nhc2UobiwgQXJnMiwgbWF4U2NvcmUoQXJnMCwgQXJnMSkpOwoJCX0KCQluKys7CgoJCS8vIHRlc3RfY2FzZV8xCgkJaWYgKChDYXNlID09IC0xKSB8fCAoQ2FzZSA9PSBuKSl7CgkJCXN0cmluZyBBcnIwW10gPSB7IlpaIiwiWloifTsKCQkJc3RyaW5nIEFycjFbXSA9IHsiMTEiLCIxMSJ9OwoJCQlpbnQgQXJnMiA9IDA7CgoJCQl2ZWN0b3IgPHN0cmluZz4gQXJnMChBcnIwLCBBcnIwICsgKHNpemVvZihBcnIwKSAvIHNpemVvZihBcnIwWzBdKSkpOwoJCQl2ZWN0b3IgPHN0cmluZz4gQXJnMShBcnIxLCBBcnIxICsgKHNpemVvZihBcnIxKSAvIHNpemVvZihBcnIxWzBdKSkpOwoJCQl2ZXJpZnlfY2FzZShuLCBBcmcyLCBtYXhTY29yZShBcmcwLCBBcmcxKSk7CgkJfQoJCW4rKzsKCgkJLy8gdGVzdF9jYXNlXzIKCQlpZiAoKENhc2UgPT0gLTEpIHx8IChDYXNlID09IG4pKXsKCQkJc3RyaW5nIEFycjBbXSA9IHsiWFhYIiwiWFhYIiwiWFhYIn07CgkJCXN0cmluZyBBcnIxW10gPSB7ImFhYSIsImFaYSIsImFhYSJ9OwoJCQlpbnQgQXJnMiA9IDI7CgoJCQl2ZWN0b3IgPHN0cmluZz4gQXJnMChBcnIwLCBBcnIwICsgKHNpemVvZihBcnIwKSAvIHNpemVvZihBcnIwWzBdKSkpOwoJCQl2ZWN0b3IgPHN0cmluZz4gQXJnMShBcnIxLCBBcnIxICsgKHNpemVvZihBcnIxKSAvIHNpemVvZihBcnIxWzBdKSkpOwoJCQl2ZXJpZnlfY2FzZShuLCBBcmcyLCBtYXhTY29yZShBcmcwLCBBcmcxKSk7CgkJfQoJCW4rKzsKCgkJLy8gdGVzdF9jYXNlXzMKCQlpZiAoKENhc2UgPT0gLTEpIHx8IChDYXNlID09IG4pKXsKCQkJc3RyaW5nIEFycjBbXSA9IHsiYXNhbSIsImF0aWsifTsKCQkJc3RyaW5nIEFycjFbXSA9IHsiMTIzQSIsIjQ1QkMifTsKCQkJaW50IEFyZzIgPSA3MTsKCgkJCXZlY3RvciA8c3RyaW5nPiBBcmcwKEFycjAsIEFycjAgKyAoc2l6ZW9mKEFycjApIC8gc2l6ZW9mKEFycjBbMF0pKSk7CgkJCXZlY3RvciA8c3RyaW5nPiBBcmcxKEFycjEsIEFycjEgKyAoc2l6ZW9mKEFycjEpIC8gc2l6ZW9mKEFycjFbMF0pKSk7CgkJCXZlcmlmeV9jYXNlKG4sIEFyZzIsIG1heFNjb3JlKEFyZzAsIEFyZzEpKTsKCQl9CgkJbisrOwoKCQkvLyB0ZXN0X2Nhc2VfNAoJCWlmICgoQ2FzZSA9PSAtMSkgfHwgKENhc2UgPT0gbikpewoJCQlzdHJpbmcgQXJyMFtdID0geyJJSUlJSUlJSSIsCiAiSUlXV1dXSUkiLAogIklJV0lJSUlJIiwKICJJSVdJSUlJSSIsCiAiSUlXV1dXSUkiLAogIklJSUlJV0lJIiwKICJJSUlJSVdJSSIsCiAiSUlXV1dXSUkiLAogIklJSUlJSUlJIn0KOwoJCQlzdHJpbmcgQXJyMVtdID0geyJJSUlJSUlJSSIsCiAiSUkwMDAwSUkiLAogIklJMElJMElJIiwKICJJSTBJSTBJSSIsCiAiSUkwMDAwSUkiLAogIklJMElJMElJIiwKICJJSTBJSTBJSSIsCiAiSUkwMDAwSUkiLAogIklJSUlJSUlJIn0KOwoJCQlpbnQgQXJnMiA9IDYwNjsKCgkJCXZlY3RvciA8c3RyaW5nPiBBcmcwKEFycjAsIEFycjAgKyAoc2l6ZW9mKEFycjApIC8gc2l6ZW9mKEFycjBbMF0pKSk7CgkJCXZlY3RvciA8c3RyaW5nPiBBcmcxKEFycjEsIEFycjEgKyAoc2l6ZW9mKEFycjEpIC8gc2l6ZW9mKEFycjFbMF0pKSk7CgkJCXZlcmlmeV9jYXNlKG4sIEFyZzIsIG1heFNjb3JlKEFyZzAsIEFyZzEpKTsKCQl9CgkJbisrOwoKCX0KCn07CgppbnQgbWFpbigpIHsKCVN1cnJvdW5kaW5nR2FtZSogX19fdGVzdCA9IG5ldyBTdXJyb3VuZGluZ0dhbWUoKTsKCV9fX3Rlc3QtPnJ1bl90ZXN0KC0xKTsKCWRlbGV0ZSBfX190ZXN0Owp9Cgo=