#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <set>


using namespace std;
struct UnionFind {
    vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2

    UnionFind(int N) : par(N) { //最初は全てが根であるとして初期化
        for(int i = 0; i < N; i++) par[i] = i;
    }

    int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }

    void unite(int x, int y) { // xとyの木を併合
        int rx = root(x); //xの根をrx
        int ry = root(y); //yの根をry
        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま
        par[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
    }

    bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
        int rx = root(x);
        int ry = root(y);
        return rx == ry;
    }
};

template<class T> inline bool chmin(T& a, T b) {
  //aの方が大きければbに値を更新、そうでなければ何もしない
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template<class T> inline bool chmax(T& a, T b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
bool compare_a_big(pair<int,int> l,pair<int,int> r){
  //firstが大きい方からソート、firstが同じならbが小さい方から
  if(l.first != r.first){
    return l.first>r.first;
  }
  else{
    return l.second < r.second;
  }
}
bool compare_a_b_big(pair<int,int> l,pair<int,int> r){
  //firstが大きい方からソート、firstが同じならbが大きい方から
  if(l.first != r.first){
    return l.first>r.first;
  }
  else{
    return l.second > r.second;
  }
}
bool compare_b_small(pair<int,int> l,pair<int,int> r){
  //secondが小さい方からソート、firstが同じならfirstが小さい方から
  if(l.second != r.second){
    return l.second<r.second;
  }
  else{
    return l.first < r.first;
  }
}
const int INF = 1e9;
const int M = 1000000007;


typedef long long int ll;
vector<ll> fac(3001); //n!(mod M)
vector<ll> ifac(3001); //k!^{M-2} (mod M)
//a,bの範囲的にこれだけ配列を用意していけば十分

ll mpow(ll x, ll n){ //x^n(mod M) ←普通にpow(x,n)では溢れてしまうため，随時mod計算
    ll ans = 1;
    while(n != 0){
        if(n&1) ans = ans*x % M;
        x = x*x % M;
        n = n >> 1;
    }
    return ans;
}

ll comb(ll a, ll b){ //aCbをmod計算、a,b<=300000でないと使えない
    fac[0] = 1;
    ifac[0] = 1;
    for(ll i = 0; i<3000; i++){
        fac[i+1] = fac[i]*(i+1) % M; // n!(mod M)
        ifac[i+1] = ifac[i]*mpow(i+1, M-2) % M; // k!^{M-2} (mod M) ←累乗にmpowを採用
    }
    if(a == 0 && b == 0)return 1;
    if(a < b || a < 0)return 0;
    ll tmp = ifac[a-b]* ifac[b] % M;
    return tmp * fac[a] % M;
}

typedef long long int ll;

int pow(int a,int b){
  if(b==0) return 1;
  else{
    return a*pow(a,b-1);

  }
}

typedef struct ListNode {
    int data;
    struct ListNode *next;
} ListNode;



int main(){
  int n,i;
  cin >> n;
  int x[n],y[n];
  for(i=0;i<n;i++){
    cin >> x[i] >> y[i];
  }
  int fx[n][4];
  for(i=0;i<n;i++){
    fx[i][0] = x[i]+y[i];
    fx[i][1] = x[i]-y[i];
    fx[i][2] = -x[i]+y[i];
    fx[i][3] = -x[i]-y[i];
  }

  int max[4] = {0,0,0,0};
  int maxv = -INF;
  int minv = INF;
  for(i=0;i<4;i++){
    for(int j=0;j<n;j++){
      if(fx[j][i] > maxv){
        maxv = fx[j][i];
      }
      if(fx[j][i] < minv){
        minv = fx[j][i];
      }
    }
    max[i] = abs(maxv-minv);
    maxv = -INF;
    minv = INF;
  }

  int ans = 0;
  for(int j=0;j<4;j++){
    if(ans < max[j]){
      ans = max[j];
    }
  }
  cout << ans << endl;



}


Measured memory_bytes: 8332