#include<bits/stdc++.h>

using namespace std;

#define INF 1000000007
#define MOD 998244353
#define LINF (1LL << 62)
#define PI 3.14159265358979

typedef long long i64;
typedef pair<i64,i64> P;

inline i64 mod(i64 a, i64 m) { return (a % m + m) % m; }

template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }

mt19937_64 rnd(time(NULL));


struct UnionFind {
	vector<int> par, depth, sz;
	UnionFind(int n) : par(n+1), depth(n+1), sz(n+1){
		for(int i = 0; i <= n; i++){
			par[i] = i;
			depth[i] = 0;
			sz[i] = 1;
		}
	}
	int find(int x){
		if(par[x] == x) return x;
		else return par[x] = find(par[x]);
	}
	void unite(int x, int y){
		x = find(x);
		y = find(y);
		if(x == y) return;
		sz[x] += sz[y];
		sz[y] = sz[x];
		if(depth[x] < depth[y]){
			par[x] = y;
		}
		else{
			par[y] = x;
			if(depth[x] == depth[y]) depth[x]++;
		}
	}
	bool same(int x, int y){
		return find(x) == find(y);
	}
	int size(int x){
		return sz[find(x)];
	}
};
i64 n, x[101], y[101];
void solve(){
	cin >> n;
	for(int i = 0; i < n; i++){
		cin >> x[i] >> y[i];
		x[i] += 100;
		y[i] += 100;
	}
	
	double l = 0, r = 200;
	for(int i = 0; i < 100; i++){
		double c = (l+r)/2;
		UnionFind uf(n+2);
		for(int j = 0; j < n; j++){
			if(y[j] < c) uf.unite(j,n);
			if(200-y[j] < c) uf.unite(j,n+1);
			
			for(int k = 0; k < n; k++){
				if(pow(pow(x[j]-x[k],2)+pow(y[j]-y[k],2),0.5) < c){
					uf.unite(j,k);
				}
			}
		}
		bool ok = 1;
		for(int i = 0; i < n; i++){
			if(uf.same(i,n) && uf.same(i,n+1)) ok = 0;
		}
		if(uf.size(0) == n+2 || !ok) r = c;
		else l = c;
	}
	printf("%.10f\n", r/2);
}

int main(){
	std::cin.tie(0);
	std::ios::sync_with_stdio(false);
	int t = 1;
	//cin >> t;
	while(t--){
		solve();
	}
	
	return 0;
}
//#include <atcoder/all>
#include <bits/stdc++.h>
#include <cassert>
#include <numeric>
using namespace std;
//using namespace atcoder;
typedef  long long LONG;
const int MOD = 1000000007;



int gcd(int x, int y) {
	if (x % y == 0) {
		return y;
	}
	else {
		return gcd(y, x % y);
		//x%y==0でないときはユーグリットの互除法を使って再帰的に関数を呼び出す。
	}
}
LONG pow2(long long x, long long n) {
	long long ret = 1;
	while (n > 0) {
		if (n & 1) ret = ret * x % MOD;  // n の最下位bitが 1 ならば x^(2^i) をかける
		x = x * x % MOD;
		n >>= 1;  // n を1bit 左にずらす
	}
	return (int)ret;
}
void printVector(const vector<int>& vec) {
	for (int value : vec) {
		cout << value << " ";
	}
	cout << endl;
}
LONG t(LONG N,LONG i) {
	return N - abs(i);
}
int count(vector<int>&a, int s) {
	int result = 0;
	for (int i = 0; i < a.size(); i++) {
		if (a[i] == s) {
			result++;
		}
	}
	return result;
}

LONG kaizyo(int N) {
	LONG result = 1;
	LONG mod = 998244353;

	for (LONG i = 2; i <= N; i++) {
		result *= i;
		result = result % mod;
	}
	return result;
}
class Point {
public:
	int x;
	int y;
};
LONG stock[3500][3500];
LONG test(int N, int task) {
	LONG mod = 998244353;
	if (N < task)return 0;
	if (N < 0)return 0;
	if (task == 0) {
		if (N == 0) {
			return 1;
		}
		else {
			return 0;
		}

	}


	if (stock[N][task] > 0)return stock[N][task];
	stock[N][task] =( test(N - 1, task - 1) + test(N, task * 2)) % mod;

	return stock[N][task]%mod ;
}
class DP {
public:
	DP() {

	}
	DP(int a, int b) {
		other = a;
		path = b;

	}
	int other;
	int path;
};

void dp(vector<vector<DP>>& list, vector<int> &able, int pos,int limit) {
	for (int i = 0; i < list[pos].size(); i++) {
		if (list[pos][i].path <= limit) {
			if (able[list[pos][i].other] == 0) {
				able[list[pos][i].other] = 1;
				dp(list, able, list[pos][i].other, limit);
			}
		}
	}
}
int main() {

	LONG N;
	cin >> N;
	vector<Point>P(N);
	for (int i = 0; i < N; i++) {
		cin >>P[i].x>>P[i].y;
	}
	vector<vector<DP>>d(N+2, vector<DP>(0));

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < i; j++) {

			int r = (P[i].x - P[j].x)*(P[i].x - P[j].x) + (P[i].y - P[j].y)*(P[i].y - P[j].y);
			if (r < 40000) {
				d[i+2].push_back(DP(j+2, r));
				d[j+2].push_back(DP(i+2, r));
			}
		}
	}
	for (int i = 0; i < N; i++) {
		d[i+2].push_back(DP(0, (100-P[i].y)* (100 - P[i].y)));
		d[0].push_back(DP(i+2, (100 - P[i].y)*(100-P[i].y)));
	}
	for (int i = 0; i < N; i++) {
		d[i + 2].push_back(DP(1, (-100 - P[i].y)* (-100 - P[i].y)));
		d[1].push_back(DP(i + 2, (-100 - P[i].y)* (-100 - P[i].y)));
	}
	d[0].push_back(DP(1, 40000));
	d[1].push_back(DP(0, 40000));
	int max = 40000;
	int min = 0;
	int mid = (max) / 2;
	while (max - min > 1) {
		bool frag = false;
		vector<int> able(N + 2, 0 );//未到達は0、到達は1
		able[0] = 1;
		dp(d, able, 0, mid);
		if (able[1] == 1) {
			max = mid;
			mid = (max + min) / 2;
		}else{
			min = mid;
			mid = (max + min) / 2;
		}
//		cout << max<<"\t" << min << "\t" << mid << "\n";
	}
	cout << std::fixed << std::setprecision(15) << sqrt(mid+1)/2.0;


	
	return 0;
}