#include<bits/stdc++.h>

typedef long double     lf;

using namespace std;

const lf EPS = 1e-9;
const lf oo = 1e15;

struct pt {
  lf x, y;
  pt( ) { }
  pt( lf x, lf y ) : x( x ), y ( y ) { }
};

inline lf x( pt P ) { return P.x; }
inline lf y( pt P ) { return P.y; }

istream& operator >> ( istream& in, pt& p ) {
  lf x,y; in >> x >> y;
  p = pt(x,y); return in;
}

ostream& operator << ( ostream& out, const pt& p ) {
  out << int(p.x) << " " << int(p.y);
  return out;
}

pt operator + ( const pt& A, const pt& B ) {
  return pt( x(A)+x(B), y(A)+y(B) );
}

pt operator - ( const pt& A, const pt& B ) {
  return pt( x(A)-x(B), y(A)-y(B) );
}


pt operator * ( const lf& B, const pt& A ) {
  return pt( x(A)*B, y(A)*B );
}

pt operator * ( const pt& A, const lf& B ) {
  return pt( x(A)*B, y(A)*B  );
}

inline lf dot( pt A, pt B ) {
  return x(A)*x(B) + y(A)*y(B);
}

inline lf norm( pt A ) {
  return x(A)*x(A) + y(A)*y(A);
}

inline lf abs( pt A ) {
  return sqrt( norm(A) );
}

inline lf dist ( pt A, pt B ) { return abs( B - A ); }

lf distToLine (pt p, pt A, pt B, pt &c) {
  lf u = dot( p-A , B-A ) / norm( B-A );
  c = A + u*( B-A );
  return dist( p , c );
}

pt refPoint(pt X, pt A, pt B) {
  pt aux; distToLine(X, A, B, aux);
  return X + lf(2.0)*(aux-X);
}

inline bool same ( lf a, lf b ) {
  return a+EPS > b && b+EPS > a;
}

inline lf cross( pt A, pt B ) {
  return x(A)*y(B) - y(A)*x(B);
}

///CHANGE
// 0  for collineal points ( angle = 0 )
// 1  for angle BAX counter clockwise
// -1 for angle BAX clockwise
inline int ccw (pt X, pt A, pt B) {
  lf c = cross( B-A, X-A );
  if( same( c, 0.0 ) ) { return 0; }
  if( c > EPS ) { return 1; }
  return -1;
}

///CHANGE
inline bool segContains ( pt X, pt A, pt B) {
  if ( !same ( 0, cross ( A-X, B-X ) ) ) return 0;
  return ( dot ( A-X, B-X ) < EPS );
}

inline bool parallel( pt A, pt B, pt C, pt D ) {
  return same ( 0, cross( B-A, D-C ) );
}

///NEW
inline bool ortho( pt A, pt B, pt C, pt D ) {
  return same ( 0, dot( B-A, D-C ) );
}

inline bool samePt ( pt A, pt B ) {
  return same ( x(A), x(B) ) && same ( y(A), y(B) );
}

pt linesIntersection ( pt A, pt B, pt C, pt D ) {
  lf x = cross ( C, D-C ) - cross ( A, D-C );
  x /= cross ( B-A, D-C );
  return A + x*(B-A);
}

inline bool collinearSegsIntersects ( pt A, pt B, pt C, pt D ) {
  return segContains(A,C,D) || segContains(B,C,D)
      || segContains(C,A,B) || segContains(D,A,B);
}

bool segmentsIntersect(pt A, pt B, pt C, pt D) {
  if( samePt(A,B) )
    return segContains( A, C, D );
  if( samePt(C,D) )
    return segContains( C, A, B );
  if( parallel(A,B,C,D) )
    return collinearSegsIntersects( A,B,C,D );
  pt aux = linesIntersection(A,B,C,D);
  return segContains(aux,A,B) && segContains(aux,C,D);
}

///CHANGE
lf distToSegment(pt p, pt A, pt B, pt &c) {
  lf u = dot( p-A , B-A ) / norm( B-A );
  if( u < -EPS ) { c = A; return dist( p , A ); }
  if( (u-1.0) > EPS ) { c = B; return dist( p, B ); }
  return distToLine(p,A,B,c);
}

// P[0] must be equal to P[n]
// Area is positive if the polygon is ccw
double signedArea(const vector<pt> &P) {
  double result = 0.0;
  for(int i = 0; i < (int)P.size()-1; i++) result += cross( P[i],P[i+1] );
  return result / 2.0;
}

double area(const vector<pt> &P) { return fabs(signedArea(P)); }

///CHANGE
// P[0] must be equal to P[n]
bool isConvex( const vector<pt> &P) {
  int sz = (int) P.size(); if(sz <= 3) return false;
  bool isL = ccw(P[0], P[1], P[2]) >= 0;
  for (int i = 1; i < sz-1; i++) {
    if( ( ccw(P[i], P[i+1], P[(i+2) == sz ? 1 : i+2]) >= 0 ) != isL) return false;
  }
  return true;
}

///CHANGE
// P[0] must be equal to P[n]     -    O(N)
// Return 1 for interior, 0 for boundary and -1 for exterior
int inPolygon(pt X, const vector<pt> &P) {
  const int n = P.size(); int cnt = 0;
  for (int i = 0; i < n-1; i++) {
    if( segContains(X, P[i], P[i+1]) ) return 0;
    if( y(P[i]) <= y(X) ) {
      if( y(P[i+1]) > y(X) )
        if( !(ccw( X, P[i], P[i+1]) >= 0) ) cnt++;
    }
    else if ( y(P[i+1]) <= y(X) ) {
      if( ccw( X, P[i], P[i+1]) >= 0 ) cnt--;
    }
  }
  if(cnt == 0) return -1;
  else return 1;
}

///CHANGE
//CONVEX HULL Monotone Chain O( N Log N )
bool cmpPt( pt A, pt B ) {
  if( !same( x(A), x(B) ) ) return x(A) < x(B);
  return y(A) < y(B);
}

int turn(pt A, pt B, pt C) {
  lf r = cross(B-A, C-A);
  if( same( r, 0.0 ) ) return 0;
  if( r > EPS ) return 1;
  return -1;
}

// Return CH in ccw order starting at leftmost - downmost x
// Doesn't return P[ n ] = P[ 0 ]
vector<pt> CH( vector<pt> &P ) {
  if ( P.size() == 1 ) return P;
  const int n = P.size();
  sort ( P.begin(), P.end(), cmpPt );
  vector<pt> up;
  up.push_back(P[0]); up.push_back(P[1]);
  vector<pt> dn;
  dn.push_back(P[0]); dn.push_back(P[1]);
  for ( int i = 2; i < n; ++i ) {
    // If collineal points are not needed, is >= and <=, otherwise > and <
    while ( up.size() > 1 && turn(up[up.size()-2],up.back(),P[i]) >= 0 )
      up.pop_back();
    while ( dn.size() > 1 && turn(dn[dn.size()-2],dn.back(),P[i]) <= 0 )
      dn.pop_back();
    up.push_back(P[i]);
    dn.push_back(P[i]);
  }
  for (int i = (int) up.size() - 2; i >= 1; i--) dn.push_back(up[i]);
  return dn;
}

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

int n;

int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout << fixed << setprecision(10);
  cin >> n;
  vector<pt> P(n);
  for( int i = 0; i < n; ++i ) {
    cin >> P[i];
  }
  int idx;
  lf ans = -oo;
  int sz = int(P.size());
  for( int i = 0, j = 0; i < n; ++i ) {
    while( dist( P[i], P[j] )+EPS < dist( P[i], P[ (j+1)%sz ] )  ) {
      j = (j+1)%sz;
    }
    ans = max( ans, dist( P[i], P[j] ) );
  }
  cout << double(ans) << '\n';
}

Measured memory_bytes: 5412