#include <bits/stdc++.h>
#define LLI long long int
#define FOR(v, a, b) for(LLI v = (a); v < (b); ++v)
#define FORE(v, a, b) for(LLI v = (a); v <= (b); ++v)
#define REP(v, n) FOR(v, 0, n)
#define REPE(v, n) FORE(v, 0, n)
#define REV(v, a, b) for(LLI v = (a); v >= (b); --v)
#define ALL(x) (x).begin(), (x).end()
#define RALL(x) (x).rbegin(), (x).rend()
#define ITR(it, c) for(auto it = (c).begin(); it != (c).end(); ++it)
#define RITR(it, c) for(auto it = (c).rbegin(); it != (c).rend(); ++it)
#define EXIST(c,x) ((c).find(x) != (c).end())
#define fst first
#define snd second
#define popcount __builtin_popcount
#define UNIQ(v) (v).erase(unique(ALL(v)), (v).end())
#define bit(i) (1LL<<(i))

#ifdef DEBUG
#include <misc/C++/Debug.cpp>
#else
#define dump(...) ((void)0)
#endif

#define gcd __gcd

using namespace std;
template <class T> constexpr T lcm(T m, T n){return m/gcd(m,n)*n;}

template <typename I> void join(ostream &ost, I s, I t, string d=" "){for(auto i=s; i!=t; ++i){if(i!=s)ost<<d; ost<<*i;}ost<<endl;}
template <typename T> istream& operator>>(istream &is, vector<T> &v){for(auto &a : v) is >> a; return is;}
template <typename T, typename U> bool chmin(T &a, const U &b){return (a>b ? a=b, true : false);}
template <typename T, typename U> bool chmax(T &a, const U &b){return (a<b ? a=b, true : false);}
template <typename T, size_t N, typename U> void fill_array(T (&a)[N], const U &v){fill((U*)a, (U*)(a+N), v);}

struct Init{
  Init(){
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(12);
    cerr << fixed << setprecision(12);
  }
}init;


template <typename Cost = int> class Edge{
public:
  int from,to;
  Cost cost;
  Edge() {}
  Edge(int to, Cost cost): to(to), cost(cost){}
  Edge(int from, int to, Cost cost): from(from), to(to), cost(cost){}

  Edge rev() const {return Edge(to,from,cost);}
  
  friend std::ostream& operator<<(std::ostream &os, const Edge &e){
    os << "(FROM: " << e.from << "," << "TO: " << e.to << "," << "COST: " << e.cost << ")";
    return os;
  }
};

template <typename T> using Graph = std::vector<std::vector<Edge<T>>>;
template <typename T> using Tree = std::vector<std::vector<Edge<T>>>;

template <typename C, typename T> void add_edge(C &g, int from, int to, T w){
  g[from].push_back(Edge<T>(from, to, w));  
}

template <typename C, typename T> void add_undirected(C &g, int a, int b, T w){
  g[a].push_back(Edge<T>(a, b, w));
  g[b].push_back(Edge<T>(b, a, w));
}


template <typename T>
struct TopologicalSort{
  bool is_dag;
  std::vector<int> result;

  TopologicalSort(const Graph<T> &g){
    int n = g.size();
    std::vector<int> indeg(n);
    
    for(int i = 0; i < n; ++i){
      for(auto &e : g[i]){
        ++indeg[e.to];
      }
    }

    std::queue<int> q;
    for(int i = n-1; i >= 0; --i){
      if(indeg[i] == 0) q.push(i);
    }

    while(!q.empty()){
      int cur = q.front(); q.pop();
      result.push_back(cur);
      for(auto &e : g[cur]){
        --indeg[e.to];
        if(indeg[e.to] == 0){
          q.push(e.to);
        }
      }
    }
  
    is_dag = (int)result.size() == n;
  }

  const int operator[](int i) const {return result[i];}
};


template <typename T>
struct SCC{
  const int n;
  std::vector<int> component;
  int scc_size;
  
private:
  std::vector<bool> visit;
  std::vector<int> check;
  
  void dfs(int cur, const Graph<T> &graph){
    visit[cur] = true;
    for(auto &e : graph[cur]){
      if(not visit[e.to]) dfs(e.to,graph);
    }
    check.push_back(cur);
  }

  void rdfs(int cur, int i, const Graph<T> &rgraph){
    component[cur] = i;
    for(auto &e : rgraph[cur]){
      if(component[e.to] == -1) rdfs(e.to,i,rgraph);
    }
  }

public:
  SCC(const Graph<T> &graph): n(graph.size()), component(n,-1), visit(n){
    for(int i = 0; i < n; ++i) if(!visit[i]) dfs(i,graph);
    std::reverse(check.begin(), check.end());
    
    Graph<T> rgraph(n);
    for(int i = 0; i < n; ++i) for(auto &e : graph[i]) rgraph[e.to].push_back(e.rev());

    int i = 0;
    for(auto c : check) if(component[c] == -1) {rdfs(c,i,rgraph); ++i;}
    scc_size = i;
  }

  const int operator[](int i) const {return component[i];}
};


class TwoSat{
  const int n;
  Graph<int> g;

  int f(int i){
    assert(i != 0);
    assert(abs(i) <= n);
    if(i > 0) return i-1;
    else return abs(i)-1 + n;
  }

public:
  TwoSat(int n): n(n), g(2*n){}

  /**
   * @brief a→bを導入する
   */
  void add_if(int a, int b){
    add_edge(g, f(a), f(b), 1);
    add_edge(g, f(-b), f(-a), 1);
  }

  /**
   * @brief a∨bを導入する
   * @note a ∨ b <=> (!a => b) ∧ (!b => a)
   */
  void add_or(int a, int b){
    add_if(-a, b);
  }

  /**
   * @brief ¬(a∧b)を導入する
   * @note !(A ∧ B) <=> (!A ∨ !B)
   */
  void not_coexist(int a, int b){
    add_or(-a, -b);
  }
  
public:
  bool solve(std::vector<bool> &ret) const {
    auto scc = SCC<int>(g);

    for(int i = 0; i < n; ++i){
      if(scc[i] == scc[i+n]) return false;
    }
    
    int m = scc.scc_size;

    Graph<int> g2(m);

    for(int i = 0; i < 2*n; ++i){
      for(auto &e : g[i]){
        if(scc[e.from] != scc[e.to]){
          add_edge(g2, scc[e.from], scc[e.to], 1);
        }
      }
    }

    auto ts = TopologicalSort<int>(g2);

    std::vector<int> r(m);
    for(int i = 0; i < m; ++i) r[ts[i]] = i;

    ret.assign(n, false);
    for(int i = 0; i < n; ++i) ret[i] = r[scc[i]] > r[scc[i+n]];

    return true;
  }
};



int main(){
  int N;
  while(cin >> N){
    vector<int> x(N), y(N);
    REP(i,N){
      cin >> x[i] >> y[i];
    }

    vector<int> v;
    for(int i : x) v.push_back(i);
    for(int i : y) v.push_back(i);
    
    vector<int> ord(2*N);
    iota(ALL(ord), 0);
    sort(ALL(ord), [&](int i, int j){return v[i] < v[j];});
    sort(ALL(v));

    vector<int> rord(2*N);
    REP(i,2*N) rord[ord[i]] = i;

    auto check =
      [&](LLI d){
        int k = 1;
        while(k < 2*N) k *= 2;

        TwoSat sat(2*k);

        REP(i,N){
          sat.not_coexist(rord[i] + k, rord[i+N] + k);
          sat.not_coexist(-(rord[i] + k), -(rord[i+N] + k));
        }

        REP(i,v.size()){
          int cur = i + k;
          while(cur > 1){
            sat.add_if(cur, cur / 2);
            cur /= 2;
          }

          int l = upper_bound(ALL(v), v[i] - d) - v.begin();
          int r = lower_bound(ALL(v), v[i] + d) - v.begin();
          
          {
            int L = l + k;
            int R = i + k;
            
            while(L < R){
              if(R & 1){
                --R;
                sat.not_coexist(i+k, R);
              }
              if(L & 1){
                sat.not_coexist(i+k, L);
                ++L;
              }
              R >>= 1;
              L >>= 1;
            }
          }

          {
            int L = i + 1 + k;
            int R = r + k;

            while(L < R){
              if(R & 1){
                --R;
                sat.not_coexist(i+k, R);
              }
              if(L & 1){
                sat.not_coexist(i+k, L);
                ++L;
              }
              R >>= 1;
              L >>= 1;
            }
          }
        }

        vector<bool> a;
        
        return sat.solve(a);
      };

    LLI lb = 0, ub = INT_MAX;
    while(abs(lb-ub) > 1){
      LLI mid = (lb+ub) / 2;

      if(check(mid)){
        lb = mid;
      }else{
        ub = mid;
      }
    }
    
    auto ans = lb;
    cout << ans << endl;
  }

  return 0;
}


Measured memory_bytes: 73324