Problem:
Diameter of a Convex Polygon
Find the diameter of a convex polygon
g
. In other words, find a pair of points that have maximum distance between them.
Input
n
x
1
y
1
x
2
y
2
:
x
n
y
n
The first integer
n
is the number of points in
g
.
In the following lines, the coordinate of the
i
-th point
p
i
is given by two real numbers
x
i
and
y
i
. The coordinates of points are given in the order of counter-clockwise visit of them. Each value is a real number with at most 6 digits after the decimal point.
Output
Print the diameter of
g
in a line. The output values should be in a decimal fraction with an error less than 0.000001.
Constraints
3 ≤
n
≤ 80000
-100 ≤
x
i
,
y
i
≤ 100
No point in the
g
will occur more than once.
Sample Input 1
3
0.0 0.0
4.0 0.0
2.0 2.0
Sample Output 1
4.00
Sample Input 2
4
0.0 0.0
1.0 0.0
1.0 1.0
0.0 1.0
Sample Output 2
1.414213562373

Submitted solutions:

code_snippet: #include <bits/stdc++.h>

#define debug(a) cout << #a << " = " << (a) << ' '

using namespace std;

#define db long double
// #define db long long

const db EPS = 1e-9;

#define cross(p1, p2, p3) (p2 - p1).det(p3 - p1)
#define crossop(p1, p2, p3) sign(cross(p1, p2, p3))

int sign(db a) { return a < -EPS ? -1 : a > EPS; }
int cmp(db a, db b) { return sign(a - b); }

struct P {
    db x, y;
    P() {}
    P(db _x, db _y) : x(_x), y(_y) {}

    P operator +(P p) { return {x + p.x, y + p.y}; }
    P operator -(P p) { return {x - p.x, y - p.y}; }
    P operator *(db d) { return {x * d, y * d}; }
    P operator /(db d) { return {x / d, y / d}; }

    bool operator < (P p) const {
        int c = cmp(x, p.x);
        if (c) return c == -1;
        return cmp(y, p.y) == -1;
    }

    bool operator == (P p) const {
        return ! cmp(x, p.x) && ! cmp(y, p.y);
    }

    db abs2() { return x * x + y * y; }
    db abs() { return sqrt(x * x + y * y); }
    db distTo(P p) { return (*this - p).abs(); }
    db alpha() { return atan2(y, x); }
    db dot(P p) { return x * p.x + y * p.y; }
    db det(P p) { return x * p.y - y * p.x; }

    int quadY() const { return sign(x) == 1 || (sign(x) == 0 && sign(y) >= 0); }
    int quadX() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); }

    P rot90() { return P(-y, x); }
    P rot(db alpha) { return { x * cos(alpha) - y * sin(alpha), x * sin(alpha) + y * cos(alpha)}; }
    P unit() { return *this / abs(); }
};

ostream & operator << (ostream &out, const P &A) {
    out << A.x << " " << A.y;
    return out;
}

// ------------------------------------
// 线段 / 线段

bool checkPll(P p1, P p2, P q1, P q2) {
    // 判断平行
    db a1 = cross(q1, q2, p1), a2 = - cross(q1, q2, p2);
    return sign(a1 + a2) == 0;
}

P isLL(P p1, P p2, P q1, P q2) {
    // 返回交点
    db a1 = cross(q1, q2, p1), a2 = - cross(q1, q2, p2);
    return (p1 * a2 + p2 * a1) / (a1 + a2);
}

bool intersect(db l1, db r1, db l2, db r2) {
    if (l1 > r1) swap(l1, r1);
    if (l2 > r2) swap(l2, r2);
    return cmp(r1, l2) >= 0 && cmp(r2, l1) >= 0;
}

bool isSS(P p1, P p2, P q1, P q2) {
    // 线段是否 不严格 相交
    // 1 : SS
    // 0 : unSS
    return intersect(p1.x, p2.x, q1.x, q2.x) &&
           intersect(p1.y, p2.y, q1.y, q2.y) && 
           crossop(p1, p2, q1) * crossop(p1, p2, q2) <= 0 && 
           crossop(q1, q2, p1) * crossop(q1, q2, p2) <= 0;
}

bool isSS_strict(P p1, P p2, P q1, P q2) {
    // 线段是否 严格 相交
    // 1 : SS
    // 0 : unSS
    return crossop(p1, p2, q1) * crossop(p1, p2, q2) < 0 && 
           crossop(q1, q2, p1) * crossop(q1, q2, p2) < 0;
}

bool isMiddle(db a, db m, db b) {
    return sign(a - m) == 0 || sign(b - m) == 0 || ((a < m) != (b < m));
}

bool isMiddle(P a, P m, P b) {
    return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
}

bool onSeg(P p1, P p2, P q) {
    // 是否在线段上 不严格
    return crossop(p1, p2, q) == 0 && isMiddle(p1, q, p2);
}

bool onSeg_strict(P p1, P p2, P q) {
    // 是否在线段上 严格
    return crossop(p1, p2, q) == 0 && 
           sign((q - p1).dot(p1 - p2)) * 
           sign((q - p2).dot(p1 - p2)) < 0;
}

P proj(P p1, P p2, P q) {
    // 求 q 到 p1 p2 的投影
    P dir = p2 - p1;
    return p1 + (p2 - p1) * (dir.dot(q - p1) / (dir).abs2());
}

P reflect(P p1, P p2, P q) {
    // 求 q 到 p1 p2 的反射点
    return proj(p1, p2, q) * 2 - q;
}

db nearest(P p1, P p2, P q) {
    // 求 q 到 p1 p2（线段）的最近点
    if (p1 == p2) return p1.distTo(q);
    P h = proj(p1, p2, q);
    if (isMiddle(p1, h, p2))
        return q.distTo(h);
    return min(p1.distTo(q), p2.distTo(q));
}

db disSS(P p1, P p2, P q1, P q2) {
    // 求线段 p1p2 和 线段 q1q2 的距离
    if (isSS(p1, p2, q1, q2)) return 0;
    db val1 = min(nearest(p1, p2, q1), nearest(p1, p2, q2));
    db val2 = min(nearest(q1, q2, p1), nearest(q1, q2, p2));
    return min(val1, val2);
}

db rad(P p1, P p2) {
    // 求向量 p1 p2 的角度（逆时针为正，顺时针为负，范围 (- pi, pi]）
    return atan2l(p1.det(p2), p1.dot(p2));
}

// ---------------------------------------------
// 多边形

db area(vector < P > ps) {
    // 求多边形面积（逆时针顺序给出点 -> 面积为正）
    db ret = 0; if (ps.size() == 0) return 0;
    for (int i = 0; i < (int)ps.size(); ++ i) {
        ret += ps[i].det(ps[(i + 1) % ps.size()]);
    }
    return ret / 2;
}

int contain(vector < P > ps, P p) {
    // 0 : outside
    // 1 : on_seg
    // 2 : inside

    // ps 是多边形，判断 p 在多边形 内部 / 线段上 / 外部
    int n = ps.size(), ret = 0;
    for (int i = 0; i < n; ++ i) {
        P u = ps[i], v = ps[(i + 1) % n];
        if (onSeg(u, v, p)) return 1;
        if (cmp(u.y, v.y) <= 0) swap(u, v);
        if (cmp(p.y, u.y) > 0 || cmp(p.y, v.y) <= 0)
            continue;
        ret ^= crossop(p, u, v) > 0;
    }
    return ret * 2;
}

vector < P > convexHull(vector < P > ps) {
    // 求凸包
    int n = ps.size();
    if (n <= 1) return ps;
    sort(ps.begin(), ps.end());
    vector < P > qs(n * 2);
    int k = 0;
    for (int i = 0; i < n; qs[k ++] = ps[i ++])
        while (k > 1 && crossop(qs[k - 2], qs[k - 1], ps[i]) <= 0)
            -- k;
    for (int i = n - 2, t = k; i >= 0; qs[k ++] = ps[i --])
        while (k > t && crossop(qs[k - 2], qs[k - 1], ps[i]) <= 0)
            -- k;
    qs.resize(k - 1);
    return qs;
}


vector < P > convexHullNonStrict(vector < P > ps) {
    // 求凸包（边界上的点也在凸包内）
    sort(ps.begin(), ps.end());
    ps.erase(unique(ps.begin(), ps.end()), ps.end());
    int n = ps.size();
    if (n <= 1) return ps;
    vector < P > qs(n * 2);
    int k = 0;
    for (int i = 0; i < n; qs[k ++] = ps[i ++])
        while (k > 1 && crossop(qs[k - 2], qs[k - 1], ps[i]) < 0)
            -- k;
    for (int i = n - 2, t = k; i >= 0; qs[k ++] = ps[i --])
        while (k > t && crossop(qs[k - 2], qs[k - 1], ps[i]) < 0)
            -- k;
    qs.resize(k - 1);
    return qs;
}

bool checkConvexHull(vector < P > p) {
    // 判断 p 多边形是否是凸包
    // 注意：不可通过再跑一次凸包判断点数是否相同
    // 因为点数相同 不能代表是凸包的连接方式
    int n = p.size();
    for (int i = 0; i < n; ++ i) {
        int pre = i - 1;
        int suf = i + 1;
        if (pre == -1) pre = n - 1;
        if (suf == n) suf = 0;
        if (crossop(p[i], p[suf], p[pre]) < 0)
            return 0;
    }
    return 1;
}

db convexDiameter(vector < P > ps) {
    int n = ps.size();
    if (n <= 1) return 0;
    int is = 0, js = 0;
    for (int k = 1; k < n; ++ k)
        is = ps[k] < ps[is] ? k : is,
        js = ps[js] < ps[k] ? k : js;
    int i = is, j = js;
    db ret = ps[i].distTo(ps[j]);
    do {
        if ((ps[(i + 1) % n] - ps[i]).det(ps[(j + 1) % n] - ps[j]) >= 0)
            (++ j) %= n;
        else (++ i) %= n;
        ret = max(ret, ps[i].distTo(ps[j]));
    } while (i != is || j != js);
    return ret;
}

bool polar_cmpX(P a, P b) {
    // 极角排序 X 轴正半轴（包含）开始逆时针转
    if (a.quadX() != b.quadX()) 
        return a.quadX() > b.quadX();
    return sign(a.det(b)) > 0;
}

bool polar_cmpY(P a, P b) {
    // 极角排序 Y 轴负半轴（不包含）开始逆时针转
    if (a.quadY() != b.quadY()) 
        return a.quadY() > b.quadY();
    return sign(a.det(b)) > 0;
}

vector < P > MincowskySum(vector < P > p, vector < P > q) {
    // p, q 为两个凸包（顺序同方向），求这两个凸包的 闵可夫斯基和
    // 得保证 p, q 的第一个点 是排序后的
    // 即 尽可能在 convexHull 后再传入 p, q 
    if (p.size() == 0) return q;
    if (q.size() == 0) return p;
    vector < P > qs; qs.push_back(p[0] + q[0]);
    P p0 = p[0], q0 = q[0];
    for (int i = 0; i + 1 < (int)p.size(); ++ i)
        p[i] = p[i + 1] - p[i];
    for (int i = 0; i + 1 < (int)q.size(); ++ i)
        q[i] = q[i + 1] - q[i];
    p[p.size() - 1] = p0 - p[p.size() - 1];
    q[q.size() - 1] = q0 - q[q.size() - 1];
    qs.resize(p.size() + q.size() + 1);
    merge(p.begin(), p.end(), q.begin(), q.end(), qs.begin() + 1, polar_cmpY);
    for (int i = 1; i < (int)qs.size(); ++ i)
        qs[i] = qs[i] + qs[i - 1];
    qs.pop_back();
    return qs;
}

void solve() {
    int n; cin >> n;
    vector < P > p(n);
    for (int i = 0; i < n; ++ i) {
        cin >> p[i].x >> p[i].y;
    }
    cout << fixed << setprecision(10) << convexDiameter(p) << '\n';
}


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t = 1;
    while (t --) {
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 8104
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define vi vector<int>
#define vl vector<ll>
#define ov4(a, b, c, d, name, ...) name
#define rep3(i, a, b, c) for(ll i = (a); i < (b); i += (c))
#define rep2(i, a, b) rep3(i, a, b, 1)
#define rep1(i, n) rep2(i, 0, n)
#define rep0(n) rep1(aaaaa, n)
#define rep(...) ov4(__VA_ARGS__, rep3, rep2, rep1, rep0)(__VA_ARGS__)
#define per(i, a, b) for(ll i = (a)-1; i >= (b); i--)
#define fore(e, v) for(auto&& e : v)
#define all(a) begin(a), end(a)
#define si(a) (int)(size(a))
#define lb(v, x) (lower_bound(all(v), x) - begin(v))
#define eb emplace_back

template<typename T, typename S> bool chmin(T& a, const S& b) { return a > b ? a = b, 1 : 0; }
template<typename T, typename S> bool chmax(T& a, const S& b) { return a < b ? a = b, 1 : 0; }

const int INF = 1e9 + 100;
const ll INFL = 3e18 + 100;

#define i128 __int128_t

struct _ {
   _() { cin.tie(0)->sync_with_stdio(0), cout.tie(0); }
} __;

template<class T> int sgn(T x) { return (x > 0) - (x < 0); }
template<class T>
struct Point {
    typedef Point P;
    T x, y;
    explicit Point(T x=0, T y=0) : x(x), y(y) {}
    bool operator<(P p) const { return tie(x, y) < tie(p.x, p.y); }
    bool operator==(P p) const { return tie(x, y) == tie(p.x, p.y); }
    P operator-(P p) const { return P(x - p.x, y - p.y); }
    P operator*(T d) const { return P(x * d, y * d); }
    P operator/(T d) const { return P(x / d, y / d); }
    T dot(P p) const { return x * p.x + y * p.y; }
    T cross(P p) const { return x * p.y - y * p.x; }
    T cross(P a, P b) const { return (a - *this).cross(b - *this); }
    T dist2() const { return x*x + y*y; }
    double dist() const { return sqrt((double)dist2()); }
    P perp() const { return P(-y, x); }
};

template<class P>
P lineProj(P a, P b, P p, bool refl=false) {
    P v = b - a;
    return p - v.perp() * (1 + refl) * v.cross(p - a) / v.dist2();
}

template<class P>
bool onSegment(P s, P e, P p) {
    return p.cross(s, e) == 0 and (s - p).dot(e - p) <= 0;
}

template<class P>
vector<P> segInter(P a, P b, P c, P d) {
    auto oa = c.cross(d, a), ob = c.cross(d, b),
         oc = a.cross(b, c), od = a.cross(b, d);
    if (sgn(oa) * sgn(ob) < 0 and sgn(oc) * sgn(od) < 0) {
        return {(a * ob - b * oa) / (ob - oa)};
    }
    set<P> s;
    if (onSegment(c, d, a)) s.insert(a);
    if (onSegment(c, d, b)) s.insert(b);
    if (onSegment(a, b, c)) s.insert(c);
    if (onSegment(a, b, d)) s.insert(d);
    return {all(s)};
}

template<class P>
double segDist(P s, P e, P p) {
    if (s == e) return (p - s).dist();
    auto d = (s - e).dist2(), t = min(d, max(.0, (p - s).dot(e - s)));
    return ((p - s) * d - (e - s) * t).dist() / d;
}

template<class P>
double segDist(P s1, P e1, P s2, P e2) {
    if (!segInter(s1, e1, s2, e2).empty()) return 0;
    auto a = min(segDist(s1, e1, s2), segDist(s1, e1, e2));
    auto b = min(segDist(s2, e2, s1), segDist(s2, e2, e1));
    return min(a, b);
}

template<class P>
pair<int, P> lineInter(P s1, P e1, P s2, P e2) {
    auto d = (e1 - s2).cross(e2 - s2);
    if(d == 0) {
        return {-(s1.cross(e1, s2) == 0), P(0, 0)};
    }
    auto p = s2.cross(e1, e2), q = s2.cross(e2, s1);
    return {1, (s1 * p + e1 * q) / d};
}

template<class T>
T polygonArea2(vector<Point<T>> &v) {
    T a = v.back().cross(v[0]);
    rep(i, si(v) - 1) a += v[i].cross(v[i + 1]);
    return a;
}

// speed star library
typedef Point<long double> P;
vector<P> convex_hull(vector<P> pts) {
    int n = si(pts), k = 0;
    if (n <= 2) return pts;
    sort(all(pts));
    vector<P> ch(2 * n);
    for(int i = 0; i < n; ch[k++] = pts[i++]) {
        while(k >= 2 and (ch[k - 1] - ch[k - 2]).cross(pts[i] - ch[k - 1]) <= -1.0e-14) --k;
    }
    for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = pts[i--]) {
        while(k >= t and (ch[k - 1] - ch[k - 2]).cross(pts[i] - ch[k - 1]) <= -1.0e-14) --k;
    }
    ch.resize(k - 1);
    return ch;
}

array<P, 2> hullDiameter(vector<P> S) {
    int n = si(S), j = n < 2 ? 0 : 1;
    pair<long double, array<P, 2>> res({0, {S[0], S[0]}});
    rep(i, j) {
        for (;; j = (j + 1) % n) {
            res = max(res, {(S[i] - S[j]).dist2(), {S[i], S[j]}});
            if ((S[(j + 1) % n] - S[j]).cross(S[i + 1] - S[i]) >= 1.0e-14) break;
        }
    }
    return res.second;
}
#undef P

template<class P>
bool inPolygon(vector<P> &p, P a, bool strict = true) {
    int cnt = 0, n = si(p);
    rep(i, n) {
        P q = p[(i + 1) % n];
        if (onSegment(p[i], q, a)) return !strict;
        cnt ^= ((a.y < p[i].y) - (a.y < q.y)) * a.cross(p[i], q) > 0;
    }
    return cnt;
}

using P = Point<long double>;

int main() {
    int n;
    cin >> n;
    vector<P> v;
    rep(i, n) {
        double x, y;
        cin >> x >> y;
        v.push_back(P(x, y));
    }

    auto cv = convex_hull(v);
    auto res = hullDiameter(cv);
    cout << setprecision(15);
    cout << (res[0] - res[1]).dist() << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 13780
final_rank: 13

code_snippet: // --- Start of include: ./../../akakoi_lib/template/template.cpp ---
// #pragma GCC target("avx2")
// #pragma GCC optimize("O3,unroll-loops")
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(ll i=0;i<(ll)(n);++i)
bool chmin(auto& a, auto b) {return a>b?a=b,1:0;}
bool chmax(auto& a, auto b) {return a<b?a=b,1:0;}
// int main() {
//     ios::sync_with_stdio(false);
//     cin.tie(0);
// }
// --- End of include: ./../../akakoi_lib/template/template.cpp ---
// --- Start of include: ./../../akakoi_lib/geometry/geometry.cpp ---
/// @brief 幾何ライブラリ
namespace Geometry {
 using Real = long double;
 const Real EPS = 1e-9;

 bool almostEqual(Real a, Real b) { return abs(a - b) < EPS; }
 bool lessThan(Real a, Real b) { return a < b && !almostEqual(a, b); }
 bool greaterThan(Real a, Real b) { return a > b && !almostEqual(a, b); }
 bool lessThanOrEqual(Real a, Real b) { return a < b || almostEqual(a, b); }
 bool greaterThanOrEqual(Real a, Real b) { return a > b || almostEqual(a, b); }

 /// @brief ２次元平面上の位置ベクトル
 struct Point {
  Real x, y;
  Point() = default;
  Point(Real x, Real y) : x(x), y(y) {}

  Point operator+(const Point& p) const { return Point(x + p.x, y + p.y); }
  Point operator-(const Point& p) const { return Point(x - p.x, y - p.y); }
  Point operator*(Real k) const { return Point(x * k, y * k); }
  Point operator/(Real k) const { return Point(x / k, y / k); }

  /// @brief p との内積を返す
  Real dot(const Point& p) const { return x * p.x + y * p.y; }

  /// @brief p との外積を返す
  Real cross(const Point& p) const { return x * p.y - y * p.x; }

  /// @brief p1 と p2 を端点とするベクトルとの外積を返す
  Real cross(const Point& p1, const Point& p2) const { return (p1.x - x) * (p2.y - y) - (p1.y - y) * (p2.x - x); }

  /// @brief ２乗ノルムを返す
  Real norm() const { return x * x + y * y; }

  /// @brief ユークリッドノルムを返す
  Real abs() const { return sqrt(norm()); }

  /// @brief 偏角を返す
  Real arg() const { return atan2(y, x); }

  bool operator==(const Point& p) const { return almostEqual(x, p.x) && almostEqual(y, p.y); }
  friend istream& operator>>(istream& is, Point& p) { return is >> p.x >> p.y; }
 };

 /// @brief 直線
 struct Line {
  Point a, b;
  Line() = default;
  Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}

  /// @brief 直線 Ax+By=C を定義する
  Line(const Real& A, const Real& B, const Real& C) {
   if (almostEqual(A, 0)) {
    assert(!almostEqual(B, 0));
    a = Point(0, C / B);
    b = Point(1, C / B);
   } else if (almostEqual(B, 0)) {
    a = Point(C / A, 0);
    b = Point(C / A, 1);
   } else if (almostEqual(C, 0)) {
    a = Point(0, C / B);
    b = Point(1, (C - A) / B);
   } else {
    a = Point(0, C / B);
    b = Point(C / A, 0);
   }
  }

  bool operator==(const Line& l) const { return a == l.a && b == l.b; }
  friend istream& operator>>(istream& is, Line& l) { return is >> l.a >> l.b; }
 };

 /// @brief 線分
 struct Segment : Line {
  Segment() = default;
  using Line::Line;
 };

 /// @brief 円
 struct Circle {
  Point center;  ///< 中心
  Real r;  ///< 半径

  Circle() = default;
  Circle(Real x, Real y, Real r) : center(x, y), r(r) {}
  Circle(Point _center, Real r) : center(_center), r(r) {}

  bool operator==(const Circle& C) const { return center == C.center && r == C.r; }
  friend istream& operator>>(istream& is, Circle& C) { return is >> C.center >> C.r; }
 };

 //-----------------------------------------------------------

 /// @brief 3点の進行方向
 enum Orientation {
  COUNTER_CLOCKWISE,  ///< 反時計回り
  CLOCKWISE,  ///< 時計回り
  ONLINE_BACK,
  ONLINE_FRONT,
  ON_SEGMENT
 };

 /// @brief 3点 p0, p1, p2 の進行方向を返す
 Orientation ccw(const Point& p0, const Point& p1, const Point& p2) {
  Point a = p1 - p0;
  Point b = p2 - p0;
  Real cross_product = a.cross(b);
  if (greaterThan(cross_product, 0)) return COUNTER_CLOCKWISE;
  if (lessThan(cross_product, 0)) return CLOCKWISE;
  if (lessThan(a.dot(b), 0)) return ONLINE_BACK;
  if (lessThan(a.norm(), b.norm())) return ONLINE_FRONT;
  return ON_SEGMENT;
 }

 string orientationToString(Orientation o) {
  switch (o) {
   case COUNTER_CLOCKWISE:
    return "COUNTER_CLOCKWISE";
   case CLOCKWISE:
    return "CLOCKWISE";
   case ONLINE_BACK:
    return "ONLINE_BACK";
   case ONLINE_FRONT:
    return "ONLINE_FRONT";
   case ON_SEGMENT:
    return "ON_SEGMENT";
   default:
    return "UNKNOWN";
  }
 }

 /// @brief ベクトル p の直線 p1, p2 への正射影ベクトルを返す
 Point projection(const Point& p1, const Point& p2, const Point& p) {
  Point base = p2 - p1;
  Real r = (p - p1).dot(base) / base.norm();
  return p1 + base * r;
 }

 /// @brief ベクトル p の直線 l への正射影ベクトルを返す
 Point projection(const Line& l, const Point& p) {
  Point base = l.b - l.a;
  Real r = (p - l.a).dot(base) / base.norm();
  return l.a + base * r;
 }

 /// @brief ベクトル p の直線 p1, p2 に対する鏡像ベクトルを返す
 Point reflection(const Point& p1, const Point& p2, const Point& p) {
  Point proj = projection(p1, p2, p);
  return proj * 2 - p;
 }

 /// @brief ベクトル p の直線 l に対する鏡像ベクトルを返す
 Point reflection(const Line& l, const Point& p) {
  Point proj = projection(l, p);
  return proj * 2 - p;
 }

 //-----------------------------------------------------------

 /// @brief 直線の平行判定
 bool isParallel(const Line& l1, const Line& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 直線の直交判定
 bool isOrthogonal(const Line& l1, const Line& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 線分の平行判定
 bool isParallel(const Segment& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 線分の直交判定
 bool isOrthogonal(const Segment& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 直線と線分の平行判定
 bool isParallel(const Line& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 直線と線分の直交判定
 bool isOrthogonal(const Line& l1, const Segment& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 線分と直線の平行判定
 bool isParallel(const Segment& l1, const Line& l2) { return almostEqual((l1.b - l1.a).cross(l2.b - l2.a), 0); }
 /// @brief 線分と直線の直交判定
 bool isOrthogonal(const Segment& l1, const Line& l2) { return almostEqual((l1.b - l1.a).dot(l2.b - l2.a), 0); }
 /// @brief 点が直線上にあるか判定
 bool isPointOnLine(const Point& p, const Line& l) { return almostEqual((l.b - l.a).cross(p - l.a), 0.0); }

 /// @brief 点が線分上にあるか判定
 bool isPointOnSegment(const Point& p, const Segment& s) {
  return lessThanOrEqual(min(s.a.x, s.b.x), p.x) &&
     lessThanOrEqual(p.x, max(s.a.x, s.b.x)) &&
     lessThanOrEqual(min(s.a.y, s.b.y), p.y) &&
     lessThanOrEqual(p.y, max(s.a.y, s.b.y)) &&
     almostEqual((s.b - s.a).cross(p - s.a), 0.0);
 }

 /// @brief 直線の交差判定
 bool isIntersecting(const Segment& s1, const Segment& s2) {
  Point p0p1 = s1.b - s1.a, p0p2 = s2.a - s1.a, p0p3 = s2.b - s1.a, p2p3 = s2.b - s2.a, p2p0 = s1.a - s2.a, p2p1 = s1.b - s2.a;
  Real d1 = p0p1.cross(p0p2), d2 = p0p1.cross(p0p3), d3 = p2p3.cross(p2p0), d4 = p2p3.cross(p2p1);
  if (lessThan(d1 * d2, 0) && lessThan(d3 * d4, 0)) return true;
  if (almostEqual(d1, 0.0) && isPointOnSegment(s2.a, s1)) return true;
  if (almostEqual(d2, 0.0) && isPointOnSegment(s2.b, s1)) return true;
  if (almostEqual(d3, 0.0) && isPointOnSegment(s1.a, s2)) return true;
  if (almostEqual(d4, 0.0) && isPointOnSegment(s1.b, s2)) return true;
  return false;
 }

 /// @brief 線分の交点を返す
 Point getIntersection(const Segment& s1, const Segment& s2) {
  assert(isIntersecting(s1, s2));
  auto cross = [](Point p, Point q) { return p.x * q.y - p.y * q.x; };
  Point base = s2.b - s2.a;
  Real d1 = abs(cross(base, s1.a - s2.a));
  Real d2 = abs(cross(base, s1.b - s2.a));
  Real t = d1 / (d1 + d2);
  return s1.a + (s1.b - s1.a) * t;
 }

 /// @brief 点と線分の距離を返す
 Real distancePointToSegment(const Point& p, const Segment& s) {
  Point proj = projection(s.a, s.b, p);
  if (isPointOnSegment(proj, s))
   return (p - proj).abs();
  else
   return min((p - s.a).abs(), (p - s.b).abs());
 }

 /// @brief 線分と線分の距離を返す
 Real distanceSegmentToSegment(const Segment& s1, const Segment& s2) {
  if (isIntersecting(s1, s2)) return 0.0;
  return min({distancePointToSegment(s1.a, s2),
     distancePointToSegment(s1.b, s2),
     distancePointToSegment(s2.a, s1),
     distancePointToSegment(s2.b, s1)});
 }

 //-----------------------------------------------------------

 /// @brief 多角形の面積を返す
 Real getPolygonArea(const vector<Point>& points) {
  int n = points.size();
  Real area = 0.0;
  for (int i = 0; i < n; i++) {
   int j = (i + 1) % n;
   area += points[i].x * points[j].y;
   area -= points[i].y * points[j].x;
  }
  return abs(area) / 2.0;
 }

 /// @brief 多角形が凸か判定
 bool isConvex(const vector<Point>& points) {
  int n = points.size();
  bool has_positive = false, has_negative = false;
  for (int i = 0; i < n; i++) {
   int j = (i + 1) % n;
   int k = (i + 2) % n;
   Point a = points[j] - points[i];
   Point b = points[k] - points[j];
   Real cross_product = a.cross(b);
   if (greaterThan(cross_product, 0)) has_positive = true;
   if (lessThan(cross_product, 0)) has_negative = true;
  }
  return !(has_positive && has_negative);
 }

 /// @brief 点が凸多角形の辺上に存在するか判定
 bool isPointOnPolygon(const vector<Point>& polygon, const Point& p) {
  int n = polygon.size();
  for (int i = 0; i < n; i++) {
   Point a = polygon[i];
   Point b = polygon[(i + 1) % n];
   Segment s(a, b);
   if (isPointOnSegment(p, s)) return true;
  }
  return false;
 }

 /// @brief 点が多角形の内部に存在するか判定（辺上は含まない）
 bool isPointInsidePolygon(const vector<Point>& polygon, const Point& p) {
  int n = polygon.size();
  bool inPolygon = false;
  for (int i = 0; i < n; i++) {
   Point a = polygon[i];
   Point b = polygon[(i + 1) % n];
   if (greaterThan(a.y, b.y)) swap(a, b);
   if (lessThanOrEqual(a.y, p.y) && lessThan(p.y, b.y) && greaterThan((b - a).cross(p - a), 0)) inPolygon = !inPolygon;
  }
  return inPolygon;
 }

 //-----------------------------------------------------------

 /// @brief 凸包を求める
 vector<Point> convexHull(vector<Point>& points, bool include_collinear = false) {
  int n = points.size();
  if (n <= 1) return points;
  sort(points.begin(), points.end(), [](const Point& l, const Point& r) -> bool {
   if (almostEqual(l.y, r.y)) return lessThan(l.x, r.x);
   return lessThan(l.y, r.y);
  });
  if (n == 2) return points;
  vector<Point> upper = {points[0], points[1]}, lower = {points[0], points[1]};
  for (int i = 2; i < n; i++) {
   while (upper.size() >= 2 && ccw(upper.end()[-2], upper.end()[-1], points[i]) != CLOCKWISE) {
    if (ccw(upper.end()[-2], upper.end()[-1], points[i]) == ONLINE_FRONT && include_collinear) break;
    upper.pop_back();
   }
   upper.push_back(points[i]);
   while (lower.size() >= 2 && ccw(lower.end()[-2], lower.end()[-1], points[i]) != COUNTER_CLOCKWISE) {
    if (ccw(lower.end()[-2], lower.end()[-1], points[i]) == ONLINE_FRONT && include_collinear) break;
    lower.pop_back();
   }
   lower.push_back(points[i]);
  }
  reverse(upper.begin(), upper.end());
  upper.pop_back();
  lower.pop_back();
  lower.insert(lower.end(), upper.begin(), upper.end());
  return lower;
 }

 /// @brief 凸包の直径を求める
 Real convexHullDiameter(const vector<Point>& hull) {
  int n = hull.size();
  if (n == 1) return 0;
  int k = 1;
  Real max_dist = 0;
  for (int i = 0; i < n; i++) {
   while (true) {
    int j = (k + 1) % n;
    Point dist1 = hull[i] - hull[j], dist2 = hull[i] - hull[k];
    max_dist = max(max_dist, dist1.abs());
    max_dist = max(max_dist, dist2.abs());
    if (dist1.abs() > dist2.abs())
     k = j;
    else
     break;
   }
   Point dist = hull[i] - hull[k];
   max_dist = max(max_dist, dist.abs());
  }
  return max_dist;
 }

 /// @brief 凸包を直線で切断して左側を返す
 vector<Point> cutPolygon(const vector<Point>& g, const Line& l) {
  auto isLeft = [](const Point& p1, const Point& p2, const Point& p) -> bool { return (p2 - p1).cross(p - p1) > 0; };
  vector<Point> newPolygon;
  int n = g.size();
  for (int i = 0; i < n; i++) {
   const Point& cur = g[i];
   const Point& next = g[(i + 1) % n];
   if (isLeft(l.a, l.b, cur)) newPolygon.push_back(cur);
   if ((isLeft(l.a, l.b, cur) && !isLeft(l.a, l.b, next)) || (!isLeft(l.a, l.b, cur) && isLeft(l.a, l.b, next))) {
    Real A1 = (next - cur).cross(l.a - cur);
    Real A2 = (next - cur).cross(l.b - cur);
    Point intersection = l.a + (l.b - l.a) * (A1 / (A1 - A2));
    newPolygon.push_back(intersection);
   }
  }
  return newPolygon;
 }

 //-----------------------------------------------------------

 /// @brief 最近点対の距離を求める
 /// @note points は x 座標でソートされている必要がある
 Real closestPair(vector<Point>& points, int l, int r) {
  if (r - l <= 1) return numeric_limits<Real>::max();
  int mid = (l + r) >> 1;
  Real x = points[mid].x;
  Real d = min(closestPair(points, l, mid), closestPair(points, mid, r));
  auto iti = points.begin(), itl = iti + l, itm = iti + mid, itr = iti + r;
  inplace_merge(itl, itm, itr, [](const Point& lhs, const Point& rhs) -> bool {
   return lessThan(lhs.y, rhs.y);
  });
  vector<Point> nearLine;
  for (int i = l; i < r; i++) {
   if (greaterThanOrEqual(fabs(points[i].x - x), d)) continue;
   int sz = nearLine.size();
   for (int j = sz - 1; j >= 0; j--) {
    Point dv = points[i] - nearLine[j];
    if (dv.y >= d) break;
    d = min(d, dv.abs());
   }
   nearLine.push_back(points[i]);
  }
  return d;
 }

 //-----------------------------------------------------------

 /// @brief 線分の交差数を数える
 int countIntersections(vector<Segment> segments) {
  struct Event {
   Real x;
   int type;  // 0:horizontal start,1:vertical,2:horizontal end
   Real y1, y2;
   Event(Real x, int type, Real y1, Real y2) : x(x), type(type), y1(y1), y2(y2) {}
   bool operator<(const Event& other) const {
    if (x == other.x) return type < other.type;
    return x < other.x;
   }
  };
  vector<Event> events;
  sort(segments.begin(), segments.end(), [](const Segment& lhs, const Segment& rhs) -> bool {
   return lessThan(min(lhs.a.x, lhs.b.x), min(rhs.a.x, rhs.b.x));
  });
  for (const auto& seg : segments) {
   if (seg.a.y == seg.b.y) {
    // Horizontal segment
    Real y = seg.a.y;
    Real x1 = min(seg.a.x, seg.b.x);
    Real x2 = max(seg.a.x, seg.b.x);
    events.emplace_back(x1, 0, y, y);
    events.emplace_back(x2, 2, y, y);
   } else {
    // Vertical segment
    Real x = seg.a.x;
    Real y1 = min(seg.a.y, seg.b.y);
    Real y2 = max(seg.a.y, seg.b.y);
    events.emplace_back(x, 1, y1, y2);
   }
  }
  sort(events.begin(), events.end());
  set<Real> activeSegments;
  int intersectionCount = 0;
  for (const auto& event : events) {
   if (event.type == 0) {
    // Add horizontal segment to active set
    activeSegments.insert(event.y1);
   } else if (event.type == 2) {
    // Remove horizontal segment from active set
    activeSegments.erase(event.y1);
   } else if (event.type == 1) {
    // Count intersections with vertical segment
    auto lower = activeSegments.lower_bound(event.y1);
    auto upper = activeSegments.upper_bound(event.y2);
    intersectionCount += distance(lower, upper);
   }
  }
  return intersectionCount;
 }

 //-----------------------------------------------------------

 /// @brief 2つの円の交点の個数を返す
 int countCirclesIntersection(const Circle& c1, const Circle& c2) {
  Real d =
   sqrt((c1.center.x - c2.center.x) * (c1.center.x - c2.center.x) +
     (c1.center.y - c2.center.y) * (c1.center.y - c2.center.y));
  Real r1 = c1.r, r2 = c2.r;
  if (greaterThan(d, r1 + r2))
   return 4;
  else if (almostEqual(d, r1 + r2))
   return 3;
  else if (greaterThan(d, fabs(r1 - r2)))
   return 2;
  else if (almostEqual(d, fabs(r1 - r2)))
   return 1;
  else
   return 0;
 }

 /// @brief 内接円を求める
 Circle getInCircle(const Point& A, const Point& B, const Point& C) {
  Real a = (B - C).abs();
  Real b = (A - C).abs();
  Real c = (A - B).abs();
  Real s = (a + b + c) / 2;
  Real area = sqrt(s * (s - a) * (s - b) * (s - c));
  Real r = area / s;
  Real cx = (a * A.x + b * B.x + c * C.x) / (a + b + c);
  Real cy = (a * A.y + b * B.y + c * C.y) / (a + b + c);
  return Circle{Point(cx, cy), r};
 }

 /// @brief 外接円を求める
 Circle getCircumCircle(const Point& A, const Point& B, const Point& C) {
  Real D = 2 * (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y));
  Real Ux = ((A.x * A.x + A.y * A.y) * (B.y - C.y) + (B.x * B.x + B.y * B.y) * (C.y - A.y) + (C.x * C.x + C.y * C.y) * (A.y - B.y)) / D;
  Real Uy = ((A.x * A.x + A.y * A.y) * (C.x - B.x) + (B.x * B.x + B.y * B.y) * (A.x - C.x) + (C.x * C.x + C.y * C.y) * (B.x - A.x)) / D;
  Point center(Ux, Uy);
  Real radius = (center - A).abs();
  return Circle{center, radius};
 }

 /// @brief 円と直線の交点を求める
 vector<Point> getCircleLineIntersection(const Circle& c, Point p1, Point p2) {
  Real cx = c.center.x, cy = c.center.y, r = c.r;
  Real dx = p2.x - p1.x;
  Real dy = p2.y - p1.y;
  Real a = dx * dx + dy * dy;
  Real b = 2 * (dx * (p1.x - cx) + dy * (p1.y - cy));
  Real c_const = (p1.x - cx) * (p1.x - cx) + (p1.y - cy) * (p1.y - cy) - r * r;
  Real discriminant = b * b - 4 * a * c_const;
  vector<Point> intersections;
  if (almostEqual(discriminant, 0)) {
   Real t = -b / (2 * a);
   Real ix = p1.x + t * dx;
   Real iy = p1.y + t * dy;
   intersections.emplace_back(ix, iy);
   intersections.emplace_back(ix, iy);
  } else if (discriminant > 0) {
   Real sqrt_discriminant = sqrt(discriminant);
   Real t1 = (-b + sqrt_discriminant) / (2 * a);
   Real t2 = (-b - sqrt_discriminant) / (2 * a);
   Real ix1 = p1.x + t1 * dx;
   Real iy1 = p1.y + t1 * dy;
   Real ix2 = p1.x + t2 * dx;
   Real iy2 = p1.y + t2 * dy;
   intersections.emplace_back(ix1, iy1);
   intersections.emplace_back(ix2, iy2);
  }
  if (almostEqual(intersections[0].x, intersections[1].x)) {
   if (greaterThan(intersections[0].y, intersections[1].y)) swap(intersections[0], intersections[1]);
  } else if (greaterThan(intersections[0].x, intersections[1].x)) {
   swap(intersections[0], intersections[1]);
  }
  return intersections;
 }

 /// @brief 2つの円の交点を求める
 vector<Point> getCirclesIntersect(const Circle& c1, const Circle& c2) {
  Real x1 = c1.center.x, y1 = c1.center.y, r1 = c1.r;
  Real x2 = c2.center.x, y2 = c2.center.y, r2 = c2.r;
  Real d = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  if (d > r1 + r2 || d < abs(r1 - r2)) return {};  // No intersection
  Real a = (r1 * r1 - r2 * r2 + d * d) / (2 * d);
  Real h = sqrt(r1 * r1 - a * a);
  Real x0 = x1 + a * (x2 - x1) / d;
  Real y0 = y1 + a * (y2 - y1) / d;
  Real rx = -(y2 - y1) * (h / d);
  Real ry = (x2 - x1) * (h / d);
  Point p1(x0 + rx, y0 + ry);
  Point p2(x0 - rx, y0 - ry);
  vector<Point> intersections;
  intersections.push_back(p1);
  intersections.push_back(p2);
  if (almostEqual(intersections[0].x, intersections[1].x)) {
   if (greaterThan(intersections[0].y, intersections[1].y)) swap(intersections[0], intersections[1]);
  } else if (greaterThan(intersections[0].x, intersections[1].x)) {
   swap(intersections[0], intersections[1]);
  }
  return intersections;
 }

 /// @brief 点から引ける円の接線の接点を求める
 vector<Point> getTangentLinesFromPoint(const Circle& c, const Point& p) {
  Real cx = c.center.x, cy = c.center.y, r = c.r;
  Real px = p.x, py = p.y;
  Real dx = px - cx;
  Real dy = py - cy;
  Real d = (p - c.center).abs();
  if (lessThan(d, r))
   return {};  // No tangents if the point is inside the circle
  else if (almostEqual(d, r))
   return {p};
  Real a = r * r / d;
  Real h = sqrt(r * r - a * a);
  Real cx1 = cx + a * dx / d;
  Real cy1 = cy + a * dy / d;
  vector<Point> tangents;
  tangents.emplace_back(cx1 + h * dy / d, cy1 - h * dx / d);
  tangents.emplace_back(cx1 - h * dy / d, cy1 + h * dx / d);
  if (almostEqual(tangents[0].x, tangents[1].x)) {
   if (greaterThan(tangents[0].y, tangents[1].y)) swap(tangents[0], tangents[1]);
  } else if (greaterThan(tangents[0].x, tangents[1].x)) {
   swap(tangents[0], tangents[1]);
  }
  return tangents;
 }

 /// @brief 2つの円の共通接線を求める
 vector<Segment> getCommonTangentsLine(const Circle& c1, const Circle& c2) {
  Real x1 = c1.center.x, y1 = c1.center.y, r1 = c1.r;
  Real x2 = c2.center.x, y2 = c2.center.y, r2 = c2.r;
  Real dx = x2 - x1;
  Real dy = y2 - y1;
  Real d = sqrt(dx * dx + dy * dy);
  vector<Segment> tangents;
  // Coincident circles(infinite tangents)
  if (almostEqual(d, 0) && almostEqual(r1, r2)) return tangents;
  // External tangents
  if (greaterThanOrEqual(d, r1 + r2)) {
   Real a = atan2(dy, dx);
   for (int sign : {-1, 1}) {
    Real theta = acos((r1 + r2) / d);
    Real cx1 = x1 + r1 * cos(a + sign * theta);
    Real cy1 = y1 + r1 * sin(a + sign * theta);
    Real cx2 = x2 + r2 * cos(a + sign * theta);
    Real cy2 = y2 + r2 * sin(a + sign * theta);
    tangents.emplace_back(Segment{Point(cx1, cy1), Point(cx2, cy2)});
    if (almostEqual(d, r1 + r2)) break;
   }
  }
  // Internal tangents
  if (greaterThanOrEqual(d, fabs(r1 - r2))) {
   Real a = atan2(dy, dx);
   for (int sign : {-1, 1}) {
    Real theta = acos((r1 - r2) / d);
    Real cx1 = x1 + r1 * cos(a + sign * theta);
    Real cy1 = y1 + r1 * sin(a + sign * theta);
    Real cx2 = x2 - r2 * cos(a + sign * theta);
    Real cy2 = y2 - r2 * sin(a + sign * theta);
    tangents.emplace_back(Segment{Point(cx1, cy1), Point(cx2, cy2)});
    if (almostEqual(d, fabs(r1 - r2))) break;
   }
  }
  sort(tangents.begin(), tangents.end(), [&](const Segment& s1, const Segment& s2) {
   if (almostEqual(s1.a.x, s2.a.x))
    return lessThan(s1.a.y, s2.a.y);
   else
    return lessThan(s1.a.x, s2.a.x);
  });
  return tangents;
 }
}
// --- End of include: ./../../akakoi_lib/geometry/geometry.cpp ---

using namespace Geometry;

void solve() {
  int n; cin >> n;
  vector<Point> P(n);
  rep(i, n) {
    Real x, y; cin >> x >> y;
    P[i] = {x, y};
  }
  P = convexHull(P);
  cout << convexHullDiameter(P) << endl;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout << fixed << setprecision(10);
  solve();
  return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 6188
final_rank: 3

code_snippet: // https://tubo28.me/compprog/algorithm/ より.

#include <bits/stdc++.h>
// #define int int64_t

#define FOR(i, a, b) for (int i = (a); i < int(b); ++i)
#define RFOR(i, a, b) for (int i = (b)-1; i >= int(a); --i)
#define rep(i, n) FOR(i, 0, n)
#define rep1(i, n) FOR(i, 1, int(n) + 1)
#define rrep(i, n) RFOR(i, 0, n)
#define rrep1(i, n) RFOR(i, 1, int(n) + 1)
#define all(c) begin(c), end(c)
namespace io {
#ifdef LOCAL
#define dump(...)                                         \
    do {                                                  \
        std::ostringstream os;                            \
        os << __LINE__ << ":\t" << #__VA_ARGS__ << " = "; \
        io::print_to(os, ", ", "\n", __VA_ARGS__);        \
        std::cerr << io::highlight(os.str());             \
    } while (0)
#define dump_(cntnr)                                 \
    do {                                             \
        std::ostringstream os;                       \
        os << __LINE__ << ":\t" << #cntnr << " = ["; \
        io::print_to_(os, ", ", "]\n", all(cntnr));  \
        std::cerr << io::highlight(os.str());        \
    } while (0)
#define dumpf(fmt, ...)                             \
    do {                                            \
        const int N = 4096;                         \
        auto b = new char[N];                       \
        int l = snprintf(b, N, "%d:\t", __LINE__);  \
        snprintf(b + l, N - l, fmt, ##__VA_ARGS__); \
        std::cerr << io::highlight(b) << std::endl; \
        delete[] b;                                 \
    } while (0)
#else
#define dump(...)
#define dump_(...)
#define dumpf(...)
#endif
std::string highlight(std::string s) {
#ifdef _MSC_VER
    return s;
#else
    return "\033[33m" + s + "\033[0m";
#endif
}
template <typename T>
void print_to(std::ostream &os, std::string, std::string tail, const T &first) {
    os << first << tail;
}
template <typename F, typename... R>
void print_to(std::ostream &os, std::string del, std::string tail, const F &first,
              const R &... rest) {
    os << first << del;
    print_to(os, del, tail, rest...);
}
template <typename I>
void print_to_(std::ostream &os, std::string del, std::string tail, I begin, I end) {
    for (I it = begin; it != end;) {
        os << *it;
        os << (++it != end ? del : tail);
    }
}
template <typename F, typename... R>
void println(const F &first, const R &... rest) {
    print_to(std::cout, "\n", "\n", first, rest...);
}
template <typename F, typename... R>
void print(const F &first, const R &... rest) {
    print_to(std::cout, " ", "\n", first, rest...);
}
template <typename I>
void println_(I begin, I end) {
    print_to_(std::cout, "\n", "\n", begin, end);
}
template <typename I>
void print_(I begin, I end) {
    print_to_(std::cout, " ", "\n", begin, end);
}
template <typename C>
void println_(const C &cntnr) {
    println_(std::begin(cntnr), std::end(cntnr));
}
template <typename C>
void print_(const C &cntnr) {
    print_(std::begin(cntnr), std::end(cntnr));
}
int _ = (
#ifndef LOCAL
    std::cin.tie(nullptr), std::ios::sync_with_stdio(false),
#endif
    std::cout.precision(10), std::cout.setf(std::ios::fixed));
}
using io::print;
using io::println;
using io::println_;
using io::print_;
template <typename T>
using vec = std::vector<T>;
using vi = vec<int>;
using vvi = vec<vi>;
using pii = std::pair<int, int>;
using ll = long long;
using ld = long double;
using namespace std;
// const int MOD = 1000000007;




using ld = long double;
using P = std::complex<ld>;
using G = std::vector<P>;
const ld pi = std::acos(-1);
const ld eps = 1e-10;
const ld inf = 1e12;

ld cross(const P &a, const P &b) { return a.real() * b.imag() - a.imag() * b.real(); }
ld dot(const P &a, const P &b) { return a.real() * b.real() + a.imag() * b.imag(); }

/*
                    CCW

 -- BEHIND -- [a -- ON -- b] --- FRONT --

                    CW
 */
enum CCW_RESULT { CCW = +1, CW = -1, BEHIND = +2, FRONT = -2, ON = 0 };
int ccw(P a, P b, P c) {
    b -= a;
    c -= a;
    if (cross(b, c) > eps) return CCW;    // counter clockwise
    if (cross(b, c) < -eps) return CW;    // clockwise
    if (dot(b, c) < 0) return BEHIND;     // c--a--b on line
    if (norm(b) < norm(c)) return FRONT;  // a--b--c on line
    return ON;
}

namespace std {
bool operator<(const P &a, const P &b) {
    return std::abs(real(a) - real(b)) > eps ? real(a) < real(b) : imag(a) < imag(b);
}
}

struct L : public std::vector<P> {
    L(const P &a = P(), const P &b = P()) : std::vector<P>(2) {
        begin()[0] = a;
        begin()[1] = b;
    }

    // Ax + By + C = 0
    L(ld A, ld B, ld C) {
        if (std::abs(A) < eps && std::abs(B) < eps) {
            abort();
        } else if (std::abs(A) < eps) {
            *this = L(P(0, -C / B), P(1, -C / B));
        } else if (std::abs(B) < eps) {
            *this = L(P(-C / A, 0), P(-C / A, 1));
        } else {
            *this = L(P(0, -C / B), P(-C / A, 0));
        }
    }
};

struct C {
    P p;
    ld r;
    C(const P &p = 0, ld r = 0) : p(p), r(r) {}
};




bool intersectLL(const L &l, const L &m) {
    return std::abs(cross(l[1] - l[0], m[1] - m[0])) > eps ||  // non-parallel
           std::abs(cross(l[1] - l[0], m[0] - l[0])) < eps;    // same line
}
bool intersectLS(const L &l, const L &s) {
    return cross(l[1] - l[0], s[0] - l[0]) *  // s[0] is left of l
               cross(l[1] - l[0], s[1] - l[0]) <
           eps;  // s[1] is right of l
}
bool intersectLP(const L &l, const P &p) { return std::abs(cross(l[1] - p, l[0] - p)) < eps; }
bool intersectSS(const L &s, const L &t) {
    return ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 &&
           ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0;
}
bool intersectSP(const L &s, const P &p) {
    return std::abs(s[0] - p) + std::abs(s[1] - p) - std::abs(s[1] - s[0]) <
           eps;  // triangle inequality
}

P projection(const L &l, const P &p) {
    ld t = dot(p - l[0], l[0] - l[1]) / norm(l[0] - l[1]);
    return l[0] + t * (l[0] - l[1]);
}
P reflection(const L &l, const P &p) { return p + (ld)2 * (projection(l, p) - p); }
ld distanceLP(const L &l, const P &p) { return std::abs(p - projection(l, p)); }
ld distanceLL(const L &l, const L &m) { return intersectLL(l, m) ? 0 : distanceLP(l, m[0]); }
ld distanceLS(const L &l, const L &s) {
    if (intersectLS(l, s)) return 0;
    return std::min(distanceLP(l, s[0]), distanceLP(l, s[1]));
}
ld distanceSP(const L &s, const P &p) {
    const P r = projection(s, p);
    if (intersectSP(s, r)) return std::abs(r - p);
    return std::min(std::abs(s[0] - p), std::abs(s[1] - p));
}
ld distanceSS(const L &s, const L &t) {
    if (intersectSS(s, t)) return 0;
    return std::min(std::min(distanceSP(s, t[0]), distanceSP(s, t[1])),
                    std::min(distanceSP(t, s[0]), distanceSP(t, s[1])));
}
P crosspointLL(const L &l, const L &m) {
    ld A = cross(l[1] - l[0], m[1] - m[0]);
    ld B = cross(l[1] - l[0], l[1] - m[0]);
    if (std::abs(A) < eps && std::abs(B) < eps) return m[0];  // same line
    if (std::abs(A) < eps) assert(false);                     // !!!PRECONDITION NOT SATISFIED!!!
    return m[0] + B / A * (m[1] - m[0]);
}


// 符号付二倍面積 abs(area2(G pol))/2.0 で求められる.
// polは頂点反時計回りで渡す
ld area2(const G& pol) {
    ld s = 0;
    int n = pol.size();
    for (int i = 0; i < n; ++i) s += cross(pol[i], pol[(i + 1) % n]);
    return s;
}


// 凸包
// == CWを != CCWに置換すると 退化した角を除く.
G andrewScan(G ps) {
    int N = ps.size(), k = 0;
    std::sort(ps.begin(), ps.end());
    G res(N * 2);
    for (int i = 0; i < N; i++) {
        while (k >= 2 && ccw(res[k - 2], res[k - 1], ps[i]) == CW) k--;
        res[k++] = ps[i];
    }
    int t = k + 1;
    for (int i = N - 2; i >= 0; i--) {
        while (k >= t && ccw(res[k - 2], res[k - 1], ps[i]) == CW) k--;
        res[k++] = ps[i];
    }
    res.resize(k - 1);
    return res;
}


// 凸多角形の直径
#define curr(P, i) P[i]
#define next(P, i) P[(i + 1) % P.size()]
#define diff(P, i) (next(P, i) - curr(P, i))
ld convex_diameter(const G &pt) {
    const int n = pt.size();
    int is = 0, js = 0;
    for (int i = 1; i < n; ++i) {
        if (imag(pt[i]) > imag(pt[is])) is = i;
        if (imag(pt[i]) < imag(pt[js])) js = i;
    }
    ld maxd = norm(pt[is] - pt[js]);
    int i, maxi, j, maxj;
    i = maxi = is;
    j = maxj = js;
    do {
        if (cross(diff(pt, i), diff(pt, j)) >= 0)
            j = (j + 1) % n;
        else
            i = (i + 1) % n;
        if (norm(pt[i] - pt[j]) > maxd) {
            maxd = norm(pt[i] - pt[j]);
            maxi = i;
            maxj = j;
        }
    } while (i != is || j != js);
    return maxd; /* farthest pair is (maxi, maxj). */
}
//凸多角形の直径終.


// P:点 point x: .real() y: .imag()
// L:直線 lineF
// S:線分 section
// G:多角形 vector<P>

signed main() {
    int N;
    cin >> N;
    G g(N);
    rep(i, N){
        ld xi, yi;
        cin >> xi >> yi;
        g[i] = P(xi, yi);
    }
    cout << fixed << setprecision(15) << sqrt(convex_diameter(g)) << endl;
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 5548
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;

#define print(s) cout << s;
#define println(s) cout << s << endl;
#define eps (1e-10)
#define bigint 2000000000
#define biglong 2000000000000000000L
#define rep(i,s,n) for(int i=s;i<(int)(n);i++)
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()//いるらしい
typedef pair<int, int> pii;
typedef vector<int> vi;


//ーーー入出力
template<typename T>
T next() {T a; cin >> a; return a;}

template<typename T>
vector<T> arrayInput(int n) {
    vector<T> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    return a;
}

template<typename T>
vector<vector<T>> arrayInput(int h, int w) {
    vector<vector<T>> a(h, vector<T>(w));
    for (int i = 0; i < h; i++)
        for (int j = 0; j < w; j++)
            cin >> a[i][j];
    return a;
}


template <typename T>
void arrayPrint(const vector<T>& vec, bool blank = true) {
    int n = vec.size();
    for (int i = 0; i < n; ++i) {
        cout << vec[i];
        if (blank && i != n - 1) cout << ' ';
    }
    cout << '\n';
}
void arrayPrint(const vector<string>& vec, bool blank = false) {
    int n = vec.size();
    for (int i = 0; i < n; ++i) {
        cout << vec[i];
        if (blank && i != n - 1) cout << ' ';
    }
    cout << '\n';
}
void arrayPrint(const vector<bool>& vec, bool blank = true) {
    int n = vec.size();
    for (int i = 0; i < n; ++i) {
        cout << (vec[i] ? 'T' : 'F');
        if (blank && i != n - 1) cout << ' ';
    }
    cout << '\n';
}

template <typename T>
void arrayPrint(const vector<vector<T>>& mat, bool blank = true) {
    for (const auto& row : mat) {
        arrayPrint(row, blank);
    }
}
void arrayPrint(const vector<vector<string>>& mat, bool blank = false) {
    for (const auto& row : mat) {
        arrayPrint(row, blank);
    }
}

void arrayPrint(const vector<vector<bool>>& mat, bool blank = true) {
    for (const auto& row : mat) {
        arrayPrint(row, blank);
    }
}

template <class T> int sgn(T x) { return (x > 0) - (x < 0); }
template<class T>
struct Point {
    typedef Point P;
    T x, y;
    explicit Point(T x=0, T y=0) : x(x), y(y) {}
    bool operator<(P p) const { return tie(x,y) < tie(p.x,p.y); }
    bool operator==(P p) const { return tie(x,y)==tie(p.x,p.y); }
    P operator+(P p) const { return P(x+p.x, y+p.y); }
    P operator-(P p) const { return P(x-p.x, y-p.y); }
    P operator*(T d) const { return P(x*d, y*d); }
    P operator/(T d) const { return P(x/d, y/d); }
    T dot(P p) const { return x*p.x + y*p.y; }
    T cross(P p) const { return x*p.y - y*p.x; }
    T cross(P a, P b) const { return (a-*this).cross(b-*this); }
    T dist2() const { return x*x + y*y; }
    double dist() const { return sqrt((double)dist2()); }
    // angle to x-axis in interval [-pi, pi]
    double angle() const { return atan2(y, x); }
    P unit() const { return *this/dist(); } // makes dist()=1
    P perp() const { return P(-y, x); } // rotates +90 degrees
    P normal() const { return perp().unit(); }
    // returns point rotated 'a' radians ccw around the origin
    P rotate(double a) const {
        return P(x*cos(a)-y*sin(a),x*sin(a)+y*cos(a)); }
    friend ostream& operator<<(ostream& os, P p) {
        return os << "(" << p.x << "," << p.y << ")"; }
};

template<class P>
pair<int, P> lineInter(P s1, P e1, P s2, P e2) {
    auto d = (e1 - s1).cross(e2 - s2);
    if (d == 0) // if parallel
        return {-(s1.cross(e1, s2) == 0), P(0, 0)};
    auto p = s2.cross(e1, e2), q = s2.cross(e2, s1);
    return {1, (s1 * p + e1 * q) / d};
}
typedef Point<ld> P;
vector<P> convexHull(vector<P> pts) {
    if (sz(pts) <= 1) return pts;
    sort(all(pts));
    vector<P> h(sz(pts)+1);
    int s = 0, t = 0;
    for (int it = 2; it--; s = --t, reverse(all(pts)))
        for (P p : pts) {
            while (t >= s + 2 && h[t-2].cross(h[t-1], p) <= 0) t--;
            h[t++] = p;
        }
    return {h.begin(), h.begin() + t - (t == 2 && h[0] == h[1])};
}

typedef Point<ld> P;
array<P, 2> hullDiameter(vector<P> S) {
    int n = sz(S), j = n < 2 ? 0 : 1;
    pair<ld, array<P, 2>> res({0.0, {S[0], S[0]}});
    rep(i,0,j)
        for (;; j = (j + 1) % n) {
            res = max(res, {(S[i] - S[j]).dist2(), {S[i], S[j]}});
            if ((S[(j + 1) % n] - S[j]).cross(S[i + 1] - S[i]) >= 0)
                break;
        }
    return res.second;
}

int main() {
    cin.tie(0) -> sync_with_stdio(0);
    cin.exceptions(cin.failbit);
    cout << fixed << setprecision(10) << endl;
    int n; cin >> n;
    vector<Point<ld>> points(n);
    for (int i = 0; i < n; ++i) {
        ld x, y;
        cin >> x >> y;
        points[i] = Point<ld>(x, y);
    }
    auto conv = convexHull(points);
    auto dia = hullDiameter(conv);
    cout << (dia[1]-dia[0]).dist() << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 10696
final_rank: 9

code_snippet: #include <bits/stdc++.h>

typedef long long int64;

typedef long double db;
 
const db eps=1e-8L,pi=std::acos(-1.0L);

struct point{
  
  db x,y;
 
  point():x(),y(){}
  point(db _x,db _y):x(_x),y(_y){}

  db &operator[](int p){return p?y:x;}
  const db &operator[](int p)const{return p?y:x;}

  friend point operator+(const point &a,const point &b){return point(a.x+b.x,a.y+b.y);}
  friend point operator-(const point &a,const point &b){return point(a.x-b.x,a.y-b.y);}
  friend point operator*(const point &a,const db &b){return point(a.x*b,a.y*b);}
  friend point operator*(const db &b,const point &a){return point(a.x*b,a.y*b);}
  friend point operator/(const point &a,const db &b){return point(a.x/b,a.y/b);}
 
  friend db operator*(const point &a,const point &b){return a.x*b.x+a.y*b.y;}
  friend db operator%(const point &a,const point &b){return a.x*b.y-a.y*b.x;}
 
  friend bool operator==(const point &a,const point &b){return std::abs(a.x-b.x)<=eps&&std::abs(a.y-b.y)<=eps;}
  friend bool operator!=(const point &a,const point &b){return std::abs(a.x-b.x)>eps||std::abs(a.y-b.y)>eps;}
 
  db mod2()const{return x*x+y*y;}
  db mod()const{return std::sqrt(x*x+y*y);}
  db dis2(const point &a)const{return (*this-a).mod2();}
  db dis(const point &a)const{return (*this-a).mod();}
  point unit()const{return *this/mod();}

  db ang()const{return std::atan2(y,x);}
  db ang(const point &a)const{return std::atan2(*this%a,*this*a);}
  point norm()const{return point(-y,x);}
  point rot(const db &th)const{db s=std::sin(th),c=std::cos(th);return point(x*c-y*s,x*s+y*c);}

  // 象限，x 轴正半轴开始逆时针旋转1 2 -2 -1，左闭右开，原点返回 0
  int quad()const{
    if (std::abs(x)<=eps&&std::abs(y)<=eps) return 0;
    if (x>eps&&y>=-eps) return 1;
    if (x<=eps&&y>eps) return 2;
    if (x<-eps&&y<=eps) return -2;
    if (x>=-eps&&y<-eps) return -1;
    assert(0);
  }

};

// 保证只有 o 半边有点的极角排序
struct HalfPolarAngleCmp{
  point o;
  HalfPolarAngleCmp(const point &_o):o(_o){}
  bool operator()(const point &a,const point &b)const{
    db t=(a-o)%(b-o);
    return std::abs(t)<=eps?(a-o).mod2()<(b-o).mod2():t>eps;
  }
};

// 完整的极角排序，行为应与使用 std::atan2 相同
struct PolarAngleCmp{
  point o;
  PolarAngleCmp(const point &_o):o(_o){}
  bool operator()(point a,point b)const{
    a=a-o;b=b-o;
    if (a.quad()!=b.quad()) return a.quad()<b.quad();
    db t=a%b;
    return std::abs(t)<=eps?a.mod2()<b.mod2():t>eps;
  }
};

struct XFirstCmp{
  bool operator()(const point &a,const point &b)const{
    return std::abs(a.x-b.x)<=eps?a.y<b.y:a.x<b.x;
  }
};

struct YFirstCmp{
  bool operator()(const point &a,const point &b)const{
    return std::abs(a.y-b.y)<=eps?a.x<b.x:a.y<b.y;
  }
};

struct line{
 
  point s,t;
 
  line():s(),t(){}
  line(point _s,point _t):s(_s),t(_t){}
  
  // todo
  // 一般式 ax+by+c=0 转两点
  // line(db a,db b,db c){
  //   if (!a){
  //     s=point();
  //   }else if (!b){

  //   }else{

  //   }
  // }
 
  point &operator[](int p){return p?t:s;}
  const point &operator[](int p)const{return p?t:s;}

  db x(db y)const{return s.x+(y-s.y)/(t.y-s.y)*(t.x-s.x);}
  db y(db x)const{return s.y+(x-s.x)/(t.x-s.x)*(t.y-s.y);}
 
  point vec()const{return t-s;}
  db len2()const{return (t-s).mod2();}
  db len()const{return (t-s).mod();}

  point pro(const point &a)const{return s+(t-s)*((a-s)*(t-s)/(t-s).mod2());}
  point ref(const point &a)const{return 2*pro(a)-a;}
  db dis2(const point &a)const{return (pro(a)-a).mod2();}
  db dis(const point &a)const{return (pro(a)-a).mod();}

  // seg_in 不包括端点   seg_on 包括端点
  bool on(const point &a)const{return std::abs((a-s)%(a-t))<=eps;}
  bool seg_in(const point &a)const{return on(a)&&((a-s)*(a-t)<-eps);}
  bool seg_on(const point &a)const{return seg_in(a)||a==s||a==t;}

  // -1 重合   0 相交   1 平行
  int para(const line &b)const{
    const line &a=*this;
    return std::abs(a.vec()%b.vec())>eps?0:on(b.s)?-1:1;
  }

  // 包括端点
  int seg_cross(const line &b)const{
    const line &a=*this;
    if (std::min(b.s.x,b.t.x)-std::max(a.s.x,a.t.x)>eps) return 0;
    if (std::min(b.s.y,b.t.y)-std::max(a.s.y,a.t.y)>eps) return 0;
    if (std::min(a.s.x,a.t.x)-std::max(b.s.x,b.t.x)>eps) return 0;
    if (std::min(a.s.y,a.t.y)-std::max(b.s.y,b.t.y)>eps) return 0;
    auto sgn=[&](db x){return std::abs(x)<=eps?0:(x>0?1:-1);};
    if (sgn((a.s-b.s)%b.vec())*sgn((a.t-b.s)%b.vec())>0) return 0;
    if (sgn((b.s-a.s)%a.vec())*sgn((b.t-a.s)%a.vec())>0) return 0;
    return 1;
  }

  point cross(const line &b)const{
    const line &a=*this;
    point u=a.s-b.s,v=a.vec(),w=b.vec();
    return a.s+(w%u)/(v%w)*v;
  }
  
};

typedef std::vector<point> polygon;

// 1 在多边形内   0 不在多边形内   -1 在多边形边界上
int InPolygon(const polygon &a,const point &b){
  int res=0;
  for (int i=0;i<(int)a.size();++i){
    point u=a[i],v=a[(i+1)%(int)a.size()];
    if (line(u,v).seg_on(b)) return -1;
    if (std::abs(u.y-v.y)<=eps) continue;
    if (u.y>v.y) std::swap(u,v);
    if ((b-u)%(v-u)>eps) continue;
    if (b.y-u.y>eps&&b.y-v.y<=eps) res^=1;
  }
  return res&1;
}

// 1 在多边形内   0 不在多边形内   -1 在多边形边界上
int InConvex(const polygon &a,const point &b){
  if (b==a[0]) return -1;
  if ((a.back()-a[0])%(b-a[0])>eps) return 0;
  if (line(a.back(),a[0]).seg_on(b)) return -1;
  const point &o=a[0];
  int p=std::lower_bound(a.begin(),a.end()-1,b,HalfPolarAngleCmp(o))-a.begin();
  if (line(a[p-1],a[p]).seg_on(b)) return -1;
  return (a[p]-a[p-1])%(b-a[p-1])>=-eps;
}

polygon ConvexHull(polygon a){
  point o=*std::min_element(a.begin(),a.end(),YFirstCmp());
  std::sort(a.begin(),a.end(),HalfPolarAngleCmp(o));
  polygon res({a.front()});
  for (int i=1;i<(int)a.size();++i){
    for (;res.size()>=2&&(res.back()-res.end()[-2])%(a[i]-res.back())<=eps;res.pop_back());
    res.push_back(a[i]);
  }
  return res;
}

// tag
db Diameter(polygon a){
  db res=0;
  int n=a.size();
  a.push_back(a[0]);
  for (int i=0,j=1;i<n;++i){
    for (;(a[i+1]-a[i])%(a[j+1]-a[i])-(a[i+1]-a[i])%(a[j]-a[i])>eps;j=(j+1)%n);
    res=std::max({res,(a[j]-a[i]).mod2(),(a[j]-a[i+1]).mod2()});
  }
  return std::sqrt(res);
}

polygon Minkowski(polygon a,polygon b){
  int n=a.size(),m=b.size();
  polygon res({a[0]+b[0]});
  a.push_back(a[0]);b.push_back(b[0]);
  for (int i=0,j=0;;){
    (a[i+1]-a[i])%(b[j+1]-b[j])>eps?i=(i+1)%n:j=(j+1)%m;
    point t=a[i]+b[j];
    if (res.size()>=2&&std::abs((t-res.back())%(res.back()-res.end()[-2]))<=eps)
      res.pop_back();
    res.push_back(t);
    if (!i&&!j) break;
  }
  res.pop_back();
  return res;
}

// 半平面在直线逆时针方向
polygon HalfPlaneIntersection(std::vector<line> a){    // 半平面在直线逆时针方向
  const db inf=1e20L;
  a.emplace_back(point(-inf,-inf),point(inf,-inf));
  a.emplace_back(point(inf,-inf),point(inf,inf));
  a.emplace_back(point(inf,inf),point(-inf,inf));
  a.emplace_back(point(-inf,inf),point(-inf,-inf));
  std::sort(a.begin(),a.end(),[&](const line &a,const line &b){
    point u=a.vec(),v=b.vec();
    if (u.quad()!=v.quad()) return u.quad()<v.quad();
    db t=u%v;
    if (std::abs(t)>eps) return t>eps;
    return (a[0]-b[0])%b.vec()<-eps;
  });
  std::deque<point> qc;
  std::deque<line> ql;
  ql.emplace_back(a[0]);
  auto popback=[&](const line &a){
    for (;!qc.empty()&&(qc.back()-a.s)%a.vec()>=-eps;)
      qc.pop_back(),ql.pop_back();
  };
  auto popfront=[&](const line &a){
    for (;!qc.empty()&&(qc.front()-a.s)%a.vec()>=-eps;)
      qc.pop_front(),ql.pop_front();
  };
  for (int i=1;i<(int)a.size();++i){
    if (a[i].vec()*a[i-1].vec()>=eps&&std::abs(a[i].vec()%a[i-1].vec())<=eps) continue;
    popback(a[i]);popfront(a[i]);
    if (!ql.empty()){
      if (std::abs(a[i].vec()%ql.back().vec())<=eps) return {};
      qc.push_back(a[i].cross(ql.back()));
    }
    ql.push_back(a[i]);
  }
  popback(ql.front());popfront(ql.back());
  qc.push_back(ql.front().cross(ql.back()));
  if (qc.size()<=2) return {};
  polygon res;
  for (auto t:qc) res.push_back(t);
  return res;
}

struct circle{

  point o;
  db r;

  circle():o(),r(){}
  circle(point _o,db _r):o(_o),r(_r){}

  point operator()(db th)const{return o+r*point(std::cos(th),std::sin(th));}

  // tag
  // 1 在圆内   -1 在圆上   0 在圆外
  int in(const point &a)const{
    db d2=(o-a).mod2();
    return std::abs(d2-r*r)<=eps?-1:r*r-d2>eps;
  }

  std::vector<point> cross(const line &b)const{
    const circle &a=*this;
    point t=b.pro(a.o);
    db d2=(t-a.o).mod2();
    if (d2-a.r*a.r>eps) return {};
    if (std::abs(d2-a.r*a.r)<=eps) return {t};
    point v=b.vec().unit()*std::sqrt(a.r*a.r-d2);
    return {t+v,t-v};
  }

  // tag
  std::vector<point> cross(const circle &b)const{
    const circle &a=*this;
    db d2=(a.o-b.o).mod2(),d=std::sqrt(d2);
    if (a.r+b.r-d<-eps||std::abs(a.r-b.r)-d>eps) return {};
    if (std::abs(a.r+b.r-d)<=eps||std::abs(std::abs(a.r-b.r)-d)<=eps)
      return {a.o+(b.o-a.o).unit()*a.r};
    db c=(d2+a.r*a.r-b.r*b.r)/(2*d2);
    point t=a.o+c*(b.o-a.o);
    point v=(b.o-a.o).norm().unit()*std::sqrt(a.r*a.r-(t-a.o).mod2());
    return {t+v,t-v};
  }

};

// 外接圆，共线时是三点的最小圆覆盖
circle CircumCircle(point a,point b,point c){
  if (std::abs((a-b)%(b-c))<=eps){
    if (YFirstCmp()(a,b)) std::swap(a,b);
    if (YFirstCmp()(a,c)) std::swap(a,c);
    if (YFirstCmp()(b,c)) std::swap(b,c);
    return circle((a+c)/2,(c-a).mod()/2);
  }
  point u=(a+b)/2,v=(a+c)/2;
  line p(u,u+(b-a).norm()),q(v,v+(c-a).norm());
  point o=p.cross(q);
  return circle(o,(o-a).mod());
}

// 内切圆，共线时可能出错
circle InCircle(const point &a,const point &b,const point &c){
  line p(a,a+(b-a).unit()+(c-a).unit());
  line q(b,b+(a-b).unit()+(c-b).unit());
  point o=p.cross(q);
  return circle(o,line(a,b).dis(o));
}

circle MinimumEnclosingCircle(polygon a){
  std::shuffle(a.begin(),a.end(),std::mt19937(time(0)));
  circle res(a[0],0);
  for (int i=1;i<(int)a.size();++i){
    if (res.in(a[i])) continue;
    res=circle(a[i],0);
    for (int j=0;j<i;++j){
      if (res.in(a[j])) continue;
      res=circle((a[i]+a[j])/2,(a[i]-a[j]).mod()/2);
      for (int k=0;k<j;++k){
        if (res.in(a[k])) continue;
        res=CircumCircle(a[i],a[j],a[k]);
      }
    }
  }
  return res;
}





db Area(const polygon &a){
  db res=0;
  for (int i=0;i<(int)a.size();++i)
    res+=a[i]%a[(i+1)%(int)a.size()];
  assert(std::abs(res)>=-eps);
  return res/2;
}

db Length(const polygon &a){
  db res=0;
  for (int i=0;i<(int)a.size();++i)
    res+=(a[i]-a[(i+1)%(int)a.size()]).mod();
  return res;
}

void work(){
  // circle a,b;
  // std::cin>>a.o.x>>a.o.y>>a.r;
  // std::cin>>b.o.x>>b.o.y>>b.r;
  // auto ans=a.cross(b);
  // if (ans.size()==1) ans.push_back(ans.front());
  // std::sort(ans.begin(),ans.end(),XFirstCmp());
  // std::cout<<ans[0][0]<<' '<<ans[0][1]<<' '<<ans[1][0]<<' '<<ans[1][1]<<'\n';
  int n;
  std::cin>>n;
  polygon a(n);
  for (auto &[x,y]:a) std::cin>>x>>y;
  std::cout<<Diameter(ConvexHull(a))<<'\n';
  // int q;
  // std::cin>>q;
  // for (;q--;){
  //   point p;
  //   std::cin>>p.x>>p.y;
  //   int ans=InPolygon(a,p);
  //   std::cout<<(ans?ans==1?2:1:0)<<'\n';
  // }
}

int main(){
  std::ios::sync_with_stdio(0);
  std::cin.tie(0);
  std::cout<<std::fixed<<std::setprecision(12);
  std::cerr<<std::fixed<<std::setprecision(2);
  int T=1;
  // std::cin>>T;
  for (;T--;) work();
  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 8188
final_rank: 12

code_snippet: #include <bits/stdc++.h>

typedef long long int64;

typedef long double db;
 
const db eps=1e-8L,pi=std::acos(-1.0L);

struct point{
  
  db x,y;
 
  point():x(),y(){}
  point(db _x,db _y):x(_x),y(_y){}

  db &operator[](int p){return p?y:x;}
  const db &operator[](int p)const{return p?y:x;}

  friend point operator+(const point &a,const point &b){return point(a.x+b.x,a.y+b.y);}
  friend point operator-(const point &a,const point &b){return point(a.x-b.x,a.y-b.y);}
  friend point operator*(const point &a,const db &b){return point(a.x*b,a.y*b);}
  friend point operator*(const db &b,const point &a){return point(a.x*b,a.y*b);}
  friend point operator/(const point &a,const db &b){return point(a.x/b,a.y/b);}
 
  friend db operator*(const point &a,const point &b){return a.x*b.x+a.y*b.y;}
  friend db operator%(const point &a,const point &b){return a.x*b.y-a.y*b.x;}
 
  friend bool operator==(const point &a,const point &b){return std::abs(a.x-b.x)<=eps&&std::abs(a.y-b.y)<=eps;}
  friend bool operator!=(const point &a,const point &b){return std::abs(a.x-b.x)>eps||std::abs(a.y-b.y)>eps;}
 
  db mod2()const{return x*x+y*y;}
  db mod()const{return std::sqrt(x*x+y*y);}
  db dis2(const point &a)const{return (*this-a).mod2();}
  db dis(const point &a)const{return (*this-a).mod();}
  point unit()const{return *this/mod();}

  db ang()const{return std::atan2(y,x);}
  db ang(const point &a)const{return std::atan2(*this%a,*this*a);}
  point norm()const{return point(-y,x);}
  point rot(const db &th)const{db s=std::sin(th),c=std::cos(th);return point(x*c-y*s,x*s+y*c);}

};
 
struct line{
 
  point s,t;
 
  line():s(),t(){}
  line(point _s,point _t):s(_s),t(_t){}
 
  point &operator[](int p){return p?t:s;}
  const point &operator[](int p)const{return p?t:s;}

  db x(db y)const{return s.x+(y-s.y)/(t.y-s.y)*(t.x-s.x);}
  db y(db x)const{return s.y+(x-s.x)/(t.x-s.x)*(t.y-s.y);}
 
  point vec()const{return t-s;}
  db len2()const{return (t-s).mod2();}
  db len()const{return (t-s).mod();}

  point pro(const point &a)const{return s+(t-s)*((a-s)*(t-s)/(t-s).mod2());}
  point ref(const point &a)const{return 2*pro(a)-a;}
  db dis2(const point &a)const{return (pro(a)-a).mod2();}
  db dis(const point &a)const{return (pro(a)-a).mod();}

  // seg_in 不包括端点   seg_on 包括端点
  bool on(const point &a)const{return std::abs((a-s)%(a-t))<=eps;}
  bool seg_in(const point &a)const{return on(a)&&((a-s)*(a-t)<-eps);}
  bool seg_on(const point &a)const{return seg_in(a)||a==s||a==t;}

  // -1 重合   0 相交   1 平行
  int para(const line &b)const{
    const line &a=*this;
    return std::abs(a.vec()%b.vec())>eps?0:on(b.s)?-1:1;
  }

  // 包括端点
  int seg_cross(const line &b)const{
    const line &a=*this;
    if (std::max(a.s.x,a.t.x)+eps<std::min(b.s.x,b.t.x)) return 0;
    if (std::max(b.s.x,b.t.x)+eps<std::min(a.s.x,a.t.x)) return 0;
    if (std::max(a.s.y,a.t.y)+eps<std::min(b.s.y,b.t.y)) return 0;
    if (std::max(b.s.y,b.t.y)+eps<std::min(a.s.y,a.t.y)) return 0;
    auto sgn=[&](db x){return std::abs(x)<=eps?0:(x>0?1:-1);};
    if (sgn((a.s-b.s)%b.vec())*sgn((a.t-b.s)%b.vec())>0) return 0;
    if (sgn((b.s-a.s)%a.vec())*sgn((b.t-a.s)%a.vec())>0) return 0;
    return 1;
  }

  point cross(const line &b)const{
    const line &a=*this;
    point u=a.s-b.s,v=a.vec(),w=b.vec();
    return a.s+(w%u)/(v%w)*v;
  }
  
};

typedef std::vector<point> polygon;

polygon ConvexHull(polygon a){
  auto o=*std::min_element(a.begin(),a.end(),[&](const point &a,const point &b){
    return a.y==b.y?a.x<b.x:a.y<b.y;
  });
  std::sort(a.begin(),a.end(),[&](const point &a,const point &b){
    int64 t=(a-o)%(b-o);
    return t==0?(a-o).mod2()<(b-o).mod2():t>0;
  });
  polygon res({a.front()});
  for (int i=1;i<(int)a.size();++i){
    for (;res.size()>=2&&(res.back()-res.end()[-2])%(a[i]-res.back())<=eps;res.pop_back());
    res.push_back(a[i]);
  }
  return res;
}

db Area(const polygon &a){
  db res=0;
  for (int i=0;i<(int)a.size();++i)
    res+=a[i]%a[(i+1)%(int)a.size()];
  assert(std::abs(res)>=-eps);
  return res/2;
}

db Length(const polygon &a){
  db res=0;
  for (int i=0;i<(int)a.size();++i)
    res+=(a[i]-a[(i+1)%(int)a.size()]).mod();
  return res;
}

db Diameter(polygon a){
  db res=0;
  int n=a.size();
  a.push_back(a[0]);
  for (int i=0,j=1;i<n;++i){
    for (;(a[i+1]-a[i])%(a[j]-a[i])<(a[i+1]-a[i])%(a[j+1]-a[i])-eps;j=(j+1)%n);
    res=std::max({res,(a[j]-a[i]).mod2(),(a[j]-a[i+1]).mod2()});
  }
  return std::sqrt(res);
}

// struct circle{

//   point o;
//   db r;

//   circle():o(),r(){}
//   circle(point _o,db _r):o(_o),r(_r){}

//   circle()

// };

void work(){
  int n;
  std::cin>>n;
  polygon a(n);
  for (auto &[x,y]:a)
    std::cin>>x>>y;
  // auto sqr=[&](db x){return x*x;};
  std::cout<<Diameter(ConvexHull(a))+eps<<'\n';
}

int main(){
  std::ios::sync_with_stdio(0);
  std::cin.tie(0);
  std::cout<<std::fixed<<std::setprecision(10);
  std::cerr<<std::fixed<<std::setprecision(2);
  int T=1;
  // std::cin>>T;
  for (;T--;) work();
  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 8164
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i = 0; i< (n); ++i)
#define repi(i, a, b) for (int i = (a); i < (b); ++i)
#define all(x) (x).begin(), (x).end()
#define fore(i, a) for(auto &i:a)
using ll = long long;
using int64 = long long;
#define DEBUG(x) cerr << #x << ": "; for (auto _ : x) cerr << _ << " "; cerr << endl;
const int64 infll = (1LL << 62) - 1;
const int inf = (1 << 30) - 1;

struct IoSetup {
	IoSetup() {
		cin.tie(nullptr);
		ios::sync_with_stdio(false);
		cout << fixed << setprecision(10);
		cerr << fixed << setprecision(10);
	}
} iosetup;

template <typename T1, typename T2>
ostream& operator<<(ostream& os, const pair<T1, T2>& p) {
	os << p.first << " " << p.second;
	return os;
}

template <typename T1, typename T2>
istream& operator>>(istream& is, pair<T1, T2>& p) {
	is >> p.first >> p.second;
	return is;
}

template <typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {
	for (int i = 0; i < (int)v.size(); i++) {
		os << v[i] << (i + 1 != v.size() ? " " : "");
	}
	return os;
}

template <typename T>
istream& operator>>(istream& is, vector<T>& v) {
	for (T& in : v) is >> in;
	return is;
}

template <typename T1, typename T2>
inline bool chmax(T1& a, T2 b) {
	return a < b && (a = b, true);
}

template <typename T1, typename T2>
inline bool chmin(T1& a, T2 b) {
	return a > b && (a = b, true);
}

template <typename T = int64>
vector<T> make_v(size_t a) {
	return vector<T>(a);
}

template <typename T, typename... Ts>
auto make_v(size_t a, Ts... ts) {
	return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...));
}

template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type fill_v(T& t, const V& v) {
	t = v;
}

template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type fill_v(T& t, const V& v) {
	for (auto& e : t) fill_v(e, v);
}

template <typename F>
struct FixPoint : F {
	explicit FixPoint(F&& f) : F(std::forward<F>(f)) {}

	template <typename... Args>
		decltype(auto) operator()(Args&&... args) const {
			return F::operator()(*this, std::forward<Args>(args)...);
		}
};

template <typename F>
inline decltype(auto) MFP(F&& f) {
	return FixPoint<F>{std::forward<F>(f)};
}
//Geometry
namespace geometry {
using Real = double;
const Real EPS = 1e-15;
const Real PI = acos(static_cast<Real>(-1));

enum { OUT, ON, IN };

inline int sign(const Real& r) { return r <= -EPS ? -1 : r >= EPS ? 1 : 0; }

inline bool equals(const Real& a, const Real& b) { return sign(a - b) == 0; }
}  // namespace geometry

namespace geometry {
using Point = complex<Real>;

istream& operator>>(istream& is, Point& p) {
  Real a, b;
  is >> a >> b;
  p = Point(a, b);
  return is;
}

ostream& operator<<(ostream& os, const Point& p) {
  return os << real(p) << " " << imag(p);
}

Point operator*(const Point& p, const Real& d) {
  return Point(real(p) * d, imag(p) * d);
}

// rotate point p counterclockwise by theta rad
Point rotate(Real theta, const Point& p) {
  return Point(cos(theta) * real(p) - sin(theta) * imag(p),
               sin(theta) * real(p) + cos(theta) * imag(p));
}

Real cross(const Point& a, const Point& b) {
  return real(a) * imag(b) - imag(a) * real(b);
}

Real dot(const Point& a, const Point& b) {
  return real(a) * real(b) + imag(a) * imag(b);
}

bool compare_x(const Point& a, const Point& b) {
  return equals(real(a), real(b)) ? imag(a) < imag(b) : real(a) < real(b);
}

bool compare_y(const Point& a, const Point& b) {
  return equals(imag(a), imag(b)) ? real(a) < real(b) : imag(a) < imag(b);
}

using Points = vector<Point>;
}  // namespace geometry

namespace geometry {
struct Line {
  Point a, b;

  Line() = default;

  Line(const Point& a, const Point& b) : a(a), b(b) {}

  Line(const Real& A, const Real& B, const Real& C) {  // Ax+By=C
    if (equals(A, 0)) {
      assert(!equals(B, 0));
      a = Point(0, C / B);
      b = Point(1, C / B);
    } else if (equals(B, 0)) {
      a = Point(C / A, 0);
      b = Point(C / A, 1);
    } else if (equals(C, 0)) {
      a = Point(0, C / B);
      b = Point(1, (C - A) / B);
    } else {
      a = Point(0, C / B);
      b = Point(C / A, 0);
    }
  }

  friend ostream& operator<<(ostream& os, Line& l) {
    return os << l.a << " to " << l.b;
  }

  friend istream& operator>>(istream& is, Line& l) { return is >> l.a >> l.b; }
};

using Lines = vector<Line>;
}  // namespace geometry

namespace geometry {
struct Segment : Line {
  Segment() = default;

  using Line::Line;
};

using Segments = vector<Segment>;
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
Point projection(const Line& l, const Point& p) {
  auto t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
  return l.a + (l.a - l.b) * t;
}
}  // namespace geometry

namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_B
Point reflection(const Line& l, const Point& p) {
  return p + (projection(l, p) - p) * 2;
}
}  // namespace geometry

namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
constexpr int COUNTER_CLOCKWISE = +1;
constexpr int CLOCKWISE = -1;
constexpr int ONLINE_BACK = +2;   // c-a-b
constexpr int ONLINE_FRONT = -2;  // a-b-c
constexpr int ON_SEGMENT = 0;     // a-c-b
int ccw(const Point& a, Point b, Point c) {
  b = b - a, c = c - a;
  if (sign(cross(b, c)) == +1) return COUNTER_CLOCKWISE;
  if (sign(cross(b, c)) == -1) return CLOCKWISE;
  if (sign(dot(b, c)) == -1) return ONLINE_BACK;
  if (norm(b) < norm(c)) return ONLINE_FRONT;
  return ON_SEGMENT;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool is_orthogonal(const Line& a, const Line& b) {
  return equals(dot(a.a - a.b, b.a - b.b), 0.0);
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool is_parallel(const Line& a, const Line& b) {
  return equals(cross(a.b - a.a, b.b - b.a), 0.0);
}
}  // namespace geometry

namespace geometry {
bool is_intersect_ss(const Segment& s, const Segment& t) {
  return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
         ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}
}  // namespace geometry
namespace geometry {
bool is_intersect_sp(const Segment& s, const Point& p) {
  return ccw(s.a, s.b, p) == ON_SEGMENT;
}
}  // namespace geometry

namespace geometry {
Point cross_point_ll(const Line& l, const Line& m) {
  Real A = cross(l.b - l.a, m.b - m.a);
  Real B = cross(l.b - l.a, l.b - m.a);
  if (equals(abs(A), 0) && equals(abs(B), 0)) return m.a;
  return m.a + (m.b - m.a) * B / A;
}
}  // namespace geometry
namespace geometry {
Real distance_sp(const Segment& s, const Point& p) {
  Point r = projection(s, p);
  if (is_intersect_sp(s, r)) return abs(r - p);
  return min(abs(s.a - p), abs(s.b - p));
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D
Real distance_ss(const Segment& a, const Segment& b) {
  if (is_intersect_ss(a, b)) return 0;
  return min({distance_sp(a, b.a), distance_sp(a, b.b), distance_sp(b, a.a),
              distance_sp(b, a.b)});
}
}  // namespace geometry
namespace geometry {
using Polygon = vector<Point>;
using Polygons = vector<Polygon>;
}  // namespace geometry

namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_A
Real area(const Polygon& p) {
  int n = (int)p.size();
  Real A = 0;
  for (int i = 0; i < n; ++i) {
    A += cross(p[i], p[(i + 1) % n]);
  }
  return A * 0.5;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_B
bool is_convex_polygon(const Polygon& p) {
  int n = (int)p.size();
  for (int i = 0; i < n; i++) {
    if (ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]) == CLOCKWISE)
      return false;
  }
  return true;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_C
int contains(const Polygon& Q, const Point& p) {
  bool in = false;
  for (int i = 0; i < Q.size(); i++) {
    Point a = Q[i] - p, b = Q[(i + 1) % Q.size()] - p;
    if (imag(a) > imag(b)) swap(a, b);
    if (sign(imag(a)) <= 0 && 0 < sign(imag(b)) && sign(cross(a, b)) < 0)
      in = !in;
    if (equals(cross(a, b), 0) && sign(dot(a, b)) <= 0) return ON;
  }
  return in ? IN : OUT;
}
}  // namespace geometry
namespace geometry {
int convex_polygon_contains(const Polygon& Q, const Point& p) {
  int N = (int)Q.size();
  Point g = (Q[0] + Q[N / 3] + Q[N * 2 / 3]) / 3.0;
  if (equals(imag(g), imag(p)) && equals(real(g), real(p))) return IN;
  Point gp = p - g;
  int l = 0, r = N;
  while (r - l > 1) {
    int mid = (l + r) / 2;
    Point gl = Q[l] - g;
    Point gm = Q[mid] - g;
    if (cross(gl, gm) > 0) {
      if (cross(gl, gp) >= 0 && cross(gm, gp) <= 0)
        r = mid;
      else
        l = mid;
    } else {
      if (cross(gl, gp) <= 0 && cross(gm, gp) >= 0)
        l = mid;
      else
        r = mid;
    }
  }
  r %= N;
  Real v = cross(Q[l] - p, Q[r] - p);
  return sign(v) == 0 ? ON : sign(v) == -1 ? OUT : IN;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
Polygon convex_hull(Polygon& p, bool strict = true) {
  int n = (int)p.size(), k = 0;
  if (n <= 2) return p;
  sort(begin(p), end(p), compare_x);
  vector<Point> ch(2 * n);
  auto check = [&](int i) {
    return sign(cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1])) <= -1 + strict;
  };
  for (int i = 0; i < n; ch[k++] = p[i++]) {
    while (k >= 2 && check(i)) --k;
  }
  for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
    while (k >= t && check(i)) --k;
  }
  ch.resize(k - 1);
  return ch;
}
}  // namespace geometry
namespace geometry {
// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_B
pair<int, int> convex_polygon_diameter(const Polygon& p) {
  int N = (int)p.size();
  int is = 0, js = 0;
  for (int i = 1; i < N; i++) {
    if (imag(p[i]) > imag(p[is])) is = i;
    if (imag(p[i]) < imag(p[js])) js = i;
  }
  Real maxdis = norm(p[is] - p[js]);

  int maxi, maxj, i, j;
  i = maxi = is;
  j = maxj = js;
  do {
    if (cross(p[(i + 1) % N] - p[i], p[(j + 1) % N] - p[j]) >= 0) {
      j = (j + 1) % N;
    } else {
      i = (i + 1) % N;
    }
    if (norm(p[i] - p[j]) > maxdis) {
      maxdis = norm(p[i] - p[j]);
      maxi = i;
      maxj = j;
    }
  } while (i != is || j != js);
  return minmax(maxi, maxj);
}
}  // namespace geometry
using namespace geometry;
int main(){
	ll n; cin >> n;
	Polygon a(n);
	rep(i, n)cin >> a[i];
	auto[p, q] = convex_polygon_diameter(a);
	cout << abs(a[p]-a[q]) << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 4396
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#ifndef ONLINE_JUDGE
#define _GLIBCXX_DEBUG
#include <iostream>
template <typename T, typename U>
ostream& operator<<(ostream& os, const pair<T, U>& p) {return os<<'(' <<p.first<<", "<<p.second<<')';}
template <typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {os<<'[';for(auto it=v.begin();it!=v.end();++it){os<<*it; if(next(it)!=v.end()){os<<", ";}}return os<<']';}
template <typename T>
ostream& operator<<(ostream& os, const vector<vector<T>>& vv) {os<<"[\n";for(auto it=vv.begin();it!=vv.end();++it){os<<"  "<<*it;if(next(it)!=vv.end()){os<<",\n";}else{os<<"\n";}}return os<<"]";}
#define dump(x) cerr << #x << " = " << (x) << '\n'
#else
#define dump(x) (void)0
#endif
using ll = long long;
using ld = long double;
using P = pair<ll,ll>;
template<class T> using pq = priority_queue<T>;
template<class T> using pq_g = priority_queue<T, vector<T>, greater<T>>;
#define out(x) cout<<x<<'\n'
#define all(v) (v).begin(),(v).end()
#define rall(v) (v).rbegin(),(v).rend()
#define OVERLOAD_REP(_1,_2,_3,name,...) name
#define REP1(i,n) for (auto i = std::decay_t<decltype(n)>{}; (i) < (n); ++(i))
#define REP2(i, l, r) for (auto i = (l); (i) < (r); ++(i))
#define rep(...) OVERLOAD_REP(__VA_ARGS__, REP2, REP1)(__VA_ARGS__)
template<class T> inline bool chmin(T& a, T b) {if(a > b){a = b; return true;} else {return false;}};
template<class T> inline bool chmax(T& a, T b) {if(a < b){a = b; return true;} else {return false;}};
const ll INF=(1LL<<60);
const ll mod=998244353;
using Graph = vector<vector<ll>>;
using Network = vector<vector<pair<ll,ll>>>;
using Grid = vector<string>;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};
const int dx2[8] = {1, 1,  1, 0,  0, -1, -1, -1};
const int dy2[8] = {1, 0, -1, 1, -1,  1,  0, -1};

// #include "../geo.hpp"

using D = long double;
const D EPS = 1e-12L;
const D PI = acosl(-1.0L);
using P2 = complex<D>;
struct L2{P2 a, b;};
struct S2{P2 a, b;};
using G2 = vector<P2>;

int sgn(D x){return (x>EPS)-(x<-EPS);}
int cmp(D a, D b){return sgn(a-b);}
bool eq(D a, D b){return cmp(a, b)==0;}
bool lt(D a, D b){return cmp(a, b)<0;}
bool le(D a, D b){return cmp(a, b)<=0;}
bool gt(D a, D b){return cmp(a, b)>0;}
bool ge(D a, D b){return cmp(a, b)>=0;}
bool eqP2(P2 a, P2 b){return eq(real(a), real(b)) && eq(imag(a), imag(b));}
bool ltP2(P2 a, P2 b) {
    return lt(real(a), real(b)) || (eq(real(a), real(b)) && lt(imag(a), imag(b)));
}

D dot(P2 a, P2 b) {return real(conj(a)*b);}
D cross(P2 a, P2 b) {return imag(conj(a)*b);}
int ccw(P2 a, P2 b, P2 c) {
    b-=a; c-=a;
    auto cr=cross(b, c), dt=dot(b, c);
    if (cr > EPS) return +1;       // counter clockwise
    if (cr < -EPS) return -1;      // clockwise
    if (dt < -EPS) return +2;         // c--a--b
    if (lt(norm(b), norm(c))) return -2; // a--b--c
    return 0;                  // a--c--b
}
int turn(P2 a, P2 b, P2 c) { return sgn(cross(b - a, c - a)); }

P2 rot(P2 a, D th) { return a*polar<D>(1, th); }
P2 rot90(P2 a) { return P2(-imag(a), real(a)); }
bool ltP2Arg(P2 a, P2 b) {
    auto up = [](P2 p) { return (imag(p) > 0 || (eq(imag(p), 0) && ge(real(p), 0))); };
    if (up(a) != up(b)) return up(a);
    if (!eq(cross(a, b), 0)) return cross(a, b) > 0;
    return lt(norm(a), norm(b));
}

inline void rd(P2& p) {D x, y; cin>>x>>y; p = P2(x, y);}
#define setp(n) cout<<setprecision(n)<<fixed
inline void pr(const P2& p) {setp(12); cout<<real(p)<<' '<<imag(p)<<'\n';}

// ----------------------------------------------------------------------------------

P2 proj(L2 l, P2 p) { P2 a=p-l.a, b=l.b-l.a; return l.a+b*dot(a, b)/norm(b);}
P2 refl(L2 l, P2 p) { return proj(l, p)*2.0L - p; }
bool isParallel(L2 l, L2 m){ return eq(cross(l.a-l.b, m.a-m.b), 0); }
bool isPerp(L2 l, L2 m){ return eq(dot(l.a-l.b, m.a-m.b), 0); }

bool isLL(L2 l, L2 m){ return !eq(cross(l.a-l.b, m.a-m.b), 0); }
bool isLS(L2 l, S2 s){ return turn(l.a, l.b, s.a)*turn(l.a, l.b, s.b)<=0; }
bool isSS(S2 s, S2 t) {
    return
    ccw(s.a,s.b,t.a)*ccw(s.a,s.b,t.b) <= 0 &&
    ccw(t.a,t.b,s.a)*ccw(t.a,t.b,s.b) <= 0;
}

P2 cpLL(L2 l, L2 m) {
    P2 a=l.b-l.a, b=m.b-m.a;
    D d=cross(a, b);
    if (eq(d, 0)) return P2(INF, INF); // parallel
    return l.a + a*cross(m.b-l.a,b)/d;
}

D distLP(L2 l, P2 p) { return abs(cross(l.b-l.a, p-l.a))/abs(l.b-l.a); }
D distSP(S2 s, P2 p) {
    P2 a=s.a, b=s.b;
    if (lt(dot(b-a, p-a), 0)) return abs(p-a);
    if (lt(dot(a-b, p-b), 0)) return abs(p-b);
    return distLP({a,b}, p);
}
D distSS(S2 s, S2 t) {
    if (isSS(s, t)) return 0;
    return min({distSP(s, t.a), distSP(s, t.b), distSP(t, s.a), distSP(t, s.b)});
}

D area(G2 g) {
    D s=0;
    int n=g.size();
    rep(i, n) s+=cross(g[i], g[(i+1)%n]);
    return s/2.0L;
}

bool isConvex(G2 g) {
    int n=g.size();
    if (n < 3) return false;
    int dir = 0;
    rep(i,n){
        int s = turn(g[i], g[(i+1)%n], g[(i+2)%n]);
        if (s == 0) return false; // -> continue : allows three points on a line
        if (dir == 0) dir = s;
        else if (dir*s < 0) return false;
    }
    return true;
}

int inG(G2 g, P2 p) {
    bool in=false;
    int n=g.size();
    rep(i, n) {
        P2 a=g[i], b=g[(i+1)%n];
        if (ccw(a, b, p) == 0) return 1; // on boundary
        if (a.imag() > b.imag()) swap(a, b);
        if (le(a.imag(), p.imag()) && lt(p.imag(), b.imag()) && turn(a, b, p) > 0) in=!in;
    }
    return in ? 2 : 0; // inside : outside
}

G2 hull(G2 g) {
    sort(all(g), ltP2);
    g.erase(unique(all(g), eqP2), g.end());
    if (g.size()<=1) return g;
    G2 lo, up;
    for (auto &p:g) {
        while (lo.size()>=2 && turn(lo[lo.size()-2], lo.back(), p)<=0) lo.pop_back(); //-> turn()<0 : allow three points on a line
        lo.push_back(p);
    }
    reverse(all(g));
    for (auto &p:g) {
        while (up.size()>=2 && turn(up[up.size()-2], up.back(), p)<=0) up.pop_back(); // -> turn()<0 : allow three points on a line
        up.push_back(p);
    }
    lo.pop_back(); up.pop_back();
    lo.insert(lo.end(), all(up));
    return lo;
}

pair<P2, P2> diameter(G2 g) {
    auto h = hull(g);
    int n=h.size();
    if(n==0) return {P2(0,0), P2(0,0)};
    if(n==1) return {h[0], h[0]};
    auto area2=[&](int i, int j, int k) {return abs(cross(h[j]-h[i], h[k]-h[i]));};
    D maxd=0;
    pair<P2, P2> res={h[0], h[0]};
    int j=1;
    rep(i, n) {
        int ni=(i+1)%n;
        while (gt(area2(i, ni, (j+1)%n), area2(i, ni, j))) j=(j+1)%n;
        if (gt(abs(h[i]-h[j]), maxd)) maxd=abs(h[i]-h[j]), res={h[i], h[j]};
        if (gt(abs(h[ni]-h[j]), maxd)) maxd=abs(h[ni]-h[j]), res={h[ni], h[j]};
    }
    return res;
}

// int inConvex(G2 h, P2 p) {
//     int n=h.size();
//     if (n==0) return 0;
//     if (n==1) return eqP2(h[0], p) ? 2 : 0;
//     if (n==2) return ccw(h[0], h[1], p)==0 && le(abs(h[0]-p)+abs(h[1]-p), abs(h[0]-h[1])) ? 2 : 0;
//     int left=1, right=n-1;
//     while (right-left>1) {
//         int mid=(left+right)/2;
//         if (turn(h[0], h[mid], p)<0) right=mid;
//         else left=mid;
//     }
//     if (turn(h[0], h[left], p)<0) return 0;
//     if (turn(h[left], h[right%n], p)<0) return 0;
//     if (turn(h[right%n], h[0], p)<0) return 0;
//     if (turn(h[0], h[left], p)==0 || turn(h[left], h[right%n], p)==0 || turn(h[right%n], h[0], p)==0) return 2;
//     return 1;
// }

G2 cutConvex(G2 h, L2 l) {
    int n=h.size();
    G2 res;
    if (n==0) return res;
    auto inside=[&](P2 p){ return turn(l.a, l.b, p) >= 0; };
    rep(i, n) {
        P2 a=h[i], b=h[(i+1)%n];
        bool ina=inside(a), inb=inside(b);
        if (ina&&inb) res.push_back(b);
        else if (ina&&!inb) {
            res.push_back(cpLL({a,b}, l));
        }
        else if (!ina&&inb) {
            res.push_back(cpLL({a,b}, l));
            res.push_back(b);
        }
    }
    return res;
}


pair<P2, P2> closestPair(G2 g) {
    auto ps = g;
    int n=ps.size();
    if (n < 2) return {P2(0,0), P2(0,0)};
    sort(all(ps), ltP2);
    D mind = norm(ps[0]-ps[1]);
    pair<P2, P2> res = {ps[0], ps[1]};
    set<pair<D,int>> box;
    int j=0;
    rep(i, n) {
        D xi=real(ps[i]), yi=imag(ps[i]);
        D rad = sqrtl(mind);
        while (j<i && gt(xi - real(ps[j]), rad)) {
            box.erase({imag(ps[j]), j});
            j++;
        }
        auto it = box.lower_bound({yi - rad, -1});
        for (; it != box.end() && le(it->first, yi + rad); ++it) {
            int k = it->second;
            D d = norm(ps[i]-ps[k]);
            if (lt(d, mind)) mind=d, res={ps[i], ps[k]}, rad = sqrtl(mind);
        }
        box.insert({yi, i});
    }
    return res;
}


struct C2 { P2 o; D r; };

C2 makeC2(P2 a, P2 b) {
    return C2{(a+b)/D(2), abs(a-b)/D(2)};
}
C2 makeC2(P2 a, P2 b, P2 c) {
    P2 B=b-a, C=c-a;
    D d=2*cross(B, C);
    if (eq(d, 0)) {
        D ab=norm(B), bc=norm(b-c), ca=norm(c-a);
        if (le(ab, bc) && le(ab, ca)) return makeC2(a, b);
        if (le(bc, ca)) return makeC2(b, c);
        return makeC2(c, a);
    }
    P2 o=a+(rot90(B)*norm(C)-rot90(C)*norm(B))/d;
    return C2{o, abs(o-a)};
}

C2 incircle(P2 a, P2 b, P2 c) {
    D ab=abs(b-a), bc=abs(c-b), ca=abs(a-c);
    P2 o=(a*bc+b*ca+c*ab)/(ab+bc+ca);
    D r=abs(cross(b-a, o-a))/abs(b-a);
    return C2{o, r};
}

vector<P2> cpCL(L2 l, C2 c) {
    vector<P2> res;
    P2 a=l.a-c.o, d=l.b-l.a;
    D A=norm(d), B=2*dot(a, d), C=norm(a)-c.r*c.r;
    if (le(A, 0)) {
        if (eq(abs(a), c.r)) res.push_back(l.a);
        return res;
    }
    D Dlt=B*B-4*A*C;
    if (lt(Dlt, 0)) return res;
    D s = le(Dlt, 0) ? 0 : sqrtl(max<D>(0, Dlt));
    D t1 = (-B - s)/(2*A), t2 = (-B + s)/(2*A);
    res.push_back(c.o + a + d*t1);
    if (gt(Dlt, 0)) res.push_back(c.o + a + d*t2);
    return res;
}

vector<P2> cpCC(C2 c1, C2 c2) {
    vector<P2> res;
    P2 d=c2.o-c1.o;
    D L=abs(d);
    if (eq(L, 0)) return res;
    D a=(c1.r*c1.r-c2.r*c2.r+L*L)/(2*L), h2=c1.r*c1.r-a*a;
    if (lt(h2, 0)) return res;
    P2 m=c1.o+d*(a/L), n=rot90(d)*( (lt(h2, 0) ? 0 : sqrtl(max<D>(0, h2))/L) );
    res.push_back(m-n);
    if (gt(h2, 0)) res.push_back(m+n);
    return res;
}

D isaCC(C2 c1, C2 c2) {
    D d=abs(c1.o-c2.o), r1=c1.r, r2=c2.r;
    if (ge(d, r1+r2)) return 0;
    if (le(d, abs(r1-r2))) {
        D r=min(r1, r2);
        return PI*r*r;
    }
    auto f = [&] (D z) { return max<D>(-1, min<D>(1, z)); };
    D a = 2*acosl(f((d*d+r1*r1-r2*r2)/(2*d*r1)));
    D b = 2*acosl(f((d*d+r2*r2-r1*r1)/(2*d*r2)));
    return 0.5L*(r1*r1*(a-sinl(a)) + r2*r2*(b-sinl(b)));
}

vector<L2> tangCP(C2 c, P2 p) {
    vector<L2> res;
    P2 v = p - c.o;
    D d2 = norm(v), r2 = c.r*c.r;
    if (lt(d2, r2)) return res;
    if (eq(d2, r2)) {
        res.push_back({c.o+v*(r2/d2), p});
        return res;
    }
    D h = c.r*sqrtl(max<D>(0, d2 - r2));
    P2 n1 = (v*r2 - rot90(v)*h)/d2;
    P2 n2 = (v*r2 + rot90(v)*h)/d2;
    res.push_back({c.o+n1, p});
    res.push_back({c.o+n2, p});
    return res;
}

vector<L2> tangCC(C2 c1, C2 c2) {
    vector<L2> res;
    P2 d = c2.o-c1.o;
    D L=abs(d);
    if (eq(L, 0)) return res;
    P2 e = d/L;
    auto add = [&] (int s) {
        D c=(s*c2.r - c1.r)/L;
        if (gt(c*c, 1)) return;
        D s2 = max<D>(0, 1 - c*c);
        if (eq(s2, 0)) {
            P2 n = e*c;
            res.push_back({c1.o-n*c1.r, c2.o-n*c2.r});
        } else {
            D s = sqrtl(s2);
            P2 n1 = e*c + rot90(e)*s;
            P2 n2 = e*c - rot90(e)*s;
            res.push_back({c1.o-n1*c1.r, c2.o-n1*c2.r});
            res.push_back({c1.o-n2*c1.r, c2.o-n2*c2.r});
        }
    };
    add(+1); // external
    add(-1); // internal
    return res;
}

C2 mec(G2 ps) {
    auto inC2=[&](C2 c, P2 p){ return le(abs(c.o - p), c.r); };
    mt19937_64 rng((uint64_t)chrono::steady_clock::now().time_since_epoch().count());
    shuffle(all(ps), rng);
    int n=ps.size();
    if(n==0) return C2{P2(0,0), 0};
    C2 c{ps[0], 0};
    rep(i, n) {
        if (inC2(c, ps[i])) continue;
        c={ps[i], 0};
        rep(j, i) {
            if (inC2(c, ps[j])) continue;
            c=makeC2(ps[i], ps[j]);
            rep(k, j) {
                if (inC2(c, ps[k])) continue;
                c=makeC2(ps[i], ps[j], ps[k]);
            }
        }
    }
    return c;
}

vector<P2> cpSegs(vector<S2> ss) {
    struct E { D x; int t; D y, y1, y2; }; // t: 0=add(H), 1=query(V), 2=remove(H)
    vector<E> es; es.reserve(ss.size()*2);

    for(auto s: ss) {
        if (eq(s.a.imag(), s.b.imag())) {
            if (gt(s.a.real(), s.b.real())) swap(s.a, s.b);
            es.push_back({s.a.real(), 0, s.a.imag(), 0, 0});
            es.push_back({s.b.real(), 2, s.a.imag(), 0, 0});
        } else {
            if (gt(s.a.imag(), s.b.imag())) swap(s.a, s.b);
            es.push_back({s.a.real(), 1, 0, s.a.imag(), s.b.imag()});
        }
    }

    sort(all(es), [](E a, E b) {
        if (!eq(a.x, b.x)) return lt(a.x, b.x);
        return a.t < b.t;
    });

    multiset<D> active;
    vector<P2> res; res.reserve(ss.size());
    for (auto e : es) {
        if (e.t == 0) { // add
            active.insert(e.y);
        } else if (e.t == 2) { // remove
            auto it = active.find(e.y);
            if (it != active.end()) active.erase(it);
        } else { // query
            for (auto it = active.lower_bound(e.y1); it != active.end() && le(*it, e.y2); ++it) {
                res.push_back(P2(e.x, *it));
            }
        }
    }

    sort(all(res), ltP2);
    res.erase(unique(all(res), eqP2), res.end());
    return res;
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_1_A
void Projection() {
    P2 a, b; rd(a); rd(b);
    L2 l = {a, b};
    ll q;cin>>q;
    while(q--) {
        P2 p; rd(p);
        pr(proj(l, p));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_1_B
void Reflection() {
    P2 a, b; rd(a); rd(b);
    L2 l = {a, b};
    ll q;cin>>q;
    while(q--) {
        P2 p; rd(p);
        pr(refl(l, p));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_1_C
void CounterClockwise() {
    P2 p0, p1;
    rd(p0); rd(p1);
    ll q; cin>>q;
    while(q--) {
        P2 p; rd(p);
        int res = ccw(p0, p1, p);
        if (res == +1) out("COUNTER_CLOCKWISE");
        else if (res == -1) out("CLOCKWISE");
        else if (res == +2) out("ONLINE_BACK");
        else if (res == -2) out("ONLINE_FRONT");
        else out("ON_SEGMENT");
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/1/CGL_2_A
void ParallelOrthogonal() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        if (isParallel({p1, p2}, {p3, p4})) out(2);
        else if(isPerp({p1, p2}, {p3, p4})) out(1);
        else out(0);
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/2/CGL_2_B
void IntersectionSS() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        S2 s1 = {p1, p2}, s2 = {p3, p4};
        out(isSS(s1, s2));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/2/CGL_2_C
void CrossPointSS() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        L2 s1 = {p1, p2}, s2 = {p3, p4};
        pr(cpLL(s1, s2));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/2/CGL_2_D
void DistanceSS() {
    ll q;cin>>q;
    while(q--) {
        P2 p1, p2, p3, p4;
        rd(p1); rd(p2); rd(p3); rd(p4);
        S2 s1 = {p1, p2}, s2 = {p3, p4};
        setp(12);
        out(distSS(s1, s2));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_A
void Area() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    setp(1);
    out(area(g));
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_B
void IsConvex() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    out(isConvex(g));
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_C
void PolygonPointContainment() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    ll q;cin>>q;
    while(q--) {
        P2 p; rd(p);
        out(inG(g, p));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_A
void ConvexHull() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    G2 h = hull(g);
    out(h.size());
    ll sx = INF, sy = INF;
    ll id = -1;
    rep(i, h.size()) {
        if (lt(imag(h[i]), sy)) {
            sx = real(h[i]);
            sy = imag(h[i]);
            id = i;
        }
        else if (eq(imag(h[i]), sy) && lt(real(h[i]), sx)) {
            sx = real(h[i]);
            sy = imag(h[i]);
            id = i;
        }
    }
    rep(i, h.size()) {
        ll ni = (id + i) % h.size();
        pr(h[ni]);
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_B
void DiameterOfConvexPolygon() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    auto [p1, p2] = diameter(g);
    setp(12);
    out(abs(p1 - p2));
    // todo
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_C
void ConvexCut() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    ll q;cin>>q;
    while(q--) {
        P2 a, b; rd(a); rd(b);
        L2 l = {a, b};
        G2 h = cutConvex(g, l);
        setp(12);
        out(area(h));
    }
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/5/CGL_5_A
void ClosestPair() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    auto [p1, p2] = closestPair(g);
    setp(12);
    out(abs(p1 - p2));
}

// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/5/CGL_5_B
void MinimumEnclosingCircle() {
    ll n;cin>>n;
    G2 g(n);
    rep(i, n) rd(g[i]);
    auto [c, r] = mec(g);
    pr(c);
    setp(12);
    out(r);
}


// https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/6/CGL_6_A
void SegmentIntersections() {
    ll n;cin>>n;
    vector<S2> seg(n);
    rep(i, n) {
        P2 a, b; rd(a); rd(b);
        seg[i] = {a, b};
    }
    out(cpSegs(seg).size());
}


// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_A
void IntersectionOfCircles() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    out(tangCC(c1, c2).size());
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_B
void IncircleOfTriangle() {
    P2 a, b, c; rd(a); rd(b); rd(c);
    auto [ic, ir] = incircle(a, b, c);
    pr(ic);
    setp(12);
    out(ir);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_C
void CircumscribedCircleOfTriangle() {
    P2 a, b, c; rd(a); rd(b); rd(c);
    auto [cc, cr] = makeC2(a, b, c);
    pr(cc);
    setp(12);
    out(cr);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_D
void CrossPointsCL() {
    P2 c; D r;
    rd(c); cin>>r;
    ll q;cin>>q;
    while(q--) {
        P2 a, b; rd(a); rd(b);
        L2 l = {a, b};
        auto ps = cpCL(l, {c, r});
        sort(all(ps), ltP2);
        pr(ps[0]);
        pr(ps[(ps.size()-1)]);
    }
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_E
void CrossPointsCC() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    auto ps = cpCC(c1, c2);
    sort(all(ps), ltP2);
    pr(ps[0]);
    pr(ps[(ps.size()-1)]);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_F
void TangentToCircle() {
    P2 p, c; D r;
    rd(p);
    rd(c); cin>>r;
    C2 c1={c, r};
    auto ls = tangCP(c1, p);
    vector<P2> ps;
    for (auto l : ls) {
        ps.push_back(l.a);
    }
    sort(all(ps), ltP2);
    for (auto p : ps) pr(p);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_G
void CommonTangent() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    auto ls = tangCC(c1, c2);
    vector<P2> ps;
    for (auto l : ls) {
        ps.push_back(l.a);
    }
    sort(all(ps), ltP2);
    for (auto p : ps) pr(p);
}

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_H
// void IntersectionCP() {
//     ll n;cin>>n;
//     D r; cin>>r;
//     C2 c = {P2(0, 0), r};
//     G2 g(n);
//     rep(i, n) rd(g[i]);
//     out(isaCP(c, g));
// }

// // https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_I
void AreaOfIntersectionBetweenTwoCircles() {
    P2 o1, o2; D r1, r2;
    rd(o1); cin>>r1;
    rd(o2); cin>>r2;
    C2 c1={o1, r1}, c2={o2, r2};
    setp(12);
    out(isaCC(c1, c2));
}

int main() {
    cin.tie(0);
    ios_base::sync_with_stdio(false);

    // Projection();
    // Reflection();
    // CounterClockwise();

    // ParallelOrthogonal();
    // IntersectionSS();
    // CrossPointSS();
    // DistanceSS();

    // Area();
    // IsConvex();
    // PolygonPointContainment();

    // ConvexHull();
    DiameterOfConvexPolygon();
    // ConvexCut();

    // ClosestPair();
    // MinimumEnclosingCircle();

    // SegmentIntersections();

    // IntersectionOfCircles();
    // IncircleOfTriangle();
    // CircumscribedCircleOfTriangle();
    // CrossPointsCL();
    // CrossPointsCC();
    // TangentToCircle();
    // CommonTangent();
    // IntersectionCP();
    // AreaOfIntersectionBetweenTwoCircles();
    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 10684
final_rank: 16

code_snippet: #include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <climits>
#include <unordered_map>
#include <queue>
#include <deque>
#include <math.h>
#include <bits/stdc++.h>
#define ll long long
#define Lt Line<T>
#define Pt Point<T>
using i64 = long long;
using namespace std;

const ll N=1e6+10,MOD=998244353;

typedef std::pair<ll,ll> pii;
typedef std::pair<ll,pii> piii;
//i64 n,m,k;

using ld = long double;
const ld PI = acos(-1);
const ld EPS = 1e-7;
const ld INF = numeric_limits<ld>::max();
#define cc(x) cout << fixed << setprecision(x);

ld fgcd(ld x, ld y) { // 实数域gcd
    return abs(y) < EPS ? abs(x) : fgcd(y, fmod(x, y));
}
template<class T, class S> bool equal(T x, S y) {
    return -EPS < x - y && x - y < EPS;
}
template<class T> int sign(T x) {
    if (-EPS < x && x < EPS) return 0;
    return x < 0 ? -1 : 1;
}

template<class T> struct Point { // 在C++17下使用 emplace_back 绑定可能会导致CE！
    T x, y;
    Point(T x_ = 0, T y_ = 0) : x(x_), y(y_) {} // 初始化
    template<class U> operator Point<U>() { // 自动类型匹配
        return Point<U>(U(x), U(y));
    }
    Point &operator+=(Point p) & { return x += p.x, y += p.y, *this; }
    Point &operator+=(T t) & { return x += t, y += t, *this; }
    Point &operator-=(Point p) & { return x -= p.x, y -= p.y, *this; }
    Point &operator-=(T t) & { return x -= t, y -= t, *this; }
    Point &operator*=(T t) & { return x *= t, y *= t, *this; }
    Point &operator/=(T t) & { return x /= t, y /= t, *this; }
    Point operator-() const { return Point(-x, -y); }
    friend Point operator+(Point a, Point b) { return a += b; }
    friend Point operator+(Point a, T b) { return a += b; }
    friend Point operator-(Point a, Point b) { return a -= b; }
    friend Point operator-(Point a, T b) { return a -= b; }
    friend Point operator*(Point a, T b) { return a *= b; }
    friend Point operator*(T a, Point b) { return b *= a; }
    friend Point operator/(Point a, T b) { return a /= b; }
    friend bool operator<(Point a, Point b) {
        return equal(a.x, b.x) ? a.y < b.y - EPS : a.x < b.x - EPS;
    }
    friend bool operator>(Point a, Point b) { return b < a; }
    friend bool operator==(Point a, Point b) { return !(a < b) && !(b < a); }
    friend bool operator!=(Point a, Point b) { return a < b || b < a; }
    friend auto &operator>>(istream &is, Point &p) {
        return is >> p.x >> p.y;
    }
    friend auto &operator<<(ostream &os, Point p) {
        return os << "(" << p.x << ", " << p.y << ")";
    }
};
template<class T> struct Line {
    Point<T> a, b;
    Line(Point<T> a_ = Point<T>(), Point<T> b_ = Point<T>()) : a(a_), b(b_) {}
    template<class U> operator Line<U>() { // 自动类型匹配
        return Line<U>(Point<U>(a), Point<U>(b));
    }
    friend auto &operator<<(ostream &os, Line l) {
        return os << "<" << l.a << ", " << l.b << ">";
    }
};

using Pd = Point<ld>;
using Ld = Line<ld>;


template<class T> T cross(Point<T> a, Point<T> b) { // 叉乘
    return a.x * b.y - a.y * b.x;
}
template<class T> T cross(Point<T> p1, Point<T> p2, Point<T> p0) { // 叉乘 (p1 - p0) x (p2 - p0);
    return cross(p1 - p0, p2 - p0);
}


template<class T> T dot(Point<T> a, Point<T> b) { // 点乘
    return a.x * b.x + a.y * b.y;
}
template<class T> T dot(Point<T> p1, Point<T> p2, Point<T> p0) { // 点乘 (p1 - p0) * (p2 - p0);
    return dot(p1 - p0, p2 - p0);
}

template <class T> ld dis(T x1, T y1, T x2, T y2) {
    ld val = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    return sqrt(val);
}
template <class T> ld dis(Point<T> a, Point<T> b) {
    return dis(a.x, a.y, b.x, b.y);
}

template<class T> Point<T> rotate(Point<T> p1, Point<T> p2) { // 旋转
    Point<T> vec = p1 - p2;
    return {-vec.y, vec.x};
}

ld toDeg(ld x) { // 弧度转角度
    return x * 180 / PI;
}
ld toArc(ld x) { // 角度转弧度
    return PI / 180 * x;
}

Point<ld> rotate(Point<ld> p, ld rad) {
    return {p.x * cos(rad) - p.y * sin(rad), p.x * sin(rad) + p.y * cos(rad)};
}

Point<ld> rotate(Point<ld> a, Point<ld> b, ld rad) {
    ld x = (a.x - b.x) * cos(rad) + (a.y - b.y) * sin(rad) + b.x;
    ld y = (b.x - a.x) * sin(rad) + (a.y - b.y) * cos(rad) + b.y;
    return {x, y};
}

template<class T> bool onLine(Point<T> a, Point<T> b, Point<T> c) {
    return sign(cross(b, a, c)) == 0;
}
template<class T> bool onLine(Point<T> p, Line<T> l) {
    return onLine(p, l.a, l.b);
}

template<class T> bool pointOnLineLeft(Pt p, Lt l) {
    return cross(l.b, p, l.a) > 0;
}

template<class T> bool pointOnLineSide(Pt p1, Pt p2, Lt vec) {
    T val = cross(p1, vec.a, vec.b) * cross(p2, vec.a, vec.b);
    return sign(val) == 1;
}
template<class T> bool pointNotOnLineSide(Pt p1, Pt p2, Lt vec) {
    T val = cross(p1, vec.a, vec.b) * cross(p2, vec.a, vec.b);
    return sign(val) == -1;
}

Pd lineIntersection(Ld l1, Ld l2) {
    ld val = cross(l2.b - l2.a, l1.a - l2.a) / cross(l2.b - l2.a, l1.a - l1.b);
    return l1.a + (l1.b - l1.a) * val;
}

template<class T> bool lineParallel(Lt p1, Lt p2) {
    return sign(cross(p1.a - p1.b, p2.a - p2.b)) == 0;
}
template<class T> bool lineVertical(Lt p1, Lt p2) {
    return sign(dot(p1.a - p1.b, p2.a - p2.b)) == 0;
}
template<class T> bool same(Line<T> l1, Line<T> l2) {
    return lineParallel(Line{l1.a, l2.b}, {l1.b, l2.a}) &&
           lineParallel(Line{l1.a, l2.a}, {l1.b, l2.b}) && lineParallel(l1, l2);
}

pair<Pd, ld> pointToLine(Pd p, Ld l) {
    Pd ans = lineIntersection({p, p + rotate(l.a, l.b)}, l);
    return {ans, dis(p, ans)};
}

template<class T> ld disPointToLine(Pt p, Lt l) {
    ld ans = cross(p, l.a, l.b);
    return abs(ans) / dis(l.a, l.b); // 面积除以底边长
}

template<class T> bool pointOnSegment(Pt p, Lt l) { // 端点也算作在直线上
    return sign(cross(p, l.a, l.b)) == 0 && min(l.a.x, l.b.x) <= p.x && p.x <= max(l.a.x, l.b.x) &&
           min(l.a.y, l.b.y) <= p.y && p.y <= max(l.a.y, l.b.y);
}

pair<Pd, ld> pointToSegment(Pd p, Ld l) {
    if (sign(dot(p, l.b, l.a)) == -1) { // 特判到两端点的距离
        return {l.a, dis(p, l.a)};
    } else if (sign(dot(p, l.a, l.b)) == -1) {
        return {l.b, dis(p, l.b)};
    }
    return pointToLine(p, l);
}

template<class T> bool segmentIntersection(Lt l1, Lt l2) {
    auto [s1, e1] = l1;
    auto [s2, e2] = l2;
    auto A = max(s1.x, e1.x), AA = min(s1.x, e1.x);
    auto B = max(s1.y, e1.y), BB = min(s1.y, e1.y);
    auto C = max(s2.x, e2.x), CC = min(s2.x, e2.x);
    auto D = max(s2.y, e2.y), DD = min(s2.y, e2.y);
    return A >= CC && B >= DD && C >= AA && D >= BB &&
           sign(cross(s1, s2, e1) * cross(s1, e1, e2)) == 1 &&
           sign(cross(s2, s1, e2) * cross(s2, e2, e1)) == 1;
}

template<class T> int pointInPolygon(Point<T> a, vector<Point<T>> p) {
    int n = p.size();
    for (int i = 0; i < n; i++) {
        if (pointOnSegment(a, Line{p[i], p[(i + 1) % n]})) {
            return 2;
        }
    }
    int t = 0;
    for (int i = 0; i < n; i++) {
        auto u = p[i], v = p[(i + 1) % n];
        if (u.x < a.x && v.x >= a.x && pointOnLineLeft(a, Line{v, u})) {
            t ^= 1;
        }
        if (u.x >= a.x && v.x < a.x && pointOnLineLeft(a, Line{u, v})) {
            t ^= 1;
        }
    }
    return t == 1;
}

template<class T> bool cmp1(Point<T> &a,Point<T> &b)
{
    Point<T> a1(a.y,a.x);
    Point<T> b1(b.y,b.x);
    return a1<b1;
}

template<class T> vector<Point<T>> staticConvexHull(vector<Point<T>> A, int flag = 1) {
    int n = A.size();
    if (n <= 2) { // 特判
        return A;
    }
    vector<Point<T>> ans(n * 2);
    sort(A.begin(), A.end());
    int now = -1;
    
    for (int i = 0; i < n; i++) { // 维护下凸包
        while (now > 0 && cross(A[i], ans[now], ans[now - 1]) <= 0) {
            now--;
        }
        ans[++now] = A[i];
    }
    int pre = now;
    for (int i = n - 2; i >= 0; i--) { // 维护上凸包
        while (now > pre && cross(A[i], ans[now], ans[now - 1]) <= 0) {
            now--;
        }
        ans[++now] = A[i];
    }
    ans.resize(now);
    return ans;
}

template<class T>T GetConvexHullDis(vector<Pt> A)
{
    int w=0; 
    T ans=max(dis(A[w],A[0]),dis(A[w],A[1]));
    for(int i=0;i<A.size();i++)
    {
        int j=(i+1)%A.size();
        int ne=(w+1)%A.size();
        while(cross(A[ne],A[i],A[j])>=cross(A[w],A[i],A[j])) w=ne,ne=(w+1)%A.size();
        ans=max(max(dis(A[w],A[i]),dis(A[w],A[j])),ans);
    }
    return ans;
}


int main() {
    
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n; cin>>n;
    vector<Pd> z(n);
    for(auto &t:z) cin>>t;

    cc(12);
    cout<<GetConvexHullDis(z);

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 8616
final_rank: 1

code_snippet: #line 1 "a.cpp"
#define PROBLEM \
  "https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_B"
#define ERROR 0.000001
#line 2 "/workspaces/cp-container/libraries/template.hpp"
#include <bits/stdc++.h>
using namespace std;

#define rep(i, n) for (int i = 0; i < (int)(n); ++i)

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;

template <typename T>
bool chmax(T& a, const T& b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}

template <typename T>
bool chmin(T& a, const T& b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
#line 3 "/workspaces/cp-container/libraries/geometry/core.hpp"

using Point = complex<double>;

struct Line {
  Point a, b;
  Line() = default;
  Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
};

constexpr double eps = 1.0e-9;

double dot(const Point& a, const Point& b) { return (conj(a) * b).real(); }
double cross(const Point& a, const Point& b) { return (conj(a) * b).imag(); }

Point projection(const Point& p0, const Point& p1, const Point& p) {
  Point base = p1 - p0, hyp = p - p0;
  double t = dot(base, hyp) / norm(base);
  return p0 + t * base;
}

Point reflection(const Point& p0, const Point& p1, const Point& p) {
  return 2.0 * projection(p0, p1, p) - p;
}

constexpr int COUNTER_CLOCKWISE = 1, CLOCKWISE = -1, ONLINE_BACK = 2,
              ONLINE_FRONT = -2, ON_SEGMENT = 0;

int ccw(const Point& p0, const Point& p1, const Point& p2) {
  Point a = p1 - p0, b = p2 - p0;
  if (eps < cross(a, b)) return COUNTER_CLOCKWISE;
  if (cross(a, b) < -eps) return CLOCKWISE;
  if (dot(a, b) < -eps) return ONLINE_BACK;
  if (norm(a) < norm(b)) return ONLINE_FRONT;
  return ON_SEGMENT;
}

bool is_parallel(const Line& l0, const Line& l1) {
  return abs(cross(l0.b - l0.a, l1.b - l1.a)) < eps;
}

bool is_orthogonal(const Line& l0, const Line& l1) {
  return abs(dot(l0.b - l0.a, l1.b - l1.a)) < eps;
}

bool has_intersection(Line s0, Line s1) {
  return ccw(s0.a, s0.b, s1.a) * ccw(s0.a, s0.b, s1.b) <= 0 &&
         ccw(s1.a, s1.b, s0.a) * ccw(s1.a, s1.b, s0.b) <= 0;
}

Point cross_point(const Line& l0, const Line& l1) {
  double d0 = cross(l0.b - l0.a, l1.b - l1.a),
         d1 = cross(l0.b - l0.a, l0.b - l1.a);
  if (abs(d0) < eps && abs(d1) < eps) return l1.a;
  return l1.a + d1 / d0 * (l1.b - l1.a);
}

double distance_between_point_and_segment(const Point& p, const Line& s) {
  if (dot(s.b - s.a, p - s.a) < eps) return abs(p - s.a);
  if (dot(s.a - s.b, p - s.b) < eps) return abs(p - s.b);
  return abs(cross(s.b - s.a, p - s.a)) / abs(s.b - s.a);
}

double distance_between_segments(const Line& s0, const Line& s1) {
  if (has_intersection(s0, s1)) return 0.0;
  double ret = numeric_limits<double>::max();
  chmin(ret, distance_between_point_and_segment(s0.a, s1));
  chmin(ret, distance_between_point_and_segment(s0.b, s1));
  chmin(ret, distance_between_point_and_segment(s1.a, s0));
  chmin(ret, distance_between_point_and_segment(s1.b, s0));
  return ret;
}

using Polygon = vector<Point>;

double area(const Polygon& g) {
  int n = g.size();
  double ret = 0.0;
  rep(i, n) ret += cross(g[i], g[(i + 1) % n]);
  ret /= 2.0;
  return ret;
}

bool is_convex(const Polygon& g) {
  int n = g.size();
  rep(i, n) {
    if (ccw(g[(i - 1 + n) % n], g[i], g[(i + 1) % n]) == CLOCKWISE)
      return false;
  }
  return true;
}

int polygon_point_containment(const Polygon& g, const Point& p) {
  bool is_contained = false;
  int n = g.size();
  rep(i, n) {
    Point a = g[i] - p, b = g[(i + 1) % n] - p;
    if (a.imag() > b.imag()) swap(a, b);
    if (a.imag() < eps && eps < b.imag() && cross(a, b) < -eps)
      is_contained = !is_contained;
    if (abs(cross(a, b)) < eps && dot(a, b) < eps) return 1;
  }
  return is_contained ? 2 : 0;
}

vi convex_hull(const Polygon& g) {
  int n = g.size();
  if (n == 0) return {};
  vi p_idx(n);
  iota(p_idx.begin(), p_idx.end(), 0);
  ranges::sort(p_idx, [&](int a, int b) {
    if (eps < abs(g[a].real() - g[b].real())) return g[a].real() < g[b].real();
    return g[a].imag() < g[b].imag() - eps;
  });
  vvi hull(2);
  rep(i, 2) {
    for (int idx : p_idx) {
      while (2 <= (int)hull[i].size() &&
             ccw(g[hull[i][(int)hull[i].size() - 2]], g[hull[i].back()],
                 g[idx]) == CLOCKWISE) {
        hull[i].pop_back();
      }
      hull[i].emplace_back(idx);
    }
    hull[i].pop_back();
    ranges::reverse(p_idx);
  }
  ranges::copy(hull[1], back_inserter(hull[0]));
  return hull[0];
}

double convex_diameter(const Polygon& g) {
  int n = g.size();
  if (n <= 2) return 0.0;
  if (n == 2) return abs(g[0] - g[1]);
  int i = 0, j = 0;
  rep(k, n) {
    if (g[k].imag() < g[i].imag()) i = k;
    if (g[j].imag() < g[k].imag()) j = k;
  }
  double ret = 0.0;
  int i0 = i, j0 = j;
  do {
    chmax(ret, abs(g[i] - g[j]));
    if (cross(g[(i + 1) % n] - g[i], g[(j + 1) % n] - g[j]) < -eps)
      i = (i + 1) % n;
    else
      j = (j + 1) % n;
  } while (i != i0 || j != j0);
  return ret;
}
#line 6 "a.cpp"
signed main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  int n;
  cin >> n;
  Polygon g(n);
  rep(i, n) {
    double x, y;
    cin >> x >> y;
    g[i] = Point(x, y);
  }
  cout << fixed << setprecision(20) << convex_diameter(g) << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 4992
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

#define maxn 100010


struct Point{
    double x,y;
    bool operator <(Point p){
        if(x!=p.x)return x<p.x;
        return y<p.y;
    }
};
double tot;
int n,k;
Point point[maxn];
int s[maxn],top;
vector<Point>p;
double det(Point a1,Point a2,Point b1,Point b2){
    return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);
}
double dis(Point a,Point b){
    return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double find(){
    if(k<2)return 0;
    else if(k<=2)return sqrt(dis(p[0],p[1]));
    p.push_back(p[0]);

    int j = 1;
    double maxd = 0.0;
    for(int i = 0;i<k;i++){
        while(det(p[i],p[i+1],p[i],p[j])<det(p[i],p[i+1],p[i],p[j+1])){
            j = (j+1)%k;
        }
        maxd = max({maxd,dis(p[i],p[j]),dis(p[i+1],p[j])});
    }
    return sqrt(maxd);
}


signed main(){
    cin>>n;
    for(int i =1;i<=n;i++)cin>>point[i].x>>point[i].y;
    sort(point+1,point+1+n);
    
    s[++top]=1;
    for(int i = 2;i<=n;i++){
        while(top>1&&det(point[s[top-1]],point[s[top]],point[s[top]],point[i])<=0){
            s[top--]=0;
        }
        s[++top]=i;
    }
    for(int i =1;i<top;i++){
        tot+=dis(point[s[i]],point[s[i+1]]);
        p.push_back(point[s[i]]);
    }

    top=0;
    s[++top]=n;
    for(int i = n-1;i>=1;i--){
        while(top>1&&det(point[s[top-1]],point[s[top]],point[s[top]],point[i])<=0){
            s[top--]=0;
        }
        s[++top]=i;
    }
    for(int i =1;i<top;i++){
        tot+=dis(point[s[i]],point[s[i+1]]);
        p.push_back(point[s[i]]);
    }
    k=p.size();
    double ans = find();
    printf("%.10lf\n",ans);


   // printf("%.2lf\n",tot);

}
accuracy: 1
time_ms: 60
memory_kb: 4720
final_rank: 15

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

#define maxn 100010


struct Point{
    double x,y;
    bool operator <(Point p){
        if(x!=p.x)return x<p.x;
        return y<p.y;
    }
};
double tot;
int n,k;
Point point[maxn];
int s[maxn],top;
vector<Point>p;
double det(Point a1,Point a2,Point b1,Point b2){
    return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);
}
double dis(Point a,Point b){
    return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double find(){
    if(k<2)return 0;
    else if(k<=2)return sqrt(dis(p[0],p[1]));
    p.push_back(p[0]);

    int j = 1;
    double maxd = 0.0;
    for(int i = 0;i<k;i++){
        while(det(p[i],p[i+1],p[i],p[j])<det(p[i],p[i+1],p[i],p[j+1])){
            j = (j+1)%k;
        }
        maxd = max(maxd,dis(p[i],p[j]));
        maxd = max(maxd,dis(p[i+1],p[j]));
    }
    return sqrt(maxd);
}


signed main(){
    cin>>n;
    for(int i =1;i<=n;i++)cin>>point[i].x>>point[i].y;
    sort(point+1,point+1+n);
    
    s[++top]=1;
    for(int i = 2;i<=n;i++){
        while(top>1&&det(point[s[top-1]],point[s[top]],point[s[top]],point[i])<=0){
            s[top--]=0;
        }
        s[++top]=i;
    }
    for(int i =1;i<top;i++){
        tot+=dis(point[s[i]],point[s[i+1]]);
        p.push_back(point[s[i]]);
    }

    top=0;
    s[++top]=n;
    for(int i = n-1;i>=1;i--){
        while(top>1&&det(point[s[top-1]],point[s[top]],point[s[top]],point[i])<=0){
            s[top--]=0;
        }
        s[++top]=i;
    }
    for(int i =1;i<top;i++){
        tot+=dis(point[s[i]],point[s[i+1]]);
        p.push_back(point[s[i]]);
    }
    k=p.size();
    double ans = find();
    printf("%.10lf\n",ans);


   // printf("%.2lf\n",tot);

}
accuracy: 1
time_ms: 60
memory_kb: 4708
final_rank: 14

code_snippet: #include<bits/stdc++.h>
#define siz(x) int((x).size())
#define all(x) std::begin(x),std::end(x)
#define fi first
#define se second
using namespace std;
using unt=unsigned;
using loli=long long;
using lolu=unsigned long long;
using pii=pair<int,int>;
mt19937_64 rng(random_device{}());
constexpr double eps=1e-8;
template<typename any>inline any sqr(const any &x){return x*x;}
inline int get_op(double x){if(fabs(x)<eps)return 0;return x>0?1:-1;}
double det(array<double,3>a0,array<double,3>a1,array<double,3>a2){
	double sum=0;
	sum+=a0[0]*a1[1]*a2[2];
	sum+=a1[0]*a2[1]*a0[2];
	sum+=a2[0]*a0[1]*a1[2];
	sum-=a0[2]*a1[1]*a2[0];
	sum-=a0[1]*a1[0]*a2[2];
	sum-=a0[0]*a1[2]*a2[1];
	return sum;
}
struct bector{
	double x,y;
	bool operator<(const bector&t)const{return fabs(x-t.x)<eps?y<t.y:x<t.x;}
	bool operator==(const bector&t)const{return fabs(x-t.x)<eps&&fabs(y-t.y)<eps;}
	bector(double a=0,double b=0):x(a),y(b){}
	double operator~()const{return sqrt(x*x+y*y);}
	double operator*()const{return x*x+y*y;}
	friend istream&operator>>(istream&a,bector&b){return a>>b.x>>b.y;}
	friend ostream&operator<<(ostream&a,const bector&b){return a<<b.x<<' '<<b.y;}
	friend bector operator+(const bector&a,const bector&b){return {a.x+b.x,a.y+b.y};}
	bector&operator+=(const bector&t){x+=t.x,y+=t.y;return *this;}
	friend bector operator-(const bector&a,const bector&b){return {a.x-b.x,a.y-b.y};}
	bector&operator-=(const bector&t){x-=t.x,y-=t.y;return *this;}
	friend bector operator*(const bector&a,double b){return {a.x*b,a.y*b};}
	friend bector operator*(double b,const bector&a){return {a.x*b,a.y*b};}
	bector&operator*=(double t){x*=t,y*=t;return *this;}
	friend bector operator/(const bector&a,double b){return {a.x/b,a.y/b};}
	bector&operator/=(double t){x/=t,y/=t;return *this;}
	friend double operator*(const bector&a,const bector&b){return a.x*b.x+a.y*b.y;}
	friend double operator/(const bector&a,const bector&b){return a.x*b.y-a.y*b.x;}
	bool frontis(const bector&t)const{auto r=*this*t;return get_op(r)>0;}
	bool backis(const bector&t)const{auto r=*this*t;return get_op(r)<0;}
	bool leftis(const bector&t)const{auto r=*this/t;return get_op(r)>0;}
	bool rightis(const bector&t)const{auto r=*this/t;return get_op(r)<0;}
	friend double dis(const bector&a,const bector&b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
	bector rot90()const{return {-y,x};}
	double arg()const{return atan2(y,x);}
	bector rotate(const bector&a,double r){
		auto u=a-*this;
		return *this+bector{u.x*cos(r)-u.y*sin(r),u.x*sin(r)+u.y*cos(r)};
	}
	friend double c0s(const bector&x,const bector&y){
		// 求 cos<x,y>
		return x*y/~x/~y;
	}
	bector unit()const{
		return (*this)/~(*this);
	}
	friend bool operator&(const bector&a,const bector&b){return !get_op(a*b);}
	friend bool operator|(const bector&a,const bector&b){return !get_op(a/b);}
	friend double triarea(const bector&x,const bector&y,const bector&z){
		return (y-x)/(z-x)/2;
	}
};
bector polar(double p,double r){return {cos(r)*p,sin(r)*p};}
struct segment{
	bector p1,p2;
	segment(const bector&a={0,0},const bector&b={0,0}):p1(a),p2(b){};
	friend istream&operator>>(istream&a,segment &b){return a>>b.p1>>b.p2;}
	friend ostream&operator<<(ostream&a,const segment&b){return a<<b.p1<<' '<<b.p2;}
	friend bool operator&(const segment&a,const segment&b){return !get_op((a.p2-a.p1)*(b.p2-b.p1));}
	friend bool operator|(const segment&a,const segment&b){return !get_op((a.p2-a.p1)/(b.p2-b.p1));}
	friend bool banana(const segment&a,const segment&b){
		// 判断是否相交
		if(min(a.p1.x,a.p2.x)>max(b.p1.x,b.p2.x)||min(b.p1.x,b.p2.x)>max(a.p1.x,a.p2.x)||min(a.p1.y,a.p2.y)>max(b.p1.y,b.p2.y)||min(b.p1.y,b.p2.y)>max(a.p1.y,a.p2.y))return false;
		return get_op(((a.p2-a.p1)/(b.p1-a.p1))*((a.p2-a.p1)/(b.p2-a.p1)))<=0&&get_op(((b.p2-b.p1)/(a.p1-b.p1))*((b.p2-b.p1)/(a.p2-b.p1)))<=0;
	}
	friend bector focus(const segment&a,const segment&b){
		// 输入必须保证有交，输出交点
		bector v=a.p2-a.p1,w=b.p2-b.p1,u=a.p1-b.p1;
		return a.p1+v*((w/u)/(v/w));
	}
	friend double dis(const segment&a,const bector&b){
		if(a.p1==a.p2)return ~(b-a.p1);
		auto v1=a.p2-a.p1,v2=b-a.p1,v3=b-a.p2;
		if(v1.backis(v2))return ~v2;
		if(v1.frontis(v3))return ~v3;
		return fabs(v1/v2)/~v1;
	}
	friend double dis(const segment&a,const segment&b){
		// 求线段间最短距离
		if(banana(a,b))return 0;
		return min({dis(a,b.p1),dis(a,b.p2),dis(b,a.p1),dis(b,a.p2)});
	}
	bector project(const bector&a)const{
		// 求点 a 在当前线段上的投影点坐标
		if(a==p1||a==p2)return a;
		auto u=p2-p1,v=a-p1;
		return c0s(u,v)*(~v)*u.unit()+p1;
	}
	double pr0ject(const bector&a)const{
		return dis(a,project(a));
	}
	bector reflect(const bector&a)const{
		// 求点 a 在当前线段上的对称点
		return 2*project(a)-a;
	}
	segment&sort(){if(p2<p1)std::swap(p1,p2);return *this;}
};
struct circle{
	bector p;double r;
	circle(const bector&a={0,0},double b=0):p(a),r(b){}
	circle(bector p1,bector p2){
		p=(p1+p2)/2;
		r=dis(p1,p2)/2;
	}
	circle(bector p1,bector p2,bector p3){
		p.x=det({*p1,p1.y,1},{*p2,p2.y,1},{*p3,p3.y,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		p.y=-det({*p1,p1.x,1},{*p2,p2.x,1},{*p3,p3.x,1})/2/det({p1.x,p1.y,1},{p2.x,p2.y,1},{p3.x,p3.y,1});
		r=dis(p,p1);
	}
	friend istream&operator>>(istream&a,circle &b){return a>>b.p>>b.r;}
	friend ostream&operator<<(ostream&a,const circle &b){return a<<b.p<<' '<<b.r;}
	bool check(bector t){
		return dis(p,t)-r<=eps;
	}
	friend segment focus(const circle &a,const segment&b){
		auto d=b.project(a.p),u=b.p2-b.p1,e=u/~u;
		auto t=sqrt(a.r*a.r-sqr(dis(d,a.p)));
		return segment{d+e*t,d-e*t}.sort();
	}
	friend segment focus(const circle &a,const circle &b){
		auto d=dis(a.p,b.p),l=acos((sqr(a.r)+sqr(d)-sqr(b.r))/(2*a.r*d)),t=(b.p-a.p).arg();
		return segment{a.p+polar(a.r,t+l),a.p+polar(a.r,t-l)}.sort();
	}
	segment tangent(const bector&a){
		auto u=a-p;
		auto l=acos(r/~u);
		u=u*r/~u;
		return segment{p.rotate(p+u,+l),p.rotate(p+u,-l)}.sort();
	}
};
struct points:vector<bector>{
#define p (*this)
	points&read(istream&t,int n=-1){
		if(n==-1)cin>>n;
		p.resize(n);
		for(int i=0;i<n;i++)t>>p[i];
		return *this;
	}
	double area()const{
		// 无需满足多边形是凸的
		double ans=0;
		for(int i=1;i<siz(p)-1;i++)ans+=(p[i]-p[0])/(p[i+1]-p[0]);
		return ans/2;
	}
	points tobag(){
		sort(all(p));
		erase(unique(all(p)),end());
		points b;
		for(int i=0;i<siz(p);i++){
			while(siz(b)>1&&get_op((b.back()-b[siz(b)-2])/(p[i]-b[siz(b)-2]))<=0)
			// while(siz(b)>1&&get_op((b.back()-b[siz(b)-2])/(p[i]-b[siz(b)-2]))<0)
				b.pop_back();
			b.push_back(p[i]);
		}
		int o=siz(b);
		for(int i=siz(p)-2;~i;i--){
			while(siz(b)>o&&get_op((b.back()-b[siz(b)-2])/(p[i]-b[siz(b)-2]))<=0)
			// while(siz(b)>o&&get_op((b.back()-b[siz(b)-2])/(p[i]-b[siz(b)-2]))<0)
				b.pop_back();
			b.push_back(p[i]);
		}
		// if(siz(p)>1)b.pop_back();
		return b;
	}
	double xzqk()const{
		if(siz(p)==1)return 0;
		if(siz(p)==2)return dis(p[0],p[1]);
		if(siz(p)==3)return max({dis(p[0],p[1]),dis(p[0],p[2]),dis(p[1],p[2])});
		double ans=0;
		for(int i=1,j=2;i<siz(p);i++){
			while(get_op(triarea(p[i-1],p[i],p[j])-triarea(p[i-1],p[i],p[(j+1)%siz(p)]))<=0)
				(++j)%=siz(p);
			ans=max({ans,dis(p[i-1],p[j]),dis(p[i],p[j])});
		}
		return ans;
	}
	// double xzqk(){
	// 	double ans=0;
	// 	for(size_t i=0,j=1;i<p.size();i++){
	// 		while((p[(i+1)%p.size()]-p[i])/(p[j]-p[i])<(p[(i+1)%p.size()]-p[i])/(p[(j+1)%p.size()]-p[i]))(++j)%=p.size();
	// 		ans=std::max({ans,dis(p[i],p[j]),dis(p[(i+1)%p.size()],p[j])});
	// 	}
	// 	return ans;
	// }
	// double xzqk()const{
	// 	if(p.size()==2)
	// 		return ~(p[0]-p[1]);
	// 	double ans=0;
	// 	for(int i=1,j=2;i<(int)p.size();i++){
	// 		while((triarea(p[i-1],p[i],p[j])<=
	// 			triarea(p[i-1],p[i],p[(j+1)%siz(p)])))
	// 			j=(j+1)%siz(p);
	// 		ans=max(ans,max(~(p[i-1]-p[j]),
	// 			~(p[i]-p[j])));
	// 	}
	// 	return ans;
	// }
	circle incircle(){
		assert(siz(p)==3);
		auto u=p[1]-p[0],v=p[2]-p[0];u=u/~u*~v;
		segment l1{p[0],p[0]+u+v};
		u=p[2]-p[1],v=p[0]-p[1];u=u/~u*~v;
		segment l2{p[1],p[1]+u+v};
		auto pp=focus(l1,l2);
		return circle{pp,dis(segment{p[0],p[1]},pp)};
	}
	circle outcirce(){
		assert(siz(p)==3);
		auto u=p[1]-p[0],mid=(p[0]+p[1])/2;
		segment l1{mid,mid+u.rot90()};
		u=p[2]-p[0],mid=(p[0]+p[2])/2;
		segment l2{mid,mid+u.rot90()};
		auto pp=focus(l1,l2);
		return circle{pp,dis(p[0],pp)};
	}
	int contains(bector t){
		// 无需满足多边形是凸的
		// 2表示包含，1表示在边上，0表示在外面
		bool res=false;
		for(int i=0;i<siz(p);i++){
			auto a=p[i]-t,b=p[(i+1)%siz(p)]-t;
			if(!a.leftis(b)&&!a.rightis(b)&&!a.frontis(b))return 1;
			if(a.y>b.y)std::swap(a,b);
			if(a.y<eps&&eps<b.y&&a.leftis(b))res=!res;
		}
		return res?2:0;
	}
#undef p
};
circle mingenshin(points b){
	shuffle(all(b),rng);
	circle ans;
	for(int i=0;i<siz(b);i++)if(!ans.check(b[i])){
		ans={b[i],0};
		for(int j=0;j<i;j++)if(!ans.check(b[j])){
			ans=circle(b[i],b[j]);
			for(int k=0;k<j;k++)if(!ans.check(b[k])){
				ans=circle(b[i],b[j],b[k]);
			}
		}
	}
	return ans;
}
points a,b;
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	std::ios::sync_with_stdio(false);cin.tie(nullptr);
	cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(10);
	a.read(cin);
	cout<<a.tobag().xzqk();
	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 4268
final_rank: 4

code_snippet: #include <iostream>
#include <cmath>
#include <algorithm>
#include <iomanip>

#define EPS 1e-10
#define N 80001

using namespace std;

typedef double T;

int n;

class POS{
public:
	T x, y;
	POS(const T& x = 0, const T& y = 0) : x(x), y(y){}
	POS(const POS& x) : x(x.x), y(x.y){}
	
	bool operator==(const POS& rhs) const{
		return x == rhs.x && y == rhs.y;
	}
	
	T dist(const POS& rhs) const{
		T tmp_x = x - rhs.x, tmp_y = y - rhs.y;
		return sqrt(tmp_x*tmp_x + tmp_y*tmp_y);
	}
} p[N], h[N], EX[N << 1];

bool cmp_convex(const POS& lhs, const POS& rhs){
	return (lhs.x < rhs.x) || ((lhs.x == rhs.x)&&(lhs.y < rhs.y));
}

T cross(const POS& o, const POS& a, const POS& b){
	T value = (a.x - o.x)*(b.y - o.y) - (a.y - o.y)*(b.x - o.x);
	if(fabs(value) < EPS) return 0;
	return value;
}

void convex_hull(POS* points, POS* need, int& n){
	sort(points, points + n, cmp_convex);
	int idx = 0;
	for(int i = 0; i < n; i++){
		while(idx >= 2 && cross(need[idx - 2], need[idx - 1], points[i]) <= 0)
			idx--;
		need[idx++] = points[i];
	}
	int half_point = idx + 1;
	for(int i = n - 2; i >= 0; i--){
		while(idx >= half_point && cross(need[idx - 2], need[idx - 1], points[i]) <= 0)
			idx--;
		need[idx++] = points[i];
	}
			
	n = idx;
}

int main(){
	
	T x, y, ans = 0;
	
	cin >> n;
	
	for(int i = 0; i < n; i++){
		cin >> x >> y;
		p[i] = POS(x, y);
	}
	
	convex_hull(p, h, n);
	
	for(int i = 0; i < n; i++)
		EX[i] = h[i];
	
	for(int i = 0; i < n ; i++){
		int L = i + 1, R = i + n;
		while(L < R){
			int mid = (L + R) / 2;
			if(EX[i].dist(EX[mid]) > EX[i].dist(EX[mid + 1]))
				R = mid;
			else
				L = mid + 1;
		}
		ans = max(ans, EX[i].dist(EX[L]));
		EX[n + i] = EX[i];
	}
		
	cout << fixed << setprecision(7) << ans << endl;
	
	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 8516
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using pll = pair<ll, ll>;
using vl = vector<ll>;
template <class T> using vec = vector<T>;
template <class T> using vv = vec<vec<T>>;
template <class T> using vvv = vv<vec<T>>;
template <class T> using minpq = priority_queue<T, vector<T>, greater<T>>;
#define rep(i, r) for(ll i = 0; i < (r); i++)
#define reps(i, l, r) for(ll i = (l); i < (r); i++)
#define rrep(i, l, r) for(ll i = (r) - 1; i >= l; i--)
template <class T> void uniq(T &a) { sort(all(a)); erase(unique(all(a)), a.end()); }
#define all(a) (a).begin(), (a).end()
#define sz(a) (ll)(a).size()
const ll INF = numeric_limits<ll>::max() / 4;
const ld inf = numeric_limits<ld>::max() / 2;
const ll mod1 = 1000000007;
const ll mod2 = 998244353;
const ld pi = 3.141592653589793238;
template <class T> void rev(T &a) { reverse(all(a)); }
ll popcnt(ll a) { return __builtin_popcountll(a); }
template<typename T>
bool chmax(T &a, const T& b) { return a < b ? a = b, true : false; }
template<typename T>
bool chmin(T &a, const T& b) { return a > b ? a = b, true : false; }
const ll mod = mod1;
struct mint {
	ll x;
	mint(ll y = 0) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
	mint &operator+=(const mint &p) {
		if ((x += p.x) >= mod) x -= mod;
		return *this;
	}
	mint &operator-=(const mint &p) {
		if ((x += mod - p.x) >= mod) x -= mod;
		return *this;
	}
	mint &operator*=(const mint &p) {
		x = (ll)(1ll * x * p.x % mod);
		return *this;
	}
	mint &operator/=(const mint &p) {
		*this *= p.inv();
		return *this;
	}
	mint operator-() const { return mint(-x); }
	mint operator+(const mint &p) const { return mint(*this) += p; }
	mint operator-(const mint &p) const { return mint(*this) -= p; }
	mint operator*(const mint &p) const { return mint(*this) *= p; }
	mint operator/(const mint &p) const { return mint(*this) /= p; }
	bool operator==(const mint &p) const { return x == p.x; }
	bool operator!=(const mint &p) const { return x != p.x; }
	friend ostream &operator<<(ostream &os, const mint &p) { return os << p.x; }
	friend istream &operator>>(istream &is, mint &a) {
		ll t; is >> t; a = mint(t); return (is);
	}
	mint inv() const { return pow(mod - 2); }
	mint pow(ll n) const {
		mint ret(1), mul(x);
		while (n > 0) {
			if (n & 1) ret *= mul;
			mul *= mul;
			n >>= 1;
		}
		return ret;
	}
};
void YesNo(bool t) {
	cout << (t ? "Yes" : "No") << endl;
}
#define EPS (1e-10)
#define eq(a, b) (fabs((a) - (b)) < EPS)
ll sign(ld x) { return (x >= EPS) - (x <= -EPS); }
class Point {
	typedef Point P;
public:
	ld x, y;

	Point(ld _x = 0, ld _y = 0) : x(_x), y(_y) {}

	P operator+(Point p) const { return P(x + p.x, y + p.y); }
	P operator-(Point p) const { return P(x - p.x, y - p.y); }
	P operator*(ld a) const { return P(a * x, a * y); }
	P operator/(ld a) const { return P(x / a, y / a); }

	ld abs() const { return sqrtl(norm()); }
	ld norm() const { return x * x + y * y; }

	bool operator<(const P& p) const { return tie(x, y) < tie(p.x, p.y); }
	bool operator==(const P& p) const { return eq(x, p.x) && eq(y, p.y); }
	ld dot(P p) const { return x * p.x + y * p.y; }
	ld cross(P p) const { return x * p.y - y * p.x; }
	ld cross(P a, P b) const { return (a-*this).cross(b-*this); }
	ld angle() const { return atan2(y, x); }
	P unit() const { return *this / abs(); }
	P perp() const { return P(-y, x); }
	P normal() const { return perp()/abs(); }
	P rotate(ld theta) const {
		return P(x*cosl(theta)-y*sinl(theta), x*sinl(theta)+y*cosl(theta));
	}
	P rotate(ld theta, Point &p) const {
		return (*this - p).rotate(theta) + p;
	}
};
typedef Point Vector;
struct Segment {
	Point s;
	Point e;
};
typedef Segment Line;
class Circle {
public:
	Point c;
	ld r;
	Circle(Point c = Point(), ld r = 0.0) : c(c), r(r) {}
};
typedef vector<Point> Polygon;
Polygon convex_hull(Polygon &p, bool strict = true) {
	sort(all(p));
	p.erase(unique(all(p)), p.end());
	ll n = sz(p), k = 0;
	if (n <= 2) return p;
	vec<Point> ch(2 * n);
	auto check = [&](ll i) {
		Point a = ch[k - 1] - ch[k - 2], b = p[i] - ch[k - 1];
		if (strict) {
			return a.cross(b) <= EPS;
		}
		else {
			return a.cross(b) <= -EPS;
		}
	};
	for (ll i = 0; i < n; ch[k++] = p[i++]) {
		while (k >= 2 && check(i)) --k;
	}
	for (ll i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
		while (k >= t && check(i)) --k;
	}
	ch.resize(k - 1);
	return ch;
}
pair<Point, Point> Diameter(Polygon S) {
	Polygon Q = convex_hull(S);
	ll n = sz(Q), j = n < 2 ? 0 : 1;
	pair<ld, pair<Point, Point>> ans({(ld)0, {Q[0], Q[0]}});
	rep(i, j) {
		for (;; j = (j + 1) % n) {
			ans = max(ans, {(Q[i] - Q[j]).abs(), {Q[i], Q[j]}});
			if ((Q[(j + 1) % n] - Q[j]).cross(Q[i + 1] - Q[i]) > -EPS) break;
		}
	}
	return ans.second;
}
void solve() {
	ll n; cin >> n;
	Polygon g(n);
	rep(i, n) cin >> g[i].x >> g[i].y;
	auto res = Diameter(g);
	cout << fixed << setprecision(12) << (res.second - res.first).abs() << endl;
}
int main() {
	ll T = 1;
	// cin >> T;
	while (T--) solve();
}
accuracy: 1
time_ms: 70
memory_kb: 13272
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using pll = pair<ll, ll>;
using vl = vector<ll>;
template <class T> using vec = vector<T>;
template <class T> using vv = vec<vec<T>>;
template <class T> using vvv = vv<vec<T>>;
template <class T> using minpq = priority_queue<T, vector<T>, greater<T>>;
#define rep(i, r) for(ll i = 0; i < (r); i++)
#define reps(i, l, r) for(ll i = (l); i < (r); i++)
#define rrep(i, l, r) for(ll i = (r) - 1; i >= l; i--)
template <class T> void uniq(T &a) { sort(all(a)); erase(unique(all(a)), a.end()); }
#define all(a) (a).begin(), (a).end()
#define sz(a) (ll)(a).size()
const ll INF = numeric_limits<ll>::max() / 4;
const ld inf = numeric_limits<ld>::max() / 2;
const ll mod1 = 1000000007;
const ll mod2 = 998244353;
const ld pi = 3.141592653589793238;
template <class T> void rev(T &a) { reverse(all(a)); }
ll popcnt(ll a) { return __builtin_popcountll(a); }
template<typename T>
bool chmax(T &a, const T& b) { return a < b ? a = b, true : false; }
template<typename T>
bool chmin(T &a, const T& b) { return a > b ? a = b, true : false; }
const ll mod = mod1;
struct mint {
	ll x;
	mint(ll y = 0) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
	mint &operator+=(const mint &p) {
		if ((x += p.x) >= mod) x -= mod;
		return *this;
	}
	mint &operator-=(const mint &p) {
		if ((x += mod - p.x) >= mod) x -= mod;
		return *this;
	}
	mint &operator*=(const mint &p) {
		x = (ll)(1ll * x * p.x % mod);
		return *this;
	}
	mint &operator/=(const mint &p) {
		*this *= p.inv();
		return *this;
	}
	mint operator-() const { return mint(-x); }
	mint operator+(const mint &p) const { return mint(*this) += p; }
	mint operator-(const mint &p) const { return mint(*this) -= p; }
	mint operator*(const mint &p) const { return mint(*this) *= p; }
	mint operator/(const mint &p) const { return mint(*this) /= p; }
	bool operator==(const mint &p) const { return x == p.x; }
	bool operator!=(const mint &p) const { return x != p.x; }
	friend ostream &operator<<(ostream &os, const mint &p) { return os << p.x; }
	friend istream &operator>>(istream &is, mint &a) {
		ll t; is >> t; a = mint(t); return (is);
	}
	mint inv() const { return pow(mod - 2); }
	mint pow(ll n) const {
		mint ret(1), mul(x);
		while (n > 0) {
			if (n & 1) ret *= mul;
			mul *= mul;
			n >>= 1;
		}
		return ret;
	}
};
void YesNo(bool t) {
	cout << (t ? "Yes" : "No") << endl;
}
#define EPS (1e-10)
#define eq(a, b) (fabs((a) - (b)) < EPS)
ll sign(ld x) { return (x >= EPS) - (x <= -EPS); }
class Point {
	typedef Point P;
public:
	ld x, y;

	Point(ld _x = 0, ld _y = 0) : x(_x), y(_y) {}

	P operator+(Point p) const { return P(x + p.x, y + p.y); }
	P operator-(Point p) const { return P(x - p.x, y - p.y); }
	P operator*(ld a) const { return P(a * x, a * y); }
	P operator/(ld a) const { return P(x / a, y / a); }

	ld abs() const { return sqrtl(norm()); }
	ld norm() const { return x * x + y * y; }

	bool operator<(const P& p) const { return tie(x, y) < tie(p.x, p.y); }
	bool operator==(const P& p) const { return eq(x, p.x) && eq(y, p.y); }
	ld dot(P p) const { return x * p.x + y * p.y; }
	ld cross(P p) const { return x * p.y - y * p.x; }
	ld cross(P a, P b) const { return (a-*this).cross(b-*this); }
	ld angle() const { return atan2(y, x); }
	P unit() const { return *this / abs(); }
	P perp() const { return P(-y, x); }
	P normal() const { return perp()/abs(); }
	P rotate(ld theta) const {
		return P(x*cosl(theta)-y*sinl(theta), x*sinl(theta)+y*cosl(theta));
	}
	P rotate(ld theta, Point &p) const {
		return (*this - p).rotate(theta) + p;
	}
};
typedef Point Vector;
struct Segment {
	Point s;
	Point e;
};
typedef Segment Line;
class Circle {
public:
	Point c;
	ld r;
	Circle(Point c = Point(), ld r = 0.0) : c(c), r(r) {}
};
typedef vector<Point> Polygon;
Polygon convex_hull(Polygon &p, bool strict = true) {
	sort(all(p));
	p.erase(unique(all(p)), p.end());
	ll n = sz(p), k = 0;
	if (n <= 2) return p;
	vec<Point> ch(2 * n);
	auto check = [&](ll i) {
		Point a = ch[k - 1] - ch[k - 2], b = p[i] - ch[k - 1];
		if (strict) {
			return a.cross(b) <= EPS;
		}
		else {
			return a.cross(b) <= -EPS;
		}
	};
	for (ll i = 0; i < n; ch[k++] = p[i++]) {
		while (k >= 2 && check(i)) --k;
	}
	for (ll i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
		while (k >= t && check(i)) --k;
	}
	ch.resize(k - 1);
	return ch;
}
pair<ld, pair<Point, Point>> Diameter(Polygon S) {
	Polygon Q = convex_hull(S, false);
	ll n = sz(Q), j = n < 2 ? 0 : 1;
	pair<ld, pair<Point, Point>> ans({(ld)0, {Q[0], Q[0]}});
	rep(i, j) {
		for (;; j = (j + 1) % n) {
			ans = max(ans, {(Q[i] - Q[j]).abs(), {Q[i], Q[j]}});
			if ((Q[(j + 1) % n] - Q[j]).cross(Q[i + 1] - Q[i]) > -EPS) break;
		}
	}
	return ans;
}
void solve() {
	ll n; cin >> n;
	Polygon g(n);
	rep(i, n) cin >> g[i].x >> g[i].y;
	auto res = Diameter(g);
	cout << fixed << setprecision(10) << res.first << endl;
}
int main() {
	ll T = 1;
	// cin >> T;
	while (T--) solve();
}
accuracy: 0.7916666666666666
time_ms: 10
memory_kb: 4372
final_rank: 20

code_snippet: #include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#include<array>
#define int long long
#define lowbit(x) (x&(-x))
#define IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef pair<int, int>PII;
typedef long long ll;
using ull = unsigned long long;
const double EPS = 1e-9;
const int N = 2e5 + 5;
//a.erase(unique(a.begin() + 1, a.end()), a.end());  去重a(从1开始)
//2^20=1048576(1e6),2^30=1073741824(1e9)
// a / b % mod == a * [ b ^ (mod-2) ]
struct Point { double x;double y; }p[N];
vector<Point>s(N + 1);
int n;    int cnt = 1;
double dis(Point a, Point b) {//计算a,b两点间距离
    return sqrt(1.0 * (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double cross(Point a, Point b, Point c) {//如果 c 在 a->b 左边返回 >0，右边返回 <0,三点共线返回 0。
    return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
double check(Point a1, Point a2, Point b1, Point b2) {
    return (a2.x - a1.x) * (b2.y - b1.y) - (b2.x - b1.x) * (a2.y - a1.y);
}
bool cmp(Point p1, Point p2) {
    double t = check(p[1], p1, p[1], p2);
    if (t > EPS) return true;
    if (fabs(t) <= EPS) return dis(p[1], p1) < dis(p[1], p2);
    return false;
}
double rorating_calipers() {
    double res = 0;
    for (int i = 1, j = 2;i <= cnt;i++) {
        while (cross(s[i], s[i + 1], s[j]) < cross(s[i], s[i + 1], s[j + 1]))j = j % cnt + 1;
        res = max(res, max(dis(s[i], s[j]), dis(s[i + 1], s[j])));
    }
    return res;
}
void solve() {
    cin >> n;
    double temp;
    for (int i = 1;i <= n;i++) {
        cin >> p[i].x >> p[i].y;
        if (i != 1 && (p[i].y < p[1].y || (fabs(p[i].y - p[1].y) <= EPS && p[i].x < p[1].x))) {
            swap(p[i], p[1]);
        }
    }
    sort(p + 2, p + 1 + n, cmp);
    s[1] = p[1];
    for (int i = 2;i <= n;i++) {
        while (cnt > 1 && check(s[cnt - 1], s[cnt], s[cnt], p[i]) <= EPS) {
            cnt--;
        }
        cnt++;
        s[cnt] = p[i];
    }
    s[cnt + 1] = p[1];
    double ans2 = rorating_calipers();
    //cout << ans2 << endl;
    cout << fixed << setprecision(12) << ans2 << endl;
}
signed main() {
    IOS;
    int t;
    //cin >> t;
    t = 1;
    while (t--) {
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 7524
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;

namespace DBG
{
    template <class T>
    void _dbg(const char *f, T t) { cerr << f << '=' << t << '\n'; }
    template <class A, class... B>
    void _dbg(const char *f, A a, B... b)
    {
        while (*f != ',')
            cerr << *f++;
        cerr << '=' << a << ",";
        _dbg(f + 1, b...);
    }
    template <typename Container>
    void _print(const char *f, const Container &v)
    {
        cerr << f << " = [ ";
        for (const auto &x : v)
            cerr << x << ", ";
        cerr << "]\n";
    }
#define bug(...) _dbg(#__VA_ARGS__, __VA_ARGS__)
#define bugv(container) _print(#container, container)
}
using namespace DBG;

void _();
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cout << fixed << setprecision(10);
    int T = 1;
    // cin >> T;
    while (T--)
        _();
    return 0;
}

#define int long long

using ld = long double;
const ld eps = 1e-9;
const ld PI = acos(-1);
#define Pt Point<T>
#define Pd Point<ld>
#define Lt Line<T>
#define Ld Line<ld>

int sign(ld x) // 判符号
{
    if (fabsl(x) < eps)
        return 0;
    else
        return x < 0 ? -1 : 1;
}
bool equal(ld x, ld y) { return !sign(x - y); } // 近似
bool big(ld has, ld tar) { return sign(has - tar) > 0; }

////////////////////////////////////////////////////////////////////////////  点类

//  点与向量
template <class T>
struct Point
{
    T x, y;

    Point(T x = 0, T y = 0) : x(x), y(y) {} // 带参构造

    // 运算符重载
    Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }
    Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }
    Point operator*(T k) const { return Point(x * k, y * k); }
    Point operator/(T k) const { return Point(x / k, y / k); }

    friend bool operator<(Point a, Point b)
    {
        if (!equal(a.x, b.x))
            return a.x < b.x;
        return a.y < b.y;
    }
    friend bool operator>(Point a, Point b) { return b < a; }
    friend bool operator==(Point a, Point b) { return equal(a.x, b.x) && equal(a.y, b.y); } // 近似
    friend bool operator!=(Point a, Point b) { return !(a == b); }
    friend auto &operator>>(istream &is, Point &p) { return is >> p.x >> p.y; }
    friend auto &operator<<(ostream &os, Point p) { return os << "(" << p.x << ", " << p.y << ")"; }
};

ld len(Point<ld> a) { return sqrtl(a.x * a.x + a.y * a.y); }; // 模长
ld dis(Point<ld> a, Point<ld> b) { return len(a - b); }       // 两点距离

ld cross(Point<ld> a, Point<ld> b) { return a.x * b.y - a.y * b.x; } // 叉积 ab*sin 两点与原点三角形面积  判两点位置关系两侧
ld cross(Point<ld> p0, Point<ld> p1, Point<ld> p2) { return cross(p1 - p0, p2 - p0); }
ld dot(Point<ld> a, Point<ld> b) { return a.x * b.x + a.y * b.y; } // 点积 ab*cos  结合判两点象限位置关系
ld dot(Point<ld> p0, Point<ld> p1, Point<ld> p2) { return dot(p1 - p0, p2 - p0); }
ld angle(Point<ld> a, Point<ld> b) { return atan2(cross(a, b), dot(a, b)); } // 向量夹角[-pi, pi]  非负fabsl

Point<ld> standardize(Point<ld> h) // 转换为单位向量
{
    ld l = len(h);
    if (sign(l) == 0)
        return {0, 0}; // 防止除零
    return h / l;
}
Pd rotate(Pd p, ld rad) { return {p.x * cos(rad) - p.y * sin(rad), p.x * sin(rad) + p.y * cos(rad)}; } // 向量旋转任意角度

Pd rotate90(Pd a) { return Point<ld>{-a.y, a.x}; }                                   // 旋转90° 垂直向量
ld toDeg(ld x) { return x * 180 / PI; }                                              // 弧度转角度
ld toArc(ld x) { return PI / 180 * x; }                                              // 角度转弧度
ld angle(ld a, ld b, ld c) { return acos((a * a + b * b - c * c) / (2.0 * a * b)); } // 余弦定理 三边求角C弧度

////////////////////////////////////////////////////////////////////////  直线与线段

// 直线
template <class T>
struct Line
{
    Point<T> a, b;
    Line(Pt a_ = {}, Pt b_ = {}) : a(a_), b(b_) {}
    friend auto &operator<<(ostream &os, Line l) { return os << "<" << l.a << ", " << l.b << ">"; }
};
bool pointOnLineLeft(Pd p, Ld l) { return cross(l.b - l.a, p - l.a) > 0; } // 点在直线左侧
bool onLine(Pd a, Pd b, Pd c) { return sign(cross(a - b, c - b)) == 0; }   // 点是否在直线上（三点是否共线）ok
bool onLine(Pd p, Ld l) { return onLine(p, l.a, l.b); }
// 线段：点是否在线段上 ok
bool pointOnSegment(Pd p, Ld l) { return fabsl(cross(l.a - p, l.b - p)) < eps && dot(l.a - p, l.b - p) <= 0; }
bool Parallel(Pd p1, Pd p2) { return equal(0, cross(p1, p2)); }                          // 平行 ok
bool Orthogonal(Pd p1, Pd p2) { return equal(0, dot(p1, p2)); }                          // 垂直 ok
Ld midSegment(Ld l) { return {(l.a + l.b) / 2, (l.a + l.b) / 2 + rotate90(l.a - l.b)}; } // 线段的中垂线

ld disPointToLine(Pd p, Ld l, bool isSegment = false) // 点到直线/线段的最近距离 ok
{
    ld len_ab = dis(l.a, l.b);
    if (sign(len_ab) == 0)
        return dis(p, l.a);
    if (!isSegment)
    {
        ld ans = cross(p, l.a, l.b);
        return fabsl(ans) / dis(l.a, l.b); // 面积除以底边长
    }
    else
    {
        if (dot(p - l.a, l.b - l.a) <= 0)
            return dis(p, l.a);
        if (dot(p - l.b, l.a - l.b) <= 0)
            return dis(p, l.b);
        ld ans = cross(p, l.a, l.b);
        return fabsl(ans) / dis(l.a, l.b);
    }
}

Pd lineIntersection(Ld l1, Ld l2, bool seg1 = false, bool seg2 = false) // 两直线/线段相交交点 ok
{
    ld det = cross(l1.b - l1.a, l2.b - l2.a);
    if (sign(det) == 0) // 1. 平行或共线
    {
        if (sign(cross(l2.a - l1.a, l1.b - l1.a)) != 0) // 如果不共线 -> 平行
            return Pd(1e18, 1e18);

        if (pointOnSegment(l1.a, l2)) // 共线情况：检查端点是否重合/相连
            return l1.a;
        if (pointOnSegment(l1.b, l2))
            return l1.b;
        if (pointOnSegment(l2.a, l1))
            return l2.a;
        if (pointOnSegment(l2.b, l1))
            return l2.b;
        return Pd(1e18, 1e18); // 完全不相交
    }

    ld t = cross(l2.a - l1.a, l2.b - l2.a) / det; // 2. 一般情况，唯一交点 ✅
    Pd inter = l1.a + (l1.b - l1.a) * t;

    if ((seg1 && !pointOnSegment(inter, l1)) || (seg2 && !pointOnSegment(inter, l2)))
        return Pd(1e18, 1e18); // 不在线段上

    return inter;
}

Pd project(Pd p, Ld l, bool isSegment = false) // 点在直线/线段上的投影点（垂足）ok
{
    Pd h = l.b - l.a;
    ld r = dot(h, p - l.a) / (h.x * h.x + h.y * h.y);
    Pd pro = l.a + h * r;
    if (isSegment)
    {
        if (dot(pro - l.a, l.b - l.a) < 0)
            return l.a;
        if (dot(pro - l.b, l.a - l.b) < 0)
            return l.b;
    }
    return pro;
}

// /////////////////////////////////////////////////////////////////////////////////////////   多边形

using Polygon = vector<Point<ld>>;
ld area(vector<Pd> P) // 任意多边形的面积 ok
{
    int n = P.size();
    ld ans = 0;
    for (int i = 0; i < n; i++)
    {
        ans += cross(P[i], P[(i + 1) % n]);
    }
    return ans / 2.0;
}
int contains(Polygon g, Point<ld> p) // 点与多边形位置关系 内2 上1 外0 ok
{
    int n = g.size();
    bool x = false;
    for (int i = 0; i < n; i++)
    {
        Point<ld> a = g[i] - p, b = g[(i + 1) % n] - p;
        if (abs(cross(a, b)) < eps && dot(a, b) < eps)
            return 1;
        if (a.y > b.y)
            swap(a, b);
        if (a.y < eps && eps < b.y && cross(a, b) > eps)
            x = !x;
    }
    return (x ? 2 : 0);
}
bool isConvexHull(Polygon p) // 是否为凸包 ok
{
    int n = p.size();
    int same[2] = {};
    for (int i = 0; i < n; i++)
    {
        int l = (i + n - 1) % n, r = (i + 1) % n;
        if (sign(cross(p[l] - p[i], p[r] - p[i])) <= 0)
            same[0] = 1;
        else
            same[1] = 1;
    }
    return same[0] + same[1] < 2;
}

Polygon getHull(Polygon p) // 点集求凸包 <=不选共线点 <选共线点
{
    Polygon h, l;
    sort(p.begin(), p.end());
    p.erase(unique(p.begin(), p.end()), p.end());
    if (p.size() <= 1)
        return p;
    for (auto a : p)
    {
        while (h.size() > 1 && cross(a - h.back(), a - h[h.size() - 2]) <= 0) //
            h.pop_back();
        while (l.size() > 1 && cross(a - l.back(), a - l[l.size() - 2]) >= 0) //
            l.pop_back();
        l.push_back(a);
        h.push_back(a);
    }
    l.pop_back();
    reverse(h.begin(), h.end());
    h.pop_back();
    l.insert(l.end(), h.begin(), h.end());
    return l;
}

Polygon sortConvexHullCCW(Polygon h) // 将凸包点集按逆时针排序，从y最小（y相同则x最小）的点开始
{
    reverse(h.begin() + 1, h.end());
    int pos = 0;
    for (int i = 0; i < h.size(); i++)
    {
        if (h[i].y < h[pos].y)
            pos = i;
        else if (h[i].y == h[pos].y)
        {
            if (h[i].x < h[pos].x)
                pos = i;
        }
    }
    rotate(h.begin(), h.begin() + pos, h.end());
    return h;
}

ld diameter(Polygon h) // 旋转卡壳求凸包直径
{
    auto poly = h;
    if (poly.size() == 1)
        return 0;
    if (poly.size() == 2)
        return len(poly[1] - poly[0]);
    if (poly.size() == 3)
        return max({len(poly[1] - poly[0]), len(poly[2] - poly[1]), len(poly[0] - poly[2])});

    size_t cur = 0;
    ld ans = 0;
    for (size_t i = 0; i < poly.size(); i++)
    {
        size_t j = (i + 1) % poly.size();
        Line<ld> line(poly[i], poly[j]);
        while (disPointToLine(poly[cur], line) <= disPointToLine(poly[(cur + 1) % poly.size()], line))
        {
            cur = (cur + 1) % poly.size();
        }
        ans = max(ans, max(len(poly[i] - poly[cur]), len(poly[j] - poly[cur])));
    }
    return ans;
}
void _()
{
    int n;
    cin >> n;
    vector<Pd> h(n);
    for (auto &v : h)
        cin >> v;

    // bug(h.size());
    h = getHull(h);
    // bug(h.size());
    // bugv(h);
    ld ans = diameter(h);

    cout << ans << '\n';
    // cout << (int)round(ans * ans) << '\n';
}
accuracy: 1
time_ms: 40
memory_kb: 8164
final_rank: 10

code_snippet: #include<bits/stdc++.h>
using namespace std;
const double eps = 1e-10;
const int N = 800010;
int sgn(double x)
{
    if (x < -eps) return -1;
    if (x > eps) return 1;
    return 0;
}
struct Vector
{
    double x, y;
    Vector(double x = 0, double y = 0) : x(x), y(y) {}
    Vector operator + (Vector v) { return Vector(x + v.x, y + v.y); }
    Vector operator - (Vector v) { return Vector(x - v.x, y - v.y); }
    bool operator == (Vector v) { return x == v.x && y == v.y; }
};
double distance(Vector A, Vector B) { return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y)); }
double cross(Vector A, Vector B) { return A.x * B.y - A.y * B.x; }
int n, m;
Vector p[N], ch[N];
int ConvexHull(Vector* p, int n, Vector* ch)
{
    n = unique(p, p + n) - p;
    sort(p, p + n, [](const Vector& A, const Vector& B) {
        return sgn(A.x - B.x) < 0 || sgn(A.x - B.x) == 0 && sgn(A.y - B.y) < 0;
        });
    int m = 1;
    ch[0] = p[0];
    for (int i = 1; i < n; i++)
    {
        while (m > 1 && sgn(cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 1])) <= 0)
            m--;
        ch[m++] = p[i];
    }
    int k = m;
    for (int i = n - 2; ~i; i--)
    {
        while (m > k && sgn(cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 1])) <= 0)
            m--;
        ch[m++] = p[i];
    }
    if (n > 1) m--;
    return m;
}
double ch_d(int m)
{
    if (m < 3) return distance(ch[0], ch[1]);
    double d = 0;
    for (int i = 0, j = 2; i < m; i++)
    {
        Vector& A = ch[i], & B = ch[(i + 1) % m];
        while (sgn(cross(A - ch[j], B - ch[j]) - cross(A - ch[(j + 1) % m], B - ch[(j + 1) % m])) <= 0)
            j = (j + 1) % m;
        d = max({ d, distance(A, ch[j]), distance(B, ch[j]) });
    }
    return d;
}
void solve()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> p[i].x >> p[i].y;
    int m = ConvexHull(p, n, ch);
    cout << fixed << setprecision(12) << ch_d(m) << endl;
}
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    solve();
    return 0; 
}
accuracy: 1
time_ms: 20
memory_kb: 28912
final_rank: 18