#include <bits/stdc++.h>
using namespace std;
using lint = long long int;
using pint = pair<int, int>;
using plint = pair<lint, lint>;
struct fast_ios { fast_ios(){ cin.tie(0); ios::sync_with_stdio(false); cout << fixed << setprecision(20); }; } fast_ios_;
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) ((lint)(x).size())
#define POW2(n) (1LL << (n))
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define IFOR(i, begin, end) for(int i=(end)-1,i##_begin_=(begin);i>=i##_begin_;i--)
#define REP(i, n) FOR(i,0,n)
#define IREP(i, n) IFOR(i,0,n)
template<typename T> istream &operator>>(istream &is, vector<T> &vec){ for (auto &v : vec) is >> v; return is; }
template<typename T> ostream &operator<<(ostream &os, const vector<T> &vec){ os << "["; for (auto v : vec) os << v << ","; os << "]"; return os; }
template<typename T> ostream &operator<<(ostream &os, const deque<T> &vec){ os << "deq["; for (auto v : vec) os << v << ","; os << "]"; return os; }
template<typename T> ostream &operator<<(ostream &os, const set<T> &vec){ os << "{"; for (auto v : vec) os << v << ","; os << "}"; return os; }
template<typename T> ostream &operator<<(ostream &os, const unordered_set<T> &vec){ os << "{"; for (auto v : vec) os << v << ","; os << "}"; return os; }
template<typename T> ostream &operator<<(ostream &os, const multiset<T> &vec){ os << "{"; for (auto v : vec) os << v << ","; os << "}"; return os; }
template<typename T> ostream &operator<<(ostream &os, const unordered_multiset<T> &vec){ os << "{"; for (auto v : vec) os << v << ","; os << "}"; return os; }
template<typename T1, typename T2> ostream &operator<<(ostream &os, const pair<T1, T2> &pa){ os << "(" << pa.first << "," << pa.second << ")"; return os; }
template<typename TK, typename TV> ostream &operator<<(ostream &os, const map<TK, TV> &mp){ os << "{"; for (auto v : mp) os << v.first << "=>" << v.second << ","; os << "}"; return os; }
template<typename TK, typename TV> ostream &operator<<(ostream &os, const unordered_map<TK, TV> &mp){ os << "{"; for (auto v : mp) os << v.first << "=>" << v.second << ","; os << "}"; return os; }
template<typename T> void ndarray(vector<T> &vec, int len) { vec.resize(len); }
template<typename T, typename... Args> void ndarray(vector<T> &vec, int len, Args... args) { vec.resize(len); for (auto &v : vec) ndarray(v, args...); }
template<typename T> bool mmax(T &m, const T q) { if (m < q) {m = q; return true;} else return false; }
template<typename T> bool mmin(T &m, const T q) { if (q < m) {m = q; return true;} else return false; }
template<typename T1, typename T2> pair<T1, T2> operator+(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first + r.first, l.second + r.second); }
template<typename T1, typename T2> pair<T1, T2> operator-(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first - r.first, l.second - r.second); }
#define dbg(x) cerr << #x << " = " << (x) << " (L" << __LINE__ << ") " << __FILE__ << endl;
/*
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
using namespace __gnu_pbds; // find_by_order(), order_of_key()
template<typename TK> using pbds_set = tree<TK, null_type, less<TK>, rb_tree_tag, tree_order_statistics_node_update>;
template<typename TK, typename TV> using pbds_map = tree<TK, TV, less<TK>, rb_tree_tag, tree_order_statistics_node_update>;
*/

using P = pair<double, double>;
using pdd = P;
vector<P> x;
double dist(P p, P q)
{
    double dx = p.first - q.first;
    double dy = p.second - q.second;
    return sqrt(dx * dx + dy * dy);
}

void lineFromPoints(pdd P, pdd Q, double &a, 
                        double &b, double &c) 
{ 
    a = Q.second - P.second; 
    b = P.first - Q.first; 
    c = a*(P.first)+ b*(P.second); 
} 
  
// Function which converts the input line to its 
// perpendicular bisector. It also inputs the points 
// whose mid-point lies on the bisector 
void perpendicularBisectorFromLine(pdd P, pdd Q, 
                 double &a, double &b, double &c) 
{ 
    pdd mid_point = make_pair((P.first + Q.first)/2, 
                            (P.second + Q.second)/2); 
  
    // c = -bx + ay 
    c = -b*(mid_point.first) + a*(mid_point.second); 
  
    double temp = a; 
    a = -b; 
    b = temp; 
} 
  
// Returns the intersection point of two lines 
pdd lineLineIntersection(double a1, double b1, double c1, 
                         double a2, double b2, double c2) 
{ 
    double determinant = a1*b2 - a2*b1; 
    if (determinant == 0) 
    { 
        // The lines are parallel. This is simplified 
        // by returning a pair of FLT_MAX 
        return make_pair(FLT_MAX, FLT_MAX); 
    } 
  
    else
    { 
        double x = (b2*c1 - b1*c2)/determinant; 
        double y = (a1*c2 - a2*c1)/determinant; 
        return make_pair(x, y); 
    } 
} 
  
pdd findCircumCenter(pdd P, pdd Q, pdd R) 
{ 
    // Line PQ is represented as ax + by = c 
    double a, b, c; 
    lineFromPoints(P, Q, a, b, c); 
  
    // Line QR is represented as ex + fy = g 
    double e, f, g; 
    lineFromPoints(Q, R, e, f, g); 
  
    // Converting lines PQ and QR to perpendicular 
    // vbisectors. After this, L = ax + by = c 
    // M = ex + fy = g 
    perpendicularBisectorFromLine(P, Q, a, b, c); 
    perpendicularBisectorFromLine(Q, R, e, f, g); 
  
    // The point of intersection of L and M gives 
    // the circumcenter 
    pdd circumcenter = 
           lineLineIntersection(a, b, c, e, f, g);
    return circumcenter;
    // if (circumcenter.first == FLT_MAX &&
    //     circumcenter.second == FLT_MAX)
    // {
    //     cout << "The two perpendicular bisectors "
    //             "found come parallel" << endl;
    //     cout << "Thus, the given points do not form "
    //             "a triangle and are collinear" << endl;
    // }

    // else
    // { 
    //     cout << "The circumcenter of the triangle PQR is: "; 
    //     cout << "(" << circumcenter.first << ", "
    //          << circumcenter.second  << ")" << endl; 
    // } 
} 

int main()
{
    lint N;
    cin >> N;
    x.resize(N);
    REP(i, N) cin >> x[i].first >> x[i].second;
    double ret = 2000;
    REP(i, N) {
        double r = 0.0;
        REP(j, N) mmax(r, dist(x[i], x[j]));
        mmin(ret, r);
    }
    REP(i, N) REP(j, i) {
        P p((x[i].first + x[j].first) / 2, (x[i].second + x[j].second) / 2);
        double r = 0;
        REP(k, N) mmax(r, dist(p, x[k]));
        mmin(ret, r);
    }
    REP(i, N) REP(j, i) REP(k, j) {
        P c = findCircumCenter(x[i], x[j], x[k]);
        double r = 0;
        REP(l, N) mmax(r, dist(c, x[l]));
        mmin(ret, r);
    }
    cout << ret << endl;
}


Measured memory_bytes: 256