Problem: Find the diameter of a convex polygon g. In other words, find a pair of points that have maximum distance between them.

Constraints

* 3 ≤ n ≤ 80000
* -100 ≤ xi, yi ≤ 100
* No point in the g will occur more than once.

Input


n
x1 y1
x2 y2
:
xn yn


The first integer n is the number of points in g.

In the following lines, the coordinate of the i-th point pi is given by two real numbers xi and yi. 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.

Examples

Input

3
0.0 0.0
4.0 0.0
2.0 2.0


Output

4.00


Input

4
0.0 0.0
1.0 0.0
1.0 1.0
0.0 1.0


Output

1.414213562373

Solution (in CPP):

Solution:
#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;
}