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

#define ld long double
#define eps 1e-9

bool cmp(ld A , ld B){return A + eps > B && A - eps < B;}

class vec{
public:
	ld x , y;
	vec(ld _x = 0 , ld _y = 0) : x(_x) , y(_y){}
	friend vec operator +(vec A , vec B){return vec(A.x + B.x , A.y + B.y);}
	friend vec operator -(vec A , vec B){return vec(A.x - B.x , A.y - B.y);}
	friend ld operator *(vec A , vec B){return A.x * B.x + A.y * B.y;}
	friend ld operator %(vec A , vec B){return A.x * B.y - A.y * B.x;}
	friend vec operator *(vec A , ld B){return vec(A.x * B , A.y * B);}
	ld len(){return sqrt(x * x + y * y);}
	ld len2(){return x * x + y * y;}
	ld angle(){return atan2(y , x);}
};
vec getvec(){ld x , y; cin >> x >> y; return vec(x , y);}

class segment{
public:
	vec st , ed , dir;
	segment(vec _a = vec() , vec _b = vec()) : st(_a) , ed(_b) , dir(_b - _a){}
	friend bool havesect(segment A , segment B){
		ld P = A.st.x , Q = A.ed.x , X = B.st.x , Y = B.ed.x;
		if(Q < P) swap(P , Q);
		if(Y < X) swap(X , Y);
		if(X - eps > Q || P - eps > Y) return 0;
		P = A.st.y , Q = A.ed.y , X = B.st.y , Y = B.ed.y;
		if(Q < P) swap(P , Q);
		if(Y < X) swap(X , Y);
		if(X - eps > Q || P - eps > Y) return 0;
		return
			((B.st - A.st) % A.dir) * (A.dir % (B.ed - A.st)) > -eps &&
			((B.st - A.ed) % A.dir) * (A.dir % (B.ed - A.ed)) > -eps &&
			((A.st - B.st) % B.dir) * (B.dir % (A.ed - B.st)) > -eps &&
			((A.st - B.ed) % B.dir) * (B.dir % (A.ed - B.ed)) > -eps;
	}
	friend vec getsect(segment A , segment B){
		ld t = ((A.st - B.st) % B.dir) / (B.dir % A.dir);
		return A.st + (A.dir * t);
	}
	ld len(){return dir.len();}
	ld len2(){return dir.len2();}
};
segment getseg(){vec A = getvec() , B = getvec(); return segment(A , B);}

ld dist(vec P , segment seg){//minimum dist from a point to a segment
	ld T = ((P - seg.st) * seg.dir) / seg.len2();
	if(T < 0) return (seg.st - P).len();
	else if(T > 1) return (seg.ed - P).len();
	else return (seg.st + (seg.dir * T) - P).len();
}

class polygon{//counter-clockwise
public:
	vector < vec > point; int sz; mt19937 rndLine;
	void input(){
		mt19937 rnd(time(0)); rndLine = rnd;
		cin >> sz; for(int i = 1 ; i <= sz ; ++i) point.push_back(getvec());
		for(int i = 0 ; i < sz ; ++i) point.push_back(point[i]);
	}
	ld area(){
		ld sum = 0;
		for(int i = 0 ; i < sz ; ++i) sum += point[i] % point[i + 1];
		return sum / 2;
	}
	bool isconvex(){
		for(int i = 1 ; i <= sz ; ++i)
			if((point[i - 1] - point[i]) % (point[i + 1] - point[i]) > eps) return 0;
		return 1;
	}
	bool checkin(vec P){
		vec tmp(pow(rndLine() , 0.1) , pow(rndLine() , 0.2));
		segment nowline(P , P + tmp * 1e10);
		int Count = 0;
		for(int i = 0 ; i < sz ; ++i)
			Count += havesect(segment(point[i] , point[i + 1]) , nowline);
		return Count & 1;
	}
	bool checkon(vec P){
		for(int i = 0 ; i < sz ; ++i)
			if(cmp(0 , (point[i + 1] - point[i]) % (P - point[i]))){
				ld t = (point[i + 1] - point[i]) * (P - point[i]) / (point[i + 1] - point[i]).len2();
				if(t > -eps && t < 1 + eps)
					return 1;
			}
		return 0;
	}
};

class convex : public polygon{
public:
	ld dosomething(){
		int cur = 0; ld ans = 0;
		for(int i = 1 ; i < sz ; ++i)
			if((point[i] - point[0]).len() > (point[cur] - point[0]).len())
				cur = i;
		for(int i = 0 ; i < sz ; ++i){
			while((point[i + 1] - point[i]) % (point[cur] - point[i]) <= (point[i + 1] - point[i]) % (point[cur + 1] - point[i])) ++cur;
			ans = max(ans , max((point[cur] - point[i]).len() , (point[cur] - point[i + 1]).len()));
		}
		return ans;
	}
};

convex getconvex(polygon now){
	vector < vec > p , p1; convex ans;
	p.insert(p.begin() , now.point.begin() , now.point.begin() + now.sz);
	sort(p.begin() , p.end() , [&](vec a , vec b){return a.y < b.y || a.y == b.y && a.x < b.x;});
	deque < vec > q;
	for(auto t : p){
		while(q.size() >= 2 && (q[q.size() - 1] - q[q.size() - 2]) % (t - q[q.size() - 2]) < eps)
			q.pop_back();
		q.push_back(t);
	}
	q.pop_back(); p1.insert(p1.end() , q.begin() , q.end()); reverse(p.begin() , p.end()); q.clear();
	for(auto t : p){
		while(q.size() >= 2 && (q[q.size() - 1] - q[q.size() - 2]) % (t - q[q.size() - 2]) < eps)
			q.pop_back();
		q.push_back(t);
	}
	q.pop_back(); p1.insert(p1.end() , q.begin() , q.end());
	p1.insert(p1.end() , p1.begin() , p1.end());
	ans.point = p1; ans.sz = p1.size() / 2; return ans;
}

int main(){
	convex now; now.input(); cout << fixed << setprecision(10) << now.dosomething() << endl;
	return 0;
}



Measured memory_bytes: 11276