//#pragma comment(linker, "/stack:200000000") //  (UNCOMMENT WHEN HAVING LOTS OF RECURSIONS)
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("unroll-loops")  // $ (UNCOMMENT WHEN TRYING TO BRUTEFORCE WITH A LOT OF LOOPS)\

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;

#define endl "\n"
#define int long long
#define ll long long
#define pb push_back
#define all(c) (c).begin(), (c).end()
#define fi first
#define se second
#define vi vector<int>
#define vii vector< vector<int> >
#define vpi vector< pair<int,int> >
#define pair <int, int> pii

typedef tree< int, null_type, less<int>, rb_tree_tag,tree_order_statistics_node_update>
    ordered_set;
// order_of_key (val): returns the no. of values less than val
// find_by_order (k): returns the iterator to kth largest element.(0-based)

#define TRACE
#ifndef ONLINE_JUDGE
#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1){
	cerr << name << " : " << arg1 << endl;}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args){
	const char* comma = strchr(names + 1, ',');cerr.write(names, comma - names) << " : " << arg1<<"  ";__f(comma+1, args...);}
#else
#define trace(...)
#endif

template<class T> ostream& operator<<(ostream& o,const vector<T> &vc){
	o<<"{";
	for(const T& v:vc) o<<v<<",";
	o<<"}";
	return o;
}

int modpow(int x, int n, int mod)
{
	x %= mod ;
	int res = 1;
	while(n > 0) {
		if(n & 1)
			res = res * x % mod ;
		x = x * x % mod ;
		n >>= 1 ;
	}
	return res;
}

const long long inf = 2e18 ;
const int mod = 1e9+7 ;
// const int mod = 998244353 ;
const int N = 1e5 + 10 ;

typedef long double ld;

#include <vector>


struct Point final {
	
	public: double x;
	public: double y;
	
	
	public: Point subtract(const Point &p) const;
	
	public: double distance(const Point &p) const;
	
	// Signed area / determinant thing
	public: double cross(const Point &p) const;
	
};


struct Circle final {
	
	public: static const Circle INVALID;
	
	private: static const double MULTIPLICATIVE_EPSILON;
	
	
	public: Point c;   // Center
	public: double r;  // Radius
	
	
	public: bool contains(const Point &p) const;
	
	public: bool contains(const std::vector<Point> &ps) const;
	
};


/* 
 * (Main function) Returns the smallest circle that encloses all the given points.
 * Runs in expected O(n) time, randomized. Note: If 0 points are given, a circle of
 * negative radius is returned. If 1 point is given, a circle of radius 0 is returned.
 */
Circle makeSmallestEnclosingCircle(const std::vector<Point> &points);

// For unit tests
Circle makeDiameter(const Point &a, const Point &b);
Circle makeCircumcircle(const Point &a, const Point &b, const Point &c);

/*---- Members of struct Point ----*/

Point Point::subtract(const Point &p) const {
	return Point{x - p.x, y - p.y};
}


double Point::distance(const Point &p) const {
	return std::hypot(x - p.x, y - p.y);
}


double Point::cross(const Point &p) const {
	return x * p.y - y * p.x;
}


/*---- Members of struct Circle ----*/

const Circle Circle::INVALID{Point{0, 0}, -1};

const double Circle::MULTIPLICATIVE_EPSILON = 1 + 1e-14;


bool Circle::contains(const Point &p) const {
	return c.distance(p) <= r * MULTIPLICATIVE_EPSILON;
}


bool Circle::contains(const vector<Point> &ps) const {
	for (const Point &p : ps) {
		if (!contains(p))
			return false;
	}
	return true;
}


/*---- Smallest enclosing circle algorithm ----*/

static Circle makeSmallestEnclosingCircleOnePoint (const vector<Point> &points, size_t end, const Point &p);
static Circle makeSmallestEnclosingCircleTwoPoints(const vector<Point> &points, size_t end, const Point &p, const Point &q);

static std::default_random_engine randGen((std::random_device())());


// Initially: No boundary points known
Circle makeSmallestEnclosingCircle(const vector<Point> &points) {
	// Clone list to preserve the caller's data, randomize order
	vector<Point> shuffled = points;
	std::shuffle(shuffled.begin(), shuffled.end(), randGen);
	
	// Progressively add points to circle or recompute circle
	Circle c = Circle::INVALID;
	for (size_t i = 0; i < shuffled.size(); i++) {
		const Point &p = shuffled.at(i);
		if (c.r < 0 || !c.contains(p))
			c = makeSmallestEnclosingCircleOnePoint(shuffled, i + 1, p);
	}
	return c;
}


// One boundary point known
static Circle makeSmallestEnclosingCircleOnePoint(const vector<Point> &points, size_t end, const Point &p) {
	Circle c{p, 0};
	for (size_t i = 0; i < end; i++) {
		const Point &q = points.at(i);
		if (!c.contains(q)) {
			if (c.r == 0)
				c = makeDiameter(p, q);
			else
				c = makeSmallestEnclosingCircleTwoPoints(points, i + 1, p, q);
		}
	}
	return c;
}


// Two boundary points known
static Circle makeSmallestEnclosingCircleTwoPoints(const vector<Point> &points, size_t end, const Point &p, const Point &q) {
	Circle circ = makeDiameter(p, q);
	Circle left  = Circle::INVALID;
	Circle right = Circle::INVALID;
	
	// For each point not in the two-point circle
	Point pq = q.subtract(p);
	for (size_t i = 0; i < end; i++) {
		const Point &r = points.at(i);
		if (circ.contains(r))
			continue;
		
		// Form a circumcircle and classify it on left or right side
		double cross = pq.cross(r.subtract(p));
		Circle c = makeCircumcircle(p, q, r);
		if (c.r < 0)
			continue;
		else if (cross > 0 && (left.r < 0 || pq.cross(c.c.subtract(p)) > pq.cross(left.c.subtract(p))))
			left = c;
		else if (cross < 0 && (right.r < 0 || pq.cross(c.c.subtract(p)) < pq.cross(right.c.subtract(p))))
			right = c;
	}
	
	// Select which circle to return
	if (left.r < 0 && right.r < 0)
		return circ;
	else if (left.r < 0)
		return right;
	else if (right.r < 0)
		return left;
	else
		return left.r <= right.r ? left : right;
}


Circle makeDiameter(const Point &a, const Point &b) {
	Point c{(a.x + b.x) / 2, (a.y + b.y) / 2};
	return Circle{c, max(c.distance(a), c.distance(b))};
}


Circle makeCircumcircle(const Point &a, const Point &b, const Point &c) {
	// Mathematical algorithm from Wikipedia: Circumscribed circle
	double ox = (min(min(a.x, b.x), c.x) + max(min(a.x, b.x), c.x)) / 2;
	double oy = (min(min(a.y, b.y), c.y) + max(min(a.y, b.y), c.y)) / 2;
	double ax = a.x - ox,  ay = a.y - oy;
	double bx = b.x - ox,  by = b.y - oy;
	double cx = c.x - ox,  cy = c.y - oy;
	double d = (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) * 2;
	if (d == 0)
		return Circle::INVALID;
	double x = ((ax*ax + ay*ay) * (by - cy) + (bx*bx + by*by) * (cy - ay) + (cx*cx + cy*cy) * (ay - by)) / d;
	double y = ((ax*ax + ay*ay) * (cx - bx) + (bx*bx + by*by) * (ax - cx) + (cx*cx + cy*cy) * (bx - ax)) / d;
	Point p{ox + x, oy + y};
	double r = max(max(p.distance(a), p.distance(b)), p.distance(c));
	return Circle{p, r};
}

signed main()
{
        ios::sync_with_stdio(false) ;
        cin.tie(0) ;
        cout.tie(0) ; 

	int n;
	cin>>n;
	double x[n+10],y[n+10];
	vector<Point> v;  
	int fx=0,fy=0;
	for(int i=0;i<n;i++) {
		cin>>x[i]>>y[i];
		v.pb({x[i],y[i]});
		fx+=x[i];
		fy+=y[i];
	}
	cout<<setprecision(10);
	Circle hp = makeSmallestEnclosingCircle(v);

	cout<<hp.r << endl;

	// ld xx=1.0*fx/n;
	// ld yy=1.0*fy/n;

	// ld mx=0;
	// for(int i=0;i<n;i++) {
	// 	ld temp=0;
	// 	temp = (xx-x[i])*(xx-x[i]) + (yy-y[i])*(yy-y[i]);
	// 	if(temp>mx) {
	// 		mx=temp;
	// 	}
	// }

	// ld ans=sqrt(mx);
	// cout<<setprecision(10);
	// cout<<ans<<endl;

return 0 ;
}


Measured memory_bytes: 256