#include <stdio.h>
#include <stdlib.h>
/************** <HINT> ******************/
// ピクロスのヒントの構造体
typedef struct{
int length;
int *data;
int sum;
} hint;
// ヒントのデータ割り当て
hint* new_hint(int length)
{
hint
*h
= (hint
*)calloc(1,sizeof(hint
)); h->length = length;
h
->data
= (int*)calloc(length
,sizeof(int)); return h;
}
// ヒントのデータコピー(戻り値0で正常)
int copy_hint(hint *src, hint *dst)
{
int i;
int len = src->length;
// サイズチェック
if(src->length != dst->length) return -1;
// コピー
for(i=0;i<len;i++){
dst->data[i] = src->data[i];
}
dst->sum = src->sum;
return 0;
}
// ヒントのクローンを作成
hint* clone_hint(hint *h)
{
hint *h2 = new_hint(h->length);
copy_hint(h,h2);
return h2;
}
// ヒントのデータ解放
void delete_hint(hint *h)
{
}
// ヒントの合計値をセットする
int set_sum_hint(hint *h)
{
int i;
int len = h->length;
h->sum = 0;
for(i=0;i<len;i++){
// 入力チェックも兼ねる
if(h->data[i] < 1){
return 0;
}
h->sum += h->data[i];
}
return h->sum;
}
// ヒントの最大長を得る
int maxlen_hints(hint **h, int count)
{
int i,len,max=0;
for(i=0;i<count;i++){
len = h[i]->length;
if(max<len) max = len;
}
return max;
}
/************** </HINT> ******************/
/************** <CELL> ******************/
// セルの状態(不明・塗りつぶし・空白・決定不可能)
typedef enum{
UNKNOWN, FILL, SPACE, ND
} stat;
// セルの構造体
typedef struct{
stat stat;
} cell;
// セルのデータ割り当て
cell* new_cell(void)
{
cell
*c
= (cell
*)calloc(1,sizeof(cell
)); c->stat = UNKNOWN;
return c;
}
// セルのデータコピー(戻り値0で正常)
int copy_cell(cell *src,cell *dst)
{
dst->stat = src->stat;
return 0;
}
// セルのデータ解放
void delete_cell(cell *c)
{
}
/************** </CELL> ******************/
/************** <PICLOS> ******************/
// ピクロスの大きさの構造体
typedef struct{
int x; // よこ
int y; // たて
} size;
// ピクロスのデータ
typedef struct{
size size; // 大きさ
hint **hintX, **hintY; // ヒント
cell ***cell; // セル
} piclos;
// ピクロスのデータ割り当て
piclos* new_piclos(int x, int y)
{
int i,j;
piclos
*p
= (piclos
*)calloc(1,sizeof(piclos
)); p->size.x = x;
p->size.y = y;
// ヒント割り当て
p
->hintX
= (hint
**)calloc(x
,sizeof(hint
*)); p
->hintY
= (hint
**)calloc(y
,sizeof(hint
*)); // セル割り当て
p
->cell
= (cell
***)calloc(x
,sizeof(cell
**)); for(i=0;i<x;i++){
p
->cell
[i
] = (cell
**)calloc(y
,sizeof(cell
*)); for(j=0;j<y;j++){
p->cell[i][j] = new_cell();
}
}
return p;
}
// ピクロスのデータコピー(戻り値0で正常)
int copy_piclos(piclos *src, piclos *dst)
{
int i,j,len;
int x = src->size.x;
int y = src->size.y;
// サイズチェック
if(src->size.x != dst->size.x) return -1;
if(src->size.y != dst->size.y) return -1;
// ヒントコピー
for(i=0;i<x;i++){
dst->hintX[i] = clone_hint(src->hintX[i]);
}
for(i=0;i<y;i++){
dst->hintY[i] = clone_hint(src->hintY[i]);
}
// セルコピー
for(i=0;i<x;i++){
for(j=0;j<y;j++){
copy_cell(src->cell[i][j],dst->cell[i][j]);
}
}
return 0;
}
// ピクロスのクローンを作成
piclos* clone_piclos(piclos *p)
{
piclos* p2 = new_piclos(p->size.x,p->size.y);
copy_piclos(p,p2);
return p2;
}
// ピクロスのデータ解放(del_hint = 1でヒントのデータも同時に開放/*解放忘れ防止*/)
void delete_piclos(piclos *p, int del_hint)
{
int i,j;
int x = p->size.x;
int y = p->size.y;
// セル解放
for(i=0;i<x;i++){
for(j=0;j<y;j++){
delete_cell(p->cell[i][j]);
}
}
// ヒント解放
if(del_hint){
for(i=0;i<x;i++){
delete_hint(p->hintX[i]);
}
for(i=0;i<y;i++){
delete_hint(p->hintY[i]);
}
}
}
/************** </PICLOS> ******************/
/************** <OUTPUT> ******************/
void print_piclos_sub(piclos *p, char *s)
{
int i,j,idx,len,lenX,lenY;
int x = p->size.x;
int y = p->size.y;
lenX = maxlen_hints(p->hintX, x);
lenY = maxlen_hints(p->hintY, y);
// 横方向のヒントの表示
for(i=0;i<lenX;i++){
for(j
=0;j
<lenY
;j
++) printf(" "); for(j=0;j<x;j++){
len = p->hintX[j]->length;
idx = len-lenX+i;
if(idx >= 0){
printf("%3d",p
->hintX
[j
]->data
[idx
]); }else{
}
}
}
for(j=0;j<y;j++){
// 縦方向のヒントの表示
for(i=0;i<lenY;i++){
len = p->hintY[j]->length;
idx = len-lenY+i;
if(idx >= 0){
printf("%3d",p
->hintY
[j
]->data
[idx
]); }else{
}
}
// セルの表示
for(i=0;i<x;i++){
switch(p->cell[i][j]->stat)
{
case UNKNOWN
: printf(" "); break; case FILL
: printf(" ##"); break; default: printf(" ? "); break; }
}
}
}
void print_piclos(piclos *p)
{
print_piclos_sub(p, " x ");
}
void print_piclos_fin(piclos *p)
{
print_piclos_sub(p, " ");
}
/************** </OUTPUT> ******************/
/************** <INPUT> ******************/
int s_count(char *s, char d)
{
int i=0;
while(*s) if(*s++==d) i++;
return i;
}
int i_split(char *s, char d, int* res, int len)
{
int i=0,a=0;
char c;
while(c=*s++){
if(c==d){
i++;a=0;
if(i>=len) return -2;
}else{
if(c < '0' || c > '9') return 0;
a *= 10;
a += (int)(c - '0');
res[i] = a;
}
}
return 1;
}
int input_piclos_hints(piclos *p)
{
int i,j,x,y,len,sum,res=1;
char str[255];
x = p->size.x;
y = p->size.y;
puts("ピクロスのヒント(列方向)を入力してください。(空白区切りで各列ごとに改行)"); for(i=0;i<x;i++){
len = s_count(str,' ') + 1;
p->hintX[i] = new_hint(len);
if(!i_split(str,' ',p->hintX[i]->data,len))
{
res
= 0; puts("入力エラーを検出しました。"); }else{
sum = set_sum_hint(p->hintX[i]);
if(sum==0 || (sum+len-1)>p->size.y){
res
= 0; puts("入力エラーを検出しました。"); }
}
}
puts("ピクロスのヒント(行方向)を入力してください。(空白区切りで各列ごとに改行)"); for(i=0;i<y;i++){
len = s_count(str,' ') + 1;
p->hintY[i] = new_hint(len);
if(!i_split(str,' ',p->hintY[i]->data,len)){
res
= 0; puts("入力エラーを検出しました。"); }else{
sum = set_sum_hint(p->hintY[i]);
if(sum==0 || (sum+len-1)>p->size.x){
res
= 0; puts("入力エラーを検出しました。"); }
}
}
return(res);
}
/************** </INPUT> ******************/
/************** <DETECT> ******************/
// 現状とヒントから決定可能なセルを埋める(指定行/列)
// 引数:row=行指定,col=列指定(0起点、行指定時はcol=-1,列指定時はrow=-1)
// 戻り値:確定セルの増分(Nセル決定状態→N+Mセル決定状態:戻り値M)
int detect_line(piclos *p, int row, int col)
{
hint *h; // 対象ヒント
stat *w0,*w1; // 作業用セル状態
int *blank; // 黒セルの間隔
int blank_left, blank_right; // 左右の空白セル数
// x,y:セル選択用イテレータ; dx,dy:イテレータの増分; idx,i,j:カウンタ変数; k:対象方向のセル数; n:blankの数; sum:合計値; ptr:現在操作中のblankを指す
int dx,dy,x,y,idx,i,j,k,n,sum,ptr;
int f0, f1; // フラグ
// count:確定セル(新);count0:確定セル(旧);
int count=0, count0=0;
// 引数チェック
if(!(row==-1 || col==-1)) return -1;
// 計算方向の初期化
if(row==-1){ dx = 0; dy = 1; x = col; y = 0; k = p->size.y; h = p->hintX[x]; n = h->length; }
if(col==-1){ dx = 1; dy = 0; x = 0; y = row; k = p->size.x; h = p->hintY[y]; n = h->length; }
// 確定セルを数える
x *= dy; y *= dx; // イテレータ初期化
for(i=0;i<k;i++){
if(p->cell[x][y]->stat != UNKNOWN) count0++;
x += dx; y += dy;
}
if(count0 == k) return 0; // すべて確定しているようなので終了
// 作業領域の準備
w0
= (stat
*)calloc(k
,sizeof(stat
)); w1
= (stat
*)calloc(k
,sizeof(stat
)); x *= dy; y *= dx; // イテレータ初期化
for(i=0;i<k;i++){
w0[i] = p->cell[x][y]->stat;
x += dx; y += dy;
}
// blank初期化
blank
= (int*)calloc(n
,sizeof(int)); for(i=0;i<n-1;i++){
blank[i] = 1;
}
// 決定可能なセルの探索
ptr = 0;
f0 = 1;
while(f0){
// 現構成における合計値の計算
sum = h->sum;
for(i=0;i<n-1;i++) sum += blank[i];
// 選択可能なすべての左右の空白セル数に対して
for(blank_left = 0; blank_left <= (k-sum); blank_left++){
blank_right = (k-sum) - blank_left;
// 仮データの生成
idx=0;
for(i=0;i<blank_left;i++) w1[idx++] = SPACE;
for(i=0;i<n;i++){
for(j=0;j<(h->data[i]);j++) w1[idx++] = FILL;
for(j=0;j<(blank[i]);j++) w1[idx++] = SPACE;
}
for(i=0;i<blank_right;i++) w1[idx++] = SPACE;
// 確定済のデータとの整合性は取れているのか
f1 = 1;
x *= dy; y *= dx; // イテレータ初期化
for(i=0;i<k;i++){
if(p->cell[x][y]->stat != UNKNOWN){
if(p->cell[x][y]->stat != w1[i]) f1 = 0;
}
x += dx; y += dy;
}
// 整合性が取れているのであれば比較して新しい候補を作る
if(f1 == 1){
for(i=0;i<k;i++){
if(w0[i] == UNKNOWN){
w0[i] = w1[i];
}else{
if(w0[i]!=w1[i]) w0[i] = ND;
}
}
}
}
// 次の構成を生成する
if(n == 1 && h->data[0] == sum){ // 全塗りの場合
f0 = 0;
}else{
ptr = (sum == k)?ptr+1:0;
blank[ptr]++;
for(i=0;i<ptr;i++) blank[i] = 1;
f0 = (blank[n-1]==0);
}
}
// 結果の上書き
x *= dy; y *= dx; // イテレータ初期化
for(i=0;i<k;i++){
if(w0[i] == ND){
p->cell[x][y]->stat = UNKNOWN;
}else{
p->cell[x][y]->stat = w0[i];
}
x += dx; y += dy;
}
// 作業領域の破棄
// 確定セルを数える
x *= dy; y *= dx; // イテレータ初期化
for(i=0;i<k;i++){
if(p->cell[x][y]->stat != UNKNOWN) count++;
x += dx; y += dy;
}
// if(count-count0 != 0) print_piclos(p);
return count-count0;
}
int detect_all(piclos *p)
{
int i,count=0;
for(i=0;i<(p->size.x);i++)
count += detect_line(p,-1,i);
for(i=0;i<(p->size.y);i++)
count += detect_line(p,i,-1);
return count;
}
void detect(piclos *p)
{
while(detect_all(p)){
// print_piclos(p);
}
}
/************* </DETECT> ******************/
/************* <ELIMINATE> ****************/
// 未確定のセルを数える
int countUNKNOWN_piclos(piclos *p)
{
int i,j,cnt=0;
for(i=0;i<p->size.x;i++)
for(j=0;j<p->size.y;j++)
if(p->cell[i][j]->stat == UNKNOWN)
cnt++;
return cnt;
}
// 行の整合性を判定
int line_enable(piclos *p, int row, int col)
{
hint *h; // 対象ヒント
stat *w; // 作業用セル状態
int *blank; // 黒セルの間隔
int blank_left, blank_right; // 左右の空白セル数
// x,y:セル選択用イテレータ; dx,dy:イテレータの増分; idx,i,j:カウンタ変数; k:対象方向のセル数; n:blankの数; sum:合計値; ptr:現在操作中のblankを指す
int dx,dy,x,y,idx,i,j,k,n,sum,ptr;
int f0, f1; // フラグ
int enable = 0;
// 引数チェック
if(!(row==-1 || col==-1)) return -1;
// 計算方向の初期化
if(row==-1){ dx = 0; dy = 1; x = col; y = 0; k = p->size.y; h = p->hintX[x]; n = h->length; }
if(col==-1){ dx = 1; dy = 0; x = 0; y = row; k = p->size.x; h = p->hintY[y]; n = h->length; }
// 作業領域の準備
w
= (stat
*)calloc(k
,sizeof(stat
)); // blank初期化
blank
= (int*)calloc(n
,sizeof(int)); for(i=0;i<n-1;i++){
blank[i] = 1;
}
// 決定可能なセルの探索
ptr = 0;
f0 = 1;
while(f0){
// 現構成における合計値の計算
sum = h->sum;
for(i=0;i<n-1;i++) sum += blank[i];
// 選択可能なすべての左右の空白セル数に対して
for(blank_left = 0; blank_left <= (k-sum); blank_left++){
blank_right = (k-sum) - blank_left;
// 仮データの生成
idx=0;
for(i=0;i<blank_left;i++) w[idx++] = SPACE;
for(i=0;i<n;i++){
for(j=0;j<(h->data[i]);j++) w[idx++] = FILL;
for(j=0;j<(blank[i]);j++) w[idx++] = SPACE;
}
for(i=0;i<blank_right;i++) w[idx++] = SPACE;
// 確定済のデータとの整合性は取れているのか
f1 = 1;
x *= dy; y *= dx; // イテレータ初期化
for(i=0;i<k;i++){
if(p->cell[x][y]->stat != UNKNOWN){
if(p->cell[x][y]->stat != w[i]) f1 = 0;
}
x += dx; y += dy;
}
if(f1 == 1) enable++;
}
// 次の構成を生成する
if(n == 1 && h->data[0] == sum){
f0 = 0;
}else{
ptr = (sum == k)?ptr+1:0;
blank[ptr]++;
for(i=0;i<ptr;i++) blank[i] = 1;
f0 = (blank[n-1]==0);
}
}
// 作業領域の破棄
return enable;
}
// 全セルの整合性を判定
int piclos_enable(piclos *p)
{
int i,res=1;
for(i=0;i<p->size.x;i++)
if(line_enable(p,-1,i) == 0) res = 0;
for(i=0;i<p->size.y;i++)
if(line_enable(p,i,-1) == 0) res = 0;
return res;
}
// 終了判定
int is_finish_piclos(piclos *p)
{
return((countUNKNOWN_piclos(p) == 0) && piclos_enable(p));
}
// 選択が有効であるかを判定する
int elm_selection_enable(piclos *src, int x, int y, stat st)
{
int enable;
piclos *p = clone_piclos(src);
p->cell[x][y]->stat = st;
enable = (line_enable(p,-1,x) && line_enable(p,y,-1));
delete_piclos(p,1);
return enable;
}
// 塗潰せるセルを選ぶ(組み合わせが最も少ない場所から選ぶことで消去法の繰り返しを減らす)
int elm_select(piclos *p, int *x, int *y)
{
int min=9999; // 全ての行・列について組み合わせ可能なパターンの個数を調べた場合の最小値
int orient=0; // 組み合わせ数が最小となるものの方向。行方向:0; 列方向:1;
int index=-1; // 組み合わせ数が最小となるもの行・列番号
int a,i,k,dx,dy;
// 最小探索
for(i=0;i<p->size.y;i++){
a = line_enable(p,i,-1);
if(a > 1 && a < min){ min = a; orient=0; index=i; }
}
for(i=0;i<p->size.x;i++){
a = line_enable(p,-1,i);
if(a > 1 && a < min){ min = a; orient=1; index=i; }
}
// おかC
if(index==-1) return 0;
// イテレータ初期化
if(orient){ dx = 0; dy = 1; *x = index; *y = 0; k = p->size.y; }
else{ dx = 1; dy = 0; *x = 0; *y = index; k = p->size.x; }
// 探索
for(i=0;i<k;i++){
if(p->cell[*x][*y]->stat == UNKNOWN){
if(elm_selection_enable(p,*x,*y,FILL)){
return 1;
}
}
*x+=dx;*y+=dy;
}
return 0;
}
// 消去法の実装
int elmination(piclos *src)
{
int x,y;
piclos *p = clone_piclos(src);
if(elm_select(p,&x,&y)){
// 塗潰し可能なセルを塗潰したと仮定して解いてみる
p->cell[x][y]->stat = FILL;
detect(p);
if(!piclos_enable(p)){
// 仮定が間違っている場合、空白にして解いてみる。
copy_piclos(src,p);
p->cell[x][y]->stat = SPACE;
detect(p);
if(!piclos_enable(p)){
// 塗潰しでも空白でもだめなので親の仮定がまちがい
delete_piclos(p,1);
return 0;
}
}
if(is_finish_piclos(p)){
// 終了判定をクリアすればめでたく終了
copy_piclos(p, src);
delete_piclos(p,1);
return 1; // やったね!
}else{
// クリアできていないので再帰的に消去法を用いる
if(elmination(p)){
// 消去法の継続により解が求められた。
copy_piclos(p, src);
delete_piclos(p,1);
return 1;
}else{
if(p->cell[x][y]->stat == SPACE){
// 塗潰しでも空白でもだめなので親の仮定がまちがい
delete_piclos(p,1);
return 0;
}
// 空白にして解いてみる。
copy_piclos(src,p);
p->cell[x][y]->stat = SPACE;
detect(p);
if(!piclos_enable(p)){
// 塗潰しでも空白でもだめなので親の仮定がまちがい
delete_piclos(p,1);
return 0;
}
// 終了判定をクリアすればめでたく終了
if(is_finish_piclos(p)){
copy_piclos(p, src);
delete_piclos(p,1);
return 1; // やったね!
}else{
// クリアできていないので再帰的に消去法を用いる
if(elmination(p)){
// 消去法で解が求められた。
copy_piclos(p, src);
delete_piclos(p,1);
return 1;
}else{
// 塗潰しでも空白でもだめなので親の仮定がまちがい
delete_piclos(p,1);
return 0;
}
}
}
}
}else{
// 選択可能なセルがないので親の仮定がまちがい
delete_piclos(p,1);
return 0;
}
}
/************* </ELIMINATE> ****************/
int main(void)
{
piclos *p;
int x,y;
puts("ピクロスの列の数を入力してください。");scanf("%d",&x
);if(x
<1){puts("ERROR.");return 0;} puts("ピクロスの行の数を入力してください。");scanf("%d",&y
);if(y
<1){puts("ERROR.");return 0;} p = new_piclos(x,y);
if(!input_piclos_hints(p)){
delete_piclos(p,1);
return 0;// ERROR
}
detect(p);
if(!is_finish_piclos(p)) elmination(p);
print_piclos_fin(p);
delete_piclos(p,1);
return 0;
}
I2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KCi8qKioqKioqKioqKioqKiA8SElOVD4gKioqKioqKioqKioqKioqKioqLwovLyDjg5Tjgq/jg63jgrnjga7jg5Ljg7Pjg4jjga7mp4vpgKDkvZMKdHlwZWRlZiBzdHJ1Y3R7CiAgICBpbnQgbGVuZ3RoOwogICAgaW50ICpkYXRhOwoJaW50IHN1bTsKfSBoaW50OwovLyDjg5Ljg7Pjg4jjga7jg4fjg7zjgr/libLjgorlvZPjgaYKaGludCogbmV3X2hpbnQoaW50IGxlbmd0aCkKewogICAgaGludCAqaCA9IChoaW50KiljYWxsb2MoMSxzaXplb2YoaGludCkpOwogICAgaC0+bGVuZ3RoID0gbGVuZ3RoOwogICAgaC0+ZGF0YSA9IChpbnQqKWNhbGxvYyhsZW5ndGgsc2l6ZW9mKGludCkpOwogICAgcmV0dXJuIGg7Cn0KLy8g44OS44Oz44OI44Gu44OH44O844K/44Kz44OU44O877yI5oi744KK5YCk77yQ44Gn5q2j5bi477yJCmludCBjb3B5X2hpbnQoaGludCAqc3JjLCBoaW50ICpkc3QpCnsKICAgIGludCBpOwogICAgaW50IGxlbiA9IHNyYy0+bGVuZ3RoOwogICAgLy8g44K144Kk44K644OB44Kn44OD44KvCiAgICBpZihzcmMtPmxlbmd0aCAhPSBkc3QtPmxlbmd0aCkgcmV0dXJuIC0xOwogICAgLy8g44Kz44OU44O8CiAgICBmb3IoaT0wO2k8bGVuO2krKyl7CiAgICAgICAgZHN0LT5kYXRhW2ldID0gc3JjLT5kYXRhW2ldOwogICAgfQoJZHN0LT5zdW0gPSBzcmMtPnN1bTsKICAgIHJldHVybiAwOwp9Ci8vIOODkuODs+ODiOOBruOCr+ODreODvOODs+OCkuS9nOaIkApoaW50KiBjbG9uZV9oaW50KGhpbnQgKmgpCnsKICAgIGhpbnQgKmgyID0gbmV3X2hpbnQoaC0+bGVuZ3RoKTsKICAgIGNvcHlfaGludChoLGgyKTsKICAgIHJldHVybiBoMjsKfQovLyDjg5Ljg7Pjg4jjga7jg4fjg7zjgr/op6PmlL4Kdm9pZCBkZWxldGVfaGludChoaW50ICpoKQp7CiAgICBmcmVlKGgtPmRhdGEpOwogICAgZnJlZShoKTsKfQovLyDjg5Ljg7Pjg4jjga7lkIjoqIjlgKTjgpLjgrvjg4Pjg4jjgZnjgosKaW50IHNldF9zdW1faGludChoaW50ICpoKQp7CiAgICBpbnQgaTsKICAgIGludCBsZW4gPSBoLT5sZW5ndGg7CgloLT5zdW0gPSAwOwogICAgZm9yKGk9MDtpPGxlbjtpKyspewoJCS8vIOWFpeWKm+ODgeOCp+ODg+OCr+OCguWFvOOBreOCiwoJCWlmKGgtPmRhdGFbaV0gPCAxKXsKCQkJcmV0dXJuIDA7CgkJfQogICAgICAgIGgtPnN1bSArPSBoLT5kYXRhW2ldOwogICAgfQoJcmV0dXJuIGgtPnN1bTsKfQovLyDjg5Ljg7Pjg4jjga7mnIDlpKfplbfjgpLlvpfjgosKaW50IG1heGxlbl9oaW50cyhoaW50ICoqaCwgaW50IGNvdW50KQp7CiAgICBpbnQgaSxsZW4sbWF4PTA7CiAgICBmb3IoaT0wO2k8Y291bnQ7aSsrKXsKICAgICAgICBsZW4gPSBoW2ldLT5sZW5ndGg7CiAgICAgICAgaWYobWF4PGxlbikgbWF4ID0gbGVuOwogICAgfQogICAgcmV0dXJuIG1heDsKfQovKioqKioqKioqKioqKiogPC9ISU5UPiAqKioqKioqKioqKioqKioqKiovCgovKioqKioqKioqKioqKiogPENFTEw+ICoqKioqKioqKioqKioqKioqKi8KLy8g44K744Or44Gu54q25oWL77yI5LiN5piO44O75aGX44KK44Gk44G244GX44O756m655m944O75rG65a6a5LiN5Y+v6IO977yJCnR5cGVkZWYgZW51bXsKICAgIFVOS05PV04sIEZJTEwsIFNQQUNFLCBORAp9IHN0YXQ7Ci8vIOOCu+ODq+OBruani+mAoOS9kwp0eXBlZGVmIHN0cnVjdHsKICAgIHN0YXQgc3RhdDsKfSBjZWxsOwovLyDjgrvjg6vjga7jg4fjg7zjgr/libLjgorlvZPjgaYKY2VsbCogbmV3X2NlbGwodm9pZCkKewogICAgY2VsbCAqYyA9IChjZWxsKiljYWxsb2MoMSxzaXplb2YoY2VsbCkpOwogICAgYy0+c3RhdCA9IFVOS05PV047CiAgICByZXR1cm4gYzsKfQovLyDjgrvjg6vjga7jg4fjg7zjgr/jgrPjg5Tjg7zvvIjmiLvjgorlgKTvvJDjgafmraPluLjvvIkKaW50IGNvcHlfY2VsbChjZWxsICpzcmMsY2VsbCAqZHN0KQp7CiAgICBkc3QtPnN0YXQgPSBzcmMtPnN0YXQ7CiAgICByZXR1cm4gMDsKfQovLyDjgrvjg6vjga7jg4fjg7zjgr/op6PmlL4Kdm9pZCBkZWxldGVfY2VsbChjZWxsICpjKQp7CiAgICBmcmVlKGMpOwp9Ci8qKioqKioqKioqKioqKiA8L0NFTEw+ICoqKioqKioqKioqKioqKioqKi8KCi8qKioqKioqKioqKioqKiA8UElDTE9TPiAqKioqKioqKioqKioqKioqKiovCi8vIOODlOOCr+ODreOCueOBruWkp+OBjeOBleOBruani+mAoOS9kwp0eXBlZGVmIHN0cnVjdHsKICAgIGludCB4OyAvLyDjgojjgZMKICAgIGludCB5OyAvLyDjgZ/jgaYKfSBzaXplOwovLyDjg5Tjgq/jg63jgrnjga7jg4fjg7zjgr8KdHlwZWRlZiBzdHJ1Y3R7CiAgICBzaXplIHNpemU7IC8vIOWkp+OBjeOBlQogICAgaGludCAqKmhpbnRYLCAqKmhpbnRZOyAvLyDjg5Ljg7Pjg4gKICAgIGNlbGwgKioqY2VsbDsgLy8g44K744OrCn0gcGljbG9zOwovLyDjg5Tjgq/jg63jgrnjga7jg4fjg7zjgr/libLjgorlvZPjgaYKcGljbG9zKiBuZXdfcGljbG9zKGludCB4LCBpbnQgeSkKewogICAgaW50IGksajsKICAgIHBpY2xvcyAqcCA9IChwaWNsb3MqKWNhbGxvYygxLHNpemVvZihwaWNsb3MpKTsKICAgIHAtPnNpemUueCA9IHg7CiAgICBwLT5zaXplLnkgPSB5OwogICAgLy8g44OS44Oz44OI5Ymy44KK5b2T44GmCiAgICBwLT5oaW50WCA9IChoaW50KiopY2FsbG9jKHgsc2l6ZW9mKGhpbnQqKSk7CiAgICBwLT5oaW50WSA9IChoaW50KiopY2FsbG9jKHksc2l6ZW9mKGhpbnQqKSk7CiAgICAvLyDjgrvjg6vlibLjgorlvZPjgaYKICAgIHAtPmNlbGwgPSAoY2VsbCoqKiljYWxsb2MoeCxzaXplb2YoY2VsbCoqKSk7CiAgICBmb3IoaT0wO2k8eDtpKyspewogICAgICAgIHAtPmNlbGxbaV0gPSAoY2VsbCoqKWNhbGxvYyh5LHNpemVvZihjZWxsKikpOwogICAgICAgIGZvcihqPTA7ajx5O2orKyl7CiAgICAgICAgICAgIHAtPmNlbGxbaV1bal0gPSBuZXdfY2VsbCgpOwogICAgICAgIH0KICAgIH0KICAgIHJldHVybiBwOwp9Ci8vIOODlOOCr+ODreOCueOBruODh+ODvOOCv+OCs+ODlOODvO+8iOaIu+OCiuWApO+8kOOBp+ato+W4uO+8iQppbnQgY29weV9waWNsb3MocGljbG9zICpzcmMsIHBpY2xvcyAqZHN0KQp7CiAgICBpbnQgaSxqLGxlbjsKICAgIGludCB4ID0gc3JjLT5zaXplLng7CiAgICBpbnQgeSA9IHNyYy0+c2l6ZS55OwogICAgLy8g44K144Kk44K644OB44Kn44OD44KvCiAgICBpZihzcmMtPnNpemUueCAhPSBkc3QtPnNpemUueCkgcmV0dXJuIC0xOwogICAgaWYoc3JjLT5zaXplLnkgIT0gZHN0LT5zaXplLnkpIHJldHVybiAtMTsKICAgIC8vIOODkuODs+ODiOOCs+ODlOODvAogICAgZm9yKGk9MDtpPHg7aSsrKXsKICAgICAgICBkc3QtPmhpbnRYW2ldID0gY2xvbmVfaGludChzcmMtPmhpbnRYW2ldKTsKICAgIH0KICAgIGZvcihpPTA7aTx5O2krKyl7CiAgICAgICAgZHN0LT5oaW50WVtpXSA9IGNsb25lX2hpbnQoc3JjLT5oaW50WVtpXSk7CiAgICB9CiAgICAvLyDjgrvjg6vjgrPjg5Tjg7wKICAgIGZvcihpPTA7aTx4O2krKyl7CiAgICAgICAgZm9yKGo9MDtqPHk7aisrKXsKICAgICAgICAgICAgY29weV9jZWxsKHNyYy0+Y2VsbFtpXVtqXSxkc3QtPmNlbGxbaV1bal0pOwogICAgICAgIH0KICAgIH0KICAgIHJldHVybiAwOwp9Ci8vIOODlOOCr+ODreOCueOBruOCr+ODreODvOODs+OCkuS9nOaIkApwaWNsb3MqIGNsb25lX3BpY2xvcyhwaWNsb3MgKnApCnsKICAgIHBpY2xvcyogcDIgPSBuZXdfcGljbG9zKHAtPnNpemUueCxwLT5zaXplLnkpOwogICAgY29weV9waWNsb3MocCxwMik7CiAgICByZXR1cm4gcDI7Cn0KLy8g44OU44Kv44Ot44K544Gu44OH44O844K/6Kej5pS+KGRlbF9oaW50ID0gMeOBp+ODkuODs+ODiOOBruODh+ODvOOCv+OCguWQjOaZguOBq+mWi+aUvi8q6Kej5pS+5b+Y44KM6Ziy5q2iKi8pCnZvaWQgZGVsZXRlX3BpY2xvcyhwaWNsb3MgKnAsIGludCBkZWxfaGludCkKewogICAgaW50IGksajsKICAgIGludCB4ID0gcC0+c2l6ZS54OwogICAgaW50IHkgPSBwLT5zaXplLnk7CiAgICAvLyDjgrvjg6vop6PmlL4KICAgIGZvcihpPTA7aTx4O2krKyl7CiAgICAgICAgZm9yKGo9MDtqPHk7aisrKXsKICAgICAgICAgICAgZGVsZXRlX2NlbGwocC0+Y2VsbFtpXVtqXSk7CiAgICAgICAgfQogICAgICAgIGZyZWUocC0+Y2VsbFtpXSk7CiAgICB9CiAgICBmcmVlKHAtPmNlbGwpOwogICAgLy8g44OS44Oz44OI6Kej5pS+CiAgICBpZihkZWxfaGludCl7CiAgICAgICAgZm9yKGk9MDtpPHg7aSsrKXsKICAgICAgICAgICAgZGVsZXRlX2hpbnQocC0+aGludFhbaV0pOwogICAgICAgIH0KICAgICAgICBmb3IoaT0wO2k8eTtpKyspewogICAgICAgICAgICBkZWxldGVfaGludChwLT5oaW50WVtpXSk7CiAgICAgICAgfQogICAgfQogICAgZnJlZShwLT5oaW50WCk7CiAgICBmcmVlKHAtPmhpbnRZKTsKICAgIGZyZWUocCk7Cn0KLyoqKioqKioqKioqKioqIDwvUElDTE9TPiAqKioqKioqKioqKioqKioqKiovCgovKioqKioqKioqKioqKiogPE9VVFBVVD4gKioqKioqKioqKioqKioqKioqLwp2b2lkIHByaW50X3BpY2xvc19zdWIocGljbG9zICpwLCBjaGFyICpzKQp7CiAgICBpbnQgaSxqLGlkeCxsZW4sbGVuWCxsZW5ZOwogICAgaW50IHggPSBwLT5zaXplLng7CiAgICBpbnQgeSA9IHAtPnNpemUueTsKICAgIGxlblggPSBtYXhsZW5faGludHMocC0+aGludFgsIHgpOwogICAgbGVuWSA9IG1heGxlbl9oaW50cyhwLT5oaW50WSwgeSk7CiAgICAvLyDmqKrmlrnlkJHjga7jg5Ljg7Pjg4jjga7ooajnpLoKICAgIGZvcihpPTA7aTxsZW5YO2krKyl7CiAgICAgICAgZm9yKGo9MDtqPGxlblk7aisrKSBwcmludGYoIiAgICIpOwogICAgICAgIGZvcihqPTA7ajx4O2orKyl7CiAgICAgICAgICAgIGxlbiA9IHAtPmhpbnRYW2pdLT5sZW5ndGg7CiAgICAgICAgICAgIGlkeCA9IGxlbi1sZW5YK2k7CiAgICAgICAgICAgIGlmKGlkeCA+PSAwKXsKICAgICAgICAgICAgICAgIHByaW50ZigiJTNkIixwLT5oaW50WFtqXS0+ZGF0YVtpZHhdKTsKICAgICAgICAgICAgfWVsc2V7CiAgICAgICAgICAgICAgICBwcmludGYoIiAgICIpOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHByaW50ZigiXG4iKTsKICAgIH0KICAgIGZvcihqPTA7ajx5O2orKyl7CiAgICAgICAgLy8g57im5pa55ZCR44Gu44OS44Oz44OI44Gu6KGo56S6CiAgICAgICAgZm9yKGk9MDtpPGxlblk7aSsrKXsKICAgICAgICAgICAgbGVuID0gcC0+aGludFlbal0tPmxlbmd0aDsKICAgICAgICAgICAgaWR4ID0gbGVuLWxlblkraTsKICAgICAgICAgICAgaWYoaWR4ID49IDApewogICAgICAgICAgICAgICAgcHJpbnRmKCIlM2QiLHAtPmhpbnRZW2pdLT5kYXRhW2lkeF0pOwogICAgICAgICAgICB9ZWxzZXsKICAgICAgICAgICAgICAgIHByaW50ZigiICAgIik7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLy8g44K744Or44Gu6KGo56S6CiAgICAgICAgZm9yKGk9MDtpPHg7aSsrKXsKICAgICAgICAgICAgc3dpdGNoKHAtPmNlbGxbaV1bal0tPnN0YXQpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgY2FzZSBVTktOT1dOOiBwcmludGYoIiAgICIpOyBicmVhazsKICAgICAgICAgICAgY2FzZSBGSUxMOiAgICBwcmludGYoIiAjIyIpOyBicmVhazsKICAgICAgICAgICAgY2FzZSBTUEFDRTogICBwcmludGYocyk7IGJyZWFrOwogICAgICAgICAgICBkZWZhdWx0OiAgICAgIHByaW50ZigiID8gIik7IGJyZWFrOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHByaW50ZigiXG4iKTsKICAgIH0KICAgIHByaW50ZigiXG4iKTsKfQp2b2lkIHByaW50X3BpY2xvcyhwaWNsb3MgKnApCnsKCXByaW50X3BpY2xvc19zdWIocCwgIiB4ICIpOwp9CnZvaWQgcHJpbnRfcGljbG9zX2ZpbihwaWNsb3MgKnApCnsKCXByaW50X3BpY2xvc19zdWIocCwgIiAgICIpOwp9Ci8qKioqKioqKioqKioqKiA8L09VVFBVVD4gKioqKioqKioqKioqKioqKioqLwoKLyoqKioqKioqKioqKioqIDxJTlBVVD4gKioqKioqKioqKioqKioqKioqLwppbnQgc19jb3VudChjaGFyICpzLCBjaGFyIGQpCnsKICAgIGludCBpPTA7CiAgICB3aGlsZSgqcykgaWYoKnMrKz09ZCkgaSsrOwogICAgcmV0dXJuIGk7Cn0KaW50IGlfc3BsaXQoY2hhciAqcywgY2hhciBkLCBpbnQqIHJlcywgaW50IGxlbikKewogICAgaW50IGk9MCxhPTA7CiAgICBjaGFyIGM7CiAgICB3aGlsZShjPSpzKyspewogICAgICAgIGlmKGM9PWQpewogICAgICAgICAgICBpKys7YT0wOwogICAgICAgICAgICBpZihpPj1sZW4pIHJldHVybiAtMjsKICAgICAgICB9ZWxzZXsKICAgICAgICAgICAgaWYoYyA8ICcwJyB8fCBjID4gJzknKSByZXR1cm4gMDsKICAgICAgICAgICAgYSAqPSAxMDsKICAgICAgICAgICAgYSArPSAoaW50KShjIC0gJzAnKTsKICAgICAgICAgICAgcmVzW2ldID0gYTsKICAgICAgICB9CiAgICB9CiAgICByZXR1cm4gMTsKfQoKaW50IGlucHV0X3BpY2xvc19oaW50cyhwaWNsb3MgKnApCnsKICAgIGludCBpLGoseCx5LGxlbixzdW0scmVzPTE7CiAgICBjaGFyIHN0clsyNTVdOwogICAgeCA9IHAtPnNpemUueDsKICAgIHkgPSBwLT5zaXplLnk7CiAgICBnZXRzKHN0cik7Ly/jg5Djg4Pjg5XjgqHjgq/jg6rjgqIKICAgIHB1dHMoIuODlOOCr+ODreOCueOBruODkuODs+ODiO+8iOWIl+aWueWQke+8ieOCkuWFpeWKm+OBl+OBpuOBj+OBoOOBleOBhOOAgijnqbrnmb3ljLrliIfjgorjgaflkITliJfjgZTjgajjgavmlLnooYwpIik7CiAgICBmb3IoaT0wO2k8eDtpKyspewogICAgICAgIGdldHMoc3RyKTsKICAgICAgICBsZW4gPSBzX2NvdW50KHN0ciwnICcpICsgMTsKICAgICAgICBwLT5oaW50WFtpXSA9IG5ld19oaW50KGxlbik7CiAgICAgICAgaWYoIWlfc3BsaXQoc3RyLCcgJyxwLT5oaW50WFtpXS0+ZGF0YSxsZW4pKQoJCXsKCQkJcmVzID0gMDsgcHV0cygi5YWl5Yqb44Ko44Op44O844KS5qSc5Ye644GX44G+44GX44Gf44CCIik7CgkJfWVsc2V7CgkJCXN1bSA9IHNldF9zdW1faGludChwLT5oaW50WFtpXSk7CgkJCWlmKHN1bT09MCB8fCAoc3VtK2xlbi0xKT5wLT5zaXplLnkpewoJCQkJcmVzID0gMDsgcHV0cygi5YWl5Yqb44Ko44Op44O844KS5qSc5Ye644GX44G+44GX44Gf44CCIik7CgkJCX0KCQl9CiAgICB9CiAgICBwdXRzKCLjg5Tjgq/jg63jgrnjga7jg5Ljg7Pjg4jvvIjooYzmlrnlkJHvvInjgpLlhaXlipvjgZfjgabjgY/jgaDjgZXjgYTjgIIo56m655m95Yy65YiH44KK44Gn5ZCE5YiX44GU44Go44Gr5pS56KGMKSIpOwogICAgZm9yKGk9MDtpPHk7aSsrKXsKICAgICAgICBnZXRzKHN0cik7CiAgICAgICAgbGVuID0gc19jb3VudChzdHIsJyAnKSArIDE7CiAgICAgICAgcC0+aGludFlbaV0gPSBuZXdfaGludChsZW4pOwogICAgICAgIGlmKCFpX3NwbGl0KHN0ciwnICcscC0+aGludFlbaV0tPmRhdGEsbGVuKSl7CgkJCXJlcyA9IDA7IHB1dHMoIuWFpeWKm+OCqOODqeODvOOCkuaknOWHuuOBl+OBvuOBl+OBn+OAgiIpOwoJCX1lbHNlewoJCQlzdW0gPSBzZXRfc3VtX2hpbnQocC0+aGludFlbaV0pOwoJCQlpZihzdW09PTAgfHwgKHN1bStsZW4tMSk+cC0+c2l6ZS54KXsKCQkJCXJlcyA9IDA7IHB1dHMoIuWFpeWKm+OCqOODqeODvOOCkuaknOWHuuOBl+OBvuOBl+OBn+OAgiIpOwoJCQl9CgkJfQogICAgfQoJcmV0dXJuKHJlcyk7Cn0KLyoqKioqKioqKioqKioqIDwvSU5QVVQ+ICoqKioqKioqKioqKioqKioqKi8KCi8qKioqKioqKioqKioqKiA8REVURUNUPiAqKioqKioqKioqKioqKioqKiovCgovLyDnj77nirbjgajjg5Ljg7Pjg4jjgYvjgonmsbrlrprlj6/og73jgarjgrvjg6vjgpLln4vjgoHjgoso5oyH5a6a6KGML+WIlykKLy8g5byV5pWw77yacm93PeihjOaMh+Wumixjb2w95YiX5oyH5a6a77yIMOi1t+eCueOAgeihjOaMh+WumuaZguOBr2NvbD0tMSzliJfmjIflrprmmYLjga9yb3c9LTHvvIkKLy8g5oi744KK5YCk77ya56K65a6a44K744Or44Gu5aKX5YiG77yITuOCu+ODq+axuuWumueKtuaFi+KGkk4rTeOCu+ODq+axuuWumueKtuaFi++8muaIu+OCiuWApE3vvIkKaW50IGRldGVjdF9saW5lKHBpY2xvcyAqcCwgaW50IHJvdywgaW50IGNvbCkKewoJaGludCAqaDsgLy8g5a++6LGh44OS44Oz44OICglzdGF0ICp3MCwqdzE7IC8vIOS9nOalreeUqOOCu+ODq+eKtuaFiwoJaW50ICpibGFuazsgLy8g6buS44K744Or44Gu6ZaT6ZqUCglpbnQgYmxhbmtfbGVmdCwgYmxhbmtfcmlnaHQ7IC8vIOW3puWPs+OBruepuueZveOCu+ODq+aVsAoJLy8geCx5OuOCu+ODq+mBuOaKnueUqOOCpOODhuODrOODvOOCvzsgZHgsZHk644Kk44OG44Os44O844K/44Gu5aKX5YiGOyBpZHgsaSxqOuOCq+OCpuODs+OCv+WkieaVsDsgazrlr77osaHmlrnlkJHjga7jgrvjg6vmlbA7IG46Ymxhbmvjga7mlbA7IHN1bTrlkIjoqIjlgKQ7IHB0cjrnj77lnKjmk43kvZzkuK3jga5ibGFua+OCkuaMh+OBmQoJaW50IGR4LGR5LHgseSxpZHgsaSxqLGssbixzdW0scHRyOwoJaW50IGYwLCBmMTsgLy8g44OV44Op44KwCgkvLyBjb3VudDrnorrlrprjgrvjg6so5pawKTtjb3VudDA656K65a6a44K744OrKOaXpyk7CglpbnQgY291bnQ9MCwgY291bnQwPTA7CgkvLyDlvJXmlbDjg4Hjgqfjg4Pjgq8KCWlmKCEocm93PT0tMSB8fCBjb2w9PS0xKSkgcmV0dXJuIC0xOwoJLy8g6KiI566X5pa55ZCR44Gu5Yid5pyf5YyWCglpZihyb3c9PS0xKXsgZHggPSAwOyBkeSA9IDE7IHggPSBjb2w7IHkgPSAwOyBrID0gcC0+c2l6ZS55OyBoID0gcC0+aGludFhbeF07IG4gPSBoLT5sZW5ndGg7IH0KCWlmKGNvbD09LTEpeyBkeCA9IDE7IGR5ID0gMDsgeCA9IDA7IHkgPSByb3c7IGsgPSBwLT5zaXplLng7IGggPSBwLT5oaW50WVt5XTsgbiA9IGgtPmxlbmd0aDsgfQoJLy8g56K65a6a44K744Or44KS5pWw44GI44KLCgl4ICo9IGR5OyB5ICo9IGR4OyAvLyDjgqTjg4bjg6zjg7zjgr/liJ3mnJ/ljJYKCWZvcihpPTA7aTxrO2krKyl7CgkJaWYocC0+Y2VsbFt4XVt5XS0+c3RhdCAhPSBVTktOT1dOKSBjb3VudDArKzsKCQl4ICs9IGR4OyB5ICs9IGR5OwoJfQoJaWYoY291bnQwID09IGspIHJldHVybiAwOyAvLyDjgZnjgbnjgabnorrlrprjgZfjgabjgYTjgovjgojjgYbjgarjga7jgafntYLkuoYKCS8vIOS9nOalremgmOWfn+OBrua6luWCmQoJdzAgPSAoc3RhdCopY2FsbG9jKGssc2l6ZW9mKHN0YXQpKTsKCXcxID0gKHN0YXQqKWNhbGxvYyhrLHNpemVvZihzdGF0KSk7Cgl4ICo9IGR5OyB5ICo9IGR4OyAvLyDjgqTjg4bjg6zjg7zjgr/liJ3mnJ/ljJYKCWZvcihpPTA7aTxrO2krKyl7CgkJdzBbaV0gPSBwLT5jZWxsW3hdW3ldLT5zdGF0OwoJCXggKz0gZHg7IHkgKz0gZHk7Cgl9CgkvLyBibGFua+WIneacn+WMlgoJYmxhbmsgPSAoaW50KiljYWxsb2MobixzaXplb2YoaW50KSk7Cglmb3IoaT0wO2k8bi0xO2krKyl7CgkJYmxhbmtbaV0gPSAxOwoJfQoJLy8g5rG65a6a5Y+v6IO944Gq44K744Or44Gu5o6i57SiCglwdHIgPSAwOwoJZjAgPSAxOwoJd2hpbGUoZjApewoJCS8vIOePvuani+aIkOOBq+OBiuOBkeOCi+WQiOioiOWApOOBruioiOeulwoJCXN1bSA9IGgtPnN1bTsKCQlmb3IoaT0wO2k8bi0xO2krKykgc3VtICs9IGJsYW5rW2ldOwoJCS8vIOmBuOaKnuWPr+iDveOBquOBmeOBueOBpuOBruW3puWPs+OBruepuueZveOCu+ODq+aVsOOBq+WvvuOBl+OBpgoJCWZvcihibGFua19sZWZ0ID0gMDsgYmxhbmtfbGVmdCA8PSAoay1zdW0pOyBibGFua19sZWZ0KyspewoJCQlibGFua19yaWdodCA9IChrLXN1bSkgLSBibGFua19sZWZ0OwoJCQkvLyDku67jg4fjg7zjgr/jga7nlJ/miJAKCQkJaWR4PTA7CgkJCWZvcihpPTA7aTxibGFua19sZWZ0O2krKykJCXcxW2lkeCsrXSA9IFNQQUNFOwoJCQlmb3IoaT0wO2k8bjtpKyspewoJCQkJZm9yKGo9MDtqPChoLT5kYXRhW2ldKTtqKyspCXcxW2lkeCsrXSA9IEZJTEw7CgkJCQlmb3Ioaj0wO2o8KGJsYW5rW2ldKTtqKyspCXcxW2lkeCsrXSA9IFNQQUNFOwoJCQl9CgkJCWZvcihpPTA7aTxibGFua19yaWdodDtpKyspCQl3MVtpZHgrK10gPSBTUEFDRTsKCQkJLy8g56K65a6a5riI44Gu44OH44O844K/44Go44Gu5pW05ZCI5oCn44Gv5Y+W44KM44Gm44GE44KL44Gu44GLCgkJCWYxID0gMTsKCQkJeCAqPSBkeTsgeSAqPSBkeDsgLy8g44Kk44OG44Os44O844K/5Yid5pyf5YyWCgkJCWZvcihpPTA7aTxrO2krKyl7CgkJCQlpZihwLT5jZWxsW3hdW3ldLT5zdGF0ICE9IFVOS05PV04pewoJCQkJCWlmKHAtPmNlbGxbeF1beV0tPnN0YXQgIT0gdzFbaV0pIGYxID0gMDsKCQkJCX0KCQkJCXggKz0gZHg7IHkgKz0gZHk7CgkJCX0KCQkJLy8g5pW05ZCI5oCn44GM5Y+W44KM44Gm44GE44KL44Gu44Gn44GC44KM44Gw5q+U6LyD44GX44Gm5paw44GX44GE5YCZ6KOc44KS5L2c44KLCgkJCWlmKGYxID09IDEpewoJCQkJZm9yKGk9MDtpPGs7aSsrKXsKCQkJCQlpZih3MFtpXSA9PSBVTktOT1dOKXsKCQkJCQkJdzBbaV0gPSB3MVtpXTsKCQkJCQl9ZWxzZXsKCQkJCQkJaWYodzBbaV0hPXcxW2ldKSB3MFtpXSA9IE5EOyAKCQkJCQl9CgkJCQl9CgkJCX0KCQl9CgkJLy8g5qyh44Gu5qeL5oiQ44KS55Sf5oiQ44GZ44KLCgkJaWYobiA9PSAxICYmIGgtPmRhdGFbMF0gPT0gc3VtKXsgLy8g5YWo5aGX44KK44Gu5aC05ZCICgkJCWYwID0gMDsKCQl9ZWxzZXsKCQkJcHRyID0gKHN1bSA9PSBrKT9wdHIrMTowOwoJCQlibGFua1twdHJdKys7CgkJCWZvcihpPTA7aTxwdHI7aSsrKSBibGFua1tpXSA9IDE7CgkJCWYwID0gKGJsYW5rW24tMV09PTApOwoJCX0KCX0KCS8vIOe1kOaenOOBruS4iuabuOOBjQoJeCAqPSBkeTsgeSAqPSBkeDsgLy8g44Kk44OG44Os44O844K/5Yid5pyf5YyWCglmb3IoaT0wO2k8aztpKyspewoJCWlmKHcwW2ldID09IE5EKXsKCQkJcC0+Y2VsbFt4XVt5XS0+c3RhdCA9IFVOS05PV047CgkJfWVsc2V7CgkJCXAtPmNlbGxbeF1beV0tPnN0YXQgPSB3MFtpXTsKCQl9CgkJeCArPSBkeDsgeSArPSBkeTsKCX0KCS8vIOS9nOalremgmOWfn+OBruegtOajhAoJZnJlZShibGFuayk7CglmcmVlKHcwKTsKCWZyZWUodzEpOwoJLy8g56K65a6a44K744Or44KS5pWw44GI44KLCgl4ICo9IGR5OyB5ICo9IGR4OyAvLyDjgqTjg4bjg6zjg7zjgr/liJ3mnJ/ljJYKCWZvcihpPTA7aTxrO2krKyl7CgkJaWYocC0+Y2VsbFt4XVt5XS0+c3RhdCAhPSBVTktOT1dOKSBjb3VudCsrOwoJCXggKz0gZHg7IHkgKz0gZHk7Cgl9CgkvLyBpZihjb3VudC1jb3VudDAgIT0gMCkgcHJpbnRfcGljbG9zKHApOwoJcmV0dXJuIGNvdW50LWNvdW50MDsKfQppbnQgZGV0ZWN0X2FsbChwaWNsb3MgKnApCnsKCWludCBpLGNvdW50PTA7Cglmb3IoaT0wO2k8KHAtPnNpemUueCk7aSsrKQoJCWNvdW50ICs9IGRldGVjdF9saW5lKHAsLTEsaSk7Cglmb3IoaT0wO2k8KHAtPnNpemUueSk7aSsrKQoJCWNvdW50ICs9IGRldGVjdF9saW5lKHAsaSwtMSk7CglyZXR1cm4gY291bnQ7Cn0Kdm9pZCBkZXRlY3QocGljbG9zICpwKQp7Cgl3aGlsZShkZXRlY3RfYWxsKHApKXsKCQkvLyBwcmludF9waWNsb3MocCk7Cgl9Cn0KLyoqKioqKioqKioqKiogPC9ERVRFQ1Q+ICoqKioqKioqKioqKioqKioqKi8KCi8qKioqKioqKioqKioqIDxFTElNSU5BVEU+ICoqKioqKioqKioqKioqKiovCi8vIOacqueiuuWumuOBruOCu+ODq+OCkuaVsOOBiOOCiwppbnQgY291bnRVTktOT1dOX3BpY2xvcyhwaWNsb3MgKnApCnsKCWludCBpLGosY250PTA7Cglmb3IoaT0wO2k8cC0+c2l6ZS54O2krKykKCQlmb3Ioaj0wO2o8cC0+c2l6ZS55O2orKykKCQkJaWYocC0+Y2VsbFtpXVtqXS0+c3RhdCA9PSBVTktOT1dOKQoJCQkJY250Kys7CglyZXR1cm4gY250Owp9Ci8vIOihjOOBruaVtOWQiOaAp+OCkuWIpOWumgppbnQgbGluZV9lbmFibGUocGljbG9zICpwLCBpbnQgcm93LCBpbnQgY29sKQp7CgloaW50ICpoOyAvLyDlr77osaHjg5Ljg7Pjg4gKCXN0YXQgKnc7IC8vIOS9nOalreeUqOOCu+ODq+eKtuaFiwoJaW50ICpibGFuazsgLy8g6buS44K744Or44Gu6ZaT6ZqUCglpbnQgYmxhbmtfbGVmdCwgYmxhbmtfcmlnaHQ7IC8vIOW3puWPs+OBruepuueZveOCu+ODq+aVsAoJLy8geCx5OuOCu+ODq+mBuOaKnueUqOOCpOODhuODrOODvOOCvzsgZHgsZHk644Kk44OG44Os44O844K/44Gu5aKX5YiGOyBpZHgsaSxqOuOCq+OCpuODs+OCv+WkieaVsDsgazrlr77osaHmlrnlkJHjga7jgrvjg6vmlbA7IG46Ymxhbmvjga7mlbA7IHN1bTrlkIjoqIjlgKQ7IHB0cjrnj77lnKjmk43kvZzkuK3jga5ibGFua+OCkuaMh+OBmQoJaW50IGR4LGR5LHgseSxpZHgsaSxqLGssbixzdW0scHRyOwoJaW50IGYwLCBmMTsgLy8g44OV44Op44KwCglpbnQgZW5hYmxlID0gMDsKCS8vIOW8leaVsOODgeOCp+ODg+OCrwoJaWYoIShyb3c9PS0xIHx8IGNvbD09LTEpKSByZXR1cm4gLTE7CgkvLyDoqIjnrpfmlrnlkJHjga7liJ3mnJ/ljJYKCWlmKHJvdz09LTEpeyBkeCA9IDA7IGR5ID0gMTsgeCA9IGNvbDsgeSA9IDA7IGsgPSBwLT5zaXplLnk7IGggPSBwLT5oaW50WFt4XTsgbiA9IGgtPmxlbmd0aDsgfQoJaWYoY29sPT0tMSl7IGR4ID0gMTsgZHkgPSAwOyB4ID0gMDsgeSA9IHJvdzsgayA9IHAtPnNpemUueDsgaCA9IHAtPmhpbnRZW3ldOyBuID0gaC0+bGVuZ3RoOyB9CgkvLyDkvZzmpa3poJjln5/jga7mupblgpkKCXcgPSAoc3RhdCopY2FsbG9jKGssc2l6ZW9mKHN0YXQpKTsKCS8vIGJsYW5r5Yid5pyf5YyWCglibGFuayA9IChpbnQqKWNhbGxvYyhuLHNpemVvZihpbnQpKTsKCWZvcihpPTA7aTxuLTE7aSsrKXsKCQlibGFua1tpXSA9IDE7Cgl9CgkvLyDmsbrlrprlj6/og73jgarjgrvjg6vjga7mjqLntKIKCXB0ciA9IDA7CglmMCA9IDE7Cgl3aGlsZShmMCl7CgkJLy8g54++5qeL5oiQ44Gr44GK44GR44KL5ZCI6KiI5YCk44Gu6KiI566XCgkJc3VtID0gaC0+c3VtOwoJCWZvcihpPTA7aTxuLTE7aSsrKSBzdW0gKz0gYmxhbmtbaV07CgkJLy8g6YG45oqe5Y+v6IO944Gq44GZ44G544Gm44Gu5bem5Y+z44Gu56m655m944K744Or5pWw44Gr5a++44GX44GmCgkJZm9yKGJsYW5rX2xlZnQgPSAwOyBibGFua19sZWZ0IDw9IChrLXN1bSk7IGJsYW5rX2xlZnQrKyl7CgkJCWJsYW5rX3JpZ2h0ID0gKGstc3VtKSAtIGJsYW5rX2xlZnQ7CgkJCS8vIOS7ruODh+ODvOOCv+OBrueUn+aIkAoJCQlpZHg9MDsKCQkJZm9yKGk9MDtpPGJsYW5rX2xlZnQ7aSsrKQkJd1tpZHgrK10gPSBTUEFDRTsKCQkJZm9yKGk9MDtpPG47aSsrKXsKCQkJCWZvcihqPTA7ajwoaC0+ZGF0YVtpXSk7aisrKQl3W2lkeCsrXSA9IEZJTEw7CgkJCQlmb3Ioaj0wO2o8KGJsYW5rW2ldKTtqKyspCXdbaWR4KytdID0gU1BBQ0U7CgkJCX0KCQkJZm9yKGk9MDtpPGJsYW5rX3JpZ2h0O2krKykJCXdbaWR4KytdID0gU1BBQ0U7CgkJCS8vIOeiuuWumua4iOOBruODh+ODvOOCv+OBqOOBruaVtOWQiOaAp+OBr+WPluOCjOOBpuOBhOOCi+OBruOBiwoJCQlmMSA9IDE7CgkJCXggKj0gZHk7IHkgKj0gZHg7IC8vIOOCpOODhuODrOODvOOCv+WIneacn+WMlgoJCQlmb3IoaT0wO2k8aztpKyspewoJCQkJaWYocC0+Y2VsbFt4XVt5XS0+c3RhdCAhPSBVTktOT1dOKXsKCQkJCQlpZihwLT5jZWxsW3hdW3ldLT5zdGF0ICE9IHdbaV0pIGYxID0gMDsKCQkJCX0KCQkJCXggKz0gZHg7IHkgKz0gZHk7CgkJCX0KCQkJaWYoZjEgPT0gMSkgZW5hYmxlKys7CgkJfQoJCS8vIOasoeOBruani+aIkOOCkueUn+aIkOOBmeOCiwoJCWlmKG4gPT0gMSAmJiBoLT5kYXRhWzBdID09IHN1bSl7CgkJCWYwID0gMDsKCQl9ZWxzZXsKCQkJcHRyID0gKHN1bSA9PSBrKT9wdHIrMTowOwoJCQlibGFua1twdHJdKys7CgkJCWZvcihpPTA7aTxwdHI7aSsrKSBibGFua1tpXSA9IDE7CgkJCWYwID0gKGJsYW5rW24tMV09PTApOwoJCX0KCX0KCS8vIOS9nOalremgmOWfn+OBruegtOajhAoJZnJlZShibGFuayk7CglmcmVlKHcpOwoJcmV0dXJuIGVuYWJsZTsKCQp9Ci8vIOWFqOOCu+ODq+OBruaVtOWQiOaAp+OCkuWIpOWumgppbnQgcGljbG9zX2VuYWJsZShwaWNsb3MgKnApCnsKCWludCBpLHJlcz0xOwoJZm9yKGk9MDtpPHAtPnNpemUueDtpKyspCgkJaWYobGluZV9lbmFibGUocCwtMSxpKSA9PSAwKSByZXMgPSAwOwoJZm9yKGk9MDtpPHAtPnNpemUueTtpKyspCgkJaWYobGluZV9lbmFibGUocCxpLC0xKSA9PSAwKSByZXMgPSAwOwoJcmV0dXJuIHJlczsKfQovLyDntYLkuobliKTlrpoKaW50IGlzX2ZpbmlzaF9waWNsb3MocGljbG9zICpwKQp7CglyZXR1cm4oKGNvdW50VU5LTk9XTl9waWNsb3MocCkgPT0gMCkgJiYgcGljbG9zX2VuYWJsZShwKSk7Cn0KLy8g6YG45oqe44GM5pyJ5Yq544Gn44GC44KL44GL44KS5Yik5a6a44GZ44KLCmludCBlbG1fc2VsZWN0aW9uX2VuYWJsZShwaWNsb3MgKnNyYywgaW50IHgsIGludCB5LCBzdGF0IHN0KQp7CglpbnQgZW5hYmxlOwoJcGljbG9zICpwID0gY2xvbmVfcGljbG9zKHNyYyk7CglwLT5jZWxsW3hdW3ldLT5zdGF0ID0gc3Q7CgllbmFibGUgPSAobGluZV9lbmFibGUocCwtMSx4KSAmJiBsaW5lX2VuYWJsZShwLHksLTEpKTsKCWRlbGV0ZV9waWNsb3MocCwxKTsKCXJldHVybiBlbmFibGU7Cn0KLy8g5aGX5r2w44Gb44KL44K744Or44KS6YG444G2KOe1hOOBv+WQiOOCj+OBm+OBjOacgOOCguWwkeOBquOBhOWgtOaJgOOBi+OCiemBuOOBtuOBk+OBqOOBp+a2iOWOu+azleOBrue5sOOCiui/lOOBl+OCkua4m+OCieOBmSkKaW50IGVsbV9zZWxlY3QocGljbG9zICpwLCBpbnQgKngsIGludCAqeSkKewoJaW50IG1pbj05OTk5OyAvLyDlhajjgabjga7ooYzjg7vliJfjgavjgaTjgYTjgabntYTjgb/lkIjjgo/jgZvlj6/og73jgarjg5Hjgr/jg7zjg7Pjga7lgIvmlbDjgpLoqr/jgbnjgZ/loLTlkIjjga7mnIDlsI/lgKQKCWludCBvcmllbnQ9MDsgLy8g57WE44G/5ZCI44KP44Gb5pWw44GM5pyA5bCP44Go44Gq44KL44KC44Gu44Gu5pa55ZCR44CC6KGM5pa55ZCROjA7IOWIl+aWueWQkToxOwoJaW50IGluZGV4PS0xOyAvLyDntYTjgb/lkIjjgo/jgZvmlbDjgYzmnIDlsI/jgajjgarjgovjgoLjga7ooYzjg7vliJfnlarlj7cKCWludCBhLGksayxkeCxkeTsKCS8vIOacgOWwj+aOoue0ogoJZm9yKGk9MDtpPHAtPnNpemUueTtpKyspewoJCWEgPSBsaW5lX2VuYWJsZShwLGksLTEpOwoJCWlmKGEgPiAxICYmIGEgPCBtaW4peyBtaW4gPSBhOyBvcmllbnQ9MDsgaW5kZXg9aTsgfQoJfQoJZm9yKGk9MDtpPHAtPnNpemUueDtpKyspewoJCWEgPSBsaW5lX2VuYWJsZShwLC0xLGkpOwoJCWlmKGEgPiAxICYmIGEgPCBtaW4peyBtaW4gPSBhOyBvcmllbnQ9MTsgaW5kZXg9aTsgfQoJfQoJLy8g44GK44GLQwoJaWYoaW5kZXg9PS0xKSByZXR1cm4gMDsKCS8vIOOCpOODhuODrOODvOOCv+WIneacn+WMlgoJaWYob3JpZW50KXsgZHggPSAwOyBkeSA9IDE7ICp4ID0gaW5kZXg7ICp5ID0gMDsgayA9IHAtPnNpemUueTsgfQoJZWxzZXsgZHggPSAxOyBkeSA9IDA7ICp4ID0gMDsgKnkgPSBpbmRleDsgayA9IHAtPnNpemUueDsgfQoJLy8g5o6i57SiCglmb3IoaT0wO2k8aztpKyspewoJCWlmKHAtPmNlbGxbKnhdWyp5XS0+c3RhdCA9PSBVTktOT1dOKXsKCQkJaWYoZWxtX3NlbGVjdGlvbl9lbmFibGUocCwqeCwqeSxGSUxMKSl7CgkJCQlyZXR1cm4gMTsKCQkJfQoJCX0KCQkqeCs9ZHg7KnkrPWR5OwoJfQoJcmV0dXJuIDA7Cn0KLy8g5raI5Y675rOV44Gu5a6f6KOFCmludCBlbG1pbmF0aW9uKHBpY2xvcyAqc3JjKQp7CglpbnQgeCx5OwoJcGljbG9zICpwID0gY2xvbmVfcGljbG9zKHNyYyk7CglpZihlbG1fc2VsZWN0KHAsJngsJnkpKXsKCQkvLyDloZfmvbDjgZflj6/og73jgarjgrvjg6vjgpLloZfmvbDjgZfjgZ/jgajku67lrprjgZfjgabop6PjgYTjgabjgb/jgosKCQlwLT5jZWxsW3hdW3ldLT5zdGF0ID0gRklMTDsKCQlkZXRlY3QocCk7CgkJaWYoIXBpY2xvc19lbmFibGUocCkpewoJCQkvLyDku67lrprjgYzplpPpgZXjgaPjgabjgYTjgovloLTlkIjjgIHnqbrnmb3jgavjgZfjgabop6PjgYTjgabjgb/jgovjgIIKCQkJY29weV9waWNsb3Moc3JjLHApOwoJCQlwLT5jZWxsW3hdW3ldLT5zdGF0ID0gU1BBQ0U7CgkJCWRldGVjdChwKTsKCQkJaWYoIXBpY2xvc19lbmFibGUocCkpewoJCQkJLy8g5aGX5r2w44GX44Gn44KC56m655m944Gn44KC44Gg44KB44Gq44Gu44Gn6Kaq44Gu5Luu5a6a44GM44G+44Gh44GM44GECgkJCQlkZWxldGVfcGljbG9zKHAsMSk7CgkJCQlyZXR1cm4gMDsKCQkJfQoJCX0KCQlpZihpc19maW5pc2hfcGljbG9zKHApKXsKCQkJLy8g57WC5LqG5Yik5a6a44KS44Kv44Oq44Ki44GZ44KM44Gw44KB44Gn44Gf44GP57WC5LqGCgkJCWNvcHlfcGljbG9zKHAsIHNyYyk7CgkJCWRlbGV0ZV9waWNsb3MocCwxKTsKCQkJcmV0dXJuIDE7IC8vIOOChOOBo+OBn+OBre+8gQoJCX1lbHNlewoJCQkvLyDjgq/jg6rjgqLjgafjgY3jgabjgYTjgarjgYTjga7jgaflho3luLDnmoTjgavmtojljrvms5XjgpLnlKjjgYTjgosKCQkJaWYoZWxtaW5hdGlvbihwKSl7CgkJCQkvLyDmtojljrvms5Xjga7ntpnntprjgavjgojjgorop6PjgYzmsYLjgoHjgonjgozjgZ/jgIIKCQkJCWNvcHlfcGljbG9zKHAsIHNyYyk7CgkJCQlkZWxldGVfcGljbG9zKHAsMSk7CgkJCQlyZXR1cm4gMTsKCQkJfWVsc2V7CgkJCQlpZihwLT5jZWxsW3hdW3ldLT5zdGF0ID09IFNQQUNFKXsKCQkJCQkvLyDloZfmvbDjgZfjgafjgoLnqbrnmb3jgafjgoLjgaDjgoHjgarjga7jgafopqrjga7ku67lrprjgYzjgb7jgaHjgYzjgYQKCQkJCQlkZWxldGVfcGljbG9zKHAsMSk7CgkJCQkJcmV0dXJuIDA7CgkJCQl9CgkJCQkvLyDnqbrnmb3jgavjgZfjgabop6PjgYTjgabjgb/jgovjgIIKCQkJCWNvcHlfcGljbG9zKHNyYyxwKTsKCQkJCXAtPmNlbGxbeF1beV0tPnN0YXQgPSBTUEFDRTsKCQkJCWRldGVjdChwKTsKCQkJCWlmKCFwaWNsb3NfZW5hYmxlKHApKXsKCQkJCQkvLyDloZfmvbDjgZfjgafjgoLnqbrnmb3jgafjgoLjgaDjgoHjgarjga7jgafopqrjga7ku67lrprjgYzjgb7jgaHjgYzjgYQKCQkJCQlkZWxldGVfcGljbG9zKHAsMSk7CgkJCQkJcmV0dXJuIDA7CgkJCQl9CgkJCQkvLyDntYLkuobliKTlrprjgpLjgq/jg6rjgqLjgZnjgozjgbDjgoHjgafjgZ/jgY/ntYLkuoYKCQkJCWlmKGlzX2ZpbmlzaF9waWNsb3MocCkpewoJCQkJCWNvcHlfcGljbG9zKHAsIHNyYyk7CgkJCQkJZGVsZXRlX3BpY2xvcyhwLDEpOwoJCQkJCXJldHVybiAxOyAvLyDjgoTjgaPjgZ/jga3vvIEKCQkJCX1lbHNlewoJCQkJCS8vIOOCr+ODquOCouOBp+OBjeOBpuOBhOOBquOBhOOBruOBp+WGjeW4sOeahOOBq+a2iOWOu+azleOCkueUqOOBhOOCiwoJCQkJCWlmKGVsbWluYXRpb24ocCkpewoJCQkJCQkvLyDmtojljrvms5Xjgafop6PjgYzmsYLjgoHjgonjgozjgZ/jgIIKCQkJCQkJY29weV9waWNsb3MocCwgc3JjKTsKCQkJCQkJZGVsZXRlX3BpY2xvcyhwLDEpOwoJCQkJCQlyZXR1cm4gMTsKCQkJCQl9ZWxzZXsKCQkJCQkJLy8g5aGX5r2w44GX44Gn44KC56m655m944Gn44KC44Gg44KB44Gq44Gu44Gn6Kaq44Gu5Luu5a6a44GM44G+44Gh44GM44GECgkJCQkJCWRlbGV0ZV9waWNsb3MocCwxKTsKCQkJCQkJcmV0dXJuIDA7CgkJCQkJfQoJCQkJfQoJCQl9CgkJfQoJfWVsc2V7CgkJLy8g6YG45oqe5Y+v6IO944Gq44K744Or44GM44Gq44GE44Gu44Gn6Kaq44Gu5Luu5a6a44GM44G+44Gh44GM44GECgkJZGVsZXRlX3BpY2xvcyhwLDEpOwoJCXJldHVybiAwOwoJfQp9Ci8qKioqKioqKioqKioqIDwvRUxJTUlOQVRFPiAqKioqKioqKioqKioqKioqLwoKaW50IG1haW4odm9pZCkKewogICAgcGljbG9zICpwOwogICAgaW50IHgseTsKCXB1dHMoIuODlOOCr+ODreOCueOBruWIl+OBruaVsOOCkuWFpeWKm+OBl+OBpuOBj+OBoOOBleOBhOOAgiIpO3NjYW5mKCIlZCIsJngpO2lmKHg8MSl7cHV0cygiRVJST1IuIik7cmV0dXJuIDA7fQogICAgcHV0cygi44OU44Kv44Ot44K544Gu6KGM44Gu5pWw44KS5YWl5Yqb44GX44Gm44GP44Gg44GV44GE44CCIik7c2NhbmYoIiVkIiwmeSk7aWYoeTwxKXtwdXRzKCJFUlJPUi4iKTtyZXR1cm4gMDt9CiAgICBwID0gbmV3X3BpY2xvcyh4LHkpOwogICAgaWYoIWlucHV0X3BpY2xvc19oaW50cyhwKSl7CgkgICAgZGVsZXRlX3BpY2xvcyhwLDEpOwoJCXJldHVybiAwOy8vIEVSUk9SCgl9CglkZXRlY3QocCk7CglpZighaXNfZmluaXNoX3BpY2xvcyhwKSkgZWxtaW5hdGlvbihwKTsKICAgIHByaW50X3BpY2xvc19maW4ocCk7CiAgICBkZWxldGVfcGljbG9zKHAsMSk7CiAgICByZXR1cm4gMDsKfQo=