/**
* Solution to WF/2017/A/Airport.
* Find the longest line contained inside a polygon, allowing for lines touching
* vertices or the boundary.
*
* This solution uses Per's solution sketch approach to determine for any pair
* of vertices (a, b),
* (a) determine if a--b crosses the polygon boundary
* (b) extend the half lines outward until the polygon boundary is crossed.
*
* The results of certain boolean predicates must be computed exactly,
* particularly the test whether the intersection point lies on the line
* or does not.
*
*/
#include <bits/stdc++.h>
using namespace std;
//typedef long coord_t; // overflows
//typedef __int128 coord_t; // passes
//typedef long double coord_t; // passes
typedef double coord_t; // passes on Kattis, too.
typedef std::tuple<coord_t, coord_t, coord_t> Hom;
Hom makePoint(coord_t x, coord_t y) {
return Hom { x, y, 1 };
}
static coord_t
dot(const Hom &v, const Hom &w) {
coord_t v0, v1, v2, w0, w1, w2;
tie (v0, v1, v2) = v;
tie (w0, w1, w2) = w;
return v0*w0 + v1*w1 + v2*w2;
}
static Hom
crossproduct(const Hom &u, const Hom &v) {
coord_t v0, v1, v2, u0, u1, u2;
tie (u0, u1, u2) = u;
tie (v0, v1, v2) = v;
return Hom { u1*v2 - u2*v1, u2*v0 - u0*v2, u0*v1 - u1*v0 };
}
static Hom connect(const Hom &p0, const Hom &p1) { return crossproduct(p0, p1); }
static Hom intersect(const Hom &p0, const Hom &p1) { return crossproduct(p0, p1); }
static coord_t
signum(coord_t x) {
return x > 0 ? 1 : x == 0 ? 0 : -1;
// return (x > 0) - (x < 0);
}
static coord_t
isCCW(const Hom &p0, const Hom &p1, const Hom &p2) {
const Hom l0 = connect(p0, p1);
return dot(l0, p2);
}
static pair<double, double>
getEuclidean(Hom &p) {
coord_t x, y, w;
tie (x, y, w) = p;
return make_pair(x/(double)w, y/(double)w);
}
static coord_t
isleft(const Hom &p0, const Hom &p1) {
coord_t x0, y0, w0, x1, y1, w1;
tie (x0, y0, w0) = p0;
tie (x1, y1, w1) = p1;
// see Ghali 16.6
return ((x0 * w1 - w0 * x1) * signum(w0 * w1));
}
static coord_t
isbelow(const Hom &p0, const Hom &p1) {
coord_t x0, y0, w0, x1, y1, w1;
tie (x0, y0, w0) = p0;
tie (x1, y1, w1) = p1;
return ((y0 * w1 - w0 * y1) * signum(w0 * w1));
}
static coord_t
isleftorbelow(const Hom &p0, const Hom &p1) {
coord_t c = isleft(p0, p1);
if (c == 0)
c = isbelow(p0, p1);
return signum(c);
}
// is p in the rectangle spanned by q0-q1?
static bool
inBounds(const Hom &q0, const Hom& q1, const Hom &p)
{
coord_t a = isleft(q0, p);
coord_t b = isleft(p, q1);
if (a * b < 0)
return false;
a = isbelow(q0, p);
b = isbelow(p, q1);
return a * b >= 0;
}
static bool
pointOnSegment(const Hom& p0, const Hom& p1, const Hom& q) {
return inBounds(p0, p1, q) && isCCW(p0, p1, q) == 0;
}
// standard winding number polygon contains
static bool
contains_wn(const vector<Hom> &points, const Hom &q) {
int wn = 0;
const int n = points.size();
for (int i = 0; i < n; i++) {
const Hom &p = points[i];
const Hom &pn = points[(i+1)%n];
if (isbelow(p, q) <= 0) {
if (isbelow(pn, q) > 0)
if (isCCW(p, pn, q) > 0)
wn++;
} else {
if (isbelow(pn, q) <= 0)
if (isCCW(p, pn, q) < 0)
wn--;
}
}
return wn != 0;
}
// contains: contains or on boundary.
static bool
contains(const vector<Hom> &points, Hom &q) {
if (contains_wn(points, q))
return true;
const int n = points.size();
for (int i = 0; i < n; i++) {
const Hom &p = points[i];
const Hom &pn = points[(i+1)%n];
if (pointOnSegment(p, pn, q))
return true;
}
return false;
}
enum IntersectType { INTERSECT_AT_VERTEX, INTERSECT_AT_POLYGON_BOUNDARY };
static Hom
midpoint(const Hom &p0, const Hom &p1)
{
coord_t x0, y0, w0, x1, y1, w1;
tie (x0, y0, w0) = p0;
tie (x1, y1, w1) = p1;
return Hom { x0*w1+x1*w0, y0*w1+y1*w0, 2*w0*w1 };
}
struct sort_by_left_or_below
{
inline bool operator() (const pair<IntersectType, Hom>& a, const pair<IntersectType, Hom>& b)
{
return isleftorbelow(get<1>(a), get<1>(b)) < 0;
}
};
static Hom
extend_half_line(vector<Hom> &p, vector<pair<IntersectType, Hom>> &relevantPoints, int i, int d, int bound) {
while (i + d != bound) {
IntersectType kind1, kind2;
Hom p1, p2;
tie(kind1, p1) = relevantPoints[i];
tie(kind2, p2) = relevantPoints[i+d];
Hom mp = midpoint(p1, p2);
if (!contains(p, mp))
return p1;
if (kind2 != INTERSECT_AT_VERTEX)
return p2;
i += d;
}
return get<1>(relevantPoints[i]);
}
static double
landingstrip(vector<Hom> &p, int a, int b)
{
const int n = p.size();
vector<pair<IntersectType, Hom>> relevantPoints;
for (int i = 0; i < n; i++) {
if (pointOnSegment(p[a], p[b], p[i])) {
relevantPoints.push_back(make_pair(INTERSECT_AT_VERTEX, p[i]));
} else
if (pointOnSegment(p[a], p[b], p[(i+1)%n])) {
relevantPoints.push_back(make_pair(INTERSECT_AT_VERTEX, p[(i+1)%n]));
} else {
Hom ls = connect(p[i], p[(i+1)%n]); // 42 bits
Hom ab = connect(p[a], p[b]); // 42 bits
Hom is = intersect(ab, ls); // 84 bits
coord_t x, y, w;
tie(x, y, w) = is;
if (w == 0) { // parallel or coinciding
if (x == 0 && y == 0) { // coinciding
relevantPoints.push_back(make_pair(INTERSECT_AT_VERTEX, p[i]));
relevantPoints.push_back(make_pair(INTERSECT_AT_VERTEX, p[(i+1)%n]));
} // ignore if parallel
} else {
if (inBounds(p[i], p[(i+1)%n], is)) {
relevantPoints.push_back(make_pair(INTERSECT_AT_POLYGON_BOUNDARY, is));
}
}
}
}
std::sort(relevantPoints.begin(), relevantPoints.end(), sort_by_left_or_below());
int ai = std::distance(relevantPoints.begin(), std::find(relevantPoints.begin(), relevantPoints.end(), make_pair(INTERSECT_AT_VERTEX, p[a])));
int bi = std::distance(relevantPoints.begin(), std::find(relevantPoints.begin(), relevantPoints.end(), make_pair(INTERSECT_AT_VERTEX, p[b])));
int di = signum(bi-ai);
// check that all points in [a, b] are inside polygon
for (int i = ai; i != bi; i += di) {
enum IntersectType kind;
Hom rp;
tie(kind, rp) = relevantPoints[i+di];
if (kind != INTERSECT_AT_VERTEX)
return -1;
Hom mp = midpoint(get<1>(relevantPoints[i]), rp); // 42 bits since rp and relevantPoints[i] are vertices
if (!contains(p, mp))
return -1;
}
Hom pmin = extend_half_line(p, relevantPoints, ai, -di, -di == -1 ? -1 : relevantPoints.size());
Hom pmax = extend_half_line(p, relevantPoints, bi, di, di == -1 ? -1 : relevantPoints.size());
// now report distance as double
double xmin, ymin, xmax, ymax;
tie(xmin, ymin) = getEuclidean(pmin);
tie(xmax, ymax) = getEuclidean(pmax);
return sqrt((double)(xmax-xmin)*(xmax-xmin) + (double)(ymax-ymin)*(ymax-ymin));
}
int main(int ac, char *av[])
{
int n;
cin >> n;
vector<Hom> p;
for (int i = 0; i < n; i++) {
long x, y;
cin >> x >> y;
p.push_back(makePoint(x, y));
}
double answer = -1.0;
for (int a = 0; a < n; a++)
for (int b = a+1; b < n; b++)
answer = max(answer, landingstrip(p, a, b));
cout.precision(17);
cout << answer << endl;
}
LyoqCiAqIFNvbHV0aW9uIHRvIFdGLzIwMTcvQS9BaXJwb3J0LgogKiBGaW5kIHRoZSBsb25nZXN0IGxpbmUgY29udGFpbmVkIGluc2lkZSBhIHBvbHlnb24sIGFsbG93aW5nIGZvciBsaW5lcyB0b3VjaGluZwogKiB2ZXJ0aWNlcyBvciB0aGUgYm91bmRhcnkuCiAqCiAqIFRoaXMgc29sdXRpb24gdXNlcyBQZXIncyBzb2x1dGlvbiBza2V0Y2ggYXBwcm9hY2ggdG8gZGV0ZXJtaW5lIGZvciBhbnkgcGFpciAKICogb2YgdmVydGljZXMgKGEsIGIpLCAKICogKGEpIGRldGVybWluZSBpZiBhLS1iIGNyb3NzZXMgdGhlIHBvbHlnb24gYm91bmRhcnkKICogKGIpIGV4dGVuZCB0aGUgaGFsZiBsaW5lcyBvdXR3YXJkIHVudGlsIHRoZSBwb2x5Z29uIGJvdW5kYXJ5IGlzIGNyb3NzZWQuCiAqCiAqIFRoZSByZXN1bHRzIG9mIGNlcnRhaW4gYm9vbGVhbiBwcmVkaWNhdGVzIG11c3QgYmUgY29tcHV0ZWQgZXhhY3RseSwgCiAqIHBhcnRpY3VsYXJseSB0aGUgdGVzdCB3aGV0aGVyIHRoZSBpbnRlcnNlY3Rpb24gcG9pbnQgbGllcyBvbiB0aGUgbGluZQogKiBvciBkb2VzIG5vdC4KICoKICovCiNpbmNsdWRlIDxiaXRzL3N0ZGMrKy5oPgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKLy90eXBlZGVmIGxvbmcgY29vcmRfdDsgLy8gb3ZlcmZsb3dzCi8vdHlwZWRlZiBfX2ludDEyOCBjb29yZF90OyAgIC8vIHBhc3NlcwovL3R5cGVkZWYgbG9uZyBkb3VibGUgY29vcmRfdDsgICAvLyBwYXNzZXMKdHlwZWRlZiBkb3VibGUgY29vcmRfdDsgLy8gcGFzc2VzIG9uIEthdHRpcywgdG9vLgoKdHlwZWRlZiBzdGQ6OnR1cGxlPGNvb3JkX3QsIGNvb3JkX3QsIGNvb3JkX3Q+IEhvbTsKCkhvbSBtYWtlUG9pbnQoY29vcmRfdCB4LCBjb29yZF90IHkpIHsKICAgIHJldHVybiBIb20geyB4LCB5LCAxIH07Cn0KCnN0YXRpYyBjb29yZF90IApkb3QoY29uc3QgSG9tICZ2LCBjb25zdCBIb20gJncpIHsKICAgIGNvb3JkX3QgdjAsIHYxLCB2MiwgdzAsIHcxLCB3MjsKICAgIHRpZSAodjAsIHYxLCB2MikgPSB2OwogICAgdGllICh3MCwgdzEsIHcyKSA9IHc7CiAgICByZXR1cm4gdjAqdzAgKyB2MSp3MSArIHYyKncyOwp9CgpzdGF0aWMgSG9tIApjcm9zc3Byb2R1Y3QoY29uc3QgSG9tICZ1LCBjb25zdCBIb20gJnYpIHsKICAgIGNvb3JkX3QgdjAsIHYxLCB2MiwgdTAsIHUxLCB1MjsKICAgIHRpZSAodTAsIHUxLCB1MikgPSB1OwogICAgdGllICh2MCwgdjEsIHYyKSA9IHY7CiAgICByZXR1cm4gSG9tIHsgdTEqdjIgLSB1Mip2MSwgdTIqdjAgLSB1MCp2MiwgdTAqdjEgLSB1MSp2MCB9Owp9CgpzdGF0aWMgSG9tIGNvbm5lY3QoY29uc3QgSG9tICZwMCwgY29uc3QgSG9tICZwMSkgeyByZXR1cm4gY3Jvc3Nwcm9kdWN0KHAwLCBwMSk7IH0KCnN0YXRpYyBIb20gaW50ZXJzZWN0KGNvbnN0IEhvbSAmcDAsIGNvbnN0IEhvbSAmcDEpIHsgcmV0dXJuIGNyb3NzcHJvZHVjdChwMCwgcDEpOyB9CgpzdGF0aWMgY29vcmRfdApzaWdudW0oY29vcmRfdCB4KSB7CiAgICByZXR1cm4geCA+IDAgPyAxIDogeCA9PSAwID8gMCA6IC0xOwogICAgLy8gcmV0dXJuICh4ID4gMCkgLSAoeCA8IDApOwp9CgpzdGF0aWMgY29vcmRfdCAKaXNDQ1coY29uc3QgSG9tICZwMCwgY29uc3QgSG9tICZwMSwgY29uc3QgSG9tICZwMikgewogICAgY29uc3QgSG9tIGwwID0gY29ubmVjdChwMCwgcDEpOwogICAgcmV0dXJuIGRvdChsMCwgcDIpOwp9CgpzdGF0aWMgcGFpcjxkb3VibGUsIGRvdWJsZT4gCmdldEV1Y2xpZGVhbihIb20gJnApIHsKICAgIGNvb3JkX3QgeCwgeSwgdzsKICAgIHRpZSAoeCwgeSwgdykgPSBwOwogICAgcmV0dXJuIG1ha2VfcGFpcih4Lyhkb3VibGUpdywgeS8oZG91YmxlKXcpOwp9CgpzdGF0aWMgY29vcmRfdAppc2xlZnQoY29uc3QgSG9tICZwMCwgY29uc3QgSG9tICZwMSkgewogICAgY29vcmRfdCB4MCwgeTAsIHcwLCB4MSwgeTEsIHcxOwogICAgdGllICh4MCwgeTAsIHcwKSA9IHAwOwogICAgdGllICh4MSwgeTEsIHcxKSA9IHAxOwogICAgLy8gc2VlIEdoYWxpIDE2LjYKICAgIHJldHVybiAoKHgwICogdzEgLSB3MCAqIHgxKSAqIHNpZ251bSh3MCAqIHcxKSk7Cn0KCnN0YXRpYyBjb29yZF90CmlzYmVsb3coY29uc3QgSG9tICZwMCwgY29uc3QgSG9tICZwMSkgewogICAgY29vcmRfdCB4MCwgeTAsIHcwLCB4MSwgeTEsIHcxOwogICAgdGllICh4MCwgeTAsIHcwKSA9IHAwOwogICAgdGllICh4MSwgeTEsIHcxKSA9IHAxOwogICAgcmV0dXJuICgoeTAgKiB3MSAtIHcwICogeTEpICogc2lnbnVtKHcwICogdzEpKTsKfQoKc3RhdGljIGNvb3JkX3QKaXNsZWZ0b3JiZWxvdyhjb25zdCBIb20gJnAwLCBjb25zdCBIb20gJnAxKSB7CiAgICBjb29yZF90IGMgPSBpc2xlZnQocDAsIHAxKTsKICAgIGlmIChjID09IDApCiAgICAgICAgYyA9IGlzYmVsb3cocDAsIHAxKTsKICAgIHJldHVybiBzaWdudW0oYyk7Cn0KCi8vIGlzIHAgaW4gdGhlIHJlY3RhbmdsZSBzcGFubmVkIGJ5IHEwLXExPwpzdGF0aWMgYm9vbAppbkJvdW5kcyhjb25zdCBIb20gJnEwLCBjb25zdCBIb20mIHExLCBjb25zdCBIb20gJnApCnsKICAgIGNvb3JkX3QgYSA9IGlzbGVmdChxMCwgcCk7CiAgICBjb29yZF90IGIgPSBpc2xlZnQocCwgcTEpOwogICAgaWYgKGEgKiBiIDwgMCkKICAgICAgICByZXR1cm4gZmFsc2U7CgogICAgYSA9IGlzYmVsb3cocTAsIHApOwogICAgYiA9IGlzYmVsb3cocCwgcTEpOwogICAgcmV0dXJuIGEgKiBiID49IDA7Cn0KCnN0YXRpYyBib29sCnBvaW50T25TZWdtZW50KGNvbnN0IEhvbSYgcDAsIGNvbnN0IEhvbSYgcDEsIGNvbnN0IEhvbSYgcSkgewogICAgcmV0dXJuIGluQm91bmRzKHAwLCBwMSwgcSkgJiYgaXNDQ1cocDAsIHAxLCBxKSA9PSAwOwp9CiAKLy8gc3RhbmRhcmQgd2luZGluZyBudW1iZXIgcG9seWdvbiBjb250YWlucwpzdGF0aWMgYm9vbApjb250YWluc193bihjb25zdCB2ZWN0b3I8SG9tPiAmcG9pbnRzLCBjb25zdCBIb20gJnEpIHsKICAgIGludCB3biA9IDA7CgogICAgY29uc3QgaW50IG4gPSBwb2ludHMuc2l6ZSgpOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBuOyBpKyspIHsKICAgICAgICBjb25zdCBIb20gJnAgPSBwb2ludHNbaV07CiAgICAgICAgY29uc3QgSG9tICZwbiA9IHBvaW50c1soaSsxKSVuXTsKICAgICAgICBpZiAoaXNiZWxvdyhwLCBxKSA8PSAwKSB7CiAgICAgICAgICAgIGlmIChpc2JlbG93KHBuLCBxKSA+IDApICAgCiAgICAgICAgICAgICAgICBpZiAoaXNDQ1cocCwgcG4sIHEpID4gMCkKICAgICAgICAgICAgICAgICAgICAgd24rKzsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBpZiAoaXNiZWxvdyhwbiwgcSkgPD0gMCkKICAgICAgICAgICAgICAgIGlmIChpc0NDVyhwLCBwbiwgcSkgPCAwKQogICAgICAgICAgICAgICAgICAgICB3bi0tOwogICAgICAgIH0KICAgIH0KCiAgICByZXR1cm4gd24gIT0gMDsKfQoKLy8gY29udGFpbnM6IGNvbnRhaW5zIG9yIG9uIGJvdW5kYXJ5LgpzdGF0aWMgYm9vbApjb250YWlucyhjb25zdCB2ZWN0b3I8SG9tPiAmcG9pbnRzLCBIb20gJnEpIHsKICAgIGlmIChjb250YWluc193bihwb2ludHMsIHEpKQogICAgICAgIHJldHVybiB0cnVlOwoKICAgIGNvbnN0IGludCBuID0gcG9pbnRzLnNpemUoKTsKICAgIGZvciAoaW50IGkgPSAwOyBpIDwgbjsgaSsrKSB7CiAgICAgICAgY29uc3QgSG9tICZwID0gcG9pbnRzW2ldOwogICAgICAgIGNvbnN0IEhvbSAmcG4gPSBwb2ludHNbKGkrMSklbl07CiAgICAgICAgaWYgKHBvaW50T25TZWdtZW50KHAsIHBuLCBxKSkKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICB9CiAgICByZXR1cm4gZmFsc2U7Cn0KCmVudW0gSW50ZXJzZWN0VHlwZSB7IElOVEVSU0VDVF9BVF9WRVJURVgsIElOVEVSU0VDVF9BVF9QT0xZR09OX0JPVU5EQVJZIH07CgpzdGF0aWMgSG9tCm1pZHBvaW50KGNvbnN0IEhvbSAmcDAsIGNvbnN0IEhvbSAmcDEpCnsKICAgIGNvb3JkX3QgeDAsIHkwLCB3MCwgeDEsIHkxLCB3MTsKICAgIHRpZSAoeDAsIHkwLCB3MCkgPSBwMDsKICAgIHRpZSAoeDEsIHkxLCB3MSkgPSBwMTsKICAgIHJldHVybiBIb20geyB4MCp3MSt4MSp3MCwgeTAqdzEreTEqdzAsIDIqdzAqdzEgfTsKfQoKc3RydWN0IHNvcnRfYnlfbGVmdF9vcl9iZWxvdwp7CiAgICBpbmxpbmUgYm9vbCBvcGVyYXRvcigpIChjb25zdCBwYWlyPEludGVyc2VjdFR5cGUsIEhvbT4mIGEsIGNvbnN0IHBhaXI8SW50ZXJzZWN0VHlwZSwgSG9tPiYgYikKICAgIHsKICAgICAgICByZXR1cm4gaXNsZWZ0b3JiZWxvdyhnZXQ8MT4oYSksIGdldDwxPihiKSkgPCAwOwogICAgfQp9OwoKc3RhdGljIEhvbQpleHRlbmRfaGFsZl9saW5lKHZlY3RvcjxIb20+ICZwLCB2ZWN0b3I8cGFpcjxJbnRlcnNlY3RUeXBlLCBIb20+PiAmcmVsZXZhbnRQb2ludHMsIGludCBpLCBpbnQgZCwgaW50IGJvdW5kKSB7CiAgICB3aGlsZSAoaSArIGQgIT0gYm91bmQpIHsKICAgICAgICBJbnRlcnNlY3RUeXBlIGtpbmQxLCBraW5kMjsKICAgICAgICBIb20gcDEsIHAyOwogICAgICAgIHRpZShraW5kMSwgcDEpID0gcmVsZXZhbnRQb2ludHNbaV07CiAgICAgICAgdGllKGtpbmQyLCBwMikgPSByZWxldmFudFBvaW50c1tpK2RdOwoKICAgICAgICBIb20gbXAgPSBtaWRwb2ludChwMSwgcDIpOwogICAgICAgIGlmICghY29udGFpbnMocCwgbXApKQogICAgICAgICAgICByZXR1cm4gcDE7CgogICAgICAgIGlmIChraW5kMiAhPSBJTlRFUlNFQ1RfQVRfVkVSVEVYKQogICAgICAgICAgICByZXR1cm4gcDI7CgogICAgICAgIGkgKz0gZDsKICAgIH0KCiAgICByZXR1cm4gZ2V0PDE+KHJlbGV2YW50UG9pbnRzW2ldKTsKfQoKc3RhdGljIGRvdWJsZQpsYW5kaW5nc3RyaXAodmVjdG9yPEhvbT4gJnAsIGludCBhLCBpbnQgYikKewogICAgY29uc3QgaW50IG4gPSBwLnNpemUoKTsKICAgIHZlY3RvcjxwYWlyPEludGVyc2VjdFR5cGUsIEhvbT4+IHJlbGV2YW50UG9pbnRzOwoKICAgIGZvciAoaW50IGkgPSAwOyBpIDwgbjsgaSsrKSB7CiAgICAgICAgaWYgKHBvaW50T25TZWdtZW50KHBbYV0sIHBbYl0sIHBbaV0pKSB7CiAgICAgICAgICAgIHJlbGV2YW50UG9pbnRzLnB1c2hfYmFjayhtYWtlX3BhaXIoSU5URVJTRUNUX0FUX1ZFUlRFWCwgcFtpXSkpOwogICAgICAgIH0gZWxzZQogICAgICAgIGlmIChwb2ludE9uU2VnbWVudChwW2FdLCBwW2JdLCBwWyhpKzEpJW5dKSkgewogICAgICAgICAgICByZWxldmFudFBvaW50cy5wdXNoX2JhY2sobWFrZV9wYWlyKElOVEVSU0VDVF9BVF9WRVJURVgsIHBbKGkrMSklbl0pKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBIb20gbHMgPSBjb25uZWN0KHBbaV0sIHBbKGkrMSklbl0pOyAvLyA0MiBiaXRzCiAgICAgICAgICAgIEhvbSBhYiA9IGNvbm5lY3QocFthXSwgcFtiXSk7ICAgICAgIC8vIDQyIGJpdHMKICAgICAgICAgICAgSG9tIGlzID0gaW50ZXJzZWN0KGFiLCBscyk7ICAgICAgICAgLy8gODQgYml0cwogICAgICAgICAgICBjb29yZF90IHgsIHksIHc7CiAgICAgICAgICAgIHRpZSh4LCB5LCB3KSA9IGlzOwogICAgICAgICAgICBpZiAodyA9PSAwKSB7ICAgLy8gcGFyYWxsZWwgb3IgY29pbmNpZGluZwogICAgICAgICAgICAgICAgaWYgKHggPT0gMCAmJiB5ID09IDApIHsgLy8gY29pbmNpZGluZwogICAgICAgICAgICAgICAgICAgIHJlbGV2YW50UG9pbnRzLnB1c2hfYmFjayhtYWtlX3BhaXIoSU5URVJTRUNUX0FUX1ZFUlRFWCwgcFtpXSkpOwogICAgICAgICAgICAgICAgICAgIHJlbGV2YW50UG9pbnRzLnB1c2hfYmFjayhtYWtlX3BhaXIoSU5URVJTRUNUX0FUX1ZFUlRFWCwgcFsoaSsxKSVuXSkpOwogICAgICAgICAgICAgICAgfSAgIC8vIGlnbm9yZSBpZiBwYXJhbGxlbAogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgaWYgKGluQm91bmRzKHBbaV0sIHBbKGkrMSklbl0sIGlzKSkgewogICAgICAgICAgICAgICAgICAgIHJlbGV2YW50UG9pbnRzLnB1c2hfYmFjayhtYWtlX3BhaXIoSU5URVJTRUNUX0FUX1BPTFlHT05fQk9VTkRBUlksIGlzKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgc3RkOjpzb3J0KHJlbGV2YW50UG9pbnRzLmJlZ2luKCksIHJlbGV2YW50UG9pbnRzLmVuZCgpLCBzb3J0X2J5X2xlZnRfb3JfYmVsb3coKSk7CgogICAgaW50IGFpID0gc3RkOjpkaXN0YW5jZShyZWxldmFudFBvaW50cy5iZWdpbigpLCBzdGQ6OmZpbmQocmVsZXZhbnRQb2ludHMuYmVnaW4oKSwgcmVsZXZhbnRQb2ludHMuZW5kKCksIG1ha2VfcGFpcihJTlRFUlNFQ1RfQVRfVkVSVEVYLCBwW2FdKSkpOwogICAgaW50IGJpID0gc3RkOjpkaXN0YW5jZShyZWxldmFudFBvaW50cy5iZWdpbigpLCBzdGQ6OmZpbmQocmVsZXZhbnRQb2ludHMuYmVnaW4oKSwgcmVsZXZhbnRQb2ludHMuZW5kKCksIG1ha2VfcGFpcihJTlRFUlNFQ1RfQVRfVkVSVEVYLCBwW2JdKSkpOwogICAgaW50IGRpID0gc2lnbnVtKGJpLWFpKTsKCiAgICAvLyBjaGVjayB0aGF0IGFsbCBwb2ludHMgaW4gW2EsIGJdIGFyZSBpbnNpZGUgcG9seWdvbgogICAgZm9yIChpbnQgaSA9IGFpOyBpICE9IGJpOyBpICs9IGRpKSB7CiAgICAgICAgZW51bSBJbnRlcnNlY3RUeXBlIGtpbmQ7CiAgICAgICAgSG9tIHJwOwogICAgICAgIHRpZShraW5kLCBycCkgPSByZWxldmFudFBvaW50c1tpK2RpXTsKCiAgICAgICAgaWYgKGtpbmQgIT0gSU5URVJTRUNUX0FUX1ZFUlRFWCkKICAgICAgICAgICAgcmV0dXJuIC0xOwoKICAgICAgICBIb20gbXAgPSBtaWRwb2ludChnZXQ8MT4ocmVsZXZhbnRQb2ludHNbaV0pLCBycCk7ICAgLy8gNDIgYml0cyBzaW5jZSBycCBhbmQgcmVsZXZhbnRQb2ludHNbaV0gYXJlIHZlcnRpY2VzCiAgICAgICAgaWYgKCFjb250YWlucyhwLCBtcCkpCiAgICAgICAgICAgIHJldHVybiAtMTsKICAgIH0KCiAgICBIb20gcG1pbiA9IGV4dGVuZF9oYWxmX2xpbmUocCwgcmVsZXZhbnRQb2ludHMsIGFpLCAtZGksIC1kaSA9PSAtMSA/IC0xIDogcmVsZXZhbnRQb2ludHMuc2l6ZSgpKTsKICAgIEhvbSBwbWF4ID0gZXh0ZW5kX2hhbGZfbGluZShwLCByZWxldmFudFBvaW50cywgYmksICBkaSwgIGRpID09IC0xID8gLTEgOiByZWxldmFudFBvaW50cy5zaXplKCkpOwoKICAgIC8vIG5vdyByZXBvcnQgZGlzdGFuY2UgYXMgZG91YmxlCiAgICBkb3VibGUgeG1pbiwgeW1pbiwgeG1heCwgeW1heDsKICAgIHRpZSh4bWluLCB5bWluKSA9IGdldEV1Y2xpZGVhbihwbWluKTsKICAgIHRpZSh4bWF4LCB5bWF4KSA9IGdldEV1Y2xpZGVhbihwbWF4KTsKCiAgICByZXR1cm4gc3FydCgoZG91YmxlKSh4bWF4LXhtaW4pKih4bWF4LXhtaW4pICsgKGRvdWJsZSkoeW1heC15bWluKSooeW1heC15bWluKSk7Cn0KCmludCBtYWluKGludCBhYywgY2hhciAqYXZbXSkKewogICAgaW50IG47CiAgICBjaW4gPj4gbjsKICAgIHZlY3RvcjxIb20+IHA7CiAgICBmb3IgKGludCBpID0gMDsgaSA8IG47IGkrKykgewogICAgICAgIGxvbmcgeCwgeTsKICAgICAgICBjaW4gPj4geCA+PiB5OwogICAgICAgIHAucHVzaF9iYWNrKG1ha2VQb2ludCh4LCB5KSk7CiAgICB9CiAgICBkb3VibGUgYW5zd2VyID0gLTEuMDsKICAgIGZvciAoaW50IGEgPSAwOyBhIDwgbjsgYSsrKQogICAgICAgIGZvciAoaW50IGIgPSBhKzE7IGIgPCBuOyBiKyspCiAgICAgICAgICAgIGFuc3dlciA9IG1heChhbnN3ZXIsIGxhbmRpbmdzdHJpcChwLCBhLCBiKSk7CgogICAgY291dC5wcmVjaXNpb24oMTcpOwogICAgY291dCA8PCBhbnN3ZXIgPDwgZW5kbDsKfQo=