// cs160_861.c
// http://i...content-available-to-author-only...e.com/gN66JU
#include <stdio.h>
#include <stdlib.h>
// [名前]
// cs160_861 : RGB → HSV
// [機能]
// RGBの3枚のグレースケールから
// HSVの3枚のグレースケールを出力する
// [資料]
// http://c...content-available-to-author-only...d.org/oNUxByE7
// http://j...content-available-to-author-only...a.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93#RGB.E3.81.8B.E3.82.89HSV.E3.81.B8.E3.81.AE.E5.A4.89.E6.8F.9B
// [コマンドオプション]
// argv[1] : 幅
// argv[2] : 高さ
// argv[3] : 入力 R ファイル名
// argv[4] : 入力 G ファイル名
// argv[5] : 入力 B ファイル名
// argv[6] : 出力Hファイル名
// argv[7] : 出力Sファイル名
// argv[8] : 出力Vファイル名
// [入出力ファイルの仕様]
// 入力RGBファイル
// ・R,G,B それぞれ1ファイルである
// ・HEADER 512 bytes
// ・1dotにつき、百分率16bitリトルエンディアン (0~65535 [100%(65536)は存在しないこととする])
// 出力HSVファイル
// ・H,S,V それぞれ1ファイルである
// ・HEADER 無し
// ・1dotにつき、百分率16bitリトルエンディアン (0~65535 [65535が100%である])
// H (0.0以上 360.0以下) ~ H値 / 360.0 * 65535
// S (0.0以上 1.0以下) ~ S値 * 65535
// V (0.0以上 1.0以下) ~ V値 * 65535
// ※未定義の場合は0x0000をセットする
// 定数
const int header_size_rgb_file = 512; // RGBファイルのヘッダサイズ
const int comvert_mode = 1; // 変換モード (1:円柱モデル変換, 2:円錐モデル変換)
// fopen(); のモード
#ifdef _WIN32
// Windows
#define FOPEN_MODE_R "rb"
#define FOPEN_MODE_W "wb"
#else
// Linux
#define FOPEN_MODE_R "r"
#define FOPEN_MODE_W "w"
#endif
enum ERR_CODE // エラーコード
{
ERR_Normal = 0 , // 正常終了
ERR_Option , // 起動オプションが足りない
ERR_Width , // 入力幅が1未満
ERR_Height , // 入力高が1未満
ERR_InFile_Size , // 入力ファイルサイズが変である
// 以下6個は並び順を変えないこと →→→→→→→→
ERR_InFile_R , // 入力Rファイルオープンエラー
ERR_InFile_G , // 入力Gファイルオープンエラー
ERR_InFile_B , // 入力Bファイルオープンエラー
ERR_OutFile_H , // 出力Hファイルオープンエラー
ERR_OutFile_S , // 出力Sファイルオープンエラー
ERR_OutFile_V , // 出力Vファイルオープンエラー
// ←←←←←←←←←←←←←←←←←←←←←←←
ERR_MemAlloc , // メモリ確保エラー
ERR_Cnv_mode , // cnv_rgb2hsv();でcomvert_modeに不正な値がセットされていた(Prog Error)
};
// 中でやり取りするデータ
typedef struct
{
// 幅、高さ
int width, height;
int img_size;
// 入出力 ファイル
FILE * fp_in[3];
FILE * fp_out[3];
// データ
unsigned short *src[3]; // [0]R, [1]G, [2]B
unsigned short *dst[3]; // [0]H, [1]S, [2]V
} RGB_HSV_DATA;
// 関数
extern void load_rgb_data(RGB_HSV_DATA* data); // Load
extern int cnv_rgb2hsv(RGB_HSV_DATA* data); // Convert
extern int save_hsv_data(RGB_HSV_DATA* data); // Save
extern void get_max_min_mxcolor // max, min, maxのcolor を調べる
( /*[in]*/unsigned short R, unsigned short G, unsigned short B,
/*[out]*/unsigned short *mx, unsigned short *mn, char *mxcolor );
#define MIN(x, y) ((x)<(y) ? (x) : (y)) // 2値の最小値
#define MIN3(x, y, z) MIN(MIN((x), (y)), (z)) // 3値の最小値
// グローバル
char g_flagEndian; // 現在の環境のエンディアン (0:ビッグ / 1:リトル)
/****************************************************************************
* main
****************************************************************************/
int main(int argc, char *argv[])
{
int res; // 終了コード
RGB_HSV_DATA rh_data; // RGB,HSV データ
int i; // 汎用
/*
* 初期化 & ファイルオープン & チェック
*/
i = 1;g_flagEndian = *((char *)(&i)); // 現在の環境のエンディアン判定 (0:ビッグ / 1:リトル)
memset(&rh_data
, 0x00, sizeof(RGB_HSV_DATA
)); res = ERR_Normal;
if(argc != 9)
{
res = ERR_Option;
goto ERR_END;
}
// 幅
rh_data.
width = atoi (argv
[1]); if(rh_data.width < 0)
{
res = ERR_Width;
goto ERR_END;
}
// 高さ
rh_data.
height = atoi (argv
[2]); if(rh_data.height < 0)
{
res = ERR_Height;
goto ERR_END;
}
rh_data.img_size = rh_data.width * rh_data.height;
// ファイルオープン
for(i=0;i<3;i++)
{
if(NULL
== (rh_data.
fp_in[i
] = fopen(argv
[3+i
], FOPEN_MODE_R
))) {
res = ERR_InFile_R + i;
goto ERR_END;
}
}
for(i=0;i<3;i++)
{
if(NULL
==(rh_data.
fp_out[i
]=fopen(argv
[6+i
], FOPEN_MODE_W
))) {
res = ERR_OutFile_H + i;
goto ERR_END;
}
}
// ファイルサイズチェック
for(i=0;i<3;i++)
{
fseek(rh_data.
fp_in[i
], 0, SEEK_END
); if(ftell(rh_data.
fp_in[i
]) != rh_data.
img_size * sizeof(unsigned short) + header_size_rgb_file
) {
res = ERR_InFile_Size;
goto ERR_END;
}
}
/*
* メモリ確保
*/
for(i=0; i<3; i++)
{
if(NULL
== (rh_data.
src[i
] = (unsigned short*)malloc(rh_data.
img_size*sizeof(unsigned short)))) {
res = ERR_MemAlloc;
goto ERR_END;
}
if(NULL
== (rh_data.
dst[i
] = (unsigned short*)malloc(rh_data.
img_size*sizeof(unsigned short)))) {
res = ERR_MemAlloc;
goto ERR_END;
}
}
/*
* ロード
*/
load_rgb_data(&rh_data);
/*
* 変換
*/
res = cnv_rgb2hsv(&rh_data);
if(ERR_Normal != res)
{
goto ERR_END;
}
/*
* セーブ
*/
res = save_hsv_data(&rh_data);
if(ERR_Normal != res)
{
goto ERR_END;
}
/*
* 終了
*/
res = ERR_Normal;
ERR_END:;
// 解放
for(i=0;i<3;i++)
{
if(rh_data.
dst[i
]) { free(rh_data.
dst[i
]); rh_data.
dst[i
] = NULL
; } if(rh_data.
src[i
]) { free(rh_data.
src[i
]); rh_data.
src[i
] = NULL
; } if(rh_data.
fp_out[i
]) { fclose(rh_data.
fp_out[i
]); rh_data.
fp_out[i
] = NULL
; } if(rh_data.
fp_in [i
]) { fclose(rh_data.
fp_in [i
]); rh_data.
fp_in [i
] = NULL
; } }
return res;
}
// Load
void load_rgb_data(RGB_HSV_DATA* data)
{
int i, j;
unsigned short *p, v;
for(i=0; i<3; i++)
{
fseek(data
->fp_in
[i
], header_size_rgb_file
, SEEK_SET
); fread(data
->src
[i
], sizeof(unsigned short), data
->img_size
, data
->fp_in
[i
]); // 全部読む if(0 == g_flagEndian)
{
// big endian (入れ替える必要がある)
p = data->src[i];
for(j=0; j<data->img_size; j++)
{
v = *p;
*p++ = ((v >> 8) & 0xFF) | ((v << 8) & 0xFF00);
}
}
}
}
// Save
int save_hsv_data(RGB_HSV_DATA* data)
{
int i, j;
unsigned short *temp = NULL; // エンディアン変換バッファ
unsigned short *p, v;
// 確保
if(0 == g_flagEndian)
{
// big endian
if(NULL
== (temp
= (unsigned short*)malloc(data
->img_size
* sizeof(unsigned short)))) {
return ERR_MemAlloc;
}
}
// 書き込み
for(i=0; i<3; i++)
{
fseek(data
->fp_out
[i
],0,SEEK_SET
); if(0 == g_flagEndian)
{
memcpy(temp
, &(data
->dst
[i
][0]), sizeof(unsigned short) * data
->img_size
); p = temp;
for(j=0; j<data->img_size; j++)
{
v = *p;
*p++ = ((v >> 8) & 0xFF) | ((v << 8) & 0xFF00);
}
p = temp;
} else {
p = &(data->dst[i][0]);
}
fwrite(p
, sizeof(unsigned short), data
->img_size
, data
->fp_out
[i
]); }
// 解放
if(temp)
{
}
return 0;
}
// max, min, maxのcolor を調べる
// mxcolor = 'R', 'G', 'B'のいずれか
void get_max_min_mxcolor(/*[in]*/unsigned short R, unsigned short G, unsigned short B, /*[out]*/unsigned short *mx, unsigned short *mn, char *mxcolor)
{
// max
*mx = R; *mxcolor='R';
if(G > *mx) { *mx = G; *mxcolor='G'; }
if(B > *mx) { *mx = B; *mxcolor='B'; }
// min
*mn = MIN3(R, G, B);
}
// Convert
int cnv_rgb2hsv(RGB_HSV_DATA* data)
{
unsigned short max, min;
char color_max; // maxの色 ('R','G','B')
unsigned short H,S,V; // HSV
double th; // H計算用
double sub_maxmin; // H計算用 (max - min)
int i; // 汎用
for(i=0; i<data->img_size; i++)
{
// max, min, maxの色を取得
get_max_min_mxcolor(data->src[0][i], data->src[1][i], data->src[2][i], &max, &min, &color_max);
// SVを求める
// ( http://j...content-available-to-author-only...a.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93#RGB.E3.81.8B.E3.82.89HSV.E3.81.B8.E3.81.AE.E5.A4.89.E6.8F.9B )
V = max;
if(max == min)
{
S = 0;
}else{
if(max == 0)
{
S = 0; // 未定義である
}else{
if(comvert_mode == 1)
{
S = (unsigned short)((double)(max - min) / (double)max * 65535.0);
}
else if(comvert_mode == 2)
{
S = max - min;
} else {
return ERR_Cnv_mode;
}
}
}
// Hを求める
if(max == min)
{
th = 0.0; // 未定義である
}else{
sub_maxmin = (double)max - (double)min;
switch(color_max)
{
case 'R':th = 60.0 * ((double)data->src[1][i] - (double)data->src[2][i]) / sub_maxmin + 0.0;break;
case 'G':th = 60.0 * ((double)data->src[2][i] - (double)data->src[0][i]) / sub_maxmin + 120.0;break;
case 'B':th = 60.0 * ((double)data->src[0][i] - (double)data->src[1][i]) / sub_maxmin + 240.0;break;
}
}
// 0.0以上360.0未満に丸める
while(th < 0.0)
{
th += 360.0;
}
while(th >= 360.0)
{
th -= 360.0;
}
H = (unsigned short)(th / 360.0 * 65535.0); // 360.0の百分率を16bit化
// HSVをセットする
data->dst[0][i] = H;
data->dst[1][i] = S;
data->dst[2][i] = V;
}
return ERR_Normal;
}
// End of cs160_861.c
Ly8gIGNzMTYwXzg2MS5jCi8vICAgIGh0dHA6Ly9pLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5lLmNvbS9nTjY2SlUKI2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KCi8vICBb5ZCN5YmNXQovLyAgICAgIGNzMTYwXzg2MSA6IFJHQiDihpIgSFNWCi8vICBb5qmf6IO9XQovLyAgICAgIFJHQuOBrjPmnprjga7jgrDjg6zjg7zjgrnjgrHjg7zjg6vjgYvjgokKLy8gICAgICBIU1bjga4z5p6a44Gu44Kw44Os44O844K544Kx44O844Or44KS5Ye65Yqb44GZ44KLCi8vICBb6LOH5paZXQovLyAgICAgIGh0dHA6Ly9jLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5kLm9yZy9vTlV4QnlFNwovLyAgICAgIGh0dHA6Ly9qLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5hLm9yZy93aWtpL0hTViVFOCU4OSVCMiVFNyVBOSVCQSVFOSU5NiU5MyNSR0IuRTMuODEuOEIuRTMuODIuODlIU1YuRTMuODEuQjguRTMuODEuQUUuRTUuQTQuODkuRTYuOEYuOUIKLy8gIFvjgrPjg57jg7Pjg4njgqrjg5fjgrfjg6fjg7NdCi8vICAgICAgYXJndlsxXSA6IOW5hQovLyAgICAgIGFyZ3ZbMl0gOiDpq5jjgZUKLy8gICAgICBhcmd2WzNdIDog5YWl5YqbIFIg44OV44Kh44Kk44Or5ZCNCi8vICAgICAgYXJndls0XSA6IOWFpeWKmyBHIOODleOCoeOCpOODq+WQjQovLyAgICAgIGFyZ3ZbNV0gOiDlhaXlipsgQiDjg5XjgqHjgqTjg6vlkI0KLy8gICAgICBhcmd2WzZdIDog5Ye65YqbSOODleOCoeOCpOODq+WQjQovLyAgICAgIGFyZ3ZbN10gOiDlh7rliptT44OV44Kh44Kk44Or5ZCNCi8vICAgICAgYXJndls4XSA6IOWHuuWKm1bjg5XjgqHjgqTjg6vlkI0KLy8gIFvlhaXlh7rlipvjg5XjgqHjgqTjg6vjga7ku5Xmp5hdCi8vICAgICAg5YWl5YqbUkdC44OV44Kh44Kk44OrCi8vICAgICAgICDjg7tSLEcsQiDjgZ3jgozjgZ7jgowx44OV44Kh44Kk44Or44Gn44GC44KLCi8vICAgICAgICDjg7tIRUFERVIgNTEyIGJ5dGVzCi8vICAgICAgICDjg7sxZG9044Gr44Gk44GN44CB55m+5YiG546HMTZiaXTjg6rjg4jjg6vjgqjjg7Pjg4fjgqPjgqLjg7MgKDDvvZ42NTUzNSBbMTAwJSg2NTUzNinjga/lrZjlnKjjgZfjgarjgYTjgZPjgajjgajjgZnjgotdKQovLyAgICAgIOWHuuWKm0hTVuODleOCoeOCpOODqwovLyAgICAgICAg44O7SCxTLFYg44Gd44KM44Ge44KMMeODleOCoeOCpOODq+OBp+OBguOCiwovLyAgICAgICAg44O7SEVBREVSIOeEoeOBlwovLyAgICAgICAg44O7MWRvdOOBq+OBpOOBjeOAgeeZvuWIhueOhzE2Yml044Oq44OI44Or44Ko44Oz44OH44Kj44Ki44OzICgw772eNjU1MzUgWzY1NTM144GMMTAwJeOBp+OBguOCi10pCi8vICAgICAgICAgICAgSCAoMC4w5Lul5LiKIDM2MC4w5Lul5LiLKSDvvZ4gSOWApCAvIDM2MC4wICogNjU1MzUKLy8gICAgICAgICAgICBTICgwLjDku6XkuIogICAxLjDku6XkuIspIO+9niBT5YCkICAgICAgICAgKiA2NTUzNQovLyAgICAgICAgICAgIFYgKDAuMOS7peS4iiAgIDEuMOS7peS4iykg772eIFblgKQgICAgICAgICAqIDY1NTM1Ci8vICAgICAgICAgIOKAu+acquWumue+qeOBruWgtOWQiOOBrzB4MDAwMOOCkuOCu+ODg+ODiOOBmeOCiwoKCi8vIOWumuaVsApjb25zdCBpbnQgaGVhZGVyX3NpemVfcmdiX2ZpbGUgID0gNTEyOyAgLy8gUkdC44OV44Kh44Kk44Or44Gu44OY44OD44OA44K144Kk44K6CmNvbnN0IGludCBjb212ZXJ0X21vZGUgICAgICAgICAgPSAgIDE7ICAvLyDlpInmj5vjg6Ljg7zjg4kgKDE65YaG5p+x44Oi44OH44Or5aSJ5o+bLCAyOuWGhumMkOODouODh+ODq+WkieaPmykKLy8gZm9wZW4oKTsg44Gu44Oi44O844OJCiNpZmRlZiBfV0lOMzIKICAgIC8vIFdpbmRvd3MKICAgICNkZWZpbmUgRk9QRU5fTU9ERV9SICAicmIiCiAgICAjZGVmaW5lIEZPUEVOX01PREVfVyAgIndiIgojZWxzZQogICAgLy8gTGludXgKICAgICNkZWZpbmUgRk9QRU5fTU9ERV9SICAiciIKICAgICNkZWZpbmUgRk9QRU5fTU9ERV9XICAidyIKI2VuZGlmCgplbnVtIEVSUl9DT0RFICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8g44Ko44Op44O844Kz44O844OJCnsKICAgIEVSUl9Ob3JtYWwgPSAwICAsIC8vIOato+W4uOe1guS6hgogICAgRVJSX09wdGlvbiAgICAgICwgLy8g6LW35YuV44Kq44OX44K344On44Oz44GM6Laz44KK44Gq44GECiAgICBFUlJfV2lkdGggICAgICAgLCAvLyDlhaXlipvluYXjgYwx5pyq5rqACiAgICBFUlJfSGVpZ2h0ICAgICAgLCAvLyDlhaXlipvpq5jjgYwx5pyq5rqACiAgICBFUlJfSW5GaWxlX1NpemUgLCAvLyDlhaXlipvjg5XjgqHjgqTjg6vjgrXjgqTjgrrjgYzlpInjgafjgYLjgosKICAgIC8vIOS7peS4izblgIvjga/kuKbjgbPpoIbjgpLlpInjgYjjgarjgYTjgZPjgagg4oaS4oaS4oaS4oaS4oaS4oaS4oaS4oaSCiAgICBFUlJfSW5GaWxlX1IgICAgLCAvLyDlhaXliptS44OV44Kh44Kk44Or44Kq44O844OX44Oz44Ko44Op44O8CiAgICBFUlJfSW5GaWxlX0cgICAgLCAvLyDlhaXliptH44OV44Kh44Kk44Or44Kq44O844OX44Oz44Ko44Op44O8CiAgICBFUlJfSW5GaWxlX0IgICAgLCAvLyDlhaXliptC44OV44Kh44Kk44Or44Kq44O844OX44Oz44Ko44Op44O8CiAgICBFUlJfT3V0RmlsZV9IICAgLCAvLyDlh7rliptI44OV44Kh44Kk44Or44Kq44O844OX44Oz44Ko44Op44O8CiAgICBFUlJfT3V0RmlsZV9TICAgLCAvLyDlh7rliptT44OV44Kh44Kk44Or44Kq44O844OX44Oz44Ko44Op44O8CiAgICBFUlJfT3V0RmlsZV9WICAgLCAvLyDlh7rliptW44OV44Kh44Kk44Or44Kq44O844OX44Oz44Ko44Op44O8CiAgICAvLyDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpDihpAKICAgIEVSUl9NZW1BbGxvYyAgICAsIC8vIOODoeODouODqueiuuS/neOCqOODqeODvAogICAgRVJSX0Nudl9tb2RlICAgICwgLy8gY252X3JnYjJoc3YoKTvjgadjb212ZXJ0X21vZGXjgavkuI3mraPjgarlgKTjgYzjgrvjg4Pjg4jjgZXjgozjgabjgYTjgZ8oUHJvZyBFcnJvcikKfTsKCi8vIOS4reOBp+OChOOCiuWPluOCiuOBmeOCi+ODh+ODvOOCvwp0eXBlZGVmIHN0cnVjdAp7CiAgICAvLyDluYXjgIHpq5jjgZUKICAgIGludCAgICAgICAgICAgICB3aWR0aCwgaGVpZ2h0OwogICAgaW50ICAgICAgICAgICAgIGltZ19zaXplOwogICAgLy8g5YWl5Ye65YqbIOODleOCoeOCpOODqwogICAgRklMRSAqICAgICAgICAgIGZwX2luWzNdOwogICAgRklMRSAqICAgICAgICAgIGZwX291dFszXTsKICAgIC8vIOODh+ODvOOCvwogICAgdW5zaWduZWQgc2hvcnQgICpzcmNbM107ICAgIC8vIFswXVIsIFsxXUcsIFsyXUIKICAgIHVuc2lnbmVkIHNob3J0ICAqZHN0WzNdOyAgICAvLyBbMF1ILCBbMV1TLCBbMl1WCn0gUkdCX0hTVl9EQVRBOwoKCi8vIOmWouaVsApleHRlcm4gdm9pZCBsb2FkX3JnYl9kYXRhKFJHQl9IU1ZfREFUQSogZGF0YSk7ICAvLyBMb2FkCmV4dGVybiBpbnQgIGNudl9yZ2IyaHN2KFJHQl9IU1ZfREFUQSogZGF0YSk7ICAgIC8vIENvbnZlcnQKZXh0ZXJuIGludCAgc2F2ZV9oc3ZfZGF0YShSR0JfSFNWX0RBVEEqIGRhdGEpOyAgLy8gU2F2ZQpleHRlcm4gdm9pZCBnZXRfbWF4X21pbl9teGNvbG9yICAgICAgICAgICAgICAgICAvLyBtYXgsIG1pbiwgbWF444GuY29sb3Ig44KS6Kq/44G544KLCiAgICAgICAgICAgICAgICAoIC8qW2luXSovdW5zaWduZWQgc2hvcnQgUiwgdW5zaWduZWQgc2hvcnQgRywgdW5zaWduZWQgc2hvcnQgQiwKICAgICAgICAgICAgICAgICAgLypbb3V0XSovdW5zaWduZWQgc2hvcnQgKm14LCB1bnNpZ25lZCBzaG9ydCAqbW4sIGNoYXIgKm14Y29sb3IgKTsKI2RlZmluZSBNSU4oeCwgeSkgICAgICAgKCh4KTwoeSkgPyAoeCkgOiAoeSkpICAgLy8gMuWApOOBruacgOWwj+WApAojZGVmaW5lIE1JTjMoeCwgeSwgeikgICBNSU4oTUlOKCh4KSwgKHkpKSwgKHopKSAvLyAz5YCk44Gu5pyA5bCP5YCkCgovLyDjgrDjg63jg7zjg5Djg6sKY2hhciBnX2ZsYWdFbmRpYW47ICAvLyDnj77lnKjjga7nkrDlooPjga7jgqjjg7Pjg4fjgqPjgqLjg7MgKDA644OT44OD44KwIC8gMTrjg6rjg4jjg6spCgoKLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioKICogICAgICAgICAgbWFpbgogKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi8KaW50IG1haW4oaW50IGFyZ2MsIGNoYXIgKmFyZ3ZbXSkKewogICAgaW50ICAgICAgICAgICAgIHJlczsgICAgICAgIC8vIOe1guS6huOCs+ODvOODiQogICAgUkdCX0hTVl9EQVRBICAgIHJoX2RhdGE7ICAgIC8vIFJHQixIU1Yg44OH44O844K/CiAgICBpbnQgICAgICAgICAgICAgaTsgICAgICAgICAgLy8g5rGO55SoCgogICAgLyoKICAgICAqICAg5Yid5pyf5YyWICYg44OV44Kh44Kk44Or44Kq44O844OX44OzICYg44OB44Kn44OD44KvCiAgICAgKi8KICAgIGkgPSAxO2dfZmxhZ0VuZGlhbiA9ICooKGNoYXIgKikoJmkpKTsgICAgICAgLy8g54++5Zyo44Gu55Kw5aKD44Gu44Ko44Oz44OH44Kj44Ki44Oz5Yik5a6aICgwOuODk+ODg+OCsCAvIDE644Oq44OI44OrKQogICAgbWVtc2V0KCZyaF9kYXRhLCAweDAwLCBzaXplb2YoUkdCX0hTVl9EQVRBKSk7CiAgICByZXMgPSBFUlJfTm9ybWFsOwogICAgaWYoYXJnYyAhPSA5KQogICAgewogICAgICAgIHJlcyA9IEVSUl9PcHRpb247CiAgICAgICAgZ290byBFUlJfRU5EOwogICAgfQogICAgLy8g5bmFCiAgICByaF9kYXRhLndpZHRoID0gYXRvaSAoYXJndlsxXSk7CiAgICBpZihyaF9kYXRhLndpZHRoIDwgMCkKICAgIHsKICAgICAgICByZXMgPSBFUlJfV2lkdGg7CiAgICAgICAgZ290byBFUlJfRU5EOwogICAgfQogICAgLy8g6auY44GVCiAgICByaF9kYXRhLmhlaWdodCA9IGF0b2kgKGFyZ3ZbMl0pOwogICAgaWYocmhfZGF0YS5oZWlnaHQgPCAwKQogICAgewogICAgICAgIHJlcyA9IEVSUl9IZWlnaHQ7CiAgICAgICAgZ290byBFUlJfRU5EOwogICAgfQogICAgcmhfZGF0YS5pbWdfc2l6ZSA9IHJoX2RhdGEud2lkdGggKiByaF9kYXRhLmhlaWdodDsKICAgIC8vIOODleOCoeOCpOODq+OCquODvOODl+ODswogICAgZm9yKGk9MDtpPDM7aSsrKQogICAgewogICAgICAgIGlmKE5VTEwgPT0gKHJoX2RhdGEuZnBfaW5baV0gPSBmb3Blbihhcmd2WzMraV0sIEZPUEVOX01PREVfUikpKQogICAgICAgIHsKICAgICAgICAgICAgcmVzID0gRVJSX0luRmlsZV9SICsgaTsKICAgICAgICAgICAgZ290byBFUlJfRU5EOwogICAgICAgIH0KICAgIH0KICAgIGZvcihpPTA7aTwzO2krKykKICAgIHsKICAgICAgICBpZihOVUxMPT0ocmhfZGF0YS5mcF9vdXRbaV09Zm9wZW4oYXJndls2K2ldLCBGT1BFTl9NT0RFX1cpKSkKICAgICAgICB7CiAgICAgICAgICAgIHJlcyA9IEVSUl9PdXRGaWxlX0ggKyBpOwogICAgICAgICAgICBnb3RvIEVSUl9FTkQ7CiAgICAgICAgfQogICAgfQogICAgLy8g44OV44Kh44Kk44Or44K144Kk44K644OB44Kn44OD44KvCiAgICBmb3IoaT0wO2k8MztpKyspCiAgICB7CiAgICAgICAgZnNlZWsocmhfZGF0YS5mcF9pbltpXSwgMCwgU0VFS19FTkQpOwogICAgICAgIGlmKGZ0ZWxsKHJoX2RhdGEuZnBfaW5baV0pICE9IHJoX2RhdGEuaW1nX3NpemUgKiBzaXplb2YodW5zaWduZWQgc2hvcnQpICsgaGVhZGVyX3NpemVfcmdiX2ZpbGUpCiAgICAgICAgewogICAgICAgICAgICByZXMgPSBFUlJfSW5GaWxlX1NpemU7CiAgICAgICAgICAgIGdvdG8gRVJSX0VORDsKICAgICAgICB9CiAgICB9CgogICAgLyoKICAgICAqICAg44Oh44Oi44Oq56K65L+dCiAgICAgKi8KICAgIGZvcihpPTA7IGk8MzsgaSsrKQogICAgewogICAgICAgIGlmKE5VTEwgPT0gKHJoX2RhdGEuc3JjW2ldID0gKHVuc2lnbmVkIHNob3J0KiltYWxsb2MocmhfZGF0YS5pbWdfc2l6ZSpzaXplb2YodW5zaWduZWQgc2hvcnQpKSkpCiAgICAgICAgewogICAgICAgICAgICByZXMgPSBFUlJfTWVtQWxsb2M7CiAgICAgICAgICAgIGdvdG8gRVJSX0VORDsKICAgICAgICB9CiAgICAgICAgaWYoTlVMTCA9PSAocmhfZGF0YS5kc3RbaV0gPSAodW5zaWduZWQgc2hvcnQqKW1hbGxvYyhyaF9kYXRhLmltZ19zaXplKnNpemVvZih1bnNpZ25lZCBzaG9ydCkpKSkKICAgICAgICB7CiAgICAgICAgICAgIHJlcyA9IEVSUl9NZW1BbGxvYzsKICAgICAgICAgICAgZ290byBFUlJfRU5EOwogICAgICAgIH0KICAgIH0KCiAgICAvKgogICAgICogICDjg63jg7zjg4kKICAgICAqLwogICAgbG9hZF9yZ2JfZGF0YSgmcmhfZGF0YSk7CgogICAgLyoKICAgICAqICAg5aSJ5o+bCiAgICAgKi8KICAgIHJlcyA9IGNudl9yZ2IyaHN2KCZyaF9kYXRhKTsKICAgIGlmKEVSUl9Ob3JtYWwgIT0gcmVzKQogICAgewogICAgICAgIGdvdG8gRVJSX0VORDsKICAgIH0KCiAgICAvKgogICAgICogICDjgrvjg7zjg5YKICAgICAqLwogICAgcmVzID0gc2F2ZV9oc3ZfZGF0YSgmcmhfZGF0YSk7CiAgICBpZihFUlJfTm9ybWFsICE9IHJlcykKICAgIHsKICAgICAgICBnb3RvIEVSUl9FTkQ7CiAgICB9CgogICAgLyoKICAgICAqIOe1guS6hgogICAgICovCiAgICByZXMgPSBFUlJfTm9ybWFsOwpFUlJfRU5EOjsKICAgIC8vIOino+aUvgogICAgZm9yKGk9MDtpPDM7aSsrKQogICAgewogICAgICAgIGlmKHJoX2RhdGEuZHN0W2ldKSB7IGZyZWUocmhfZGF0YS5kc3RbaV0pOyByaF9kYXRhLmRzdFtpXSA9IE5VTEw7IH0KICAgICAgICBpZihyaF9kYXRhLnNyY1tpXSkgeyBmcmVlKHJoX2RhdGEuc3JjW2ldKTsgcmhfZGF0YS5zcmNbaV0gPSBOVUxMOyB9CiAgICAgICAgaWYocmhfZGF0YS5mcF9vdXRbaV0pIHsgZmNsb3NlKHJoX2RhdGEuZnBfb3V0W2ldKTsgcmhfZGF0YS5mcF9vdXRbaV0gPSBOVUxMOyB9CiAgICAgICAgaWYocmhfZGF0YS5mcF9pbiBbaV0pIHsgZmNsb3NlKHJoX2RhdGEuZnBfaW4gW2ldKTsgcmhfZGF0YS5mcF9pbiBbaV0gPSBOVUxMOyB9CiAgICB9CiAgICByZXR1cm4gcmVzOwp9CgovLyBMb2FkCnZvaWQgbG9hZF9yZ2JfZGF0YShSR0JfSFNWX0RBVEEqIGRhdGEpCnsKICAgIGludCAgICAgICAgICAgICBpLCBqOwogICAgdW5zaWduZWQgc2hvcnQgICpwLCB2OwoKICAgIGZvcihpPTA7IGk8MzsgaSsrKQogICAgewogICAgICAgIGZzZWVrKGRhdGEtPmZwX2luW2ldLCBoZWFkZXJfc2l6ZV9yZ2JfZmlsZSwgU0VFS19TRVQpOwogICAgICAgIGZyZWFkKGRhdGEtPnNyY1tpXSwgc2l6ZW9mKHVuc2lnbmVkIHNob3J0KSwgZGF0YS0+aW1nX3NpemUsIGRhdGEtPmZwX2luW2ldKTsgLy8g5YWo6YOo6Kqt44KACiAgICAgICAgaWYoMCA9PSBnX2ZsYWdFbmRpYW4pCiAgICAgICAgewogICAgICAgICAgICAvLyBiaWcgZW5kaWFuICjlhaXjgozmm7/jgYjjgovlv4XopoHjgYzjgYLjgospCiAgICAgICAgICAgIHAgPSBkYXRhLT5zcmNbaV07CiAgICAgICAgICAgIGZvcihqPTA7IGo8ZGF0YS0+aW1nX3NpemU7IGorKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdiA9ICpwOwogICAgICAgICAgICAgICAgKnArKyA9ICgodiA+PiA4KSAmIDB4RkYpIHwgKCh2IDw8IDgpICYgMHhGRjAwKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KfQoKLy8gU2F2ZQppbnQgc2F2ZV9oc3ZfZGF0YShSR0JfSFNWX0RBVEEqIGRhdGEpCnsKICAgIGludCAgICAgICAgICAgICBpLCBqOwogICAgdW5zaWduZWQgc2hvcnQgICp0ZW1wID0gTlVMTDsgICAvLyDjgqjjg7Pjg4fjgqPjgqLjg7PlpInmj5vjg5Djg4Pjg5XjgqEKICAgIHVuc2lnbmVkIHNob3J0ICAqcCwgdjsKCiAgICAvLyDnorrkv50KICAgIGlmKDAgPT0gZ19mbGFnRW5kaWFuKQogICAgewogICAgICAgIC8vIGJpZyBlbmRpYW4KICAgICAgICBpZihOVUxMID09ICh0ZW1wID0gKHVuc2lnbmVkIHNob3J0KiltYWxsb2MoZGF0YS0+aW1nX3NpemUgKiBzaXplb2YodW5zaWduZWQgc2hvcnQpKSkpCiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gRVJSX01lbUFsbG9jOwogICAgICAgIH0KICAgIH0KICAgIC8vIOabuOOBjei+vOOBvwogICAgZm9yKGk9MDsgaTwzOyBpKyspCiAgICB7CiAgICAgICAgZnNlZWsoZGF0YS0+ZnBfb3V0W2ldLDAsU0VFS19TRVQpOwogICAgICAgIGlmKDAgPT0gZ19mbGFnRW5kaWFuKQogICAgICAgIHsKICAgICAgICAgICAgbWVtY3B5KHRlbXAsICYoZGF0YS0+ZHN0W2ldWzBdKSwgc2l6ZW9mKHVuc2lnbmVkIHNob3J0KSAqIGRhdGEtPmltZ19zaXplKTsKICAgICAgICAgICAgcCA9IHRlbXA7CiAgICAgICAgICAgIGZvcihqPTA7IGo8ZGF0YS0+aW1nX3NpemU7IGorKykKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdiA9ICpwOwogICAgICAgICAgICAgICAgKnArKyA9ICgodiA+PiA4KSAmIDB4RkYpIHwgKCh2IDw8IDgpICYgMHhGRjAwKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBwID0gdGVtcDsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBwID0gJihkYXRhLT5kc3RbaV1bMF0pOwogICAgICAgIH0KICAgICAgICBmd3JpdGUocCwgc2l6ZW9mKHVuc2lnbmVkIHNob3J0KSwgZGF0YS0+aW1nX3NpemUsIGRhdGEtPmZwX291dFtpXSk7CiAgICB9CiAgICAvLyDop6PmlL4KICAgIGlmKHRlbXApCiAgICB7CiAgICAgICAgZnJlZSh0ZW1wKTsKICAgIH0KICAgIHJldHVybiAwOwp9CgovLyBtYXgsIG1pbiwgbWF444GuY29sb3Ig44KS6Kq/44G544KLCi8vIG14Y29sb3IgPSAnUicsICdHJywgJ0In44Gu44GE44Ga44KM44GLCnZvaWQgZ2V0X21heF9taW5fbXhjb2xvcigvKltpbl0qL3Vuc2lnbmVkIHNob3J0IFIsIHVuc2lnbmVkIHNob3J0IEcsIHVuc2lnbmVkIHNob3J0IEIsIC8qW291dF0qL3Vuc2lnbmVkIHNob3J0ICpteCwgdW5zaWduZWQgc2hvcnQgKm1uLCBjaGFyICpteGNvbG9yKQp7CiAgICAvLyBtYXgKICAgICAgICAgICAgICAgICAgKm14ID0gUjsgKm14Y29sb3I9J1InOwogICAgaWYoRyA+ICpteCkgeyAqbXggPSBHOyAqbXhjb2xvcj0nRyc7IH0KICAgIGlmKEIgPiAqbXgpIHsgKm14ID0gQjsgKm14Y29sb3I9J0InOyB9CiAgICAvLyBtaW4KICAgICptbiA9IE1JTjMoUiwgRywgQik7Cn0KCi8vIENvbnZlcnQKaW50IGNudl9yZ2IyaHN2KFJHQl9IU1ZfREFUQSogZGF0YSkKewogICAgdW5zaWduZWQgc2hvcnQgIG1heCwgbWluOwogICAgY2hhciAgICAgICAgICAgIGNvbG9yX21heDsgIC8vIG1heOOBruiJsiAoJ1InLCdHJywnQicpCiAgICB1bnNpZ25lZCBzaG9ydCAgSCxTLFY7ICAgICAgLy8gSFNWCiAgICBkb3VibGUgICAgICAgICAgdGg7ICAgICAgICAgLy8gSOioiOeul+eUqAogICAgZG91YmxlICAgICAgICAgIHN1Yl9tYXhtaW47IC8vIEjoqIjnrpfnlKggKG1heCAtIG1pbikKICAgIGludCAgICAgICAgICAgICBpOyAgICAgICAgICAvLyDmsY7nlKgKCiAgICBmb3IoaT0wOyBpPGRhdGEtPmltZ19zaXplOyBpKyspCiAgICB7CiAgICAgICAgLy8gbWF4LCBtaW4sIG1heOOBruiJsuOCkuWPluW+lwogICAgICAgIGdldF9tYXhfbWluX214Y29sb3IoZGF0YS0+c3JjWzBdW2ldLCBkYXRhLT5zcmNbMV1baV0sIGRhdGEtPnNyY1syXVtpXSwgJm1heCwgJm1pbiwgJmNvbG9yX21heCk7CgogICAgICAgIC8vIFNW44KS5rGC44KB44KLCiAgICAgICAgLy8gICggaHR0cDovL2ouLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmEub3JnL3dpa2kvSFNWJUU4JTg5JUIyJUU3JUE5JUJBJUU5JTk2JTkzI1JHQi5FMy44MS44Qi5FMy44Mi44OUhTVi5FMy44MS5COC5FMy44MS5BRS5FNS5BNC44OS5FNi44Ri45QiApCiAgICAgICAgViA9IG1heDsKICAgICAgICBpZihtYXggPT0gbWluKQogICAgICAgIHsKICAgICAgICAgICAgUyA9IDA7CiAgICAgICAgfWVsc2V7CiAgICAgICAgICAgIGlmKG1heCA9PSAwKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBTID0gMDsgLy8g5pyq5a6a576p44Gn44GC44KLCiAgICAgICAgICAgIH1lbHNlewogICAgICAgICAgICAgICAgaWYoY29tdmVydF9tb2RlID09IDEpCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgUyA9ICh1bnNpZ25lZCBzaG9ydCkoKGRvdWJsZSkobWF4IC0gbWluKSAvIChkb3VibGUpbWF4ICogNjU1MzUuMCk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlIGlmKGNvbXZlcnRfbW9kZSA9PSAyKQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIFMgPSBtYXggLSBtaW47CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIHJldHVybiBFUlJfQ252X21vZGU7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLy8gSOOCkuaxguOCgeOCiwogICAgICAgIGlmKG1heCA9PSBtaW4pCiAgICAgICAgewogICAgICAgICAgICB0aCA9IDAuMDsgLy8g5pyq5a6a576p44Gn44GC44KLCiAgICAgICAgfWVsc2V7CiAgICAgICAgICAgIHN1Yl9tYXhtaW4gPSAoZG91YmxlKW1heCAtIChkb3VibGUpbWluOwogICAgICAgICAgICBzd2l0Y2goY29sb3JfbWF4KQogICAgICAgICAgICB7CiAgICAgICAgICAgIGNhc2UgJ1InOnRoID0gNjAuMCAqICgoZG91YmxlKWRhdGEtPnNyY1sxXVtpXSAtIChkb3VibGUpZGF0YS0+c3JjWzJdW2ldKSAvIHN1Yl9tYXhtaW4gKyAgIDAuMDticmVhazsKICAgICAgICAgICAgY2FzZSAnRyc6dGggPSA2MC4wICogKChkb3VibGUpZGF0YS0+c3JjWzJdW2ldIC0gKGRvdWJsZSlkYXRhLT5zcmNbMF1baV0pIC8gc3ViX21heG1pbiArIDEyMC4wO2JyZWFrOwogICAgICAgICAgICBjYXNlICdCJzp0aCA9IDYwLjAgKiAoKGRvdWJsZSlkYXRhLT5zcmNbMF1baV0gLSAoZG91YmxlKWRhdGEtPnNyY1sxXVtpXSkgLyBzdWJfbWF4bWluICsgMjQwLjA7YnJlYWs7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLy8gMC4w5Lul5LiKMzYwLjDmnKrmuoDjgavkuLjjgoHjgosKICAgICAgICB3aGlsZSh0aCA8ICAgIDAuMCkKICAgICAgICB7CiAgICAgICAgICAgIHRoICs9IDM2MC4wOwogICAgICAgIH0KICAgICAgICB3aGlsZSh0aCA+PSAzNjAuMCkKICAgICAgICB7CiAgICAgICAgICAgIHRoIC09IDM2MC4wOwogICAgICAgIH0KICAgICAgICBIID0gKHVuc2lnbmVkIHNob3J0KSh0aCAvIDM2MC4wICogNjU1MzUuMCk7IC8vIDM2MC4w44Gu55m+5YiG546H44KSMTZiaXTljJYKICAgICAgICAvLyBIU1bjgpLjgrvjg4Pjg4jjgZnjgosKICAgICAgICBkYXRhLT5kc3RbMF1baV0gPSBIOwogICAgICAgIGRhdGEtPmRzdFsxXVtpXSA9IFM7CiAgICAgICAgZGF0YS0+ZHN0WzJdW2ldID0gVjsKICAgIH0KICAgIHJldHVybiBFUlJfTm9ybWFsOwp9CgovLyBFbmQgb2YgIGNzMTYwXzg2MS5jCg==