#include "regex.h"
#define connect_e(src_node, dst_node, i) \
{ \
src_node->edges[i] = dst_node; \
src_node->noname[i] = true; \
}
#define connect(src_node, dst_node, c, i) \
{ \
connect_e(src_node, dst_node, i); \
src_node->labels[(int)c] = i; \
src_node->noname[i] = false; \
}
#define return_rv(headp, tailp, index) \
{ \
ReturnValue rv = {.head = headp, \
.tail = tailp, \
.str_index = index}; \
return rv; \
}
typedef struct _ReturnValue {
NFA head;
NFA tail;
int str_index;
} ReturnValue;
static NFA new_node(void);
static ReturnValue new_connect_node(NFA head, char c);
static ReturnValue compile_class(const char *str, int begin);
static ReturnValue nfa_compile(const char *str, int begin);
static void nfa_to_graphviz(NFA head, const char *dot_file, const char *image_file);
static int count_id = 1;
static inline bool check_char(char c)
{
}
static NFA new_node(void)
/* ノードを確保し値を初期化して返す
labelsの使わない添字には-1を入れておく */
{
NFA node
= malloc(sizeof(struct _NFA
)); node->edges[0] = node->edges[1] = NULL;
node->noname[0] = node->noname[1] = false;
memset(node
->labels
, -1, LABELS_SIZE
* sizeof(node
->labels
[0])); node->id = count_id++;
return node;
}
static ReturnValue new_connect_node(NFA head, char c)
/* head --[c]--> newnode */
{
NFA tail = new_node();
connect(head, tail, c, 0);
ReturnValue rv = {.head = head, .tail = tail};
return rv;
}
static NFA reverse_labels(NFA node)
{
for (int i = 0; i < LABELS_SIZE; i++) {
node->labels[i] = (node->labels[i] == -1 ? 0 : -1);
if (!check_char(i)) {
node->labels[i] = -1;
}
}
return node;
}
static ReturnValue compile_class(const char *str, int begin)
{
NFA head = new_node();
NFA tail = new_node();
bool reverse_flag = false;
if (str[begin] == '^') {
begin++;
reverse_flag = true;
}
for (int i = begin; str[i]; i++) {
switch (str[i]) {
case ']':
{
if (reverse_flag) {
head = reverse_labels(head);
}
ReturnValue rv = {.head = head, .tail = tail, .str_index = i};
return rv;
}
case '-':
{
char c1 = str[i-1];
char c2 = str[i+1];
for (; c1 <= c2; c1++) {
connect(head, tail, c1, 0);
}
i++;
break;
}
default:
if (str[i+1] != '-') {
connect(head, tail, str[i], 0);
}
break;
}
}
fputs("クラスの終わりがない\n", stderr
); }
static ReturnValue nfa_compile(const char *str, int begin)
{
NFA head_node = new_node();
NFA tail_node = head_node;
int i;
for (i = begin; str[i]; i++) {
switch (str[i]) {
case '[':
{
ReturnValue rv = compile_class(str, i + 1);
connect_e(tail_node, rv.head, 0);
tail_node = rv.tail;
i = rv.str_index;
break;
}
case '(':
{
ReturnValue rv = nfa_compile(str, i + 1);
connect_e(tail_node, rv.head, 0);
tail_node = rv.tail;
i = rv.str_index;
break;
}
case ')':
{
ReturnValue rv = {.head = head_node, .tail = tail_node, .str_index = i};
return rv;
}
case '|':
{
NFA head = new_node(), tail = new_node();
connect_e(head, head_node, 0);
connect_e(tail_node, tail, 0);
ReturnValue rv = nfa_compile(str, i + 1);
connect_e(head, rv.head, 1);
connect_e(rv.tail, tail, 0);
i = rv.str_index;
head_node = head, tail_node = tail;
break;
}
case '*':
{
NFA head = new_node(), tail = new_node();
connect_e(head, head_node, 0);
connect_e(tail_node, tail, 0);
connect_e(head, tail, 1);
connect_e(tail_node, head_node, 1);
head_node = head;
tail_node = tail;
break;
}
default:
{
ReturnValue rv = new_connect_node(tail_node, str[i]);
tail_node = rv.tail;
break;
}
}
}
ReturnValue rv = {.head = head_node, .tail = tail_node, .str_index = i - 1};
return rv;
}
static void nfa_to_graphviz(NFA head, const char *dot_file, const char *image_file)
{
FILE
*fp
= fopen(dot_file
, "w"); if (fp == NULL) {
fputs("ファイルを読み込めない\n", stderr
); }
int id_array[100000];
memset(id_array
, 0, sizeof(id_array
));
bool do_node(NFA node, int index)
{
if (node->edges[index]) {
if (node->noname[index]) {
fprintf(fp
, "%d -> %d [label = \"%s\"];\n", node->id, node->edges[index]->id, "ε");
} else {
for (int i = 0; i < LABELS_SIZE; i++) {
if ((node->labels[i] != -1) && (index == node->labels[i])) {
if (i == '"' || i == '\\') {
fprintf(fp
, "%d -> %d [label = \"\\%c\"];\n", node->id, node->edges[index]->id, i);
} else {
fprintf(fp
, "%d -> %d [label = \"%c\"];\n", node->id, node->edges[index]->id, i);
}
}
}
}
return true;
}
return false;
}
void recur(NFA node)
{
if (id_array[node->id]) {
return;
} else {
id_array[node->id] = 1;
}
if (do_node(node, 0)) recur(node->edges[0]);
if (do_node(node, 1)) recur(node->edges[1]);
}
fputs("digraph test {\n", fp
); fputs("graph [rankdir = LR];\n", fp
); recur(head);
if (image_file != NULL) {
char buf[512];
sprintf(buf
, "dot -Tgif %s -o %s", dot_file
, image_file
); }
}
NFA regex_compile(const char *regstr)
{
ReturnValue rv = nfa_compile(regstr, 0);
return rv.head;
}
static char g_word[256];
static int g_word_index;
#define branch(edge, str) \
{ \
char *p = regex_recur(edge, str); \
if (p) return p; \
}
static char* regex_recur(NFA node, const char *str)
{
if (node->edges[0] == NULL && node->edges[1] == NULL) {
if (g_word_index == 0) return NULL;
g_word[g_word_index] = '\0';
return g_word;
}
for (int i = 0; i <= 1; i++) {
if (node->edges[i] && node->noname[i] && node->id > node->edges[i]->id) {
branch(node->edges[i], str);
}
}
for (int i = 0; i <= 1; i++) {
if (node->edges[i] && node->noname[i]) {
branch(node->edges[i], str);
}
}
int index = node->labels[(int)*str];
if (index != -1) {
g_word[g_word_index++] = *str;
branch(node->edges[index], str + 1);
}
return NULL;
}
char* regex_search(NFA node, const char *str)
{
for (int i = 0; str[i]; i++) {
g_word_index = 0;
branch(node, str + i);
}
return NULL;
}
int main(void)
{
NFA head = regex_compile("[a-z]*");
//nfa_to_graphviz(head, "test.dot", "test.gif");
char *p = regex_search(head, "213abcdefgjf3");
return 0;
}
I2luY2x1ZGUgInJlZ2V4LmgiCgojZGVmaW5lIGNvbm5lY3RfZShzcmNfbm9kZSwgZHN0X25vZGUsIGkpICAgIFwKICB7CQkJCQkJXAogICAgc3JjX25vZGUtPmVkZ2VzW2ldID0gZHN0X25vZGU7CQlcCiAgICBzcmNfbm9kZS0+bm9uYW1lW2ldID0gdHJ1ZTsJCQlcCiAgfQoKI2RlZmluZSBjb25uZWN0KHNyY19ub2RlLCBkc3Rfbm9kZSwgYywgaSkJXAogIHsJCQkJCQlcCiAgICBjb25uZWN0X2Uoc3JjX25vZGUsIGRzdF9ub2RlLCBpKTsJCVwKICAgIHNyY19ub2RlLT5sYWJlbHNbKGludCljXSA9IGk7CQlcCiAgICBzcmNfbm9kZS0+bm9uYW1lW2ldID0gZmFsc2U7CQlcCiAgfQoKI2RlZmluZSByZXR1cm5fcnYoaGVhZHAsIHRhaWxwLCBpbmRleCkJCVwKICB7CQkJCQkJXAogICAgUmV0dXJuVmFsdWUgcnYgPSB7LmhlYWQgPSBoZWFkcCwJCVwKCQkgICAgICAudGFpbCA9IHRhaWxwLAkJXAoJCSAgICAgIC5zdHJfaW5kZXggPSBpbmRleH07CVwKICAgIHJldHVybiBydjsJCQkJCVwKICB9Cgp0eXBlZGVmIHN0cnVjdCBfUmV0dXJuVmFsdWUgewogIE5GQSBoZWFkOwogIE5GQSB0YWlsOwogIGludCBzdHJfaW5kZXg7Cn0gUmV0dXJuVmFsdWU7CgpzdGF0aWMgTkZBIG5ld19ub2RlKHZvaWQpOwpzdGF0aWMgUmV0dXJuVmFsdWUgbmV3X2Nvbm5lY3Rfbm9kZShORkEgaGVhZCwgY2hhciBjKTsKc3RhdGljIFJldHVyblZhbHVlIGNvbXBpbGVfY2xhc3MoY29uc3QgY2hhciAqc3RyLCBpbnQgYmVnaW4pOwpzdGF0aWMgUmV0dXJuVmFsdWUgbmZhX2NvbXBpbGUoY29uc3QgY2hhciAqc3RyLCBpbnQgYmVnaW4pOwpzdGF0aWMgdm9pZCBuZmFfdG9fZ3JhcGh2aXooTkZBIGhlYWQsIGNvbnN0IGNoYXIgKmRvdF9maWxlLCBjb25zdCBjaGFyICppbWFnZV9maWxlKTsKCnN0YXRpYyBpbnQgY291bnRfaWQgPSAxOwoKc3RhdGljIGlubGluZSBib29sIGNoZWNrX2NoYXIoY2hhciBjKQp7CiAgcmV0dXJuIChpc3ByaW50KGMpICYmICFpc2NudHJsKGMpKTsKfQoKc3RhdGljIE5GQSBuZXdfbm9kZSh2b2lkKQovKiDjg47jg7zjg4njgpLnorrkv53jgZflgKTjgpLliJ3mnJ/ljJbjgZfjgabov5TjgZkKICAgbGFiZWxz44Gu5L2/44KP44Gq44GE5re75a2X44Gr44GvLTHjgpLlhaXjgozjgabjgYrjgY8gKi8KewogIE5GQSBub2RlID0gbWFsbG9jKHNpemVvZihzdHJ1Y3QgX05GQSkpOwogIG5vZGUtPmVkZ2VzWzBdID0gbm9kZS0+ZWRnZXNbMV0gPSBOVUxMOwogIG5vZGUtPm5vbmFtZVswXSA9IG5vZGUtPm5vbmFtZVsxXSA9IGZhbHNlOwogIG1lbXNldChub2RlLT5sYWJlbHMsIC0xLCBMQUJFTFNfU0laRSAqIHNpemVvZihub2RlLT5sYWJlbHNbMF0pKTsKICBub2RlLT5pZCA9IGNvdW50X2lkKys7CiAgcmV0dXJuIG5vZGU7Cn0KCnN0YXRpYyBSZXR1cm5WYWx1ZSBuZXdfY29ubmVjdF9ub2RlKE5GQSBoZWFkLCBjaGFyIGMpCi8qIGhlYWQgLS1bY10tLT4gbmV3bm9kZSAqLwp7CiAgTkZBIHRhaWwgPSBuZXdfbm9kZSgpOwogIGNvbm5lY3QoaGVhZCwgdGFpbCwgYywgMCk7CiAgUmV0dXJuVmFsdWUgcnYgPSB7LmhlYWQgPSBoZWFkLCAudGFpbCA9IHRhaWx9OwogIHJldHVybiBydjsKfQoKc3RhdGljIE5GQSByZXZlcnNlX2xhYmVscyhORkEgbm9kZSkKewogIGZvciAoaW50IGkgPSAwOyBpIDwgTEFCRUxTX1NJWkU7IGkrKykgewogICAgbm9kZS0+bGFiZWxzW2ldID0gKG5vZGUtPmxhYmVsc1tpXSA9PSAtMSA/IDAgOiAtMSk7CiAgICBpZiAoIWNoZWNrX2NoYXIoaSkpIHsKICAgICAgbm9kZS0+bGFiZWxzW2ldID0gLTE7CiAgICB9CiAgfQogIHJldHVybiBub2RlOwp9CgpzdGF0aWMgUmV0dXJuVmFsdWUgY29tcGlsZV9jbGFzcyhjb25zdCBjaGFyICpzdHIsIGludCBiZWdpbikKewogIE5GQSBoZWFkID0gbmV3X25vZGUoKTsKICBORkEgdGFpbCA9IG5ld19ub2RlKCk7CgogIGJvb2wgcmV2ZXJzZV9mbGFnID0gZmFsc2U7CgogIGlmIChzdHJbYmVnaW5dID09ICdeJykgewogICAgYmVnaW4rKzsKICAgIHJldmVyc2VfZmxhZyA9IHRydWU7CiAgfQoKICBmb3IgKGludCBpID0gYmVnaW47IHN0cltpXTsgaSsrKSB7CiAgICBzd2l0Y2ggKHN0cltpXSkgewogICAgY2FzZSAnXSc6CiAgICAgIHsKCWlmIChyZXZlcnNlX2ZsYWcpIHsKCSAgaGVhZCA9IHJldmVyc2VfbGFiZWxzKGhlYWQpOwoJfQoJUmV0dXJuVmFsdWUgcnYgPSB7LmhlYWQgPSBoZWFkLCAudGFpbCA9IHRhaWwsIC5zdHJfaW5kZXggPSBpfTsKCXJldHVybiBydjsKICAgICAgfQogICAgY2FzZSAnLSc6CiAgICAgIHsKCWNoYXIgYzEgPSBzdHJbaS0xXTsKCWNoYXIgYzIgPSBzdHJbaSsxXTsKCglmb3IgKDsgYzEgPD0gYzI7IGMxKyspIHsKCSAgY29ubmVjdChoZWFkLCB0YWlsLCBjMSwgMCk7Cgl9CgoJaSsrOwoJYnJlYWs7CiAgICAgIH0KICAgIGRlZmF1bHQ6CiAgICAgIGlmIChzdHJbaSsxXSAhPSAnLScpIHsKCWNvbm5lY3QoaGVhZCwgdGFpbCwgc3RyW2ldLCAwKTsKICAgICAgfQogICAgICBicmVhazsKICAgIH0KICB9CgogIGZwdXRzKCLjgq/jg6njgrnjga7ntYLjgo/jgorjgYzjgarjgYRcbiIsIHN0ZGVycik7CiAgZXhpdChFWElUX0ZBSUxVUkUpOwp9CgpzdGF0aWMgUmV0dXJuVmFsdWUgbmZhX2NvbXBpbGUoY29uc3QgY2hhciAqc3RyLCBpbnQgYmVnaW4pCnsKICBORkEgaGVhZF9ub2RlID0gbmV3X25vZGUoKTsKICBORkEgdGFpbF9ub2RlID0gaGVhZF9ub2RlOwoKICBpbnQgaTsKICBmb3IgKGkgPSBiZWdpbjsgc3RyW2ldOyBpKyspIHsKICAgIHN3aXRjaCAoc3RyW2ldKSB7CiAgICBjYXNlICdbJzoKICAgICAgewogICAgICAJUmV0dXJuVmFsdWUgcnYgPSBjb21waWxlX2NsYXNzKHN0ciwgaSArIDEpOwoJY29ubmVjdF9lKHRhaWxfbm9kZSwgcnYuaGVhZCwgMCk7CiAgICAgIAl0YWlsX25vZGUgPSBydi50YWlsOwoJaSA9IHJ2LnN0cl9pbmRleDsKICAgICAgCWJyZWFrOwogICAgICB9CiAgICBjYXNlICcoJzoKICAgICAgewoJUmV0dXJuVmFsdWUgcnYgPSBuZmFfY29tcGlsZShzdHIsIGkgKyAxKTsKCWNvbm5lY3RfZSh0YWlsX25vZGUsIHJ2LmhlYWQsIDApOwoJdGFpbF9ub2RlID0gcnYudGFpbDsKCWkgPSBydi5zdHJfaW5kZXg7CglicmVhazsKICAgICAgfQogICAgY2FzZSAnKSc6CiAgICAgIHsKCVJldHVyblZhbHVlIHJ2ID0gey5oZWFkID0gaGVhZF9ub2RlLCAudGFpbCA9IHRhaWxfbm9kZSwgLnN0cl9pbmRleCA9IGl9OwoJcmV0dXJuIHJ2OwogICAgICB9CiAgICBjYXNlICd8JzoKICAgICAgewogICAgICAJTkZBIGhlYWQgPSBuZXdfbm9kZSgpLCB0YWlsID0gbmV3X25vZGUoKTsKICAgICAgCWNvbm5lY3RfZShoZWFkLCBoZWFkX25vZGUsIDApOwogICAgICAJY29ubmVjdF9lKHRhaWxfbm9kZSwgdGFpbCwgMCk7CiAgICAgIAlSZXR1cm5WYWx1ZSBydiA9IG5mYV9jb21waWxlKHN0ciwgaSArIDEpOwogICAgICAJY29ubmVjdF9lKGhlYWQsIHJ2LmhlYWQsIDEpOwogICAgICAJY29ubmVjdF9lKHJ2LnRhaWwsIHRhaWwsIDApOwogICAgICAJaSA9IHJ2LnN0cl9pbmRleDsKICAgICAgCWhlYWRfbm9kZSA9IGhlYWQsIHRhaWxfbm9kZSA9IHRhaWw7CiAgICAgIAlicmVhazsKICAgICAgfQogICAgY2FzZSAnKic6CiAgICAgIHsKICAgICAgCU5GQSBoZWFkID0gbmV3X25vZGUoKSwgdGFpbCA9IG5ld19ub2RlKCk7CiAgICAgIAljb25uZWN0X2UoaGVhZCwgaGVhZF9ub2RlLCAwKTsKICAgICAgCWNvbm5lY3RfZSh0YWlsX25vZGUsIHRhaWwsIDApOwogICAgICAJY29ubmVjdF9lKGhlYWQsIHRhaWwsIDEpOwogICAgICAJY29ubmVjdF9lKHRhaWxfbm9kZSwgaGVhZF9ub2RlLCAxKTsKICAgICAgCWhlYWRfbm9kZSA9IGhlYWQ7Cgl0YWlsX25vZGUgPSB0YWlsOwogICAgICAJYnJlYWs7CiAgICAgIH0KICAgIGRlZmF1bHQ6CiAgICAgIHsKCVJldHVyblZhbHVlIHJ2ID0gbmV3X2Nvbm5lY3Rfbm9kZSh0YWlsX25vZGUsIHN0cltpXSk7Cgl0YWlsX25vZGUgPSBydi50YWlsOwoJYnJlYWs7CiAgICAgIH0KICAgIH0KICB9CgogIFJldHVyblZhbHVlIHJ2ID0gey5oZWFkID0gaGVhZF9ub2RlLCAudGFpbCA9IHRhaWxfbm9kZSwgLnN0cl9pbmRleCA9IGkgLSAxfTsKICByZXR1cm4gcnY7Cn0KCnN0YXRpYyB2b2lkIG5mYV90b19ncmFwaHZpeihORkEgaGVhZCwgY29uc3QgY2hhciAqZG90X2ZpbGUsIGNvbnN0IGNoYXIgKmltYWdlX2ZpbGUpCnsKICBGSUxFICpmcCA9IGZvcGVuKGRvdF9maWxlLCAidyIpOwogIGlmIChmcCA9PSBOVUxMKSB7CiAgICBmcHV0cygi44OV44Kh44Kk44Or44KS6Kqt44G/6L6844KB44Gq44GEXG4iLCBzdGRlcnIpOwogICAgZXhpdChFWElUX0ZBSUxVUkUpOwogIH0KCiAgaW50IGlkX2FycmF5WzEwMDAwMF07CiAgbWVtc2V0KGlkX2FycmF5LCAwLCBzaXplb2YoaWRfYXJyYXkpKTsKCiAgYm9vbCBkb19ub2RlKE5GQSBub2RlLCBpbnQgaW5kZXgpCiAgewogICAgaWYgKG5vZGUtPmVkZ2VzW2luZGV4XSkgewogICAgICBpZiAobm9kZS0+bm9uYW1lW2luZGV4XSkgewoJZnByaW50ZihmcCwgIiVkIC0+ICVkIFtsYWJlbCA9IFwiJXNcIl07XG4iLAoJCW5vZGUtPmlkLCBub2RlLT5lZGdlc1tpbmRleF0tPmlkLCAizrUiKTsKICAgICAgfSBlbHNlIHsKCWZvciAoaW50IGkgPSAwOyBpIDwgTEFCRUxTX1NJWkU7IGkrKykgewoJICBpZiAoKG5vZGUtPmxhYmVsc1tpXSAhPSAtMSkgJiYgKGluZGV4ID09IG5vZGUtPmxhYmVsc1tpXSkpIHsKCSAgICBpZiAoaSA9PSAnIicgfHwgaSA9PSAnXFwnKSB7CgkgICAgICBmcHJpbnRmKGZwLCAiJWQgLT4gJWQgW2xhYmVsID0gXCJcXCVjXCJdO1xuIiwKCQkgICAgICBub2RlLT5pZCwgbm9kZS0+ZWRnZXNbaW5kZXhdLT5pZCwgaSk7CgkgICAgfSBlbHNlIHsKCSAgICAgIGZwcmludGYoZnAsICIlZCAtPiAlZCBbbGFiZWwgPSBcIiVjXCJdO1xuIiwKCQkgICAgICBub2RlLT5pZCwgbm9kZS0+ZWRnZXNbaW5kZXhdLT5pZCwgaSk7CgkgICAgfQoJICB9Cgl9CiAgICAgIH0KICAgICAgcmV0dXJuIHRydWU7CiAgICB9CiAgICByZXR1cm4gZmFsc2U7CiAgfQoKICB2b2lkIHJlY3VyKE5GQSBub2RlKQogIHsKICAgIGlmIChpZF9hcnJheVtub2RlLT5pZF0pIHsKICAgICAgcmV0dXJuOwogICAgfSBlbHNlIHsKICAgICAgaWRfYXJyYXlbbm9kZS0+aWRdID0gMTsKICAgIH0KCiAgICBpZiAoZG9fbm9kZShub2RlLCAwKSkgcmVjdXIobm9kZS0+ZWRnZXNbMF0pOwogICAgaWYgKGRvX25vZGUobm9kZSwgMSkpIHJlY3VyKG5vZGUtPmVkZ2VzWzFdKTsKICB9CgogIGZwdXRzKCJkaWdyYXBoIHRlc3Qge1xuIiwgZnApOwogIGZwdXRzKCJncmFwaCBbcmFua2RpciA9IExSXTtcbiIsIGZwKTsKICByZWN1cihoZWFkKTsKICBmcHV0cygifVxuIiwgZnApOwoKICBmY2xvc2UoZnApOwoKICBpZiAoaW1hZ2VfZmlsZSAhPSBOVUxMKSB7CiAgICBjaGFyIGJ1Zls1MTJdOwogICAgc3ByaW50ZihidWYsICJkb3QgLVRnaWYgJXMgLW8gJXMiLCBkb3RfZmlsZSwgaW1hZ2VfZmlsZSk7CiAgICBzeXN0ZW0oYnVmKTsKICB9Cn0KCk5GQSByZWdleF9jb21waWxlKGNvbnN0IGNoYXIgKnJlZ3N0cikKewogIFJldHVyblZhbHVlIHJ2ID0gbmZhX2NvbXBpbGUocmVnc3RyLCAwKTsKICByZXR1cm4gcnYuaGVhZDsKfQoKCnN0YXRpYyBjaGFyIGdfd29yZFsyNTZdOwpzdGF0aWMgaW50IGdfd29yZF9pbmRleDsKCiNkZWZpbmUgYnJhbmNoKGVkZ2UsIHN0cikJCQlcCiAgewkJCQkJCVwKICAgIGNoYXIgKnAgPSByZWdleF9yZWN1cihlZGdlLCBzdHIpOwkJXAogICAgaWYgKHApIHJldHVybiBwOwkJCQlcCiAgfQoKc3RhdGljIGNoYXIqIHJlZ2V4X3JlY3VyKE5GQSBub2RlLCBjb25zdCBjaGFyICpzdHIpCnsKICBpZiAobm9kZS0+ZWRnZXNbMF0gPT0gTlVMTCAmJiBub2RlLT5lZGdlc1sxXSA9PSBOVUxMKSB7CiAgICBpZiAoZ193b3JkX2luZGV4ID09IDApIHJldHVybiBOVUxMOwogICAgZ193b3JkW2dfd29yZF9pbmRleF0gPSAnXDAnOwogICAgcmV0dXJuIGdfd29yZDsKICB9CgogIGZvciAoaW50IGkgPSAwOyBpIDw9IDE7IGkrKykgewogICAgaWYgKG5vZGUtPmVkZ2VzW2ldICYmIG5vZGUtPm5vbmFtZVtpXSAmJiBub2RlLT5pZCA+IG5vZGUtPmVkZ2VzW2ldLT5pZCkgewogICAgICBicmFuY2gobm9kZS0+ZWRnZXNbaV0sIHN0cik7CiAgICB9CiAgfQoKICBmb3IgKGludCBpID0gMDsgaSA8PSAxOyBpKyspIHsKICAgIGlmIChub2RlLT5lZGdlc1tpXSAmJiBub2RlLT5ub25hbWVbaV0pIHsKICAgICAgYnJhbmNoKG5vZGUtPmVkZ2VzW2ldLCBzdHIpOwogICAgfQogIH0KCiAgaW50IGluZGV4ID0gbm9kZS0+bGFiZWxzWyhpbnQpKnN0cl07CiAgaWYgKGluZGV4ICE9IC0xKSB7CiAgICBnX3dvcmRbZ193b3JkX2luZGV4KytdID0gKnN0cjsKICAgIGJyYW5jaChub2RlLT5lZGdlc1tpbmRleF0sIHN0ciArIDEpOwogIH0KCiAgcmV0dXJuIE5VTEw7Cn0KCmNoYXIqIHJlZ2V4X3NlYXJjaChORkEgbm9kZSwgY29uc3QgY2hhciAqc3RyKQp7CiAgZm9yIChpbnQgaSA9IDA7IHN0cltpXTsgaSsrKSB7CiAgICBnX3dvcmRfaW5kZXggPSAwOwogICAgYnJhbmNoKG5vZGUsIHN0ciArIGkpOwogIH0KICByZXR1cm4gTlVMTDsKfQoKaW50IG1haW4odm9pZCkKewogIE5GQSBoZWFkID0gcmVnZXhfY29tcGlsZSgiW2Etel0qIik7CiAgLy9uZmFfdG9fZ3JhcGh2aXooaGVhZCwgInRlc3QuZG90IiwgInRlc3QuZ2lmIik7CiAgY2hhciAqcCA9IHJlZ2V4X3NlYXJjaChoZWFkLCAiMjEzYWJjZGVmZ2pmMyIpOwogIHB1dHMocCk7CgogIHJldHVybiAwOwp9Cg==