#include <bits/stdc++.h>
#define rep(i,n) for (int i = 0; i < (int)(n); i++)
#define REP(i,n) for (int i = 1; i < (int)(n); i++)
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()
#define debug(var) do{cout << #var << " : "; view(var);}while(0)
template<class T> bool chmin(T &a, T b) {if(a>b) {a=b;return 1;}return 0;}
template<class T> bool chmax(T &a, T b) {if(a<b) {a=b;return 1;}return 0;}
using namespace std;
template<class T> void view(T e) {cout << e << endl;}
template<class T> void view(const vector<T> &v) {for(const auto &e : v){cout << e << " ";} cout << endl;}
template<class T> void view(const vector<vector<T>> &vv) {for(const auto &v : vv){view(v);}}
using vint = vector<int>;
using vvint = vector<vector<int>>;
using ll = long long;
using vll = vector<ll>;
using vvll = vector<vector<ll>>;
using P = pair<int,int>;
const int inf = 1<<30;
const ll inf_l = 1LL<<61;
const int MAX = 1e5;

// union-find
struct union_find {
    vector<int> dat;

    union_find(int size) {dat.resize(size, -1);}

    int find(int x) {
        if (dat[x] < 0) {return x;}
        return dat[x] = find(dat[x]);
    }

    bool unite(int x, int y) {
        x = find(x); y = find(y);
        if (x == y) {return false;}
        if (dat[x] > dat[y]) swap(x, y);
        dat[x] += dat[y];
        dat[y] = x;
        return true;
    }

    int size(int x) {return -dat[find(x)];}

    vvint groups() {
        int n = dat.size();
        vector<vector<int>> res(n);
        for (int i = 0; i < n; i++) {
            res[find(i)].push_back(i);
        }
        res.erase(
            remove_if(res.begin(), res.end(), 
                [](const vint &v) {return v.empty();}), 
            res.end());
        return res;
    }
};

int main() {
    int n; cin >> n;
    vint x(n), y(n);
    rep(i,n) cin >> x[i] >> y[i];
    double ok = 0, ng = 200;
    while (ng - ok > 1e-10) {
        double mid = (ok + ng) / 2;
        union_find uf(n + 2);
        rep(i,n)rep(j,i) {
            double dis = sqrt(pow(x[i] - x[j], 2) + pow(y[i] - y[j], 2));
            if (dis < 2 * mid) {
                uf.unite(i, j);
            }
        }
        rep(i,n) {
            if (y[i] - (-100) < 2 * mid) uf.unite(i, n);
            if (100 - y[i] < 2 * mid) uf.unite(i, n + 1);
        }
        if (uf.find(n) == uf.find(n + 1)) ng = mid;
        else ok = mid;
    }
    printf("%.10f\n", ok);
}
#include<iostream>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
int N_MAX=11000;
typedef pair<int,int> P;
typedef pair<int,P> PP;
int main()
{
  int n,i,j,k=0;
  PP b[N_MAX]; // 枝データ
  int c[N_MAX]; // 頂点iの属する集合番号
  int nc[N_MAX]; // 集合jの要素数
  int pc[N_MAX]; // 集合jの先頭の要素番号
  int pi[N_MAX]; // 頂点iと同じ集合の要素へのポインタ
  int i1,i2,s1,s2,p,pt;
  int x[N_MAX],y[N_MAX];
  cin >> n;
  for(i=1;i<=n;i++)
    cin >> x[i] >> y[i];
  for(i=1;i<=n+2;i++){
    c[i]=i;
    nc[i]=1;
    pc[i]=i;
    pi[i]=0;
  }
  for(i=1;i<=n;i++){
    for(j=i+1;j<=n;j++,k++){
      b[k].first=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
      b[k].second.first=i;
      b[k].second.second=j;
    }
    b[k].first=(y[i]-100)*(y[i]-100);
    b[k].second.first=i;
    b[k].second.second=n+1;
    k++;
    b[k].first=(y[i]+100)*(y[i]+100);
    b[k].second.first=i;
    b[k].second.second=n+2;
    k++;
  }
  b[k].first=200*200;
  b[k].second.first=n+1;
  b[k].second.second=n+2;
  k++;
  sort(b,b+k);
  for(i=0;i<k;i++){
    i1=b[i].second.first;
    i2=b[i].second.second;
    if(c[i1]!=c[i2]){
// 集合のマージここから
      s1=c[i1]; s2=c[i2];
      if(nc[s1]<nc[s2]){// 小さい方を潰す【重要】
        s1=c[i2]; s2=c[i1];
      }
      // res[i-1]=res[i]-nc[s1]*nc[s2];
      pt=pc[s1];
      p=pc[s2];
      pc[s1]=p;
      nc[s1]+=nc[s2];
      pc[s2]=0;
      nc[s2]=0;
      while(true){
        c[p]=s1;
        if(pi[p]==0) break;
        p=pi[p];
      }
      pi[p]=pt;
// 集合のマージここまで
    }
    if(c[n+1]==c[n+2]){
      cout << setprecision(8) << sqrt(b[i].first)/2 << endl;
      return 0;
    }
  }
  return 0;
}