#include<iostream>
#include<cmath>     // fabs(), hypot()
#include<vector>    // Circle::crosspoint()の戻り値, Polygonのメンバ変数
#include<algorithm> // min(), sort(), reverse()

const double EPS = 1e-10;
enum POSITION {ONLINE_FRONT = -2, CLOCKWISE, ON_SEGMENT, COUNTER_CLOCKWISE, ONLINE_BACK};   // ccw(a, b, p)で3点a, b, pの位置関係を返す
                                                                                            //   0: a-p-b, 2: p-a-b, -2: a-b-p, 1: -a-b-の上にp, -1: -a-b-の下にp
// class
class Point;
class Segment;
class Line;
class Circle;
class Polygon;
// member
class Point {
    double x_value, y_value;
public:
    Point() :x_value(0.0), y_value(0.0) {}
    Point(double d) :x_value(d), y_value(d) {}
    Point(double a, double b) :x_value(a), y_value(b) {}
    double x() const {return x_value;}
    double y() const {return y_value;}
    double x(double d) {return x_value = d;}
    double y(double d) {return y_value = d;}
    const Point operator-() const;
    const Point& operator=(const Point&);
    const Point& operator+=(const Point&);
    const Point& operator-=(const Point&);
    const Point& operator*=(const Point&);
    const Point& operator/=(const Point&);
    double dot(const Point&) const;
    double cross(const Point&) const;
    double abs() const;
    double norm() const;
    const Point normalize() const;
    double distance(const Point&) const;
    double distance(const Segment&) const;
    double distance(const Line&) const;
    bool intersect(const Point&) const;
    bool intersect(const Segment&) const;
    bool intersect(const Line&) const;
    const Point projection(const Line&) const;
    const Point reflection(const Line&) const;
    POSITION ccw(const Point&, const Point&) const;
    void print() const;
};
bool operator==(const Point& lhs, const Point& rhs);
bool operator!=(const Point& lhs, const Point& rhs);
bool operator<(const Point& lhs, const Point& rhs);
bool operator>(const Point& lhs, const Point& rhs);
bool operator<=(const Point& lhs, const Point& rhs);
bool operator>=(const Point& lhs, const Point& rhs);
const Point operator+(const Point& lhs, const Point& rhs);
const Point operator-(const Point& lhs, const Point& rhs);
const Point operator*(const Point& lhs, const Point& rhs);
const Point operator/(const Point& lhs, const Point& rhs);
class Segment {
    Point source_point, target_point;
public:
    Segment(double x1 = 0.0, double y1 = 0.0, double x2 = 0.0, double y2 = 0.0) :source_point(Point(x1, y1)), target_point(Point(x2, y2)) {}
    Segment(const Point& a, const Point& b) :source_point(a), target_point(b) {}
    const Point& source() const {return source_point;}
    const Point& target() const {return target_point;}
    const Point& source(const Point& p) {return source_point = p;}
    const Point& target(const Point& p) {return target_point = p;}
    const Line line() const;
    double distance(const Point&) const;
    double distance(const Segment&) const;
    double distance(const Line&) const;
    bool intersect(const Point&) const;
    bool intersect(const Segment&) const;
    bool intersect(const Line&) const;
    bool parallel(const Segment&) const;
    bool parallel(const Line&) const;
    bool orthogonal(const Segment&) const;
    bool orthogonal(const Line&) const;
    POSITION ccw(const Point&) const;
    void print() const;
};
class Line {
    Point source_point, target_point;
public:
    Line(double x1 = 0.0, double y1 = 0.0, double x2 = 0.0, double y2 = 0.0) :source_point(Point(x1, y1)), target_point(Point(x2, y2)) {}
    Line(const Point& a, const Point& b) :source_point(a), target_point(b) {}
    const Point& source() const {return source_point;}
    const Point& target() const {return target_point;}
    const Point& source(const Point& p) {return source_point = p;}
    const Point& target(const Point& p) {return target_point = p;}
    const Segment segment() const;
    double distance(const Point&) const;
    double distance(const Segment&) const;
    double distance(const Line&) const;
    bool intersect(const Point&) const;
    bool intersect(const Segment&) const;
    bool intersect(const Line&) const;
    bool parallel(const Segment&) const;
    bool parallel(const Line&) const;
    bool orthogonal(const Segment&) const;
    bool orthogonal(const Line&) const;
    const Point projection(const Point&) const;
    const Point reflection(const Point&) const;
    const Point crosspoint(const Line&) const;
    POSITION ccw(const Point&) const;
    void print() const;
};
class Circle {
    Point center_point;
    double radius;
public:
    Circle(double a = 0.0, double b = 0.0, double c = 0.0) :center_point(Point(a, b)), radius(c) {}
    const Point& center() const {return center_point;}
    double x() const {return center_point.x();}
    double y() const {return center_point.y();}
    double r() const {return radius;}
    const Point& center(const Point& p) {return center_point = p;}
    double x(double d) {return center_point.x(d);}
    double y(double d) {return center_point.y(d);}
    double r(double d) {return radius = d;}
    bool in(const Point&) const;
    bool out(const Point&) const;
    bool include(const Point&) const;
    bool include(const Segment&) const;
    bool include(const Line&) const;
    bool include(const Circle&) const;
    bool intersect(const Circle&) const;
    std::vector<Point> crosspoint(const Circle&) const;
    void print() const;
};
class Polygon {
    std::vector<Point> point;
public:
    Polygon() {}
    explicit Polygon(std::vector<Point> p) :point(p) {}
    const std::vector<Point>& vertex() const {return point;}
    const Point& vertex(int i) const {return point.at(i);}
    const std::vector<Segment> edge() const {
        std::vector<Segment> result;
        for(int i = 0; i < size(); ++i) result.push_back(Segment(vertex(i), vertex((i + 1) % size())));
        return result;
    }
    const Segment edge(int i) const {return Segment(vertex(i), vertex((i + 1) % size()));}
    int size() const {return point.size();}
    void add(const Point&);
    double area() const;
    bool convex() const;
    bool include(const Point&) const;
    bool intersect(const Polygon&) const;
    bool on_edge(const Point&) const;
    const Polygon convex_hull() const;
    const std::vector<int> farthest_point_pair() const;
    void print() const;
};
// Point
bool operator==(const Point& lhs, const Point& rhs) {return lhs.x() == rhs.x() && lhs.y() == rhs.y();}
bool operator!=(const Point& lhs, const Point& rhs) {return lhs.x() != rhs.x() || lhs.y() != rhs.y();}
bool operator<(const Point& lhs, const Point& rhs) {return lhs.x() != rhs.x() ? lhs.x() < rhs.x() : lhs.y() < rhs.y();}
bool operator>(const Point& lhs, const Point& rhs) {return lhs.x() != rhs.x() ? lhs.x() > rhs.x() : lhs.y() > rhs.y();}
bool operator<=(const Point& lhs, const Point& rhs) {return lhs.x() != rhs.x() ? lhs.x() < rhs.x() : lhs.y() <= rhs.y();}
bool operator>=(const Point& lhs, const Point& rhs) {return lhs.x() != rhs.x() ? lhs.x() > rhs.x() : lhs.y() >= rhs.y();}
const Point operator+(const Point& lhs, const Point& rhs) {return Point(lhs.x() + rhs.x(), lhs.y() + rhs.y());}
const Point operator-(const Point& lhs, const Point& rhs) {return Point(lhs.x() - rhs.x(), lhs.y() - rhs.y());}
const Point operator*(const Point& lhs, const Point& rhs) {return Point(lhs.x() * rhs.x(), lhs.y() * rhs.y());}
const Point operator/(const Point& lhs, const Point& rhs) {return Point(lhs.x() / rhs.x(), lhs.y() / rhs.y());}
const Point Point::operator-() const {return Point(-x(), -y());}
const Point& Point::operator=(const Point& p) {x(p.x()); y(p.y()); return *this;}
const Point& Point::operator+=(const Point& p) {return *this = *this + p;}
const Point& Point::operator-=(const Point& p) {return *this = *this - p;}
const Point& Point::operator*=(const Point& p) {return *this = *this * p;}
const Point& Point::operator/=(const Point& p) {return *this = *this / p;}
double Point::dot(const Point& p) const {return x() * p.x() + y() * p.y();}
double Point::cross(const Point& p) const {return x() * p.y() - y() * p.x();}
double Point::abs() const {return hypot(x(), y());}
double Point::norm() const {return x() * x() + y() * y();}
const Point Point::normalize() const {return abs() == 0.0 ? Point(0.0, 0.0) : Point(x(), y()) / abs();}
double Point::distance(const Point& p) const {return (p - (*this)).abs();}
double Point::distance(const Segment& s) const {return s.distance(*this);}
double Point::distance(const Line& l) const {return l.distance(*this);}
bool Point::intersect(const Point& p) const {return *this == p;}
bool Point::intersect(const Segment& s) const {return s.intersect(*this);}
bool Point::intersect(const Line& l) const {return l.intersect(*this);}
const Point Point::projection(const Line& l) const {return l.projection(*this);}
const Point Point::reflection(const Line& l) const {return l.reflection(*this);}
POSITION Point::ccw(const Point& b, const Point& p) const {
    const Point& a = *this;
    Point v1 = b - a;
    Point v2 = p - a;
    if(v1.cross(v2) > EPS) return COUNTER_CLOCKWISE;
    if(v1.cross(v2) < -EPS) return CLOCKWISE;
    if(v1.dot(v2) < -EPS) return ONLINE_BACK;
    if(v2.norm() - v1.norm() > EPS) return ONLINE_FRONT;
    return ON_SEGMENT;
}
void Point::print() const {std::cout << x() << ", " << y() << std::endl;}
// Segment
const Line Segment::line() const {return Line(source(), target());}
double Segment::distance(const Point& p) const {
    Point q = line().projection(p);
    if(intersect(q)) return q.distance(p);
    return std::min(source().distance(p), target().distance(p));
}
double Segment::distance(const Segment& s) const {
    if(intersect(s)) return 0.0;
    double a = distance(s.source());
    double b = distance(s.target());
    double c = s.distance(source());
    double d = s.distance(target());
    return std::min({a, b, c, d});
}
double Segment::distance(const Line& l) const {return l.distance(*this);}
bool Segment::intersect(const Point& p) const {return source().distance(p) + target().distance(p) - target().distance(source()) <= EPS;}
bool Segment::intersect(const Segment& s) const {
    bool a = ccw(s.source()) * ccw(s.target()) <= 0;
    bool b = s.ccw(source()) * s.ccw(target()) <= 0;
    return a && b;
}
bool Segment::intersect(const Line& l) const {return l.intersect(*this);}
bool Segment::parallel(const Line& l) const {line().parallel(l);}
bool Segment::parallel(const Segment& s) const {return line().parallel(s.line());}
bool Segment::orthogonal(const Line& l) const {return line().orthogonal(l);}
bool Segment::orthogonal(const Segment& s) const {return line().orthogonal(s.line());}
POSITION Segment::ccw(const Point& p) const {return source().ccw(target(), p);}
void Segment::print() const {std::cout << "source: "; source().print(); std::cout << "target: "; target().print();}
// Line
const Segment Line::segment() const {return Segment(source(), target());}
double Line::distance(const Point& p) const {return p.distance(projection(p));}
double Line::distance(const Segment& s) const {
    if(intersect(s)) return 0.0;
    double a = distance(s.source());
    double b = distance(s.target());
    return a < b ? a : b;
}
double Line::distance(const Line& l) const {return intersect(l) ? 0.0 : distance(l.source());}
bool Line::intersect(const Point& p) const {
    Point v1 = source() - p;
    Point v2 = target() - p;
    return fabs(v2.cross(v1)) < EPS;
}
bool Line::intersect(const Segment& s) const {
    Point v1 = target() - source();
    Point v2 = s.source() - source();
    Point v3 = s.target() - source();
    return v1.cross(v2) * v1.cross(v3) <= EPS;
}
bool Line::intersect(const Line& l) const {
    Point v1 = target() - source();
    Point v2 = l.target() - l.source();
    Point v3 = l.source() - source();
    return fabs(v1.cross(v2)) > EPS || fabs(v1.cross(v3)) < EPS;
}
bool Line::parallel(const Line& l) const {
    double dx = target().x() - source().x();
    double dy = target().y() - source().y();
    double dxl = l.target().x() - l.source().x();
    double dyl = l.target().y() - l.source().y();
    return fabs(dy * dxl - dyl * dx) <= EPS;
}
bool Line::parallel(const Segment& s) const {return parallel(s.line());}
bool Line::orthogonal(const Line& l) const {
    double dx = target().x() - source().x();
    double dy = target().y() - source().y();
    double dxl = l.target().x() - l.source().x();
    double dyl = l.target().y() - l.source().y();
    return fabs(dy * dyl + dxl * dx) <= EPS;
}
bool Line::orthogonal(const Segment& s) const {return orthogonal(s.line());}
const Point Line::projection(const Point& p) const {
    Point v1 = p - source();
    Point v2 = source() - target();
    return Point(source() + v2 * (v1.dot(v2) / v2.norm()));
}
const Point Line::reflection(const Point& p) const {
    return p + (projection(p) - p) * 2.0;
}
const Point Line::crosspoint(const Line& l) const {
    Point v1 = target() - source();
    Point v2 = l.target() - l.source();
    Point v3 = target() - l.source();
    double a = v1.cross(v2);
    double b = v1.cross(v3);
    if(fabs(a) <= EPS && fabs(b) <= EPS) return l.source();
    return l.source() + v2 * (b / a);
}
POSITION Line::ccw(const Point& p) const {return source().ccw(target(), p);}
void Line::print() const {std::cout << "source: "; source().print(); std::cout << "target: "; target().print();}
// Circle
bool Circle::in(const Point& p) const {return center().distance(p) < r() - EPS;}
bool Circle::out(const Point& p) const {return r() + EPS < center().distance(p);}
bool Circle::include(const Point& p) const {return center().distance(p) <= r() + EPS;}
bool Circle::include(const Segment& s) const {return center().distance(s) <= r() + EPS;}
bool Circle::include(const Line& l) const {return center().distance(l) <= r() + EPS;}
bool Circle::include(const Circle& c) const {return center().distance(c.center()) <= r() - c.r() + EPS;}
bool Circle::intersect(const Circle& c) const {return center().distance(c.center()) <= r() + c.r() + EPS;}
std::vector<Point> Circle::crosspoint(const Circle& c) const {
    double angle = atan2(c.y() - y(), c.x() - x());
    double d = center().distance(c.center());
    double add = acos((d * d + r() * r() - c.r() * c.r()) / (2.0 * d * r()));
    std::vector<Point> result;
    result.push_back(Point(x() + r() * cos(angle + add), y() + r() * sin(angle + add)));
    result.push_back(Point(x() + r() * cos(angle - add), y() + r() * sin(angle - add)));
    return result;
}
void Circle::print() const {std::cout << "radius: " << r() << " - point: "; center().print();}
// Polygon
void Polygon::add(const Point& p) {point.push_back(p);}
double Polygon::area() const {
    double sum = 0.0;
    for(int i = 0; i < size(); ++i) sum += vertex(i).cross(vertex((i+1) % size()));
    return fabs(sum) / 2.0;
}
bool Polygon::convex() const {
    POSITION direction = ONLINE_BACK;
    for(int i = 1; i < size(); ++i) {
        POSITION d = vertex(i - 1).ccw(vertex(i), vertex((i + 1) % size()));
        if(d != CLOCKWISE && d != COUNTER_CLOCKWISE) continue;
        if(direction == ONLINE_BACK) direction = d;
        if(direction != d) return false;
    }
    return true;
}
bool Polygon::include(const Point& p) const {
    bool in = false;
    for(int i = 0; i < size(); ++i) {
        Point a = vertex(i) - p;
        Point b = vertex((i + 1) % size()) - p;
        if(a.y() > b.y()) std::swap(a, b);
        if(a.y() <= 0.0 && 0 < b.y() && a.cross(b) < 0.0) in = !in;
        if(fabs(a.cross(b)) <= EPS && a.dot(b) <= EPS) return true;
    }
    return in;
}
bool Polygon::intersect(const Polygon& p) const {
    for(const auto& e1: edge()) for(const auto& e2: p.edge()) if(e1.intersect(e2)) return true;
    return false;
}
bool Polygon::on_edge(const Point& p) const {
    for(int i = 0; i < size(); ++i) if(vertex(i).ccw(vertex((i + 1) % size()), p) == ON_SEGMENT) return true;
    return false;
}
const Polygon Polygon::convex_hull() const {                // Andrew's Monotone Chain
    Polygon result;
    std::vector<Point> res;
    std::vector<Point> original = vertex();
    sort(original.begin(), original.end());
    original.insert(original.end(), original.rbegin() + 1, original.rend());
    for(const auto& v: original) {
        while(res.size() >= 2 && res[res.size() - 2].ccw(res[res.size() - 1], v) == CLOCKWISE) res.pop_back();
        res.push_back(v);
    }
    res.pop_back();
    for(const auto& v: res) result.add(v);
    return result;
}
const std::vector<int> Polygon::farthest_point_pair() const {  // Rotating calipers
    auto v = vertex();
    int p, q, start_p, start_q, max_p, max_q;
    p = start_p = max_p = std::min_element(v.begin(), v.end()) - v.begin();
    q = start_q = max_q = std::max_element(v.begin(), v.end()) - v.begin();;
    double max_d = (vertex(p) - vertex(q)).norm();
    do {
        if((vertex((p + 1) % size()) - vertex(p)).cross(vertex((q + 1) % size()) - vertex(q)) >= 0) q = (q + 1) % size();
        else p = (p + 1) % size();
        if((vertex(p) - vertex(q)).norm() > max_d) {
            max_d = (vertex(p) - vertex(q)).norm();
            max_p = p;
            max_q = q;
        }
    } while(start_p != q || start_q != p);
    return {max_p, max_q};
}
void Polygon::print() const {for(const auto& v: vertex()) std::cout << v.x() << ", " << v.y() << std::endl;}

////////////////////////////////////////////////////////////////////////////////
#include<iomanip>
using namespace std;
int main() {
    int n;
    Polygon p;
    for(cin>>n;n>0;--n) {
        double x,y;
        cin>>x>>y;
        p.add(Point(x,y));
    }
    auto v = p.farthest_point_pair();
    cout<<setprecision(12)<<fixed<<p.vertex(v[0]).distance(p.vertex(v[1]))<<endl;
}

Measured memory_bytes: 3660