/**
 * 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;
}
