/* -----------------------------------------------------------------------
* Από: migf1 Primitive Text Graphics Plotter v1.5
*
* Πολύ απλοϊκό παράδειγμα προγράμματος κονσόλας που παράγει στο 1ο καρτεσιανό
* τεταρτημόριο τα εξής:
* α) το γράφημα μιας συνάρτησης: f(x) = a*x + b
* β) μια γραμμή μεταξύ δυο σημείων: (x1,y1) και (x2,y2)
* γ) έναν κύκλο με κέντρο στο σημείο: (x,y) και ακτίνα radius
* δ) μια έλλειψη που οριοθετείται από παραλληλόγραμμο με αντικριστές κορυφές
* τα σημεία: (x1,y1) πάνω αριστερά και (x2,y2) κάτω δεξιά
*
* Μπορείτε να αλλάξετε τιμές σε οποιοδήποτε από τα x, pt2.x, x2, pt2.y, y2 & radius,
* αλλά σημειώστε πως το πρόγραμμα απεικονίζει μονάχα όσα σημεία παράγονται στην
* περιοχή: (0,0) ... (74,22)
*
* ΣΗΜΕΙΩΣΕΙΣ ΥΛΟΠΟΙΗΣΗΣ:
* Οι αλγόριθμοι για τον σχεδιασμό της γραμμής του κύκλου και της έλλειψης
* είναι ΕΤΟΙΜΕΣ υλοποιήσεις των αλγορίθμων Bresenham, που χρησιμοποιήσα
* ατόφιους (με μόνη αλλαγή την προσαρμογή τους στις δομές δεδομέννων Screen
* και Point του παρόντος προγράμματος).
*
* Δείτε επίσης:
* http://f...content-available-to-author-only...s.at/easyfilter/bresenham.html
* http://e...content-available-to-author-only...a.org/wiki/Midpoint_circle_algorithm
*
* Tο πρόγραμμα δεν κάνει ούτε scaling ούτε κανονικό clipping και απεικονίζει
* ΜΟΝΑΧΑ το 1ο τεταρτημόριο του καρτεσιανού επιπέδου, με το σημείο (0,0) να
* βρίσκεται στην κάτω αριστερή μεριά της οθόνης.
* Ο κύριος σκοπός για τον οποίον γράφτηκε το πρόγραμμα ήταν η παρουσίαση μιας
* απλοϊκής υλοποίησης buffered σχεδιασμού (οι συναρτήσεις αναβοσβήνουν τα
* κατάλληλα pixels μέσα στο buffer, το οποίο κατόπιν τυπώνεται στην οθόνη
* σειρά προς σειρά, μεσω της συνάρτησης: draw_screen() ).
* -----------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h> // for exit()
#include <string.h> // for memset()
#include <ctype.h> // for tolower()
#define MAXCOLS 75 // μέγιστο πλάτος της οθόνης μας
#define MAXROWS 23 // μέγιστο ύψος της οθόνης μας
#define PIXEL_ON '#' // χαρακτήρας απεικόνισης "αναμμένων" pixels
#define PIXEL_OFF '.' // χαρακτήρας απεικόνισης "σβησμένων" pixels
// έλεγχος για pixels εκτός ορίων
#define VALIDPIXEL(x,y, w,h) ( (y) > -1 && (y) < (h) && (x) > -1 && (x) < (w) )
#define REVAXIS(y,h) (h) - (y) - 1 // προσαρμόζει την συντεταγμένη y ώστε το σημείο
// που της αντιστοιχεί να απεικονιστεί στην
// αντεστραμμένη φορά του άξονά (χρήσιμο για τον
// άξονα Υ που τον θέλουμε να μετράει από κάτω
// προς τα πάνω)
#define ABS(x) ( (x) < 0 ? -(x) : (x) )
#define PAUSE() do{\
char myinbuf[256]; \
printf("press ENTER to continue..."); \
fgets(myinbuf, 256, stdin); \
}while(0)
typedef struct point { // δομή καρτεσιανού σημείου
int x; // καρτεσιανή συντεταγμένη x του σημείου
int y; // καρτεσιανή συντεταγμένη y του σημείου
} Point;
typedef struct screen { // δομή για το buffer της οθόνης μας
int nrows; // τρέχον πλάτος της οθόνης μας
int ncols; // τρέχον ύψος της οθόνης μας
char pixel[MAXROWS][MAXCOLS]; // το buffer της οθόνης μας
} Screen;
// ------------------------------------------------------------------------------------
// set_pixel:
//
// "Ανάβει" το pixel x,y στο buffer της οθόνης. Θα εμφανιστεί αναμένο όταν αργότερα
// τυπωθεί το buffer μέσω της συνάρτησης: draw_screen
//
void set_pixel( Screen *screen, int x, int y )
{
// απόρριψη pixels που βγαίνουν εκτός οθόνης
if ( !VALIDPIXEL(x,y, screen->ncols, screen->nrows ) )
return;
// προσαρμογή του y ώστε το μέτρημα για την
y = REVAXIS(y, screen->nrows); // απεικόνιση να ξεκινά από κάτω προς τα επανω
screen->pixel[y][x] = PIXEL_ON;
return;
}
// ------------------------------------------------------------------------------------
// Η συνάρτηση που παράγει το 1ο γράφημα, δοκιμάστε και με άλλες τιμές
//
int f( int x, float a, float b )
{
return a*x + b;
}
// ------------------------------------------------------------------------------------
// plot_f():
//
// "Ανάβει" τα pixels που αντιστοιχούν στο γράφημα της συνάρτησης f(), στο buffer της
// οθόνης (θα εμφανιστούν αναμμένα όταν αργότερα τυπωθεί το buffer μέσω της συνάρτησης:
// draw_screen() )
//
void plot_f( Screen *screen, float a, float b )
{
register int x;
for (x=0; x < screen->ncols; x++)
set_pixel( screen, x, f(x, a, b) );
return;
}
// ------------------------------------------------------------------------------------
// plot_line():
// αλγόριθμος: http://f...content-available-to-author-only...s.at/easyfilter/bresenham.html
//
// "Ανάβει" τα pixels της γραμμής που οριοθετείται από τα σημεία pt1 και pt2, στο
// buffer της οθόνης (θα εμφανιστούν αναμμένα όταν αργότερα τυπωθεί το buffer μέσω
// της συνάρτησης: draw_screen() )
//
void plot_line( Screen *screen, Point pt1, Point pt2)
{
int dx = ABS( pt2.x - pt1.x );
int dy = -ABS( pt2.y - pt1.y );
int sx = (pt1.x < pt2.x) ? 1 : -1;
int sy = (pt1.y < pt2.y) ? 1 : -1;
int err = dx + dy, e2; // error value e_xy
for (;;)
{
set_pixel(screen, pt1.x, pt1.y);
if ( pt1.x == pt2.x && pt1.y == pt2.y )
break;
e2 = 2 * err;
if (e2 >= dy) { // e_xy + e_x > 0
err += dy;
pt1.x += sx;
}
if (e2 <= dx) { // e_xy + e_y < 0 */
err += dx;
pt1.y += sy;
}
}
return;
}
// ------------------------------------------------------------------------------------
// plot_circle():
// αλγόριθμος: http://f...content-available-to-author-only...s.at/easyfilter/bresenham.html
//
// "Ανάβει" τα pixels της περιφέρειας του κύκλου με κέντρο το σημείο: center και ακτίνα
// radius, στο buffer της οθόνης (θα εμφανιστούν αναμμένα όταν αργότερα τυπωθεί το
// buffer μέσω της συνάρτησης: draw_screen() )
//
void plot_circle(Screen *screen, Point center, int radius)
{
int x = -radius, y = 0, err = 2-2*radius; // II. Quadrant
do {
set_pixel(screen, center.x-x, center.y+y); // I. Quadrant
set_pixel(screen, center.x-y, center.y-x); // II. Quadrant
set_pixel(screen, center.x+x, center.y-y); // III. Quadrant
set_pixel(screen, center.x+y, center.y+x); // IV. Quadrant
radius = err;
if (radius > x) // e_xy + e_x > 0
err += ++x * 2 + 1;
if (radius <= y)
err += ++y * 2 + 1; // e_xy + e_y < 0
} while (x < 0);
return;
}
// ------------------------------------------------------------------------------------
// plot_ellipse():
// αλγόριθμος: http://f...content-available-to-author-only...s.at/easyfilter/bresenham.html
//
// "Ανάβει" στο buffer της οθόνης τα pixels της περιφέρειας της έλλειψης που
// οριοθετείται από ένα παραλληλόγραμμο με την πάνω-αριστερή γωνία του στο
// σημείο: rsta και την κάτω-δεξιά γωνία στου στο σημείο: rend (τα pixels θσ
// εμφανιστούν αναμμένα όταν αργότερα τυπωθεί το buffer μέσω της συνάρτησης:
// draw_screen() )
//
void plot_ellipse( Screen *screen, Point rsta, Point rend )
{
// values of diameter
int a = ABS( rend.x - rsta.x );
int b = ABS( rend.y - rsta.y );
int b1 = b & 1;
// error increment
long dx = 4 * (1-a) * b * b;
long dy = 4 * (b1+1) *a * a;
// error of 1.step
long err = dx + dy + b1 * a * a;
long e2;
if (rsta.x > rend.x) { // if called with swapped points
rsta.x = rend.x;
rend.x += a;
}
if (rsta.y > rend.y) // ... exchange them
rsta.y = rend.y;
// starting pixel
rsta.y += (b+1)/2;
rend.y = rsta.y-b1;
a *= 8 * a;
b1 = 8 * b * b;
do {
set_pixel(screen, rend.x, rsta.y); // I. Quadrant
set_pixel(screen, rsta.x, rsta.y); // II. Quadrant
set_pixel(screen, rsta.x, rend.y); // III. Quadrant
set_pixel(screen, rend.x, rend.y); // IV. Quadrant
e2 = 2 * err;
if (e2 >= dx) { // x step
rsta.x++;
rend.x--;
err += dx += b1;
}
if (e2 <= dy) { // y step
rsta.y++;
rend.y--;
err += dy += a;
}
} while ( rsta.x <= rend.x );
while ( rsta.y - rend.y < b ) { // too early stop of flat ellipses a=1
set_pixel(screen, rsta.x - 1, rsta.y);// -> finish tip of ellipse
set_pixel(screen, rend.x + 1, rsta.y++);
set_pixel(screen, rsta.x - 1, rend.y);
set_pixel(screen, rend.x + 1, rend.y--);
}
return;
}
// ------------------------------------------------------------------------------------
// "Σβήνει" όλα τα pixels στο buffer της οθόνης (α εμφανιστούν σβησμένα όταν αργότερα
// τυπωθεί η οθόνη μέσω της συνάρτησης: draw_screen() )
//
void clear_screen( Screen *screen )
{
// γρήγορος κώδικας
memset( &screen
->pixel
, PIXEL_OFF
, screen
->nrows
* screen
->ncols
* sizeof(char));
/*** DISABLED: αργός εναλλακτικός κώδικας, αλλά ενδεχομένως πιο συμβατός
register int x,y;
for (y=0; y < screen->nrows; y++)
for (x=0; x < screen->ncols; x++)
screen->pixel[y][x] = PIXEL_OFF;
***/
return;
}
// ------------------------------------------------------------------------------------
// draw_screen():
//
// Τυπώνει όλα τα pixels του buffer (αναμμένα και σβηστά) καθώς και την αρίθμηση
// των αξόνων Χ και Υ
//
void draw_screen( Screen screen )
{
register int x, y;
for (y=0; y < screen.nrows; y++)
{
// τύπωμα ετικετών αριστερά από τον άξονα Υ
int ylab = y;
printf("%3d ", REVAXIS
(ylab
, screen.
nrows) );
// τύπωμα των pixels της y-οστής σειράς του buffer
for (x=0; x < screen.ncols; x++)
}
// τύπωμα ετικετών κάτω από τον άξονα Χ
for (x=0; x < screen.ncols; x++)
return;
}
// ------------------------------------------------------------------------------------
void show_help( void )
{
puts("\n\tHELP:\n\n\tA sample program in C for plotting primitive 2D text graphics."); puts("\tIt shows buffered plotting of a Line, a Circle, an Ellipse"); puts("\tand a function of the form: f(x) = a * x + b");
puts("\n\tIt also shows how to read input lines as strings, brake 'em"); puts("\tup to tokens, and convert those tokens to desirable numbers"); puts("\t(using atoi() and atof() for keeping things simple).");
puts("\n\tFor each plot you will be first asked for appropriate input:"); puts("\t-\ta and b, for the function"); puts("\t-\tstarting and ending points, for the line"); puts("\t-\tcenter point and radius, for the circle"); puts("\t-\ttop-left and right-bottom points for the ellipse"); puts("\t\t( they define the rectangle the ellipse \"lives in\" )");
puts("\n\tComas, dashes, parenthesis, spaces and tabs are ignored"); puts("\tduring the input. Any other non-numerical input is either"); puts("\tignored or cause the conversion to produce 0 as the result"); puts("\tof the converted numbers.");
puts("\n\tEXCEPTION: in the main menu, only the first input char is checked");
return;
}
// ------------------------------------------------------------------------------------
void show_menu( void )
{
puts("\n\n\n\n\n\n\n\n\n\n\n\n\n"); puts("\n\tPrimitive Text Graphics Plotter\n\t-------------------------------"); puts("\tf\t: plot F(x) = a*x + b"); puts("\tl\t: plot a Line"); puts("\tc\t: plot a Circle"); puts("\te\t: plot an Ellipse");
return;
}
// ------------------------------------------------------------------------------------
int s_tokenize(char *s, char *tokens[], int maxtokens, const char *delimiters)
{
register int i=0;
tokens
[0] = strtok(s
, delimiters
); if (tokens[0] == NULL)
return 0;
for (i
=1; i
< maxtokens
&& (tokens
[i
]=strtok(NULL
, delimiters
)) != NULL
; i
++);
return i;
}
// ------------------------------------------------------------------------------------
void exec_selected( Screen *screen, char choice )
{
Point ptsta, ptend;
float a, b; int radius, ntoks;
char input[255+1] = ""; char *tokens[4]; const char *delims = "\n\t (),-";
switch( choice )
{
case 'h': // HELP
show_help();
PAUSE();
break;
case 'f': // FUNCTION: y = a*x + b
puts("\n\tPlotting: y = a * x + b ...");
// διάβασμα του a
do {
fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 1, delims);
} while ( ntoks < 1);
// διάβασμα του b
do {
fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 1, delims);
} while ( ntoks < 1);
// σχεδιασμός του γραφήματος της συναρτησης
clear_screen( screen );
plot_f( screen, a, b);
draw_screen( *screen );
printf("\tFunction: y = %.2f * x + %.2f ... ", a
, b
); PAUSE();
break;
case 'l': // LINE
puts("\n\tPlotting a line...");
// διάβασμα αρχικού σημείου: ptsta
do {
printf("\tenter 1st point (x,y): "); fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 2, delims);
} while( ntoks < 2);
ptsta.
x = atoi( tokens
[0] ); ptsta.
y = atoi( tokens
[1] );
// διάβασμα τελικού σημείου: ptend
do {
printf("\tenter 2nd point (x,y): "); fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 2, delims);
} while( ntoks < 2);
ptend.
x = atoi( tokens
[0] ); ptend.
y = atoi( tokens
[1] );
// σχεδιασμός της γραμμής
clear_screen( screen );
plot_line( screen, ptsta, ptend );
draw_screen( *screen );
printf( "\tLine from (%d,%d) to (%d,%d) ... ", ptsta.x, ptsta.y, ptend.x, ptend.y );
PAUSE();
break;
case 'c': // CIRCLE
puts("\n\tPlotting a circle...");
// διάβασμα κέντρου: ptsta
do {
printf("\tenter center point (x,y): "); fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 2, delims);
} while( ntoks < 2);
ptsta.
x = atoi( tokens
[0] ); ptsta.
y = atoi( tokens
[1] );
// διάβασμα της ακτίνας: radius
do {
fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 1, delims);
} while ( ntoks < 1);
// σχεδιασμός της κύκλου
clear_screen( screen );
plot_circle( screen, ptsta, radius );
draw_screen( *screen );
printf( "\tCircle with center (%d,%d) & radius %d ... ", ptsta.x, ptsta.y, radius );
PAUSE();
break;
case 'e': // ELLIPSE
puts("\n\tPlotting an ellipse...");
// διάβασμα πάνω-αριστερής γωνίας: ptsta
do {
printf("\tenter top-left point (x,y): "); fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 2, delims);
} while( ntoks < 2);
ptsta.
x = atoi( tokens
[0] ); ptsta.
y = atoi( tokens
[1] );
// διάβασμα κάτω-δεξιάς γωνίας: ptend
do {
printf("\tenter bottom-right point (x,y): "); fgets(input
, 255+1, stdin
); ntoks = s_tokenize(input, tokens, 2, delims);
} while( ntoks < 2);
ptend.
x = atoi( tokens
[0] ); ptend.
y = atoi( tokens
[1] );
// σχεδιασμός της έλλειψης
clear_screen( screen );
plot_ellipse( screen, ptsta, ptend );
draw_screen( *screen );
printf( "\tEllipse from (%d,%d) to (%d,%d) ... ", ptsta.x, ptsta.y, ptend.x, ptend.y );
PAUSE();
break;
case '\n': // empty selection
break;
default:
puts("\t*** error: unrecognised selection, try again"); break;
}
return;
}
// ------------------------------------------------------------------------------------
int main( void )
{
char input[255+1] = "";
Screen screen = { .ncols = MAXCOLS, .nrows = MAXROWS };
do {
show_menu();
fgets(input
, 255+1, stdin
); exec_selected( &screen, *input );
} while ( *input != 'x' );
}
LyogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICogzpHPgM+MOiBtaWdmMQkJCQkJUHJpbWl0aXZlIFRleHQgR3JhcGhpY3MgUGxvdHRlciB2MS41CiAqCiAqIM6gzr/Ou8+NIM6xz4DOu86/z4rOus+MIM+AzrHPgc6szrTOtc65zrPOvM6xIM+Az4HOv86zz4HOrM68zrzOsc+Ezr/PgiDOus6/zr3Pg8+MzrvOsc+CIM+Azr/PhSDPgM6xz4HOrM6zzrXOuSDPg8+Ezr8gMc6/IM66zrHPgc+EzrXPg865zrHOvc+MCiAqIM+EzrXPhM6xz4HPhM63zrzPjM+BzrnOvyDPhM6xIM61zr7Ors+COgogKiDOsSkJz4TOvyDOs8+BzqzPhs63zrzOsSDOvM65zrHPgiDPg8+Fzr3OrM+Bz4TOt8+DzrfPgjogZih4KSA9IGEqeCArIGIKICogzrIpCc68zrnOsSDOs8+BzrHOvM68zq4gzrzOtc+EzrHOvs+NIM60z4XOvyDPg863zrzOtc6vz4nOvTogKHgxLHkxKSDOus6xzrkgKHgyLHkyKQogKiDOsykJzq3Ovc6xzr0gzrrPjc66zrvOvyDOvM61IM66zq3Ovc+Ez4HOvyDPg8+Ezr8gz4POt868zrXOr86/OiAoeCx5KSDOus6xzrkgzrHOus+Ezq/Ovc6xIHJhZGl1cwogKiDOtCkJzrzOuc6xIM6tzrvOu861zrnPiM63IM+Azr/PhSDOv8+BzrnOv864zrXPhM61zq/PhM6xzrkgzrHPgM+MIM+AzrHPgc6xzrvOu863zrvPjM6zz4HOsc68zrzOvyDOvM61IM6xzr3PhM65zrrPgc65z4PPhM6tz4IgzrrOv8+Bz4XPhs6tz4IKICoJz4TOsSDPg863zrzOtc6vzrE6ICh4MSx5MSkgz4DOrM69z4kgzrHPgc65z4PPhM61z4HOrCDOus6xzrkgKHgyLHkyKSDOus6sz4TPiSDOtM61zr7Ouc6sCiAqCiAqIM6cz4DOv8+BzrXOr8+EzrUgzr3OsSDOsc67zrvOrM6+zrXPhM61IM+EzrnOvM6tz4Igz4POtSDOv8+Azr/Ouc6/zrTOrs+Azr/PhM61IM6xz4DPjCDPhM6xIHgsIHB0Mi54LCB4MiwgcHQyLnksIHkyICYgcmFkaXVzLAogKiDOsc67zrvOrCDPg863zrzOtc65z47Pg8+EzrUgz4DPic+CIM+Ezr8gz4DPgc+MzrPPgc6xzrzOvM6xIM6xz4DOtc65zrrOv869zq/Ots61zrkgzrzOv869zqzPh86xIM+Mz4POsSDPg863zrzOtc6vzrEgz4DOsc+BzqzOs86/zr3PhM6xzrkgz4PPhM63zr0KICogz4DOtc+BzrnOv8+Hzq46ICgwLDApIC4uLiAoNzQsMjIpCiAqCiAqIM6jzpfOnM6VzpnOqc6jzpXOmc6jIM6lzpvOn86gzp/Omc6XzqPOl86jOgogKgnOn865IM6xzrvOs8+Mz4HOuc64zrzOv865IM6zzrnOsSDPhM6/zr0gz4PPh861zrTOuc6xz4POvM+MIM+EzrfPgiDOs8+BzrHOvM68zq7PgiDPhM6/z4UgzrrPjc66zrvOv8+FIM66zrHOuSDPhM63z4Igzq3Ou867zrXOuc+IzrfPggogKgnOtc6vzr3Osc65IM6VzqTOn86ZzpzOlc6jIM+FzrvOv8+Azr/Ouc6uz4POtc65z4Igz4TPic69IM6xzrvOs86/z4HOr864zrzPic69IEJyZXNlbmhhbSwgz4DOv8+FIM+Hz4HOt8+DzrnOvM6/z4DOv865zq7Pg86xCiAqCc6xz4TPjM+GzrnOv8+Fz4IgKM68zrUgzrzPjM69zrcgzrHOu867zrHOs86uIM+EzrfOvSDPgM+Bzr/Pg86xz4HOvM6/zrPOriDPhM6/z4XPgiDPg8+EzrnPgiDOtM6/zrzOrc+CIM60zrXOtM6/zrzOrc69zr3Pic69IFNjcmVlbgogKgnOus6xzrkgUG9pbnQgz4TOv8+FIM+AzrHPgc+Mzr3PhM6/z4Igz4DPgc6/zrPPgc6szrzOvM6xz4TOv8+CKS4KICoKICogCc6UzrXOr8+EzrUgzrXPgM6vz4POt8+COgogKglodHRwOi8vZi4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4ucy5hdC9lYXN5ZmlsdGVyL2JyZXNlbmhhbS5odG1sCiAqCWh0dHA6Ly9lLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5hLm9yZy93aWtpL01pZHBvaW50X2NpcmNsZV9hbGdvcml0aG0KICoKICoJVM6/IM+Az4HPjM6zz4HOsc68zrzOsSDOtM61zr0gzrrOrM69zrXOuSDOv8+Nz4TOtSBzY2FsaW5nIM6/z43PhM61IM66zrHOvc6/zr3Ouc66z4wgY2xpcHBpbmcgzrrOsc65IM6xz4DOtc65zrrOv869zq/Ots61zrkKICoJzpzOn86dzpHOp86RIM+Ezr8gMc6/IM+EzrXPhM6xz4HPhM63zrzPjM+BzrnOvyDPhM6/z4UgzrrOsc+Bz4TOtc+DzrnOsc69zr/PjSDOtc+AzrnPgM6tzrTOv8+FLCDOvM61IM+Ezr8gz4POt868zrXOr86/ICgwLDApIM69zrEKICoJzrLPgc6vz4POus61z4TOsc65IM+Dz4TOt869IM66zqzPhM+JIM6xz4HOuc+Dz4TOtc+Bzq4gzrzOtc+BzrnOrCDPhM63z4Igzr/OuM+Mzr3Ot8+CLgogKgnOnyDOus+Nz4HOuc6/z4Igz4POus6/z4DPjM+CIM6zzrnOsSDPhM6/zr0gzr/PgM6/zq/Ov869IM6zz4HOrM+Gz4TOt866zrUgz4TOvyDPgM+Bz4zOs8+BzrHOvM68zrEgzq7PhM6xzr0gzrcgz4DOsc+Bzr/Phc+Dzq/Osc+DzrcgzrzOuc6xz4IKICoJzrHPgM67zr/Pis66zq7PgiDPhc67zr/PgM6/zq/Ot8+DzrfPgiBidWZmZXJlZCDPg8+HzrXOtM65zrHPg868zr/PjSAozr/OuSDPg8+Fzr3Osc+Bz4TOrs+DzrXOuc+CIM6xzr3Osc6yzr/Pg86yzq7Ovc6/z4XOvSDPhM6xCiAqCc66zrHPhM6szrvOu863zrvOsSBwaXhlbHMgzrzOrc+DzrEgz4PPhM6/IGJ1ZmZlciwgz4TOvyDOv8+Azr/Or86/IM66zrHPhM+Mz4DOuc69IM+Ez4XPgM+Ozr3Otc+EzrHOuSDPg8+EzrfOvSDOv864z4zOvc63CiAqCc+DzrXOuc+Bzqwgz4DPgc6/z4Igz4POtc65z4HOrCwgzrzOtc+Dz4kgz4TOt8+CIM+Dz4XOvc6sz4HPhM63z4POt8+COiBkcmF3X3NjcmVlbigpICkuCiAqIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAqLwoKI2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4JCQkvLyBmb3IgZXhpdCgpCiNpbmNsdWRlIDxzdHJpbmcuaD4JCQkvLyBmb3IgbWVtc2V0KCkKI2luY2x1ZGUgPGN0eXBlLmg+CQkJLy8gZm9yIHRvbG93ZXIoKQoKI2RlZmluZQlNQVhDT0xTCQk3NSAgICAgCQkvLyDOvM6tzrPOuc+Dz4TOvyDPgM67zqzPhM6/z4Igz4TOt8+CIM6/zrjPjM69zrfPgiDOvM6xz4IKI2RlZmluZSBNQVhST1dTCQkyMwkJLy8gzrzOrc6zzrnPg8+Ezr8gz43PiM6/z4Igz4TOt8+CIM6/zrjPjM69zrfPgiDOvM6xz4IKCiNkZWZpbmUgUElYRUxfT04JJyMnCQkvLyDPh86xz4HOsc66z4TOrs+BzrHPgiDOsc+AzrXOuc66z4zOvc65z4POt8+CICLOsc69zrHOvM68zq3Ovc+Jzr0iIHBpeGVscwojZGVmaW5lIFBJWEVMX09GRgknLicgCQkvLyDPh86xz4HOsc66z4TOrs+BzrHPgiDOsc+AzrXOuc66z4zOvc65z4POt8+CICLPg86yzrfPg868zq3Ovc+Jzr0iIHBpeGVscwoKCQkJCQkvLyDOrc67zrXOs8+Hzr/PgiDOs865zrEgcGl4ZWxzIM61zrrPhM+Mz4Igzr/Pgc6vz4nOvQojZGVmaW5lCVZBTElEUElYRUwoeCx5LCB3LGgpCSggKHkpID4gLTEgJiYgKHkpIDwgKGgpICYmICh4KSA+IC0xICYmICh4KSA8ICh3KSApCgojZGVmaW5lCVJFVkFYSVMoeSxoKQkoaCkgLSAoeSkgLSAxICAgLy8gz4DPgc6/z4POsc+BzrzPjM62zrXOuSDPhM63zr0gz4PPhc69z4TOtc+EzrHOs868zq3Ovc63IHkgz47Pg8+EzrUgz4TOvyDPg863zrzOtc6vzr8KCQkJCQkvLyDPgM6/z4Ugz4TOt8+CIM6xzr3PhM65z4PPhM6/zrnPh861zq8gzr3OsSDOsc+AzrXOuc66zr/Ovc65z4PPhM61zq8gz4PPhM63zr0KCQkJCQkvLyDOsc69z4TOtc+Dz4TPgc6xzrzOvM6tzr3OtyDPhs6/z4HOrCDPhM6/z4UgzqzOvs6/zr3OrCAoz4fPgc6uz4POuc68zr8gzrPOuc6xIM+Ezr/OvQoJCQkJCS8vIM6szr7Ov869zrEgzqUgz4DOv8+FIM+Ezr/OvSDOuM6tzrvOv8+FzrzOtSDOvc6xIM68zrXPhM+BzqzOtc65IM6xz4DPjCDOus6sz4TPiQoJCQkJCS8vIM+Az4HOv8+CIM+EzrEgz4DOrM69z4kpCgojZGVmaW5lIEFCUyh4KQkJKCAoeCkgPCAwID8gLSh4KSA6ICh4KSApCgojZGVmaW5lIFBBVVNFKCkgZG97XAoJY2hhciBteWluYnVmWzI1Nl07IFwKCXByaW50ZigicHJlc3MgRU5URVIgdG8gY29udGludWUuLi4iKTsJXAoJZmdldHMobXlpbmJ1ZiwgMjU2LCBzdGRpbik7IFwKfXdoaWxlKDApCgoKdHlwZWRlZiBzdHJ1Y3QgcG9pbnQgewkJCS8vIM60zr/OvM6uIM66zrHPgc+EzrXPg865zrHOvc6/z40gz4POt868zrXOr86/z4UKCWludCB4OwkJCQkJLy8gzrrOsc+Bz4TOtc+DzrnOsc69zq4gz4PPhc69z4TOtc+EzrHOs868zq3Ovc63IHggz4TOv8+FIM+DzrfOvM61zq/Ov8+FCglpbnQgeTsJCQkJCS8vIM66zrHPgc+EzrXPg865zrHOvc6uIM+Dz4XOvc+EzrXPhM6xzrPOvM6tzr3OtyB5IM+Ezr/PhSDPg863zrzOtc6vzr/PhQp9IFBvaW50OwoKdHlwZWRlZiBzdHJ1Y3Qgc2NyZWVuIHsJCQkvLyDOtM6/zrzOriDOs865zrEgz4TOvyBidWZmZXIgz4TOt8+CIM6/zrjPjM69zrfPgiDOvM6xz4IKCWludCBucm93czsJCQkJLy8gz4TPgc6tz4fOv869IM+AzrvOrM+Ezr/PgiDPhM63z4Igzr/OuM+Mzr3Ot8+CIM68zrHPggoJaW50IG5jb2xzOwkJCQkvLyDPhM+Bzq3Ph86/zr0gz43PiM6/z4Igz4TOt8+CIM6/zrjPjM69zrfPgiDOvM6xz4IKCWNoYXIgcGl4ZWxbTUFYUk9XU11bTUFYQ09MU107CQkvLyDPhM6/IGJ1ZmZlciDPhM63z4Igzr/OuM+Mzr3Ot8+CIM68zrHPggp9IFNjcmVlbjsKCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyBzZXRfcGl4ZWw6Ci8vCi8vICLOkc69zqzOss61zrkiIM+Ezr8gcGl4ZWwgeCx5IM+Dz4TOvyBidWZmZXIgz4TOt8+CIM6/zrjPjM69zrfPgi4gzpjOsSDOtc68z4bOsc69zrnPg8+EzrXOryDOsc69zrHOvM6tzr3OvyDPjM+EzrHOvSDOsc+BzrPPjM+EzrXPgc6xCi8vIM+Ez4XPgM+JzrjOtc6vIM+Ezr8gYnVmZmVyIM68zq3Pg8+JIM+EzrfPgiDPg8+Fzr3OrM+Bz4TOt8+DzrfPgjogZHJhd19zY3JlZW4KLy8Kdm9pZCBzZXRfcGl4ZWwoIFNjcmVlbiAqc2NyZWVuLCBpbnQgeCwgaW50IHkgKQp7CgkJCQkJLy8gzrHPgM+Mz4HPgc65z4jOtyBwaXhlbHMgz4DOv8+FIM6yzrPOsc6vzr3Ov8+Fzr0gzrXOus+Ez4zPgiDOv864z4zOvc63z4IKCWlmICggIVZBTElEUElYRUwoeCx5LCBzY3JlZW4tPm5jb2xzLCBzY3JlZW4tPm5yb3dzICkgKQoJCXJldHVybjsKCQkJCQkvLyDPgM+Bzr/Pg86xz4HOvM6/zrPOriDPhM6/z4UgeSDPjs+Dz4TOtSDPhM6/IM68zq3PhM+BzrfOvM6xIM6zzrnOsSDPhM63zr0KCXkgPSBSRVZBWElTKHksIHNjcmVlbi0+bnJvd3MpOwkvLyDOsc+AzrXOuc66z4zOvc65z4POtyDOvc6xIM6+zrXOus65zr3OrCDOsc+Az4wgzrrOrM+Ez4kgz4DPgc6/z4Igz4TOsSDOtc+AzrHOvc+JCglzY3JlZW4tPnBpeGVsW3ldW3hdID0gUElYRUxfT047CgoJcmV0dXJuOwp9CgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gzpcgz4PPhc69zqzPgc+EzrfPg863IM+Azr/PhSDPgM6xz4HOrM6zzrXOuSDPhM6/IDHOvyDOs8+BzqzPhs63zrzOsSwgzrTOv866zrnOvM6sz4PPhM61IM66zrHOuSDOvM61IM6szrvOu861z4Igz4TOuc68zq3PggovLwppbnQgZiggaW50IHgsIGZsb2F0IGEsIGZsb2F0IGIgKQp7CglyZXR1cm4gYSp4ICsgYjsKfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIHBsb3RfZigpOgovLwovLyAizpHOvc6szrLOtc65IiDPhM6xIHBpeGVscyDPgM6/z4UgzrHOvc+EzrnPg8+Ezr/Ouc+Hzr/Pjc69IM+Dz4TOvyDOs8+BzqzPhs63zrzOsSDPhM63z4Igz4PPhc69zqzPgc+EzrfPg863z4IgZigpLCDPg8+Ezr8gYnVmZmVyIM+EzrfPggovLyDOv864z4zOvc63z4IgKM64zrEgzrXOvM+GzrHOvc65z4PPhM6/z43OvSDOsc69zrHOvM68zq3Ovc6xIM+Mz4TOsc69IM6xz4HOs8+Mz4TOtc+BzrEgz4TPhc+Az4nOuM61zq8gz4TOvyBidWZmZXIgzrzOrc+Dz4kgz4TOt8+CIM+Dz4XOvc6sz4HPhM63z4POt8+COgovLyBkcmF3X3NjcmVlbigpICkKLy8Kdm9pZCBwbG90X2YoIFNjcmVlbiAqc2NyZWVuLCBmbG9hdCBhLCBmbG9hdCBiICkKewoJcmVnaXN0ZXIgaW50IHg7Cglmb3IgKHg9MDsgeCA8IHNjcmVlbi0+bmNvbHM7IHgrKykKCQlzZXRfcGl4ZWwoIHNjcmVlbiwgeCwgZih4LCBhLCBiKSApOwoKCXJldHVybjsKfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIHBsb3RfbGluZSgpOgovLwnOsc67zrPPjM+BzrnOuM68zr/PgjogaHR0cDovL2YuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLnMuYXQvZWFzeWZpbHRlci9icmVzZW5oYW0uaHRtbAovLwovLyAizpHOvc6szrLOtc65IiDPhM6xIHBpeGVscyDPhM63z4IgzrPPgc6xzrzOvM6uz4Igz4DOv8+FIM6/z4HOuc6/zrjOtc+EzrXOr8+EzrHOuSDOsc+Az4wgz4TOsSDPg863zrzOtc6vzrEgcHQxIM66zrHOuSBwdDIsIM+Dz4TOvwovLyBidWZmZXIgz4TOt8+CIM6/zrjPjM69zrfPgiAozrjOsSDOtc68z4bOsc69zrnPg8+Ezr/Pjc69IM6xzr3Osc68zrzOrc69zrEgz4zPhM6xzr0gzrHPgc6zz4zPhM61z4HOsSDPhM+Fz4DPic64zrXOryDPhM6/IGJ1ZmZlciDOvM6tz4PPiQovLyDPhM63z4Igz4PPhc69zqzPgc+EzrfPg863z4I6IGRyYXdfc2NyZWVuKCkgKQovLwp2b2lkIHBsb3RfbGluZSggU2NyZWVuICpzY3JlZW4sIFBvaW50IHB0MSwgUG9pbnQgcHQyKQp7CglpbnQgZHggPSAgQUJTKCBwdDIueCAtIHB0MS54ICk7CglpbnQgZHkgPSAtQUJTKCBwdDIueSAtIHB0MS55ICk7CglpbnQgc3ggPSAocHQxLnggPCBwdDIueCkgPyAxIDogLTE7CglpbnQgc3kgPSAocHQxLnkgPCBwdDIueSkgPyAxIDogLTE7IAoJaW50IGVyciA9IGR4ICsgZHksIGUyOwkJCQkvLyBlcnJvciB2YWx1ZSBlX3h5CgoJZm9yICg7OykKCXsKCQlzZXRfcGl4ZWwoc2NyZWVuLCBwdDEueCwgcHQxLnkpOwoJCWlmICggcHQxLnggPT0gcHQyLnggJiYgcHQxLnkgPT0gcHQyLnkgKQoJCQlicmVhazsKCQllMiA9IDIgKiBlcnI7CgkJaWYgKGUyID49IGR5KSB7CQkJCS8vIGVfeHkgKyBlX3ggPiAwCgkJCWVyciArPSBkeTsKCQkJcHQxLnggKz0gc3g7CgkJfSAKCQlpZiAoZTIgPD0gZHgpIHsJCQkJLy8gZV94eSArIGVfeSA8IDAgKi8KCQkJZXJyICs9IGR4OwoJCQlwdDEueSArPSBzeTsKCQl9Cgl9CgoJcmV0dXJuOwp9CgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gcGxvdF9jaXJjbGUoKToKLy8JzrHOu86zz4zPgc65zrjOvM6/z4I6IGh0dHA6Ly9mLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5zLmF0L2Vhc3lmaWx0ZXIvYnJlc2VuaGFtLmh0bWwKLy8KLy8gIs6Rzr3OrM6yzrXOuSIgz4TOsSBwaXhlbHMgz4TOt8+CIM+AzrXPgc65z4bOrc+BzrXOuc6xz4Igz4TOv8+FIM66z43Ous67zr/PhSDOvM61IM66zq3Ovc+Ez4HOvyDPhM6/IM+DzrfOvM61zq/OvzogY2VudGVyIM66zrHOuSDOsc66z4TOr869zrEKLy8gcmFkaXVzLCDPg8+Ezr8gYnVmZmVyIM+EzrfPgiDOv864z4zOvc63z4IgKM64zrEgzrXOvM+GzrHOvc65z4PPhM6/z43OvSDOsc69zrHOvM68zq3Ovc6xIM+Mz4TOsc69IM6xz4HOs8+Mz4TOtc+BzrEgz4TPhc+Az4nOuM61zq8gz4TOvwovLyBidWZmZXIgzrzOrc+Dz4kgz4TOt8+CIM+Dz4XOvc6sz4HPhM63z4POt8+COiBkcmF3X3NjcmVlbigpICkKLy8Kdm9pZCBwbG90X2NpcmNsZShTY3JlZW4gKnNjcmVlbiwgUG9pbnQgY2VudGVyLCBpbnQgcmFkaXVzKQp7CglpbnQgeCA9IC1yYWRpdXMsIHkgPSAwLCBlcnIgPSAyLTIqcmFkaXVzOwkJLy8gSUkuIFF1YWRyYW50CglkbyB7CgkJc2V0X3BpeGVsKHNjcmVlbiwgY2VudGVyLngteCwgY2VudGVyLnkreSk7CS8vICAgSS4gUXVhZHJhbnQKCQlzZXRfcGl4ZWwoc2NyZWVuLCBjZW50ZXIueC15LCBjZW50ZXIueS14KTsJLy8gIElJLiBRdWFkcmFudAoJCXNldF9waXhlbChzY3JlZW4sIGNlbnRlci54K3gsIGNlbnRlci55LXkpOwkvLyBJSUkuIFF1YWRyYW50CgkJc2V0X3BpeGVsKHNjcmVlbiwgY2VudGVyLngreSwgY2VudGVyLnkreCk7CS8vICBJVi4gUXVhZHJhbnQKCQlyYWRpdXMgPSBlcnI7CgkJaWYgKHJhZGl1cyA+ICB4KQkJCQkvLyBlX3h5ICsgZV94ID4gMAoJCQllcnIgKz0gKyt4ICogMiArIDE7CgkJaWYgKHJhZGl1cyA8PSB5KQoJCQllcnIgKz0gKyt5ICogMiArIDE7CQkJLy8gZV94eSArIGVfeSA8IDAKCX0gd2hpbGUgKHggPCAwKTsKCglyZXR1cm47Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyBwbG90X2VsbGlwc2UoKToKLy8JzrHOu86zz4zPgc65zrjOvM6/z4I6IGh0dHA6Ly9mLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5zLmF0L2Vhc3lmaWx0ZXIvYnJlc2VuaGFtLmh0bWwKLy8KLy8gIs6Rzr3OrM6yzrXOuSIgz4PPhM6/IGJ1ZmZlciDPhM63z4Igzr/OuM+Mzr3Ot8+CIM+EzrEgcGl4ZWxzIM+EzrfPgiDPgM61z4HOuc+Gzq3Pgc61zrnOsc+CIM+EzrfPgiDOrc67zrvOtc65z4jOt8+CIM+Azr/PhQovLyDOv8+BzrnOv864zrXPhM61zq/PhM6xzrkgzrHPgM+MIM6tzr3OsSDPgM6xz4HOsc67zrvOt867z4zOs8+BzrHOvM68zr8gzrzOtSDPhM63zr0gz4DOrM69z4ktzrHPgc65z4PPhM61z4HOriDOs8+Jzr3Or86xIM+Ezr/PhSDPg8+Ezr8KLy8gz4POt868zrXOr86/OiByc3RhIM66zrHOuSDPhM63zr0gzrrOrM+Ez4ktzrTOtc6+zrnOrCDOs8+Jzr3Or86xIM+Dz4TOv8+FIM+Dz4TOvyDPg863zrzOtc6vzr86IHJlbmQgKM+EzrEgcGl4ZWxzIM64z4MKLy8gzrXOvM+GzrHOvc65z4PPhM6/z43OvSDOsc69zrHOvM68zq3Ovc6xIM+Mz4TOsc69IM6xz4HOs8+Mz4TOtc+BzrEgz4TPhc+Az4nOuM61zq8gz4TOvyBidWZmZXIgzrzOrc+Dz4kgz4TOt8+CIM+Dz4XOvc6sz4HPhM63z4POt8+COgovLyBkcmF3X3NjcmVlbigpICkKLy8Kdm9pZCBwbG90X2VsbGlwc2UoIFNjcmVlbiAqc2NyZWVuLCBQb2ludCByc3RhLCBQb2ludCByZW5kICkKewoJLy8gdmFsdWVzIG9mIGRpYW1ldGVyCglpbnQgYSA9IEFCUyggcmVuZC54IC0gcnN0YS54ICk7CglpbnQgYiA9IEFCUyggcmVuZC55IC0gcnN0YS55ICk7CglpbnQgYjEgPSBiICYgMTsJCgoJLy8gZXJyb3IgaW5jcmVtZW50Cglsb25nIGR4ID0gNCAqICgxLWEpICogYiAqIGI7Cglsb25nIGR5ID0gNCAqIChiMSsxKSAqYSAqIGE7CgoJLy8gZXJyb3Igb2YgMS5zdGVwCglsb25nIGVyciA9IGR4ICsgZHkgKyBiMSAqIGEgKiBhOwoJbG9uZyBlMjsKCglpZiAocnN0YS54ID4gcmVuZC54KSB7CQkJLy8gaWYgY2FsbGVkIHdpdGggc3dhcHBlZCBwb2ludHMKCQlyc3RhLnggPSByZW5kLng7CgkJcmVuZC54ICs9IGE7Cgl9CglpZiAocnN0YS55ID4gcmVuZC55KQkJCQkgLy8gLi4uIGV4Y2hhbmdlIHRoZW0KCQlyc3RhLnkgPSByZW5kLnk7CgoJLy8gc3RhcnRpbmcgcGl4ZWwKCXJzdGEueSArPSAoYisxKS8yOwoJcmVuZC55ID0gcnN0YS55LWIxOwoKCWEgKj0gOCAqIGE7CgliMSA9IDggKiBiICogYjsKCglkbyB7CgkJc2V0X3BpeGVsKHNjcmVlbiwgcmVuZC54LCByc3RhLnkpOwkvLyAgIEkuIFF1YWRyYW50CgkJc2V0X3BpeGVsKHNjcmVlbiwgcnN0YS54LCByc3RhLnkpOwkvLyAgSUkuIFF1YWRyYW50CgkJc2V0X3BpeGVsKHNjcmVlbiwgcnN0YS54LCByZW5kLnkpOwkvLyBJSUkuIFF1YWRyYW50CgkJc2V0X3BpeGVsKHNjcmVlbiwgcmVuZC54LCByZW5kLnkpOwkvLyAgSVYuIFF1YWRyYW50CgkJZTIgPSAyICogZXJyOwoJCWlmIChlMiA+PSBkeCkgewkJCQkvLyB4IHN0ZXAKCQkJcnN0YS54Kys7CgkJCXJlbmQueC0tOwoJCQllcnIgKz0gZHggKz0gYjE7CgkJfQoJCWlmIChlMiA8PSBkeSkgewkJCQkvLyB5IHN0ZXAKCQkJcnN0YS55Kys7CgkJCXJlbmQueS0tOwoJCQllcnIgKz0gZHkgKz0gYTsKCQl9Cgl9IHdoaWxlICggcnN0YS54IDw9IHJlbmQueCApOwoKCXdoaWxlICggcnN0YS55IC0gcmVuZC55IDwgYiApIHsJLy8gdG9vIGVhcmx5IHN0b3Agb2YgZmxhdCBlbGxpcHNlcyBhPTEKCQlzZXRfcGl4ZWwoc2NyZWVuLCByc3RhLnggLSAxLCByc3RhLnkpOy8vIC0+IGZpbmlzaCB0aXAgb2YgZWxsaXBzZQoJCXNldF9waXhlbChzY3JlZW4sIHJlbmQueCArIDEsIHJzdGEueSsrKTsgCgkJc2V0X3BpeGVsKHNjcmVlbiwgcnN0YS54IC0gMSwgcmVuZC55KTsKCQlzZXRfcGl4ZWwoc2NyZWVuLCByZW5kLnggKyAxLCByZW5kLnktLSk7IAoJfQoKCXJldHVybjsKfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vICLOo86yzq7Ovc61zrkiIM+MzrvOsSDPhM6xIHBpeGVscyDPg8+Ezr8gYnVmZmVyIM+EzrfPgiDOv864z4zOvc63z4IgKM6xIM61zrzPhs6xzr3Ouc+Dz4TOv8+Nzr0gz4POss63z4POvM6tzr3OsSDPjM+EzrHOvSDOsc+BzrPPjM+EzrXPgc6xCi8vIM+Ez4XPgM+JzrjOtc6vIM63IM6/zrjPjM69zrcgzrzOrc+Dz4kgz4TOt8+CIM+Dz4XOvc6sz4HPhM63z4POt8+COiBkcmF3X3NjcmVlbigpICkKLy8Kdm9pZCBjbGVhcl9zY3JlZW4oIFNjcmVlbiAqc2NyZWVuICkKewoJLy8gzrPPgc6uzrPOv8+Bzr/PgiDOus+OzrTOuc66zrHPggoJbWVtc2V0KCAmc2NyZWVuLT5waXhlbCwgUElYRUxfT0ZGLCBzY3JlZW4tPm5yb3dzICogc2NyZWVuLT5uY29scyAqIHNpemVvZihjaGFyKSk7CgovKioqIERJU0FCTEVEOiDOsc+BzrPPjM+CIM61zr3Osc67zrvOsc66z4TOuc66z4zPgiDOus+OzrTOuc66zrHPgiwgzrHOu867zqwgzrXOvc60zrXPh86/zrzOrc69z4nPgiDPgM65zr8gz4PPhc68zrLOsc+Ez4zPggoJcmVnaXN0ZXIgaW50IHgseTsKCglmb3IgKHk9MDsgeSA8IHNjcmVlbi0+bnJvd3M7IHkrKykKCQlmb3IgKHg9MDsgeCA8IHNjcmVlbi0+bmNvbHM7IHgrKykKCQkJc2NyZWVuLT5waXhlbFt5XVt4XSA9IFBJWEVMX09GRjsKKioqLwoKCXJldHVybjsKfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIGRyYXdfc2NyZWVuKCk6Ci8vCi8vIM6kz4XPgM+Ozr3Otc65IM+MzrvOsSDPhM6xIHBpeGVscyDPhM6/z4UgYnVmZmVyICjOsc69zrHOvM68zq3Ovc6xIM66zrHOuSDPg86yzrfPg8+EzqwpIM66zrHOuM+Oz4IgzrrOsc65IM+EzrfOvSDOsc+Bzq/OuM68zrfPg863Ci8vIM+Ez4nOvSDOsc6+z4zOvc+Jzr0gzqcgzrrOsc65IM6lCi8vCnZvaWQgZHJhd19zY3JlZW4oIFNjcmVlbiBzY3JlZW4gKQp7CglyZWdpc3RlciBpbnQgeCwgeTsKCglmb3IgKHk9MDsgeSA8IHNjcmVlbi5ucm93czsgeSsrKQoJewoJCS8vIM+Ez43PgM+JzrzOsSDOtc+EzrnOus61z4TPjs69IM6xz4HOuc+Dz4TOtc+BzqwgzrHPgM+MIM+Ezr/OvSDOrM6+zr/Ovc6xIM6lCgkJaW50IHlsYWIgPSB5OwoJCXByaW50ZigiJTNkICIsIFJFVkFYSVMoeWxhYiwgc2NyZWVuLm5yb3dzKSApOwoKCQkvLyDPhM+Nz4DPic68zrEgz4TPic69IHBpeGVscyDPhM63z4IgeS3Ov8+Dz4TOrs+CIM+DzrXOuc+BzqzPgiDPhM6/z4UgYnVmZmVyCgkJZm9yICh4PTA7IHggPCBzY3JlZW4ubmNvbHM7IHgrKykKCQkJcHV0Y2hhciggc2NyZWVuLnBpeGVsW3ldW3hdICk7CgkJcHV0Y2hhcignXG4nKTsKCX0KCgkvLyDPhM+Nz4DPic68zrEgzrXPhM65zrrOtc+Ez47OvSDOus6sz4TPiSDOsc+Az4wgz4TOv869IM6szr7Ov869zrEgzqcKCXByaW50ZigiWS9YICIpOwoJZm9yICh4PTA7IHggPCBzY3JlZW4ubmNvbHM7IHgrKykKCQlwcmludGYoIiVkIiwgeCUxMCk7CglwdXRjaGFyKCdcbicpOwoKCXJldHVybjsKfQoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnZvaWQgc2hvd19oZWxwKCB2b2lkICkKewoJcHV0cygiXG5cdEhFTFA6XG5cblx0QSBzYW1wbGUgcHJvZ3JhbSBpbiBDIGZvciBwbG90dGluZyBwcmltaXRpdmUgMkQgdGV4dCBncmFwaGljcy4iKTsKCXB1dHMoIlx0SXQgc2hvd3MgYnVmZmVyZWQgcGxvdHRpbmcgb2YgYSBMaW5lLCBhIENpcmNsZSwgYW4gRWxsaXBzZSIpOwoJcHV0cygiXHRhbmQgYSBmdW5jdGlvbiBvZiB0aGUgZm9ybTogZih4KSA9IGEgKiB4ICsgYiIpOwoKCXB1dHMoIlxuXHRJdCBhbHNvIHNob3dzIGhvdyB0byByZWFkIGlucHV0IGxpbmVzIGFzIHN0cmluZ3MsIGJyYWtlICdlbSIpOwoJcHV0cygiXHR1cCB0byB0b2tlbnMsIGFuZCBjb252ZXJ0IHRob3NlIHRva2VucyB0byBkZXNpcmFibGUgbnVtYmVycyIpOwoJcHV0cygiXHQodXNpbmcgYXRvaSgpIGFuZCBhdG9mKCkgZm9yIGtlZXBpbmcgdGhpbmdzIHNpbXBsZSkuIik7CgoJcHV0cygiXG5cdEZvciBlYWNoIHBsb3QgeW91IHdpbGwgYmUgZmlyc3QgYXNrZWQgZm9yIGFwcHJvcHJpYXRlIGlucHV0OiIpOwoJcHV0cygiXHQtXHRhIGFuZCBiLCBmb3IgdGhlIGZ1bmN0aW9uIik7CglwdXRzKCJcdC1cdHN0YXJ0aW5nIGFuZCBlbmRpbmcgcG9pbnRzLCBmb3IgdGhlIGxpbmUiKTsKCXB1dHMoIlx0LVx0Y2VudGVyIHBvaW50IGFuZCByYWRpdXMsIGZvciB0aGUgY2lyY2xlIik7CglwdXRzKCJcdC1cdHRvcC1sZWZ0IGFuZCByaWdodC1ib3R0b20gcG9pbnRzIGZvciB0aGUgZWxsaXBzZSIpOwoJcHV0cygiXHRcdCggdGhleSBkZWZpbmUgdGhlIHJlY3RhbmdsZSB0aGUgZWxsaXBzZSBcImxpdmVzIGluXCIgKSIpOwoKCXB1dHMoIlxuXHRDb21hcywgZGFzaGVzLCBwYXJlbnRoZXNpcywgc3BhY2VzIGFuZCB0YWJzIGFyZSBpZ25vcmVkIik7CglwdXRzKCJcdGR1cmluZyB0aGUgaW5wdXQuIEFueSBvdGhlciBub24tbnVtZXJpY2FsIGlucHV0IGlzIGVpdGhlciIpOwoJcHV0cygiXHRpZ25vcmVkIG9yIGNhdXNlIHRoZSBjb252ZXJzaW9uIHRvIHByb2R1Y2UgMCBhcyB0aGUgcmVzdWx0Iik7CglwdXRzKCJcdG9mIHRoZSBjb252ZXJ0ZWQgbnVtYmVycy4iKTsKCglwdXRzKCJcblx0RVhDRVBUSU9OOiBpbiB0aGUgbWFpbiBtZW51LCBvbmx5IHRoZSBmaXJzdCBpbnB1dCBjaGFyIGlzIGNoZWNrZWQiKTsKCglwcmludGYoIlxuXHQiKTsKCglyZXR1cm47Cn0KLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnZvaWQgc2hvd19tZW51KCB2b2lkICkKewoJcHV0cygiXG5cblxuXG5cblxuXG5cblxuXG5cblxuXG4iKTsKCXByaW50ZigiJTM4c1xuIiwgImJ5IG1pZ2YxIik7CglwdXRzKCJcblx0UHJpbWl0aXZlIFRleHQgR3JhcGhpY3MgUGxvdHRlclxuXHQtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIik7CglwdXRzKCJcdGhcdDogSGVscCIpOwoJcHV0cygiXHRmXHQ6IHBsb3QgRih4KSA9IGEqeCArIGIiKTsKCXB1dHMoIlx0bFx0OiBwbG90IGEgTGluZSIpOwoJcHV0cygiXHRjXHQ6IHBsb3QgYSBDaXJjbGUiKTsKCXB1dHMoIlx0ZVx0OiBwbG90IGFuIEVsbGlwc2UiKTsKCXB1dHMoIlx0eFx0OiBlWGl0Iik7CglwcmludGYoIlx0c2VsZWN0OiAiKTsKCglyZXR1cm47Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQppbnQgc190b2tlbml6ZShjaGFyICpzLCBjaGFyICp0b2tlbnNbXSwgaW50IG1heHRva2VucywgY29uc3QgY2hhciAqZGVsaW1pdGVycykKewoJcmVnaXN0ZXIgaW50IGk9MDsKCgl0b2tlbnNbMF0gPSBzdHJ0b2socywgZGVsaW1pdGVycyk7CglpZiAodG9rZW5zWzBdID09IE5VTEwpCgkJcmV0dXJuIDA7Cglmb3IgKGk9MTsgaSA8IG1heHRva2VucyAmJiAodG9rZW5zW2ldPXN0cnRvayhOVUxMLCBkZWxpbWl0ZXJzKSkgIT0gTlVMTDsgaSsrKTsgCgoJcmV0dXJuIGk7Cn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp2b2lkIGV4ZWNfc2VsZWN0ZWQoIFNjcmVlbiAqc2NyZWVuLCBjaGFyIGNob2ljZSApCnsKCVBvaW50IHB0c3RhLCBwdGVuZDsKCWZsb2F0IGEsIGI7CWludCByYWRpdXMsIG50b2tzOwoJY2hhciBpbnB1dFsyNTUrMV0gPSAiIjsgIGNoYXIgKnRva2Vuc1s0XTsgIGNvbnN0IGNoYXIgKmRlbGltcyA9ICJcblx0ICgpLC0iOwoKCXN3aXRjaCggY2hvaWNlICkKCXsKCQljYXNlICdoJzoJLy8gSEVMUAoJCQlzaG93X2hlbHAoKTsKCQkJUEFVU0UoKTsKCQlicmVhazsKCgkJY2FzZSAnZic6CS8vIEZVTkNUSU9OOiB5ID0gYSp4ICsgYgoJCQlwdXRzKCJcblx0UGxvdHRpbmc6IHkgPSBhICogeCArIGIgLi4uIik7CgoJCQkvLyDOtM65zqzOss6xz4POvM6xIM+Ezr/PhSBhCgkJCWRvIHsKCQkJCXByaW50ZigiXHRlbnRlciBhOiAiKTsKCQkJCWZnZXRzKGlucHV0LCAyNTUrMSwgc3RkaW4pOwoJCQkJbnRva3MgPSBzX3Rva2VuaXplKGlucHV0LCB0b2tlbnMsIDEsIGRlbGltcyk7CgkJCX0gd2hpbGUgKCBudG9rcyA8IDEpOwoJCQlhID0gYXRvZihpbnB1dCk7CgoJCQkvLyDOtM65zqzOss6xz4POvM6xIM+Ezr/PhSBiCgkJCWRvIHsKCQkJCXByaW50ZigiXHRlbnRlciBiOiAiKTsKCQkJCWZnZXRzKGlucHV0LCAyNTUrMSwgc3RkaW4pOwoJCQkJbnRva3MgPSBzX3Rva2VuaXplKGlucHV0LCB0b2tlbnMsIDEsIGRlbGltcyk7CgkJCX0gd2hpbGUgKCBudG9rcyA8IDEpOwoJCQliID0gYXRvZihpbnB1dCk7CgoJCQkvLyDPg8+HzrXOtM65zrHPg868z4zPgiDPhM6/z4UgzrPPgc6xz4bOrs68zrHPhM6/z4Igz4TOt8+CIM+Dz4XOvc6xz4HPhM63z4POt8+CCgkJCWNsZWFyX3NjcmVlbiggc2NyZWVuICk7CgkJCXBsb3RfZiggc2NyZWVuLCBhLCBiKTsKCQkJZHJhd19zY3JlZW4oICpzY3JlZW4gKTsKCgkJCXByaW50ZigiXHRGdW5jdGlvbjogeSA9ICUuMmYgKiB4ICsgJS4yZiAuLi4gIiwgYSwgYik7CgkJCVBBVVNFKCk7CgkJYnJlYWs7CgoJCWNhc2UgJ2wnOgkvLyBMSU5FCgkJCXB1dHMoIlxuXHRQbG90dGluZyBhIGxpbmUuLi4iKTsKCgkJCS8vIM60zrnOrM6yzrHPg868zrEgzrHPgc+HzrnOus6/z40gz4POt868zrXOr86/z4U6IHB0c3RhCgkJCWRvIHsKCQkJCXByaW50ZigiXHRlbnRlciAxc3QgcG9pbnQgKHgseSk6ICIpOwoJCQkJZmdldHMoaW5wdXQsIDI1NSsxLCBzdGRpbik7CgkJCQludG9rcyA9IHNfdG9rZW5pemUoaW5wdXQsIHRva2VucywgMiwgZGVsaW1zKTsKCQkJfSB3aGlsZSggbnRva3MgPCAyKTsKCQkJcHRzdGEueCA9IGF0b2koIHRva2Vuc1swXSApOwoJCQlwdHN0YS55ID0gYXRvaSggdG9rZW5zWzFdICk7CgoJCQkvLyDOtM65zqzOss6xz4POvM6xIM+EzrXOu865zrrOv8+NIM+DzrfOvM61zq/Ov8+FOiBwdGVuZAoJCQlkbyB7CgkJCXByaW50ZigiXHRlbnRlciAybmQgcG9pbnQgKHgseSk6ICIpOwoJCQkJZmdldHMoaW5wdXQsIDI1NSsxLCBzdGRpbik7CgkJCQludG9rcyA9IHNfdG9rZW5pemUoaW5wdXQsIHRva2VucywgMiwgZGVsaW1zKTsKCQkJfSB3aGlsZSggbnRva3MgPCAyKTsKCQkJcHRlbmQueCA9IGF0b2koIHRva2Vuc1swXSApOwoJCQlwdGVuZC55ID0gYXRvaSggdG9rZW5zWzFdICk7CgoJCQkvLyDPg8+HzrXOtM65zrHPg868z4zPgiDPhM63z4IgzrPPgc6xzrzOvM6uz4IKCQkJY2xlYXJfc2NyZWVuKCBzY3JlZW4gKTsKCQkJcGxvdF9saW5lKCBzY3JlZW4sIHB0c3RhLCBwdGVuZCApOwoJCQlkcmF3X3NjcmVlbiggKnNjcmVlbiApOwoKCQkJcHJpbnRmKAkiXHRMaW5lIGZyb20gKCVkLCVkKSB0byAoJWQsJWQpIC4uLiAiLAoJCQkJcHRzdGEueCwgcHRzdGEueSwgcHRlbmQueCwgcHRlbmQueSApOwoJCQlQQVVTRSgpOwoJCWJyZWFrOwoKCQljYXNlICdjJzoJLy8gQ0lSQ0xFCgkJCXB1dHMoIlxuXHRQbG90dGluZyBhIGNpcmNsZS4uLiIpOwoKCQkJLy8gzrTOuc6szrLOsc+DzrzOsSDOus6tzr3PhM+Bzr/PhTogcHRzdGEKCQkJZG8gewoJCQkJcHJpbnRmKCJcdGVudGVyIGNlbnRlciBwb2ludCAoeCx5KTogIik7CgkJCQlmZ2V0cyhpbnB1dCwgMjU1KzEsIHN0ZGluKTsKCQkJCW50b2tzID0gc190b2tlbml6ZShpbnB1dCwgdG9rZW5zLCAyLCBkZWxpbXMpOwoJCQl9IHdoaWxlKCBudG9rcyA8IDIpOwoJCQlwdHN0YS54ID0gYXRvaSggdG9rZW5zWzBdICk7CgkJCXB0c3RhLnkgPSBhdG9pKCB0b2tlbnNbMV0gKTsKCgkJCS8vIM60zrnOrM6yzrHPg868zrEgz4TOt8+CIM6xzrrPhM6vzr3Osc+COiByYWRpdXMKCQkJZG8gewoJCQkJcHJpbnRmKCJcdGVudGVyIHJhZGl1czogIik7CgkJCQlmZ2V0cyhpbnB1dCwgMjU1KzEsIHN0ZGluKTsKCQkJCW50b2tzID0gc190b2tlbml6ZShpbnB1dCwgdG9rZW5zLCAxLCBkZWxpbXMpOwoJCQl9IHdoaWxlICggbnRva3MgPCAxKTsKCQkJcmFkaXVzID0gYXRvaSggaW5wdXQgKTsKCgkJCS8vIM+Dz4fOtc60zrnOsc+DzrzPjM+CIM+EzrfPgiDOus+NzrrOu86/z4UKCQkJY2xlYXJfc2NyZWVuKCBzY3JlZW4gKTsKCQkJcGxvdF9jaXJjbGUoIHNjcmVlbiwgcHRzdGEsIHJhZGl1cyApOwoJCQlkcmF3X3NjcmVlbiggKnNjcmVlbiApOwoKCQkJcHJpbnRmKAkiXHRDaXJjbGUgd2l0aCBjZW50ZXIgKCVkLCVkKSAmIHJhZGl1cyAlZCAuLi4gIiwKCQkJCXB0c3RhLngsIHB0c3RhLnksIHJhZGl1cyApOwoJCQlQQVVTRSgpOwoKCQlicmVhazsKCgkJY2FzZSAnZSc6CS8vIEVMTElQU0UKCQkJcHV0cygiXG5cdFBsb3R0aW5nIGFuIGVsbGlwc2UuLi4iKTsKCgkJCS8vIM60zrnOrM6yzrHPg868zrEgz4DOrM69z4ktzrHPgc65z4PPhM61z4HOrs+CIM6zz4nOvc6vzrHPgjogcHRzdGEKCQkJZG8gewoJCQkJcHJpbnRmKCJcdGVudGVyIHRvcC1sZWZ0ICAgICBwb2ludCAoeCx5KTogIik7CgkJCQlmZ2V0cyhpbnB1dCwgMjU1KzEsIHN0ZGluKTsKCQkJCW50b2tzID0gc190b2tlbml6ZShpbnB1dCwgdG9rZW5zLCAyLCBkZWxpbXMpOwoJCQl9IHdoaWxlKCBudG9rcyA8IDIpOwoJCQlwdHN0YS54ID0gYXRvaSggdG9rZW5zWzBdICk7CgkJCXB0c3RhLnkgPSBhdG9pKCB0b2tlbnNbMV0gKTsKCgkJCS8vIM60zrnOrM6yzrHPg868zrEgzrrOrM+Ez4ktzrTOtc6+zrnOrM+CIM6zz4nOvc6vzrHPgjogcHRlbmQKCQkJZG8gewoJCQkJcHJpbnRmKCJcdGVudGVyIGJvdHRvbS1yaWdodCBwb2ludCAoeCx5KTogIik7CgkJCQlmZ2V0cyhpbnB1dCwgMjU1KzEsIHN0ZGluKTsKCQkJCW50b2tzID0gc190b2tlbml6ZShpbnB1dCwgdG9rZW5zLCAyLCBkZWxpbXMpOwoJCQl9IHdoaWxlKCBudG9rcyA8IDIpOwoJCQlwdGVuZC54ID0gYXRvaSggdG9rZW5zWzBdICk7CgkJCXB0ZW5kLnkgPSBhdG9pKCB0b2tlbnNbMV0gKTsKCgkJCS8vIM+Dz4fOtc60zrnOsc+DzrzPjM+CIM+EzrfPgiDOrc67zrvOtc65z4jOt8+CCgkJCWNsZWFyX3NjcmVlbiggc2NyZWVuICk7CgkJCXBsb3RfZWxsaXBzZSggc2NyZWVuLCBwdHN0YSwgcHRlbmQgKTsKCQkJZHJhd19zY3JlZW4oICpzY3JlZW4gKTsKCgkJCXByaW50ZigJIlx0RWxsaXBzZSBmcm9tICglZCwlZCkgdG8gKCVkLCVkKSAuLi4gIiwKCQkJCXB0c3RhLngsIHB0c3RhLnksIHB0ZW5kLngsIHB0ZW5kLnkgKTsKCQkJUEFVU0UoKTsKCQlicmVhazsKCgkJY2FzZSAnXG4nOiAvLyBlbXB0eSBzZWxlY3Rpb24KCQlicmVhazsKCgkJZGVmYXVsdDoKCQkJcHV0cygiXHQqKiogZXJyb3I6IHVucmVjb2duaXNlZCBzZWxlY3Rpb24sIHRyeSBhZ2FpbiIpOwoJCWJyZWFrOwoJfQoKCXJldHVybjsKfQovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KaW50IG1haW4oIHZvaWQgKQp7CgljaGFyIGlucHV0WzI1NSsxXSA9ICIiOwoJU2NyZWVuIHNjcmVlbiA9IHsgLm5jb2xzID0gTUFYQ09MUywgLm5yb3dzID0gTUFYUk9XUyB9OwoKCWRvIHsKCQlzaG93X21lbnUoKTsKCQlmZ2V0cyhpbnB1dCwgMjU1KzEsIHN0ZGluKTsKCQlpZiAoIHRvbG93ZXIoKmlucHV0KSAhPSAneCcgKQoJCQlleGVjX3NlbGVjdGVkKCAmc2NyZWVuLCAqaW5wdXQgKTsKCX0gd2hpbGUgKCAqaW5wdXQgIT0gJ3gnICk7CgoJZXhpdCggRVhJVF9TVUNDRVNTICk7Cn0K