#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
typedef long long ll;
int sgn(double x){
    if(fabs(x)<eps){
        return 0;
    }
    if(x<0){
        return -1;
    }else{
        return 1;
    }
}

struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;
        y = _y;
    }
    void input(){
        scanf("%lf%lf",&x,&y);
    }
    bool operator < (Point b)const{
        // return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x;
        return sgn(y-b.y)==0?x<b.x:y<b.y;
    }
    bool operator == (Point b)const{
        return sgn(x-b.x)==0&&sgn(y-b.y)==0;
    }
    Point operator -(const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    double operator ^(const Point &b)const{
        return x*b.y-y*b.x;
    }
    Point operator /(const double& k)const{
        return Point(x/k,y/k);
    }
    double distance(Point p){
        return hypot(x-p.x,y-p.y);
    }
};
struct Line{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e){
        s  = _s;
        e = _e;
    }
};
struct polygon{
    int n;
    vector<Point> p;
    void input(int _n){
        n = _n;
        Point tt;
        for(int i = 0;i < n;++i ){
            tt.input();
            p.push_back(tt);
        }
    }
    // 安德鲁算法
     void getconvex(polygon &convex){
        sort(p.begin(),p.end());
        convex.n = n;
        for(int i = 0;i < min(n,2);++i ){
            convex.p.push_back(p[i]);
        }
        if(convex.n == 2&&(convex.p[0] == convex.p[1])){
            convex.n--;   //特判
        }
        if(n<=2){
            return;
        }
        for(int i = 2;i < n;++i ){
            while(convex.p.size()>1 && sgn((convex.p[convex.p.size()-1]-p[i])^(convex.p[convex.p.size()-2]-p[i]))<=0){
                convex.p.pop_back();
            }
            convex.p.push_back(p[i]);
        }
        int temp = convex.p.size()-1;
        convex.p.push_back(p[n-2]);
        for(int i = n-3;i >= 0;i--){
            while(convex.p.size()-1 != temp && sgn((convex.p[convex.p.size()-1]-p[i])^(convex.p[convex.p.size()-2]-p[i]))<=0){
                convex.p.pop_back();
            }
            convex.p.push_back(p[i]);
        }
        // convex.n = convex.p.size();
        if(convex.n == 2 && (convex.p[0] == convex.p[1])){
            convex.n--;
        }
       reverse(convex.p.begin(),convex.p.end());    //!!!!
       convex.p.pop_back();            //!!!!
       convex.n = convex.p.size();
    }
    //旋转卡壳
    double rc(){
        int q = 1;
        double ans = 0;
        int tmp = p.size();
        p.push_back(p[0]);  //p[n] = p[0]
        for(int i = 0;i < tmp;++i ){
            while(sgn(((p[q+1]-p[i+1])^(p[i]-p[i+1]))-((p[q]-p[i+1])^(p[i]-p[i+1])))>0){
                q = (q+1)%tmp;
            }
            ans = max(ans,max(p[i].distance(p[q]),p[i+1].distance(p[q+1])));
        }
        return ans;
    }
};

int main(){
    int n;
    scanf("%d",&n);
    polygon pp;
    pp.input(n);
    polygon ans;
    pp.getconvex(ans);
    // int tt = ans.n;
    // for(int i = 0;i < tt;++i ){
    //     printf("%lf %lf\n",(ans.p[i].x),(ans.p[i].y));
    // }
    double res = ans.rc();
    printf("%.12f\n",res);
 
    return 0;
}
