#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <stdio.h> /* nur für printf nötig */
#include <malloc.h> /* wegen alloca; nicht C-Standard! */
/*** mysql Zeugs, ohne das es hier nicht geht; muss natürlich wieder raus ***/
#define UDF_INIT void
typedef long longlong;
typedef struct {
int arg_count;
void ** args;
int * lengths;
} UDF_ARGS;
/************************************************/
longlong levenshtein_k_base( UDF_INIT * initid, char * str1, char * str2, int distance, char * is_null, char * error)
{
char * s = str1;
char * t = str2;
int n
= ( s
== NULL
) ? 0 : strlen ( str1
) ; int m
= ( t
== NULL
) ? 0 : strlen ( str2
) ;
//order the strings so that the first always has the minimum length l
if ( n > m) {
int aux = n;
n = m;
m = aux;
char * auxs = s;
s = t;
t = auxs;
}
// const int k = *((int*) distance);
const int k = ( int ) distance;
const int ignore = k + 1 ; //lev dist between s and t is at least greater than k
const int r = m - n;
if ( 0 == n)
return ( m > k) ? ignore : m;
if ( 0 == m)
return ( n > k) ? ignore : n;
if ( r > k)
return ignore;
const int lsize = ( ( ( k > m) ? m : k) - r) / 2 ; //left space for insertions
const int rsize = lsize + r; //right space for deletions, rsize >= lsize (rsize == lsize iff r == 0)
const int stripsize = lsize + rsize + 1 ; // + 1 for the diagonal cell
const int stripsizem1 = stripsize - 1 ; //see later, not to repeat calculations
int d[ 2 * stripsize] ; //Current and last rows
int currentrow;
int lastrow;
/* Initialization */
//currentrow = 0
int i;
for ( i = lsize; i < stripsize; i++ ) //start from diagonal cell
d[ i] = i - lsize;
/* Recurrence */
currentrow = stripsize;
lastrow = 0 ;
//j index for virtual recurrence matrix, jv index for rows
//bl & br = left & right bounds for j
int j, jv, bl, br;
int im1 = 0 , jm1;
int a, b, c, min; //for minimum function, coded directly here for maximum speed
for ( i = 1 ; i <= n; i++ ) {
//bl = max(i - lsize, 0), br = min(i + rsize, m)
bl = i - lsize;
if ( bl < 0 ) {
jv
= abs ( bl
) ; //no space for all allowed insertions bl = 0 ;
}
else
jv = 0 ;
br = i + rsize;
if ( br > m)
br = m;
jm1 = bl - 1 ;
for ( j = bl; j <= br; j++ ) {
if ( 0 == j) //postponed part of initialization
d[ currentrow + jv] = i;
else {
//By observation 3, the indices change for the lastrow (always +1)
if ( s[ im1] == t[ jm1] ) {
d[ currentrow + jv] = d[ lastrow + jv] ;
}
else {
//get the minimum of these 3 operations
a = ( 0 == jv) ? ignore : d[ currentrow + jv - 1 ] ; //deletion
b = ( stripsizem1 == jv) ? ignore : d[ lastrow + jv + 1 ] ; //insertion
c = d[ lastrow + jv] ; //substitution
min = a;
if ( b < min)
min = b;
if ( c < min)
min = c;
d[ currentrow + jv] = min + 1 ;
}
}
jv++;
jm1 = j;
}
//obsv: the cost of a following diagonal never decreases
if ( d[ currentrow + lsize + r] > k)
return ignore;
im1 = i;
//swap
currentrow = currentrow ^ stripsize;
lastrow = lastrow ^ stripsize;
}
//only here if levenhstein(s, t) <= k
return ( longlong) d[ lastrow + lsize + r] ; //d[n, m] }
}
longlong abweichung( UDF_INIT * initid, char * suche, char * in, const char * e, int distance, char * is_null, char * error)
{
#define MINDESTLAENGE 4
int index = 0 ;
while ( in!= e ) /* im zu durchsuchenden char-Array alle "Worte" durchsuchen, also z.B. "abcd" in "xyz\0qwer\0usw\0" */
{
if ( strlen ( suche
) >= MINDESTLAENGE
&& strlen ( in
) >= MINDESTLAENGE
) /* nur ab MINDESTLAENGE Zeichen mit Levensthein vergleichen */ {
longlong tmp= levenshtein_k_base( initid, suche, in, distance, is_null, error) ;
assert ( tmp
>= 0 ) ; /* Code geht davon aus, dass keine neg. Werte geliefert werden */ if ( tmp <= distance)
return index; /* Suchwort gefunden - Index zurückgegeben */
}
else { /* Wenn Wörter kleiner/gleich 3 Zeichen direkten Vergleich durchführen */
return index; /* Suchwort gefunden - Index zurückgegeben */
}
index++;
}
return - 1 ; /* Suchwort wurde nie gefunden */
}
longlong relevance( UDF_INIT * initid, UDF_ARGS * args, char * is_null, char * error)
{
/* Such-Array bilden z.B. "C Entwickler" => "c\0entwickler\0centwickler\0" */
char * s0
= alloca
( 2 + 2 * ( args
-> lengths
[ 0 ] ) ) ,* c
= s0
? s0
: ( abort ( ) , NULL
) ,* e
= c
; memcpy ( c
, args
-> args
[ 0 ] , args
-> lengths
[ 0 ] ) ; c
[ args
-> lengths
[ 0 ] ] = 0 ; while ( * e
) { * e
= tolower ( ( unsigned char ) * e
) , e
++; } c= s0;
while ( * c) { if ( * c== ' ' ) * c= 0 ; ++ c; }
/* FRAGE:
Diesen oberen Teil versteh ich einfach nicht. Ich würde gerne einbauen dass er den Suchstring nur zusammensetzt wenn jeder wort min 2 Zeichen hat.
Im Fall "C Entwickler" würde dann das zusammengesetze Wort "centwickler" nämlich durch die levenshtein immer einen treffer bei "entwickler" haben.
Beispiel Array:
"PHP Entwickler"
[0] = php, [1] = enwickler, [2] = phpentwickler
"C Entwickler"
[0] = c, [1] = entwickler
*/
/***************************************************************************/
/*
FRAGE:
Nachdem ich den oberen Teil nicht verstehe weiß ich nicht wie ich auf die Anzahl der Suchworte komme (anz_suchbegriffe dh unten hardkodiert gesetzt).
Ich brauche diese deshalb da ja jedes Suchwort gefunden werden muss
*/
int anz_suchbegriffe = 2 ;
/* Inserattitel-Wörter durchsuchen und Ende, falls "beste" Übereinstimmung hier schon gefunden */
c= s0;
{
int treffer = 0 ;
int phrase = 0 ;
int index_old = - 1 ;
char * s1
= alloca
( 1 + args
-> lengths
[ 1 ] ) ,* x
= s1
? s1
: ( abort ( ) , NULL
) ; memcpy ( x
, args
-> args
[ 1 ] , args
-> lengths
[ 1 ] ) ; x
[ args
-> lengths
[ 1 ] ] = 0 ; while ( * x
) * x
= tolower ( ( unsigned char ) * x
) , x
++; x= s1;
while ( * x) { if ( * x== ' ' ) * x= 0 ; ++ x; } ++ x;
while ( c!= e ) /* alle Such-Wörter durchlaufen */
{
longlong index = abweichung( initid, c, s1, x,* ( int * ) args-> args[ 3 ] , is_null, error) ;
/* Es wird die Stelle im Inserattitel benötigt da die Suchwörter für eine 100%ige Relevanz hintereinander stehen müssen */
if ( index > - 1 ) {
treffer++;
if ( ( index- 1 ) == index_old || ( treffer == 1 ) ) /* Phrase-Variable dann setzen wenn das aktuelle Wort unmittelbar hinter dem letzten gefunden wurde bzw beim ersten Treffer sowieso */
phrase = 1 ;
else
phrase = 0 ;
index_old = index;
/*
FRAGE:
Hier muss jetzt noch eingebaut werden, dass wenn der letzte Schleifendurchlauf erfolgt (dann wird ja das zusammengesetzte Wort (zB "CEntwickler") gegen den Inserattitel geprüft),
die Trefferanzahl natürlich nicht gleich der der maximalen Suchbegriffe entspricht sondern einfach die Funktion abbrechen kann.
Beispiel:
Begriff: "PHP Entwickler" Inserat: "PHP MySQL Entwickler" => Zwei Suchbegriffe - dh wenn zwei Treffer Relevanz von 75 zurückgeben
Begriff "Software Entwickler" Inserat: "Softwareentwickler" => Ist auch korrekt (Relevanz von 100)
*/
if ( treffer == anz_suchbegriffe) {
if ( phrase == 1 )
return 100 ; /* Wenn alle Suchwörter im Titel gefunden werden und diese hinteinaner stehen (Phrase) dann Relevanz von 100 */
else
return 75 ; /* Es wurden zwar alle Suchwörter gefunden aber diese standen nicht hintereinander -> dh 75 */
}
}
else phrase = 0 ;
}
}
/* Inserattext-Wörter durchsuchen - Phrasen */
c= s0;
{
char suchbegriff[ args-> lengths[ 0 ] + 1 ] ;
memcpy ( suchbegriff
, args
-> args
[ 0 ] , args
-> lengths
[ 0 ] ) ; suchbegriff[ args-> lengths[ 0 ] ] = '\0 ' ;
char * s1
= alloca
( 1 + args
-> lengths
[ 2 ] ) ,* x
= s1
? s1
: ( abort ( ) , NULL
) ; memcpy ( x
, args
-> args
[ 2 ] , args
-> lengths
[ 2 ] ) ; x
[ args
-> lengths
[ 2 ] ] = 0 ; while ( * x
) * x
= tolower ( ( unsigned char ) * x
) , x
++; x= s1;
while ( * x) { if ( * x== '|' ) * x= 0 ; ++ x; } ++ x;
longlong tmp= abweichung( initid, ( char * ) suchbegriff, s1, x,* ( int * ) args-> args[ 3 ] , is_null, error) ;
if ( tmp > - 1 )
return 75 ;
}
/* Inserattext-Wörter durchsuchen */
c= s0;
{
int treffer = 0 ;
char * s1
= alloca
( 1 + args
-> lengths
[ 2 ] ) ,* x
= s1
? s1
: ( abort ( ) , NULL
) ; memcpy ( x
, args
-> args
[ 2 ] , args
-> lengths
[ 2 ] ) ; x
[ args
-> lengths
[ 2 ] ] = 0 ; while ( * x
) * x
= tolower ( ( unsigned char ) * x
) , x
++; x= s1;
while ( * x) { if ( * x== ' ' ||* x== '|' ) * x= 0 ; ++ x; } ++ x;
while ( c!= e )
{
longlong index= abweichung( initid, c, s1, x,* ( int * ) args-> args[ 3 ] , is_null, error) ;
if ( index > - 1 ) {
treffer++;
if ( anz_suchbegriffe == treffer)
return 50 ;
}
}
}
return 1 ;
}
int main( )
{
int i= 1 ;
/*
Beispiele:
void *s[]={"php entwickler","php entwickler mysql","Software Entwickler|entwickler|Programmierer|php",&i}; => Relevanz 100
void *s[]={"php entwickler","phpentwickler mysql","Software Entwickler|entwickler|Programmierer|php",&i}; => Relevanz 100 (Derzeit nicht möglich!!!)
void *s[]={"php entwickler","php mysql entwickler","Software Entwickler|entwickler|Programmierer|php",&i}; => Relevanz 75 (Titel)
void *s[]={"php entwickler","php mysql developer","Software Entwickler|entwickler|Programmierer|php entwickler",&i}; => Relevanz 75 (Keyowords Phrasen)
void *s[]={"php entwickler","Software Developer","Software Entwickler|entwickler|Programmierer|php",&i}; => Relevanz 50 (Keywords)
*/
void * s[ ] = { "php entwickler" , "Software Developer" , "Software Entwickler|entwickler|Programmierer|php" ,& i} ;
int l[ ] = { 14 , 18 , 48 , 0 } ;
UDF_ARGS u= { 4 , s, l} ;
printf ( "\n \n Ergebnis: %ld \n " , relevance
( 0 ,& u
, 0 , 0 ) ) ; return 0 ;
}
I2luY2x1ZGUgPHN0ZGxpYi5oPgojaW5jbHVkZSA8bGltaXRzLmg+CiNpbmNsdWRlIDxjdHlwZS5oPgojaW5jbHVkZSA8c3RyaW5nLmg+CiNpbmNsdWRlIDxhc3NlcnQuaD4KIAojaW5jbHVkZSA8c3RkaW8uaD4gLyogbnVyIGbDvHIgcHJpbnRmIG7DtnRpZyAqLwogCiNpbmNsdWRlIDxtYWxsb2MuaD4gLyogd2VnZW4gYWxsb2NhOyBuaWNodCBDLVN0YW5kYXJkISAqLwogCi8qKiogbXlzcWwgWmV1Z3MsIG9obmUgZGFzIGVzIGhpZXIgbmljaHQgZ2VodDsgbXVzcyBuYXTDvHJsaWNoIHdpZWRlciByYXVzICoqKi8KI2RlZmluZSBVREZfSU5JVCB2b2lkCnR5cGVkZWYgbG9uZyBsb25nbG9uZzsKIAp0eXBlZGVmIHN0cnVjdCB7CmludCBhcmdfY291bnQ7CnZvaWQgKiphcmdzOwppbnQgKmxlbmd0aHM7Cn0gVURGX0FSR1M7Ci8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovCgogCmxvbmdsb25nIGxldmVuc2h0ZWluX2tfYmFzZShVREZfSU5JVCAqaW5pdGlkLCBjaGFyICpzdHIxLCBjaGFyICpzdHIyLCBpbnQgZGlzdGFuY2UsIGNoYXIgKmlzX251bGwsIGNoYXIgKmVycm9yKQp7CiAgY2hhciAqcyA9IHN0cjE7CiAgY2hhciAqdCA9IHN0cjI7CgogIGludCBuID0gKHMgPT0gTlVMTCkgPyAwIDogc3RybGVuKHN0cjEpOwogIGludCBtID0gKHQgPT0gTlVMTCkgPyAwIDogc3RybGVuKHN0cjIpOwoKICAvL29yZGVyIHRoZSBzdHJpbmdzIHNvIHRoYXQgdGhlIGZpcnN0IGFsd2F5cyBoYXMgdGhlIG1pbmltdW0gbGVuZ3RoIGwKICBpZiAobiA+IG0pIHsKICAgIGludCBhdXggPSBuOwogICAgbiA9IG07CiAgICBtID0gYXV4OwogICAgY2hhciAqYXV4cyA9IHM7CiAgICBzID0gdDsKICAgIHQgPSBhdXhzOwogIH0KCiAvLyBjb25zdCBpbnQgayA9ICooKGludCopIGRpc3RhbmNlKTsKICBjb25zdCBpbnQgayA9IChpbnQpIGRpc3RhbmNlOwoKCiAgCiAgY29uc3QgaW50IGlnbm9yZSA9IGsgKyAxOyAvL2xldiBkaXN0IGJldHdlZW4gcyBhbmQgdCBpcyBhdCBsZWFzdCBncmVhdGVyIHRoYW4gawogIGNvbnN0IGludCByID0gbSAtIG47CgogIGlmICgwID09IG4pCiAgICByZXR1cm4gKG0gPiBrKSA/IGlnbm9yZSA6IG07CiAgaWYgKDAgPT0gbSkKICAgIHJldHVybiAobiA+IGspID8gaWdub3JlIDogbjsKICBpZiAociA+IGspCiAgICByZXR1cm4gaWdub3JlOwoKICBjb25zdCBpbnQgbHNpemUgPSAoKChrID4gbSkgPyBtIDogaykgLSByKSAvIDI7IC8vbGVmdCBzcGFjZSBmb3IgaW5zZXJ0aW9ucwogIGNvbnN0IGludCByc2l6ZSA9IGxzaXplICsgcjsgLy9yaWdodCBzcGFjZSBmb3IgZGVsZXRpb25zLCByc2l6ZSA+PSBsc2l6ZSAocnNpemUgPT0gbHNpemUgaWZmIHIgPT0gMCkKICBjb25zdCBpbnQgc3RyaXBzaXplID0gbHNpemUgKyByc2l6ZSArIDE7IC8vICsgMSBmb3IgdGhlIGRpYWdvbmFsIGNlbGwKICBjb25zdCBpbnQgc3RyaXBzaXplbTEgPSBzdHJpcHNpemUgLSAxOyAvL3NlZSBsYXRlciwgbm90IHRvIHJlcGVhdCBjYWxjdWxhdGlvbnMKCiAgaW50IGRbMiAqIHN0cmlwc2l6ZV07IC8vQ3VycmVudCBhbmQgbGFzdCByb3dzCiAgaW50IGN1cnJlbnRyb3c7CiAgaW50IGxhc3Ryb3c7CgoKICAvKiBJbml0aWFsaXphdGlvbiAqLwoKICAvL2N1cnJlbnRyb3cgPSAwCiAgaW50IGk7CiAgZm9yIChpID0gbHNpemU7IGkgPCBzdHJpcHNpemU7IGkrKykgLy9zdGFydCBmcm9tIGRpYWdvbmFsIGNlbGwKICAgIGRbaV0gPSBpIC0gbHNpemU7CgoKICAvKiBSZWN1cnJlbmNlICovCgogIGN1cnJlbnRyb3cgPSBzdHJpcHNpemU7CiAgbGFzdHJvdyA9IDA7CgogIC8vaiBpbmRleCBmb3IgdmlydHVhbCByZWN1cnJlbmNlIG1hdHJpeCwganYgaW5kZXggZm9yIHJvd3MKICAvL2JsICYgYnIgPSBsZWZ0ICYgcmlnaHQgYm91bmRzIGZvciBqCiAgaW50IGosIGp2LCBibCwgYnI7CiAgaW50IGltMSA9IDAsIGptMTsKICBpbnQgYSwgYiwgYywgbWluOyAvL2ZvciBtaW5pbXVtIGZ1bmN0aW9uLCBjb2RlZCBkaXJlY3RseSBoZXJlIGZvciBtYXhpbXVtIHNwZWVkCiAgZm9yIChpID0gMTsgaSA8PSBuOyBpKyspIHsKCiAgICAvL2JsID0gbWF4KGkgLSBsc2l6ZSwgMCksIGJyID0gbWluKGkgKyByc2l6ZSwgbSkKICAgIGJsID0gaSAtIGxzaXplOwogICAgaWYgKGJsIDwgMCkgewogICAgICBqdiA9IGFicyhibCk7IC8vbm8gc3BhY2UgZm9yIGFsbCBhbGxvd2VkIGluc2VydGlvbnMKICAgICAgYmwgPSAwOwogICAgfQogICAgZWxzZQogICAgICBqdiA9IDA7CiAgICBiciA9IGkgKyByc2l6ZTsKICAgIGlmIChiciA+IG0pCiAgICAgIGJyID0gbTsKCiAgICBqbTEgPSBibCAtIDE7CiAgICBmb3IgKGogPSBibDsgaiA8PSBicjsgaisrKSB7CiAgICAgIGlmICgwID09IGopIC8vcG9zdHBvbmVkIHBhcnQgb2YgaW5pdGlhbGl6YXRpb24KICAgICAgZFtjdXJyZW50cm93ICsganZdID0gaTsKICAgICAgZWxzZSB7CgkvL0J5IG9ic2VydmF0aW9uIDMsIHRoZSBpbmRpY2VzIGNoYW5nZSBmb3IgdGhlIGxhc3Ryb3cgKGFsd2F5cyArMSkKICAJaWYgKHNbaW0xXSA9PSB0W2ptMV0pIHsKICAJICBkW2N1cnJlbnRyb3cgKyBqdl0gPSBkW2xhc3Ryb3cgKyBqdl07Cgl9CiAgCWVsc2UgewoJICAvL2dldCB0aGUgbWluaW11bSBvZiB0aGVzZSAzIG9wZXJhdGlvbnMKCSAgYSA9ICgwID09IGp2KSA/IGlnbm9yZSA6IGRbY3VycmVudHJvdyArIGp2IC0gMV07IC8vZGVsZXRpb24KCSAgYiA9IChzdHJpcHNpemVtMSA9PSBqdikgPyBpZ25vcmUgOiBkW2xhc3Ryb3cgKyBqdiArIDFdOyAvL2luc2VydGlvbgoJICBjID0gZFtsYXN0cm93ICsganZdOyAvL3N1YnN0aXR1dGlvbgoKCSAgbWluID0gYTsKCSAgaWYgKGIgPCBtaW4pCgkgICAgbWluID0gYjsKCSAgaWYgKGMgPCBtaW4pCgkgICAgbWluID0gYzsKCgkgIGRbY3VycmVudHJvdyArIGp2XSA9IG1pbiArIDE7CiAgCX0KICAgICAgfQogICAgICBqdisrOwogICAgICBqbTEgPSBqOwogICAgfQoKICAgIC8vb2JzdjogdGhlIGNvc3Qgb2YgYSBmb2xsb3dpbmcgZGlhZ29uYWwgbmV2ZXIgZGVjcmVhc2VzCiAgICBpZiAoZFtjdXJyZW50cm93ICsgbHNpemUgKyByXSA+IGspCiAgICAgIHJldHVybiBpZ25vcmU7CgogICAgaW0xID0gaTsKCiAgICAvL3N3YXAKICAgIGN1cnJlbnRyb3cgPSBjdXJyZW50cm93IF4gc3RyaXBzaXplOwogICAgbGFzdHJvdyA9IGxhc3Ryb3cgXiBzdHJpcHNpemU7CiAgfQoKICAvL29ubHkgaGVyZSBpZiBsZXZlbmhzdGVpbihzLCB0KSA8PSBrCiAgcmV0dXJuIChsb25nbG9uZykgZFtsYXN0cm93ICsgbHNpemUgKyByXTsgLy9kW24sIG1dIH0KfQogCmxvbmdsb25nIGFid2VpY2h1bmcoVURGX0lOSVQgKmluaXRpZCxjaGFyICpzdWNoZSxjaGFyICppbixjb25zdCBjaGFyICplLGludCBkaXN0YW5jZSxjaGFyICppc19udWxsLGNoYXIgKmVycm9yKQp7CiAgI2RlZmluZSBNSU5ERVNUTEFFTkdFIDQKICBpbnQgaW5kZXggPSAwOwogIHdoaWxlKCBpbiE9ZSApICAvKiBpbSB6dSBkdXJjaHN1Y2hlbmRlbiBjaGFyLUFycmF5IGFsbGUgIldvcnRlIiBkdXJjaHN1Y2hlbiwgYWxzbyB6LkIuICJhYmNkIiBpbiAieHl6XDBxd2VyXDB1c3dcMCIgKi8KICB7CiAgICBhc3NlcnQoIChwcmludGYoIiVzID8gJXNcbiIsc3VjaGUsaW4pLDEpICk7ICAgCiAgICBpZiggc3RybGVuKHN1Y2hlKT49TUlOREVTVExBRU5HRSAmJiBzdHJsZW4oaW4pPj1NSU5ERVNUTEFFTkdFICkgICAvKiBudXIgYWIgTUlOREVTVExBRU5HRSBaZWljaGVuIG1pdCBMZXZlbnN0aGVpbiB2ZXJnbGVpY2hlbiAqLwogICAgewogICAgICAgIGxvbmdsb25nIHRtcD1sZXZlbnNodGVpbl9rX2Jhc2UoaW5pdGlkLHN1Y2hlLGluLGRpc3RhbmNlLGlzX251bGwsZXJyb3IpOwogICAgICAgIAogICAgICAgIGFzc2VydCggdG1wPj0wICk7ICAvKiBDb2RlIGdlaHQgZGF2b24gYXVzLCBkYXNzIGtlaW5lIG5lZy4gV2VydGUgZ2VsaWVmZXJ0IHdlcmRlbiAqLwogICAgICAgIGlmKHRtcCA8PSBkaXN0YW5jZSkgCiAgICAgICAgICByZXR1cm4gaW5kZXg7IC8qIFN1Y2h3b3J0IGdlZnVuZGVuIC0gSW5kZXggenVyw7xja2dlZ2ViZW4gKi8KCiAgICB9CiAgICBlbHNlIHsgLyogV2VubiBXw7ZydGVyIGtsZWluZXIvZ2xlaWNoIDMgWmVpY2hlbiBkaXJla3RlbiBWZXJnbGVpY2ggZHVyY2hmw7xocmVuICovCiAgICAgICAgaWYgKHN0cmNtcChzdWNoZSxpbikgPT0gMCkgCiAgICAgICAgICByZXR1cm4gaW5kZXg7IC8qIFN1Y2h3b3J0IGdlZnVuZGVuIC0gSW5kZXggenVyw7xja2dlZ2ViZW4gKi8gICAgIAogICAgfQogICAgaW4rPXN0cmxlbihpbikrMTsKICAgIGluZGV4Kys7CiAgfQogIHJldHVybiAtMTsgLyogU3VjaHdvcnQgd3VyZGUgbmllIGdlZnVuZGVuICovCn0KIApsb25nbG9uZyByZWxldmFuY2UoVURGX0lOSVQgKmluaXRpZCwgVURGX0FSR1MgKmFyZ3MsIGNoYXIgKmlzX251bGwsIGNoYXIgKmVycm9yKQp7CiAKICAvKiBTdWNoLUFycmF5IGJpbGRlbiB6LkIuICJDIEVudHdpY2tsZXIiID0+ICJjXDBlbnR3aWNrbGVyXDBjZW50d2lja2xlclwwIiAqLwogIGNoYXIgKnMwPWFsbG9jYSgyKzIqKGFyZ3MtPmxlbmd0aHNbMF0pKSwqYz1zMD9zMDooYWJvcnQoKSxOVUxMKSwqZT1jOyAgbWVtY3B5KGMsYXJncy0+YXJnc1swXSxhcmdzLT5sZW5ndGhzWzBdKTtjW2FyZ3MtPmxlbmd0aHNbMF1dPTA7CiAgd2hpbGUoKmUpIHsgKmU9dG9sb3dlcigodW5zaWduZWQgY2hhcikqZSksZSsrO30KICBzdHJjcHkoKytlLHMwKTsgIHMwW3N0cmxlbihzMCldPScgJzsKICB3aGlsZSgqZSkge2lmKCplPT0nICcpIG1lbW1vdmUoZSxlKzEsc3RybGVuKGUpKTsgKytlOyB9ICsrZTsKICBjPXMwOwogIHdoaWxlKCpjKSB7aWYoKmM9PScgJykgKmM9MDsgKytjO30KICAKICAvKiBGUkFHRToKICAgICBEaWVzZW4gb2JlcmVuIFRlaWwgdmVyc3RlaCBpY2ggZWluZmFjaCBuaWNodC4gSWNoIHfDvHJkZSBnZXJuZSBlaW5iYXVlbiBkYXNzIGVyIGRlbiBTdWNoc3RyaW5nIG51ciB6dXNhbW1lbnNldHp0IHdlbm4gamVkZXIgd29ydCBtaW4gMiBaZWljaGVuIGhhdC4KICAgICBJbSBGYWxsICJDIEVudHdpY2tsZXIiIHfDvHJkZSBkYW5uIGRhcyB6dXNhbW1lbmdlc2V0emUgV29ydCAiY2VudHdpY2tsZXIiIG7DpG1saWNoIGR1cmNoIGRpZSBsZXZlbnNodGVpbiBpbW1lciBlaW5lbiB0cmVmZmVyIGJlaSAiZW50d2lja2xlciIgaGFiZW4uIAogICAgIAogICAgIEJlaXNwaWVsIEFycmF5OgogICAgICJQSFAgRW50d2lja2xlciIKICAgICBbMF0gPSBwaHAsIFsxXSA9IGVud2lja2xlciwgWzJdID0gcGhwZW50d2lja2xlcgogICAgIAogICAgICJDIEVudHdpY2tsZXIiCiAgICAgWzBdID0gYywgWzFdID0gZW50d2lja2xlcgogICovCiAgCiAgLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi8KICAKICAvKgogIEZSQUdFOgogIE5hY2hkZW0gaWNoIGRlbiBvYmVyZW4gVGVpbCBuaWNodCB2ZXJzdGVoZSB3ZWnDnyBpY2ggbmljaHQgd2llIGljaCBhdWYgZGllIEFuemFobCBkZXIgU3VjaHdvcnRlIGtvbW1lIChhbnpfc3VjaGJlZ3JpZmZlIGRoIHVudGVuIGhhcmRrb2RpZXJ0IGdlc2V0enQpLgogIEljaCBicmF1Y2hlIGRpZXNlIGRlc2hhbGIgZGEgamEgamVkZXMgU3VjaHdvcnQgZ2VmdW5kZW4gd2VyZGVuIG11c3MKICAqLwogIGludCBhbnpfc3VjaGJlZ3JpZmZlID0gMjsgCiAgCiAgLyogSW5zZXJhdHRpdGVsLVfDtnJ0ZXIgZHVyY2hzdWNoZW4gdW5kIEVuZGUsIGZhbGxzICJiZXN0ZSIgw5xiZXJlaW5zdGltbXVuZyBoaWVyIHNjaG9uIGdlZnVuZGVuICovCiAgCiAgYz1zMDsgCiAgewogICAgaW50IHRyZWZmZXIgPSAwOwogICAgaW50IHBocmFzZSA9IDA7CiAgICBpbnQgaW5kZXhfb2xkID0gLTE7IAogICAgCiAgICBjaGFyICpzMT1hbGxvY2EoMSthcmdzLT5sZW5ndGhzWzFdKSwqeD1zMT9zMTooYWJvcnQoKSxOVUxMKTsgbWVtY3B5KHgsYXJncy0+YXJnc1sxXSxhcmdzLT5sZW5ndGhzWzFdKTt4W2FyZ3MtPmxlbmd0aHNbMV1dPTA7CiAgICB3aGlsZSgqeCkgKng9dG9sb3dlcigodW5zaWduZWQgY2hhcikqeCkseCsrOwogICAgeD1zMTsKICAgIHdoaWxlKCp4KSB7aWYoKng9PScgJykgKng9MDsgKyt4O30gKyt4OwogICAgd2hpbGUoIGMhPWUgKSAvKiBhbGxlIFN1Y2gtV8O2cnRlciBkdXJjaGxhdWZlbiAqLwogICAgewogICAgICBsb25nbG9uZyBpbmRleCA9YWJ3ZWljaHVuZyhpbml0aWQsYyxzMSx4LCooaW50ICopIGFyZ3MtPmFyZ3NbM10saXNfbnVsbCxlcnJvcik7CiAgICAgIC8qIEVzIHdpcmQgZGllIFN0ZWxsZSBpbSBJbnNlcmF0dGl0ZWwgYmVuw7Z0aWd0IGRhIGRpZSBTdWNod8O2cnRlciBmw7xyIGVpbmUgMTAwJWlnZSBSZWxldmFueiBoaW50ZXJlaW5hbmRlciBzdGVoZW4gbcO8c3NlbiAqLwogICAgICBpZiggaW5kZXggPiAtMSkgewogICAgICAgIHRyZWZmZXIrKzsKICAgICAgICAKICAgICAgICBpZiAoKGluZGV4LTEpID09IGluZGV4X29sZCB8fCAodHJlZmZlciA9PSAxKSkgLyogUGhyYXNlLVZhcmlhYmxlIGRhbm4gc2V0emVuIHdlbm4gZGFzIGFrdHVlbGxlIFdvcnQgdW5taXR0ZWxiYXIgaGludGVyIGRlbSBsZXR6dGVuIGdlZnVuZGVuIHd1cmRlIGJ6dyBiZWltIGVyc3RlbiBUcmVmZmVyIHNvd2llc28gKi8KICAgICAgICAgICBwaHJhc2UgPSAxOwogICAgICAgIGVsc2UKICAgICAgICAgICBwaHJhc2UgPSAwOwogICAgICAgICAgIAogICAgICAgIGluZGV4X29sZCA9IGluZGV4OyAgIAogICAgICAgIAogICAgICAgIC8qCiAgICAgICAgRlJBR0U6IAogICAgICAgIEhpZXIgbXVzcyBqZXR6dCBub2NoIGVpbmdlYmF1dCB3ZXJkZW4sIGRhc3Mgd2VubiBkZXIgbGV0enRlIFNjaGxlaWZlbmR1cmNobGF1ZiBlcmZvbGd0IChkYW5uIHdpcmQgamEgZGFzIHp1c2FtbWVuZ2VzZXR6dGUgV29ydCAoekIgIkNFbnR3aWNrbGVyIikgZ2VnZW4gZGVuIEluc2VyYXR0aXRlbCBnZXByw7xmdCksCiAgICAgICAgZGllIFRyZWZmZXJhbnphaGwgbmF0w7xybGljaCBuaWNodCBnbGVpY2ggZGVyIGRlciBtYXhpbWFsZW4gU3VjaGJlZ3JpZmZlIGVudHNwcmljaHQgc29uZGVybiBlaW5mYWNoIGRpZSBGdW5rdGlvbiBhYmJyZWNoZW4ga2Fubi4KICAgICAgICBCZWlzcGllbDoKICAgICAgICBCZWdyaWZmOiAiUEhQIEVudHdpY2tsZXIiIEluc2VyYXQ6ICJQSFAgTXlTUUwgRW50d2lja2xlciIgPT4gWndlaSBTdWNoYmVncmlmZmUgLSBkaCB3ZW5uIHp3ZWkgVHJlZmZlciBSZWxldmFueiB2b24gNzUgenVyw7xja2dlYmVuCiAgICAgICAgQmVncmlmZiAiU29mdHdhcmUgRW50d2lja2xlciIgSW5zZXJhdDogIlNvZnR3YXJlZW50d2lja2xlciIgPT4gSXN0IGF1Y2gga29ycmVrdCAoUmVsZXZhbnogdm9uIDEwMCkKICAgICAgICAqLwogICAgICAgIAogICAgICAgIGlmICh0cmVmZmVyID09IGFuel9zdWNoYmVncmlmZmUpIHsKICAgICAgICAgICBpZiAocGhyYXNlID09IDEpCiAgICAgICAgICAgICAgcmV0dXJuIDEwMDsgLyogV2VubiBhbGxlIFN1Y2h3w7ZydGVyIGltIFRpdGVsIGdlZnVuZGVuIHdlcmRlbiB1bmQgZGllc2UgaGludGVpbmFuZXIgc3RlaGVuIChQaHJhc2UpIGRhbm4gUmVsZXZhbnogdm9uIDEwMCAgKi8KICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgcmV0dXJuIDc1OyAvKiBFcyB3dXJkZW4gendhciBhbGxlIFN1Y2h3w7ZydGVyIGdlZnVuZGVuIGFiZXIgZGllc2Ugc3RhbmRlbiBuaWNodCBoaW50ZXJlaW5hbmRlciAtPiBkaCA3NSAqLwogICAgICAgIH0KICAgICAgfQogICAgICBlbHNlIHBocmFzZSA9IDA7CgogICAgICBjKz1zdHJsZW4oYykrMTsKICAgIH0KICB9CiAKICAvKiBJbnNlcmF0dGV4dC1Xw7ZydGVyIGR1cmNoc3VjaGVuIC0gUGhyYXNlbiAqLwoKICBjPXMwOwogIHsKICAgIGNoYXIgc3VjaGJlZ3JpZmZbYXJncy0+bGVuZ3Roc1swXSsxXTsKICAgIG1lbWNweShzdWNoYmVncmlmZixhcmdzLT5hcmdzWzBdLGFyZ3MtPmxlbmd0aHNbMF0pOwogICAgc3VjaGJlZ3JpZmZbYXJncy0+bGVuZ3Roc1swXV0gPSAnXDAnOwogICAgCiAgICBjaGFyICpzMT1hbGxvY2EoMSthcmdzLT5sZW5ndGhzWzJdKSwqeD1zMT9zMTooYWJvcnQoKSxOVUxMKTsgbWVtY3B5KHgsYXJncy0+YXJnc1syXSxhcmdzLT5sZW5ndGhzWzJdKTt4W2FyZ3MtPmxlbmd0aHNbMl1dPTA7CiAgICB3aGlsZSgqeCkgKng9dG9sb3dlcigodW5zaWduZWQgY2hhcikqeCkseCsrOwogICAgeD1zMTsKICAgIHdoaWxlKCp4KSB7aWYoKng9PSd8JykgKng9MDsgKyt4O30gKyt4OwogICAgbG9uZ2xvbmcgdG1wPWFid2VpY2h1bmcoaW5pdGlkLChjaGFyKikgc3VjaGJlZ3JpZmYsczEseCwqKGludCAqKSBhcmdzLT5hcmdzWzNdLGlzX251bGwsZXJyb3IpOyAKICAgIGlmICh0bXAgPiAtMSkKICAgICAgIHJldHVybiA3NTsKICB9IAogIAogIAogIC8qIEluc2VyYXR0ZXh0LVfDtnJ0ZXIgZHVyY2hzdWNoZW4gKi8KICBjPXMwOwogIHsKICAgIGludCB0cmVmZmVyID0gMDsKICAgIGNoYXIgKnMxPWFsbG9jYSgxK2FyZ3MtPmxlbmd0aHNbMl0pLCp4PXMxP3MxOihhYm9ydCgpLE5VTEwpOyBtZW1jcHkoeCxhcmdzLT5hcmdzWzJdLGFyZ3MtPmxlbmd0aHNbMl0pO3hbYXJncy0+bGVuZ3Roc1syXV09MDsKICAgIHdoaWxlKCp4KSAqeD10b2xvd2VyKCh1bnNpZ25lZCBjaGFyKSp4KSx4Kys7CiAgICB4PXMxOwogICAgd2hpbGUoKngpIHtpZigqeD09JyAnfHwqeD09J3wnKSAqeD0wOyArK3g7fSArK3g7CiAgICB3aGlsZSggYyE9ZSApCiAgICB7CiAgICAgIGxvbmdsb25nIGluZGV4PWFid2VpY2h1bmcoaW5pdGlkLGMsczEseCwqKGludCAqKSBhcmdzLT5hcmdzWzNdLGlzX251bGwsZXJyb3IpOwogICAgICAKICAgICAgaWYgKGluZGV4ID4gLTEpIHsKICAgICAgICB0cmVmZmVyKys7CiAgICAgICAgaWYgKGFuel9zdWNoYmVncmlmZmUgPT0gdHJlZmZlcikgCiAgICAgICAgICAgcmV0dXJuIDUwOyAgICAgICAgICAgICAgICAgICAKICAgICAgfQoKICAgICAgYys9c3RybGVuKGMpKzE7CiAgICB9CiAgfQogCiAgcmV0dXJuIDE7IAp9CiAKaW50IG1haW4oKQp7CiAgaW50IGk9MTsKICAvKgogIEJlaXNwaWVsZToKICB2b2lkICpzW109eyJwaHAgZW50d2lja2xlciIsInBocCBlbnR3aWNrbGVyIG15c3FsIiwiU29mdHdhcmUgRW50d2lja2xlcnxlbnR3aWNrbGVyfFByb2dyYW1taWVyZXJ8cGhwIiwmaX07ID0+IFJlbGV2YW56IDEwMAogIHZvaWQgKnNbXT17InBocCBlbnR3aWNrbGVyIiwicGhwZW50d2lja2xlciBteXNxbCIsIlNvZnR3YXJlIEVudHdpY2tsZXJ8ZW50d2lja2xlcnxQcm9ncmFtbWllcmVyfHBocCIsJml9OyA9PiBSZWxldmFueiAxMDAgKERlcnplaXQgbmljaHQgbcO2Z2xpY2ghISEpCiAgdm9pZCAqc1tdPXsicGhwIGVudHdpY2tsZXIiLCJwaHAgbXlzcWwgZW50d2lja2xlciIsIlNvZnR3YXJlIEVudHdpY2tsZXJ8ZW50d2lja2xlcnxQcm9ncmFtbWllcmVyfHBocCIsJml9OyA9PiBSZWxldmFueiA3NSAoVGl0ZWwpCiAgdm9pZCAqc1tdPXsicGhwIGVudHdpY2tsZXIiLCJwaHAgbXlzcWwgZGV2ZWxvcGVyIiwiU29mdHdhcmUgRW50d2lja2xlcnxlbnR3aWNrbGVyfFByb2dyYW1taWVyZXJ8cGhwIGVudHdpY2tsZXIiLCZpfTsgPT4gUmVsZXZhbnogNzUgKEtleW93b3JkcyBQaHJhc2VuKSAgCiAgdm9pZCAqc1tdPXsicGhwIGVudHdpY2tsZXIiLCJTb2Z0d2FyZSBEZXZlbG9wZXIiLCJTb2Z0d2FyZSBFbnR3aWNrbGVyfGVudHdpY2tsZXJ8UHJvZ3JhbW1pZXJlcnxwaHAiLCZpfTsgPT4gUmVsZXZhbnogNTAgKEtleXdvcmRzKQogICovCiAgdm9pZCAqc1tdPXsicGhwIGVudHdpY2tsZXIiLCJTb2Z0d2FyZSBEZXZlbG9wZXIiLCJTb2Z0d2FyZSBFbnR3aWNrbGVyfGVudHdpY2tsZXJ8UHJvZ3JhbW1pZXJlcnxwaHAiLCZpfTsKICBpbnQgbFtdPXsxNCwxOCw0OCwwfTsgCiAgVURGX0FSR1MgdT17NCxzLGx9OwogIHByaW50ZigiXG5cbkVyZ2VibmlzOiAlbGQgXG4iLHJlbGV2YW5jZSgwLCZ1LDAsMCkpOwogIHN5c3RlbSgiUEFVU0UiKTsKICByZXR1cm4gMDsKfQo=