import java.util.*;

public class Solution {
    public static void mainA(String[] args) {
        final Scanner in = new Scanner(System.in);

        final int T = in.nextInt();

        for (int t = 0; t < T; t += 1) {
            final int r = in.nextInt();
            final int g = in.nextInt();
            final int b = in.nextInt();
            final int w = in.nextInt();

            boolean possible =
                (r & 1) + (g & 1) + (b & 1) + (w & 1) <= 1 ||
                ((r > 0 && g > 0 && b > 0) && ((~r) & 1) + ((~g) & 1) + ((~b) & 1) + ((~w) & 1) <= 1);

            System.out.println(possible ? "Yes" : "No");
        }
    }

    private static void append(StringBuilder sb, int x, int y) {
        sb.append(x);
        sb.append(' ');
        sb.append(y);
        sb.append('\n');
    }

    public static void mainB(String[] args) {
        final Scanner in = new Scanner(System.in);

        final int rows = in.nextInt();
        final int cols = in.nextInt();

        final int x = in.nextInt();
        final int y = in.nextInt();

        StringBuilder sb = new StringBuilder();

        append(sb, x, y);

        for (int c = 1; c <= cols; c += 1) {
            if (c == x) continue;
            append(sb, c, y);
        }

        boolean left = true;

        for (int r = 1; r <= rows; r += 1) {
            if (r == y) continue;
            if (left) {
                for (int c = cols; c >= 1; c -= 1) {
                    append(sb, c, r);
                }
            } else {
                for (int c = 1; c <= cols; c += 1) {
                    append(sb, c, r);
                }
            }
            left = !left;
        }

        System.out.println(sb);
    }

    public static void mainC(String[] args) {
        final Scanner in = new Scanner(System.in);

        final int T = in.nextInt();

        for (int t = 0; t < T; t += 1) {
            final int n = in.nextInt();

            final int[] w = new int[n];

            for (int i = 0; i < n; i += 1) w[i] = in.nextInt();

            int max = 0;

            System.out.println(max);
//            System.out.println("=======");
        }
    }

    public static void mainD(String[] args) {
        final Scanner in = new Scanner(System.in);

        final int T = in.nextInt();

        for (int t = 0; t < T; t += 1) {
            final int n = in.nextInt();
            in.nextLine();

            final char[] s = in.nextLine().toCharArray();

            StringBuilder sb = new StringBuilder();

            System.out.println(sb);
        }
    }

    public static void mainE1(String[] args) {
        final Scanner in = new Scanner(System.in);

        final int T = in.nextInt();

        for (int t = 0; t < T; t += 1) {
            final int n = in.nextInt();

            final long s = in.nextLong();

            final int[][] edges = new int[n - 1][];

            for (int i = n - 2; i >= 0; i -= 1) {
                edges[i] = new int[] { in.nextInt(), in.nextInt(), in.nextInt() };
            }

            for (int i = 0; i < n - 1; i += 1) {
                System.out.println(Arrays.toString(edges[i]));
            }
        }

    }

    public static void mainF(String[] args) {
        final Scanner in = new Scanner(System.in);

        final int T = in.nextInt();

        for (int t = 0; t < T; t += 1) {
            final int n = in.nextInt();

            System.out.println(n);
//            System.out.println("=======");
        }
    }

    public static void main(String[] args) {
        mainB(args);
    }
}


import java.util.*;

public class Code2 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int a = sc.nextInt();
        int b = sc.nextInt();

        int x = a;
        int y = b;
        for (int i = 0; i < n; i++) {
            x = (x % n == 0) ? n : x % n;

            for (int j = 0; j < m; j++) {
                System.out.println(x + " " + y);
                y = ((y + 1) % m == 0) ? m : (y + 1) % m;
            }
            y = (y - 1 == 0) ? 4 : y - 1;
            x++;
        }

        sc.close();
    }
}


import java.util.*;

public class Code2 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int a = sc.nextInt();
        int b = sc.nextInt();

        int x = a;
        int y = b;
        for (int i = 0; i < n; i++) {
            x = (x % n == 0) ? n : x % n;
            y = (y - 1 == 0) ? 4 : y - 1;

            for (int j = 0; j < m; j++) {
                if (x != a || y != b) {
                    System.out.println(x + " " + y);
                }
                y = ((y + 1) % m == 0) ? m : (y + 1) % m;
            }
            x++;
        }

        sc.close();
    }
}


#include<cstdio>
#include<algorithm>
#include<set>
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define dep(i,x,y) for(int i=(x);i>=(y);i--)
#define inf 200000002
#define mx 268435456
#define N 200000
#define M 2000000
using namespace std;
int n,m,k,ll,rr,mid,x1,y1,x2,y2,tot;bool u;
int a[M],L[M],R[M],l[M],r[M],L1[M],R1[M],l1[M],r1[M];
struct pr{int x,y,s;}t[N],p[N];set<int>se[M];
bool cmp(const pr &x,const pr &y){return x.x<y.x;}
bool cmp0(const pr &x,const pr &y){return x.s>y.s;}
void f(int x,int y,int z){if(z<a[x])a[x]=z;
	if(L[x]==y&&R[x]==y)return;
	if(y<=(L[x]+R[x])/2){
		if(!l[x]){
			l[x]=++tot;a[tot]=inf*2;l[tot]=r[tot]=0;
			L[tot]=L[x];R[tot]=(L[x]+R[x])/2;
		}f(l[x],y,z);
	}else{
		if(!r[x]){
			r[x]=++tot;a[tot]=inf*2;l[tot]=r[tot]=0;
			L[tot]=(L[x]+R[x])/2+1;R[tot]=R[x];
		}f(r[x],y,z);
	}
}
int q(int x,int l0,int r0){if(!x)return inf*4;
	if(L[x]==l0&&R[x]==r0)return a[x];
	if((L[x]+R[x])/2>=r0)return q(l[x],l0,r0);
	if((L[x]+R[x])/2<l0)return q(r[x],l0,r0);
	return min(q(l[x],l0,(L[x]+R[x])/2),
	q(r[x],(L[x]+R[x])/2+1,r0));
}
void nw(int l,int r){
	tot++;l1[tot]=r1[tot]=0;L1[tot]=l;R1[tot]=r;
}
void ins(int x,int y,int z){se[x].insert(z);
	if(L1[x]==y&&R1[x]==y)return;
	if(y<=(L1[x]+R1[x])/2){
		if(!l1[x]){
			l1[x]=++tot;l1[tot]=r1[tot]=0;
			L1[tot]=L1[x];R1[tot]=(L1[x]+R1[x])/2;
		}ins(l1[x],y,z);
	}else{
		if(!r1[x]){
			r1[x]=++tot;l1[tot]=r1[tot]=0;
			L1[tot]=(L1[x]+R1[x])/2+1;R1[tot]=R1[x];
		}ins(r1[x],y,z);
	}
}
bool qr(int x,int l0,int r0,int L0,int R0){if(!x)return 0;
	if(L1[x]==l0&&R1[x]==r0)return
		se[x].lower_bound(L0)!=se[x].upper_bound(R0);
	if((L1[x]+R1[x])/2>=r0)return qr(l1[x],l0,r0,L0,R0);
	if((L1[x]+R1[x])/2<l0)return qr(r1[x],l0,r0,L0,R0);
	return qr(l1[x],l0,(L1[x]+R1[x])/2,L0,R0)||
	qr(r1[x],(L1[x]+R1[x])/2+1,r0,L0,R0);
}
int main(){
	scanf("%d%d",&n,&m);
	rep(i,1,n)scanf("%d%d",&t[i].x,&t[i].y);
	rep(i,1,m)scanf("%d%d",&p[i].x,&p[i].y);
	rep(i,1,n)t[i].x+=inf/2,t[i].y+=inf/2,t[i].s=inf*2;
	rep(i,1,m)p[i].x+=inf/2,p[i].y+=inf/2;
	sort(t+1,t+n+1,cmp);sort(p+1,p+m+1,cmp);
	l[1]=r[1]=0;k=tot=1;a[1]=inf*4;L[1]=1;R[1]=mx;
	rep(i,1,n){
		for(;k<=m&&p[k].x<=t[i].x;k++)f(1,p[k].y,-p[k].x-p[k].y);
		t[i].s=min(t[i].s,t[i].x+t[i].y+q(1,1,t[i].y));
	}
	l[1]=r[1]=0;k=tot=1;a[1]=inf*4;L[1]=1;R[1]=mx;
	rep(i,1,n){
		for(;k<=m&&p[k].x<=t[i].x;k++)f(1,p[k].y,-p[k].x+p[k].y);
		t[i].s=min(t[i].s,t[i].x-t[i].y+q(1,t[i].y,inf));
	}
	l[1]=r[1]=0;k=m;tot=1;a[1]=inf*4;L[1]=1;R[1]=mx;
	dep(i,n,1){
		for(;k>0&&p[k].x>=t[i].x;k--)f(1,p[k].y,p[k].x-p[k].y);
		t[i].s=min(t[i].s,-t[i].x+t[i].y+q(1,1,t[i].y));
	}
	l[1]=r[1]=0;k=m;tot=1;a[1]=inf*4;L[1]=1;R[1]=mx;
	dep(i,n,1){
		for(;k>0&&p[k].x>=t[i].x;k--)f(1,p[k].y,p[k].x+p[k].y);
		t[i].s=min(t[i].s,-t[i].x-t[i].y+q(1,t[i].y,inf));
	}
	tot=1;L1[1]=1;R1[1]=mx*2;
	sort(t+1,t+n+1,cmp0);ll=0;rr=inf*2;
	rep(i,1,m)ins(1,p[i].x-p[i].y+inf,p[i].x+p[i].y);
	for(mid=(ll+rr)/2;ll<rr;mid=(ll+rr)/2){
		x1=1;y1=1;x2=inf*2;y2=inf*2;u=0;
		rep(i,1,n){
			int X=t[i].x-t[i].y+inf,Y=t[i].x+t[i].y;
			if(X-mid>x1)x1=X-mid;if(Y-mid>y1)y1=Y-mid;
			if(X+mid<x2)x2=X+mid;if(Y+mid<y2)y2=Y+mid;
			if(x1>x2||y1>y2)break;
			if(t[i+1].s>mid)continue;
			if(i==n||qr(1,max(x1-mid+t[i+1].s,1),
			min(x2+mid-t[i+1].s,inf*2),
			max(y1-mid+t[i+1].s,1),
			min(y2+mid-t[i+1].s,inf*2))){u=1;break;}
		}
		if(u)rr=mid;else ll=mid+1;
	}
	printf("%d\n",mid);
}


#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 200005, M = 300005, TN = 400005, TM = 600005, LM = 1000005, INF = 400000000;
int n, k, l, r, m, seg_wid, seg_wid2, ev_top, p5_top, rec_cnt, x[N], y[N], ds[N], s1[N], s2[N], s3[N], s4[M], p5[LM], s5[LM], ord_p5[LM], ord[N], event[M], times[M], pri[M];

struct SegTA {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], ma[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; ma[i] = -INF;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int pos, const int val) {
    if (l[i] == pos && r[i] == pos) {
      ma[i] = val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (pos > k) change(rc[i], pos, val); else change(lc[i], pos, val);
    ma[i] = max(ma[lc[i]], ma[rc[i]]);
  }
  int find(const int i, const int left, const int right) {
    if (l[i] == left && r[i] == right) return ma[i];
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      return find(rc[i], left, right);
    } else if (right <= k) {
      return find(lc[i], left, right);
    } else {
      return max(find(lc[i], left, k), find(rc[i], k + 1, right));
    }
  }
} ul, ur, dl, dr;

struct SegTB {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], cnt[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; cnt[i] = 0;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int left, const int right, const int val) {
    if (l[i] == left && r[i] == right) {
      cnt[i] += val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      change(rc[i], left, right, val);
    } else if (right <= k) {
      change(lc[i], left, right, val);
    } else {
      change(lc[i], left, k, val);
      change(rc[i], k + 1, right, val);
    }
  }
  bool query(const int i, const int pos) {
    if (cnt[i]) return true;
    if (l[i] == r[i]) return false;
    return (pos > (l[i] + r[i]) >> 1) ? query(rc[i], pos) : query(lc[i], pos);
  }
} rt;

bool cmp_p1(int i, int j) {return x[i] < x[j];}
void pro_discr() {
  for (int i = 0; i < n + k; i++) s1[i] = i;
  sort(s1, s1 + n + k, cmp_p1);
  ord[s1[0]] = 1;
  for (int i = 1; i < n + k; i++) ord[s1[i]] = ord[s1[i - 1]] + (x[s1[i]] == x[s1[i - 1]] ? 0 : 1);
  seg_wid = ord[s1[n + k - 1]];
}
bool cmp_p2(int i, int j) {return y[i] < y[j];}
bool cmp_p3(int i, int j) {return ds[i] > ds[j];}
void cal_metro() {
  for (int i = 0; i < n + k; i++) s2[i] = i;
  sort(s2, s2 + n + k, cmp_p2);
  ul.init(seg_wid); ur.init(seg_wid); dl.init(seg_wid); dr.init(seg_wid);
  for (int i = n; i < n + k; i++) {
    ul.change(1, ord[i], x[i] - y[i]);
    ur.change(1, ord[i], -x[i] - y[i]);
  }
  for (int i = 0; i < n + k; i++) {
    if (s2[i] < n) {
      ds[s2[i]] = INF;
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] - y[s2[i]] - ul.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] - y[s2[i]] - ur.find(1, ord[s2[i]], seg_wid));
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] + y[s2[i]] - dl.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] + y[s2[i]] - dr.find(1, ord[s2[i]], seg_wid));
    } else {
      ul.change(1, ord[s2[i]], -INF);
      ur.change(1, ord[s2[i]], -INF);
      dl.change(1, ord[s2[i]], x[s2[i]] + y[s2[i]]);
      dr.change(1, ord[s2[i]], -x[s2[i]] + y[s2[i]]);
    }
  }
  for (int i = 0; i < n; i++) s3[i] = i;
  sort(s3, s3 + n, cmp_p3);
  r = min(r, ds[s3[0]]);
}

struct rectan {
  int u, d, l, r;
  bool is_empty() {return u < d || l > r;}
} rect[N];
int radi[N];

rectan merge(const rectan a, const rectan b) {
  rectan c;
  c.u = min(a.u, b.u);
  c.d = max(a.d, b.d);
  c.l = max(a.l, b.l);
  c.r = min(a.r, b.r);
  return c;
}
void empi(const int i, const int rad) {
  rect[i].u += rad;
  rect[i].d -= rad;
  rect[i].l -= rad;
  rect[i].r += rad;
}
rectan mak_squar(const int i, const int rad) {
  rectan ret;
  ret.u = x[i] + y[i] + rad;
  ret.d = x[i] + y[i] - rad;
  ret.l = x[i] - y[i] - rad;
  ret.r = x[i] - y[i] + rad;
  return ret;
}
void add_event(const int i) {
  if (i < n) {
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].d; pri[ev_top] = 1; s4[ev_top] = ev_top;
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].u; pri[ev_top] = -1; s4[ev_top] = ev_top;
  } else {
    ev_top++; event[ev_top] = i; times[ev_top] = y[i] + x[i]; pri[ev_top] = 0; s4[ev_top] = ev_top;
  }
}
bool cmp_p5(int i, int j) {return p5[i] < p5[j];}
void tmp_discr() {
  p5_top = 0;
  for (int i = 1; i <= rec_cnt; i++) {
    p5[++p5_top] = rect[event[i]].l; s5[p5_top] = p5_top;
    p5[++p5_top] = rect[event[i]].r; s5[p5_top] = p5_top;
  }
  for (int i = rec_cnt + 1; i <= ev_top; i++) {
    p5[++p5_top] = x[event[i]] - y[event[i]]; s5[p5_top] = p5_top;
  }
  sort(s5 + 1, s5 + p5_top + 1, cmp_p5);
  ord_p5[s5[1]] = 1;
  for (int i = 2; i <= p5_top; i++) ord_p5[s5[i]] = ord_p5[s5[i - 1]] + (p5[s5[i]] == p5[s5[i - 1]] ? 0 : 1);
  seg_wid2 = ord_p5[s5[p5_top]];
}
bool cmp_p4(int i, int j) {return times[i] < times[j] || times[i] == times[j] && pri[i] > pri[j];}
bool jug_alive(const int t) {
  int bar = n - 1;
  ev_top = 0; rec_cnt = 0;
  rect[0] = mak_squar(s3[0], t);
  radi[0] = t - ds[s3[1]];
  for (int i = 1; i < n; i++) {
    rect[i] = merge(rect[i - 1], mak_squar(s3[i], t));
    radi[i] = t - ds[s3[i + 1]];
    if (rect[i].is_empty()) {
      bar = i - 1;
      break;
    }
  }
  if (bar == n - 1) return true;
  for (int i = 0; i <= bar; i++) {
    if (radi[i] >= 0) {
      empi(i, radi[i]);
      add_event(i);
      rec_cnt += 2;
    }
  }
  for (int i = n; i < n + k; i++) add_event(i);
  sort(s4 + 1, s4 + ev_top + 1, cmp_p4);
  tmp_discr();
  rt.init(seg_wid2);
  for (int i = 1; i <= ev_top; i++) {
    if (pri[s4[i]]) {
      if (pri[s4[i]] > 0) rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], 1); else rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], -1);
    } else {
      if (rt.query(1, ord_p5[s4[i] + rec_cnt])) return true;
    }
  }
  return false;
}

int main() {
  scanf("%d %d", &n, &k);
  for (int i = 0; i < n; i++) scanf("%d %d", &x[i], &y[i]);
  for (int i = n; i < n + k; i++) scanf("%d %d", &x[i], &y[i]);
  pro_discr();
  r = (n == 1) ? 0 : 200000000;
  if (k) cal_metro(); else for (int i = 0; i < n; i++) s3[i] = i;
  l = 0;
  while (r - l > 1) {
    m = (l + r) >> 1;
    if (jug_alive(m)) r = m; else l = m;
  }
  printf("%d\n", r);
  return 0;
}


//185E
#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 200005, TN = 400005, TM = 600005, LM = 1000005, INF = 400000000;
int n, k, l, r, m, seg_wid, seg_wid2, ev_top, p5_top, rec_cnt, x[N], y[N], ds[N], s1[N], s2[N], s3[N], s4[LM], p5[LM], s5[LM], ord_p5[LM], ord[N], event[LM], times[LM], pri[LM];

struct SegTA {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], ma[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; ma[i] = -INF;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int pos, const int val) {
    if (l[i] == pos && r[i] == pos) {
      ma[i] = val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (pos > k) change(rc[i], pos, val); else change(lc[i], pos, val);
    ma[i] = max(ma[lc[i]], ma[rc[i]]);
  }
  int find(const int i, const int left, const int right) {
    if (l[i] == left && r[i] == right) return ma[i];
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      return find(rc[i], left, right);
    } else if (right <= k) {
      return find(lc[i], left, right);
    } else {
      return max(find(lc[i], left, k), find(rc[i], k + 1, right));
    }
  }
} ul, ur, dl, dr;

struct SegTB {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], cnt[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; cnt[i] = 0;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int left, const int right, const int val) {
    if (l[i] == left && r[i] == right) {
      cnt[i] += val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      change(rc[i], left, right, val);
    } else if (right <= k) {
      change(lc[i], left, right, val);
    } else {
      change(lc[i], left, k, val);
      change(rc[i], k + 1, right, val);
    }
  }
  bool query(const int i, const int pos) {
    if (cnt[i]) return true;
    if (l[i] == r[i]) return false;
    return (pos > (l[i] + r[i]) >> 1) ? query(rc[i], pos) : query(lc[i], pos);
  }
} rt;

bool cmp_p1(int i, int j) {return x[i] < x[j];}
void pro_discr() {
  for (int i = 0; i < n + k; i++) s1[i] = i;
  sort(s1, s1 + n + k, cmp_p1);
  ord[s1[0]] = 1;
  for (int i = 1; i < n + k; i++) ord[s1[i]] = ord[s1[i - 1]] + (x[s1[i]] == x[s1[i - 1]] ? 0 : 1);
  seg_wid = ord[s1[n + k - 1]];
}
bool cmp_p2(int i, int j) {return y[i] < y[j];}
bool cmp_p3(int i, int j) {return ds[i] > ds[j];}
void cal_metro() {
  for (int i = 0; i < n + k; i++) s2[i] = i;
  sort(s2, s2 + n + k, cmp_p2);
  ul.init(seg_wid); ur.init(seg_wid); dl.init(seg_wid); dr.init(seg_wid);
  for (int i = n; i < n + k; i++) {
    ul.change(1, ord[i], x[i] - y[i]);
    ur.change(1, ord[i], -x[i] - y[i]);
  }
  for (int i = 0; i < n + k; i++) {
    if (s2[i] < n) {
      ds[s2[i]] = INF;
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] - y[s2[i]] - ul.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] - y[s2[i]] - ur.find(1, ord[s2[i]], seg_wid));
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] + y[s2[i]] - dl.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] + y[s2[i]] - dr.find(1, ord[s2[i]], seg_wid));
    } else {
      ul.change(1, ord[s2[i]], -INF);
      ur.change(1, ord[s2[i]], -INF);
      dl.change(1, ord[s2[i]], x[s2[i]] + y[s2[i]]);
      dr.change(1, ord[s2[i]], -x[s2[i]] + y[s2[i]]);
    }
  }
  for (int i = 0; i < n; i++) s3[i] = i;
  sort(s3, s3 + n, cmp_p3);
  r = min(r, ds[s3[0]]);
}

struct rectan {
  int u, d, l, r;
  bool is_empty() {return u < d || l > r;}
} rect[N];
int radi[N];

rectan merge(const rectan a, const rectan b) {
  rectan c;
  c.u = min(a.u, b.u);
  c.d = max(a.d, b.d);
  c.l = max(a.l, b.l);
  c.r = min(a.r, b.r);
  return c;
}
void empi(const int i, const int rad) {
  rect[i].u += rad;
  rect[i].d -= rad;
  rect[i].l -= rad;
  rect[i].r += rad;
}
rectan mak_squar(const int i, const int rad) {
  rectan ret;
  ret.u = x[i] + y[i] + rad;
  ret.d = x[i] + y[i] - rad;
  ret.l = x[i] - y[i] - rad;
  ret.r = x[i] - y[i] + rad;
  return ret;
}
void add_event(const int i) {
  if (i < n) {
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].d; pri[ev_top] = 1; s4[ev_top] = ev_top;
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].u; pri[ev_top] = -1; s4[ev_top] = ev_top;
  } else {
    ev_top++; event[ev_top] = i; times[ev_top] = y[i] + x[i]; pri[ev_top] = 0; s4[ev_top] = ev_top;
  }
}
bool cmp_p5(int i, int j) {return p5[i] < p5[j];}
void tmp_discr() {
  p5_top = 0;
  for (int i = 1; i <= rec_cnt; i++) {
    p5[++p5_top] = rect[event[i]].l; s5[p5_top] = p5_top;
    p5[++p5_top] = rect[event[i]].r; s5[p5_top] = p5_top;
  }
  for (int i = rec_cnt + 1; i <= ev_top; i++) {
    p5[++p5_top] = x[event[i]] - y[event[i]]; s5[p5_top] = p5_top;
  }
  sort(s5 + 1, s5 + p5_top + 1, cmp_p5);
  ord_p5[s5[1]] = 1;
  for (int i = 2; i <= p5_top; i++) ord_p5[s5[i]] = ord_p5[s5[i - 1]] + (p5[s5[i]] == p5[s5[i - 1]] ? 0 : 1);
  seg_wid2 = ord_p5[s5[p5_top]];
}
bool cmp_p4(int i, int j) {return times[i] < times[j] || times[i] == times[j] && pri[i] > pri[j];}
bool jug_alive(const int t) {
  int bar = n - 1;
  ev_top = 0; rec_cnt = 0;
  rect[0] = mak_squar(s3[0], t);
  radi[0] = t - ds[s3[1]];
  for (int i = 1; i < n; i++) {
    rect[i] = merge(rect[i - 1], mak_squar(s3[i], t));
    radi[i] = t - ds[s3[i + 1]];
    if (rect[i].is_empty()) {
      bar = i - 1;
      break;
    }
  }
  if (bar == n - 1) return true;
  for (int i = 0; i <= bar; i++) {
    if (radi[i] >= 0) {
      empi(i, radi[i]);
      add_event(i);
      rec_cnt += 2;
    }
  }
  for (int i = n; i < n + k; i++) add_event(i);
  sort(s4 + 1, s4 + ev_top + 1, cmp_p4);
  tmp_discr();
  rt.init(seg_wid2);
  for (int i = 1; i <= ev_top; i++) {
    if (pri[s4[i]]) {
      if (pri[s4[i]] > 0) rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], 1); else rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], -1);
    } else {
      if (rt.query(1, ord_p5[s4[i] + rec_cnt])) return true;
    }
  }
  return false;
}

int main() {
  scanf("%d %d", &n, &k);
  for (int i = 0; i < n + k; i++) scanf("%d %d", &x[i], &y[i]);
  pro_discr();
  l = 0; r = (n == 1) ? 0 : 200000000;
  if (k) cal_metro(); else for (int i = 0; i < n; i++) s3[i] = i;
  while (r - l > 1) {
    m = (l + r) >> 1;
    if (jug_alive(m)) r = m; else l = m;
  }
  printf("%d\n", r);
  return 0;
}


#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 200005, M = 300005, TN = 400005, TM = 600005, LM = 1000005, INF = 400000000;
int n, k, l, r, m, seg_wid, seg_wid2, ev_top, p5_top, rec_cnt, x[N], y[N], ds[N], s1[N], s2[N], s3[N], s4[M], p5[LM], s5[LM], ord_p5[LM], ord[N], event[M], times[M], pri[M];

struct SegTA {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], ma[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; ma[i] = -INF;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int pos, const int val) {
    if (l[i] == pos && r[i] == pos) {
      ma[i] = val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (pos > k) change(rc[i], pos, val); else change(lc[i], pos, val);
    ma[i] = max(ma[lc[i]], ma[rc[i]]);
  }
  int find(const int i, const int left, const int right) {
    if (l[i] == left && r[i] == right) return ma[i];
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      return find(rc[i], left, right);
    } else if (right <= k) {
      return find(lc[i], left, right);
    } else {
      return max(find(lc[i], left, k), find(rc[i], k + 1, right));
    }
  }
} ul, ur, dl, dr;

struct SegTB {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], cnt[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; cnt[i] = 0;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int left, const int right, const int val) {
    if (l[i] == left && r[i] == right) {
      cnt[i] += val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      change(rc[i], left, right, val);
    } else if (right <= k) {
      change(lc[i], left, right, val);
    } else {
      change(lc[i], left, k, val);
      change(rc[i], k + 1, right, val);
    }
  }
  bool query(const int i, const int pos) {
    if (cnt[i]) return true;
    if (l[i] == r[i]) return false;
    return (pos > (l[i] + r[i]) >> 1) ? query(rc[i], pos) : query(lc[i], pos);
  }
} rt;

bool cmp_p1(int i, int j) {return x[i] < x[j];}
void pro_discr() {
  for (int i = 0; i < n + k; i++) s1[i] = i;
  sort(s1, s1 + n + k, cmp_p1);
  ord[s1[0]] = 1;
  for (int i = 1; i < n + k; i++) ord[s1[i]] = ord[s1[i - 1]] + (x[s1[i]] == x[s1[i - 1]] ? 0 : 1);
  seg_wid = ord[s1[n + k - 1]];
}
bool cmp_p2(int i, int j) {return y[i] < y[j];}
bool cmp_p3(int i, int j) {return ds[i] > ds[j];}
void cal_metro() {
  for (int i = 0; i < n + k; i++) s2[i] = i;
  sort(s2, s2 + n + k, cmp_p2);
  ul.init(seg_wid); ur.init(seg_wid); dl.init(seg_wid); dr.init(seg_wid);
  for (int i = n; i < n + k; i++) {
    ul.change(1, ord[i], x[i] - y[i]);
    ur.change(1, ord[i], -x[i] - y[i]);
  }
  for (int i = 0; i < n + k; i++) {
    if (s2[i] < n) {
      ds[s2[i]] = INF;
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] - y[s2[i]] - ul.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] - y[s2[i]] - ur.find(1, ord[s2[i]], seg_wid));
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] + y[s2[i]] - dl.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] + y[s2[i]] - dr.find(1, ord[s2[i]], seg_wid));
    } else {
      ul.change(1, ord[s2[i]], -INF);
      ur.change(1, ord[s2[i]], -INF);
      dl.change(1, ord[s2[i]], x[s2[i]] + y[s2[i]]);
      dr.change(1, ord[s2[i]], -x[s2[i]] + y[s2[i]]);
    }
  }
  for (int i = 0; i < n; i++) s3[i] = i;
  sort(s3, s3 + n, cmp_p3);
  r = min(r, ds[s3[0]]);
}

struct rectan {
  int u, d, l, r;
  bool is_empty() {return u < d || l > r;}
} rect[N];
int radi[N];

rectan merge(const rectan a, const rectan b) {
  rectan c;
  c.u = min(a.u, b.u);
  c.d = max(a.d, b.d);
  c.l = max(a.l, b.l);
  c.r = min(a.r, b.r);
  return c;
}
void empi(const int i, const int rad) {
  rect[i].u += rad;
  rect[i].d -= rad;
  rect[i].l -= rad;
  rect[i].r += rad;
}
rectan mak_squar(const int i, const int rad) {
  rectan ret;
  ret.u = x[i] + y[i] + rad;
  ret.d = x[i] + y[i] - rad;
  ret.l = x[i] - y[i] - rad;
  ret.r = x[i] - y[i] + rad;
  return ret;
}
void add_event(const int i) {
  if (i < n) {
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].d; pri[ev_top] = 1; s4[ev_top] = ev_top;
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].u; pri[ev_top] = -1; s4[ev_top] = ev_top;
  } else {
    ev_top++; event[ev_top] = i; times[ev_top] = y[i] + x[i]; pri[ev_top] = 0; s4[ev_top] = ev_top;
  }
}
bool cmp_p5(int i, int j) {return p5[i] < p5[j];}
void tmp_discr() {
  p5_top = 0;
  for (int i = 1; i <= rec_cnt; i++) {
    p5[++p5_top] = rect[event[i]].l; s5[p5_top] = p5_top;
    p5[++p5_top] = rect[event[i]].r; s5[p5_top] = p5_top;
  }
  for (int i = rec_cnt + 1; i <= ev_top; i++) {
    p5[++p5_top] = x[event[i]] - y[event[i]]; s5[p5_top] = p5_top;
  }
  sort(s5 + 1, s5 + p5_top + 1, cmp_p5);
  ord_p5[s5[1]] = 1;
  for (int i = 2; i <= p5_top; i++) ord_p5[s5[i]] = ord_p5[s5[i - 1]] + (p5[s5[i]] == p5[s5[i - 1]] ? 0 : 1);
  seg_wid2 = ord_p5[s5[p5_top]];
}
bool cmp_p4(int i, int j) {return times[i] < times[j] || times[i] == times[j] && pri[i] > pri[j];}
bool jug_alive(const int t) {
  int bar = n - 1;
  ev_top = 0; rec_cnt = 0;
  rect[0] = mak_squar(s3[0], t);
  radi[0] = t - ds[s3[1]];
  for (int i = 1; i < n; i++) {
    rect[i] = merge(rect[i - 1], mak_squar(s3[i], t));
    radi[i] = t - ds[s3[i + 1]];
    if (rect[i].is_empty()) {
      bar = i - 1;
      break;
    }
  }
  if (bar == n - 1) return true;
  for (int i = 0; i <= bar; i++) {
    if (radi[i] >= 0) {
      empi(i, radi[i]);
      add_event(i);
      rec_cnt += 2;
    }
  }
  for (int i = n; i < n + k; i++) add_event(i);
  sort(s4 + 1, s4 + ev_top + 1, cmp_p4);
  tmp_discr();
  rt.init(seg_wid2);
  for (int i = 1; i <= ev_top; i++) {
    if (pri[s4[i]]) {
      if (pri[s4[i]] > 0) rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], 1); else rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], -1);
    } else {
      if (rt.query(1, ord_p5[s4[i] + rec_cnt])) return true;
    }
  }
  return false;
}

int main() {
  freopen("185E.in", "r", stdin);
  freopen("185E.out", "w", stdout);
  scanf("%d %d", &n, &k);
  for (int i = 0; i < n; i++) scanf("%d %d", &x[i], &y[i]);
  for (int i = n; i < n + k; i++) scanf("%d %d", &x[i], &y[i]);
  pro_discr();
  r = (n == 1) ? 0 : 200000000;
  if (k) cal_metro(); else for (int i = 0; i < n; i++) s3[i] = i;
  l = 0;
  while (r - l > 1) {
    m = (l + r) >> 1;
    if (jug_alive(m)) r = m; else l = m;
  }
  printf("%d\n", r);
  return 0;
}


#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 200005, TN = 400005, TM = 600005, LM = 1000005, INF = 400000000;
int n, k, l, r, m, seg_wid, seg_wid2, ev_top, p5_top, rec_cnt, x[N], y[N], ds[N], s1[N], s2[N], s3[N], s4[LM], p5[LM], s5[LM], ord_p5[LM], ord[N], xord[N], yord[N], event[LM], times[LM], pri[LM];

struct SegTA {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], mi[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; mi[i] = INF;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int pos, const int val) {
    if (l[i] == pos && r[i] == pos) {
      mi[i] = val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (pos > k) change(rc[i], pos, val); else change(lc[i], pos, val);
    mi[i] = min(mi[lc[i]], mi[rc[i]]);
  }
  int find(const int i, const int left, const int right) {
    if (l[i] == left && r[i] == right) return mi[i];
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      return find(rc[i], left, right);
    } else if (right <= k) {
      return find(lc[i], left, right);
    } else {
      return min(find(lc[i], left, k), find(rc[i], k + 1, right));
    }
  }
} ul, ur, dl, dr;

bool cmp_p1(int i, int j) {return x[i] < x[j];}
void pro_discr() {
  for (int i = 0; i < n + k; i++) s1[i] = i;
  sort(s1, s1 + n + k, cmp_p1);
  ord[s1[0]] = 1; xord[s1[0]] = 1;
  for (int i = 1; i < n + k; i++) {
    ord[s1[i]] = ord[s1[i - 1]] + 1;
    xord[s1[i]] = (x[s1[i]] == x[s1[i - 1]] ? xord[s1[i - 1]] : ord[s1[i]]);
  }
  yord[s1[n + k - 1]] = n + k - 1;
  for (int i = n + k - 2; i > -1; i--) yord[s1[i]] = (x[s1[i]] == x[s1[i + 1]] ? yord[s1[i + 1]] : ord[s1[i]]);
  seg_wid = ord[s1[n + k - 1]];
}
bool cmp_p2(int i, int j) {return y[i] < y[j] || y[i] == y[j] && x[i] < x[j];}
bool cmp_p3(int i, int j) {return ds[i] > ds[j];}
void cal_metro() {
  for (int i = 0; i < n + k; i++) s2[i] = i;
  sort(s2, s2 + n + k, cmp_p2);
  ul.init(seg_wid); ur.init(seg_wid); dl.init(seg_wid); dr.init(seg_wid);
  for (int i = n; i < n + k; i++) {
    ul.change(1, ord[i], -x[i] + y[i]);
    ur.change(1, ord[i], x[i] + y[i]);
  }
  for (int i = 0; i < n + k; i++) {
    if (s2[i] < n) {
      ds[s2[i]] = INF;
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] - y[s2[i]] + ul.find(1, 1, yord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] - y[s2[i]] + ur.find(1, xord[s2[i]], seg_wid));
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] + y[s2[i]] + dl.find(1, 1, yord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] + y[s2[i]] + dr.find(1, xord[s2[i]], seg_wid));
    } else {
      ul.change(1, ord[s2[i]], INF);
      ur.change(1, ord[s2[i]], INF);
      dl.change(1, ord[s2[i]], -x[s2[i]] - y[s2[i]]);
      dr.change(1, ord[s2[i]], x[s2[i]] - y[s2[i]]);
    }
  }
  for (int i = 0; i < n; i++) s3[i] = i;
  sort(s3, s3 + n, cmp_p3);
  r = min(r, ds[s3[0]]);
}

struct rectan {
  int u, d, l, r;
  bool is_empty() {return u < d || l > r;}
} rect[N];
int radi[N];

struct SegTB {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], cnt[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; cnt[i] = 0;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int left, const int right, const int val) {
    if (l[i] == left && r[i] == right) {
      cnt[i] += val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      change(rc[i], left, right, val);
    } else if (right <= k) {
      change(lc[i], left, right, val);
    } else {
      change(lc[i], left, k, val);
      change(rc[i], k + 1, right, val);
    }
  }
  bool query(const int i, const int pos) {
    if (cnt[i]) return true;
    if (l[i] == r[i]) return false;
    return (pos > (l[i] + r[i]) >> 1) ? query(rc[i], pos) : query(lc[i], pos);
  }
} rt;

rectan merge(const rectan a, const rectan b) {
  rectan c;
  c.u = min(a.u, b.u);
  c.d = max(a.d, b.d);
  c.l = max(a.l, b.l);
  c.r = min(a.r, b.r);
  return c;
}
void empi(const int i, const int rad) {
  rect[i].u += rad;
  rect[i].d -= rad;
  rect[i].l -= rad;
  rect[i].r += rad;
}
rectan mak_squar(const int i, const int rad) {
  rectan ret;
  ret.u = x[i] + y[i] + rad;
  ret.d = x[i] + y[i] - rad;
  ret.l = x[i] - y[i] - rad;
  ret.r = x[i] - y[i] + rad;
  return ret;
}
void add_event(const int i) {
  if (i < n) {
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].d; pri[ev_top] = 1; s4[ev_top] = ev_top;
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].u; pri[ev_top] = -1; s4[ev_top] = ev_top;
  } else {
    ev_top++; event[ev_top] = i; times[ev_top] = y[i] + x[i]; pri[ev_top] = 0; s4[ev_top] = ev_top;
  }
}
bool cmp_p5(int i, int j) {return p5[i] < p5[j];}
void tmp_discr() {
  p5_top = 0;
  for (int i = 1; i <= rec_cnt; i++) {
    p5[++p5_top] = rect[event[i]].l; s5[p5_top] = p5_top;
    p5[++p5_top] = rect[event[i]].r; s5[p5_top] = p5_top;
  }
  for (int i = rec_cnt + 1; i <= ev_top; i++) {
    p5[++p5_top] = x[event[i]] - y[event[i]]; s5[p5_top] = p5_top;
  }
  sort(s5 + 1, s5 + p5_top + 1, cmp_p5);
  ord_p5[s5[1]] = 1;
  for (int i = 2; i <= p5_top; i++) ord_p5[s5[i]] = ord_p5[s5[i - 1]] + (p5[s5[i]] == p5[s5[i - 1]] ? 0 : 1);
  seg_wid2 = ord_p5[s5[p5_top]];
}
bool cmp_p4(int i, int j) {return times[i] < times[j] || times[i] == times[j] && pri[i] > pri[j];}
bool jug_alive(const int t) {
  int bar = n - 1;
  ev_top = 0; rec_cnt = 0;
  rect[0] = mak_squar(s3[0], t);
  radi[0] = t - ds[s3[1]];
  for (int i = 1; i <= bar; i++) {
    rect[i] = merge(rect[i - 1], mak_squar(s3[i], t));
    radi[i] = t - ds[s3[i + 1]];
    if (radi[i - 1] >= 0) {
      empi(i - 1, radi[i - 1]);
      add_event(i - 1);
      rec_cnt += 2;
    }
    if (rect[i].is_empty()) bar = i - 1;
  }
  if (bar == n - 1) return true;
  if (!rec_cnt) return false;
  for (int i = n; i < n + k; i++) add_event(i);
  sort(s4 + 1, s4 + ev_top + 1, cmp_p4);
  tmp_discr();
  rt.init(seg_wid2);
  for (int i = 1; i <= ev_top; i++) {
    if (pri[s4[i]]) {
      rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], pri[s4[i]]);
    } else {
      if (rt.query(1, ord_p5[s4[i] + rec_cnt])) return true;
    }
  }
  return false;
}

int main() {
  scanf("%d %d", &n, &k);
  for (int i = 0; i < n + k; i++) scanf("%d %d", &x[i], &y[i]);
  pro_discr();
  l = 0; r = (n == 1) ? 0 : 200000000;
  if (k) cal_metro(); else for (int i = 0; i < n; i++) s3[i] = i;
  while (r - l > 1) {
    m = (l + r) >> 1;
    if (jug_alive(m)) r = m; else l = m;
  }
  printf("%d\n", r);
  return 0;
}


//185E
#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 200005, M = 300005, TN = 400005, TM = 600005, LM = N + M, INF = 400000000;
int n, k, l, r, m, seg_wid, seg_wid2, ev_top, p5_top, rec_cnt, x[N], y[N], ds[N], s1[N], s2[N], s3[N], s4[M], p5[LM], s5[LM], ord_p5[LM], ord[N], event[M], times[M], pri[M];

struct SegTA {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], ma[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; ma[i] = -INF;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int pos, const int val) {
    if (l[i] == pos && r[i] == pos) {
      ma[i] = val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (pos > k) change(rc[i], pos, val); else change(lc[i], pos, val);
    ma[i] = max(ma[lc[i]], ma[rc[i]]);
  }
  int find(const int i, const int left, const int right) {
    if (l[i] == left && r[i] == right) return ma[i];
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      return find(rc[i], left, right);
    } else if (right <= k) {
      return find(lc[i], left, right);
    } else {
      return max(find(lc[i], left, k), find(rc[i], k + 1, right));
    }
  }
} ul, ur, dl, dr;

struct SegTB {
  int seg_top, l[TN], r[TN], lc[TN], rc[TN], cnt[TN];
  void buildtree(const int i, const int left, const int right) {
    l[i] = left; r[i] = right; cnt[i] = 0;
    if (l[i] == r[i]) return;
    int k = (l[i] + r[i]) >> 1;
    seg_top++; lc[i] = seg_top; buildtree(lc[i], left, k);
    seg_top++; rc[i] = seg_top; buildtree(rc[i], k + 1, right);
  }
  void init(const int seg_wid) {
    seg_top = 1;
    buildtree(1, 1, seg_wid);
  }
  void change(const int i, const int left, const int right, const int val) {
    if (l[i] == left && r[i] == right) {
      cnt[i] += val;
      return ;
    }
    int k = (l[i] + r[i]) >> 1;
    if (left > k) {
      change(rc[i], left, right, val);
    } else if (right <= k) {
      change(lc[i], left, right, val);
    } else {
      change(lc[i], left, k, val);
      change(rc[i], k + 1, right, val);
    }
  }
  bool query(const int i, const int pos) {
    if (cnt[i]) return true;
    if (l[i] == r[i]) return false;
    return (pos > (l[i] + r[i]) >> 1) ? query(rc[i], pos) : query(lc[i], pos);
  }
} rt;

bool cmp_p1(int i, int j) {return x[i] < x[j];}
void pro_discr() {
  for (int i = 0; i < n + k; i++) s1[i] = i;
  sort(s1, s1 + n + k, cmp_p1);
  ord[s1[0]] = 1;
  for (int i = 1; i < n + k; i++) ord[s1[i]] = ord[s1[i - 1]] + (x[s1[i]] == x[s1[i - 1]] ? 0 : 1);
  seg_wid = ord[s1[n + k - 1]];
}
bool cmp_p2(int i, int j) {return y[i] < y[j];}
bool cmp_p3(int i, int j) {return ds[i] > ds[j];}
void cal_metro() {
  for (int i = 0; i < n + k; i++) s2[i] = i;
  sort(s2, s2 + n + k, cmp_p2);
  ul.init(seg_wid); ur.init(seg_wid); dl.init(seg_wid); dr.init(seg_wid);
  for (int i = n; i < n + k; i++) {
    ul.change(1, ord[i], x[i] - y[i]);
    ur.change(1, ord[i], -x[i] - y[i]);
  }
  for (int i = 0; i < n + k; i++) {
    if (s2[i] < n) {
      ds[s2[i]] = INF;
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] - y[s2[i]] - ul.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] - y[s2[i]] - ur.find(1, ord[s2[i]], seg_wid));
      ds[s2[i]] = min(ds[s2[i]], x[s2[i]] + y[s2[i]] - dl.find(1, 1, ord[s2[i]]));
      ds[s2[i]] = min(ds[s2[i]], -x[s2[i]] + y[s2[i]] - dr.find(1, ord[s2[i]], seg_wid));
    } else {
      ul.change(1, ord[s2[i]], -INF);
      ur.change(1, ord[s2[i]], -INF);
      dl.change(1, ord[s2[i]], x[s2[i]] + y[s2[i]]);
      dr.change(1, ord[s2[i]], -x[s2[i]] + y[s2[i]]);
    }
  }
  for (int i = 0; i < n; i++) s3[i] = i;
  sort(s3, s3 + n, cmp_p3);
  r = min(r, ds[s3[0]]);
}

struct rectan {
  int u, d, l, r;
  bool is_empty() {return u < d || l > r;}
} rect[N];
int radi[N];

rectan merge(const rectan a, const rectan b) {
  rectan c;
  c.u = min(a.u, b.u);
  c.d = max(a.d, b.d);
  c.l = max(a.l, b.l);
  c.r = min(a.r, b.r);
  return c;
}
void empi(const int i, const int rad) {
  rect[i].u += rad;
  rect[i].d -= rad;
  rect[i].l -= rad;
  rect[i].r += rad;
}
rectan mak_squar(const int i, const int rad) {
  rectan ret;
  ret.u = x[i] + y[i] + rad;
  ret.d = x[i] + y[i] - rad;
  ret.l = x[i] - y[i] - rad;
  ret.r = x[i] - y[i] + rad;
  return ret;
}
void add_event(const int i) {
  if (i < n) {
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].d; pri[ev_top] = 1; s4[ev_top] = ev_top;
    ev_top++; event[ev_top] = i; times[ev_top] = rect[i].u; pri[ev_top] = -1; s4[ev_top] = ev_top;
  } else {
    ev_top++; event[ev_top] = i; times[ev_top] = y[i] + x[i]; pri[ev_top] = 0; s4[ev_top] = ev_top;
  }
}
bool cmp_p5(int i, int j) {return p5[i] < p5[j];}
void tmp_discr() {
  p5_top = 0;
  for (int i = 1; i <= rec_cnt; i++) {
    p5[++p5_top] = rect[event[i]].l; s5[p5_top] = p5_top;
    p5[++p5_top] = rect[event[i]].r; s5[p5_top] = p5_top;
  }
  for (int i = rec_cnt + 1; i <= ev_top; i++) {
    p5[++p5_top] = x[event[i]] - y[event[i]]; s5[p5_top] = p5_top;
  }
  sort(s5 + 1, s5 + p5_top + 1, cmp_p5);
  ord_p5[s5[1]] = 1;
  for (int i = 2; i <= p5_top; i++) ord_p5[s5[i]] = ord_p5[s5[i - 1]] + (p5[s5[i]] == p5[s5[i - 1]] ? 0 : 1);
  seg_wid2 = ord_p5[s5[p5_top]];
}
bool cmp_p4(int i, int j) {return times[i] < times[j] || times[i] == times[j] && pri[i] > pri[j];}
bool jug_alive(const int t) {
  int bar = n - 1;
  ev_top = 0; rec_cnt = 0;
  rect[0] = mak_squar(s3[0], t);
  radi[0] = t - ds[s3[1]];
  for (int i = 1; i < n; i++) {
    rect[i] = merge(rect[i - 1], mak_squar(s3[i], t));
    radi[i] = t - ds[s3[i + 1]];
    if (rect[i].is_empty()) {
      bar = i - 1;
      break;
    }
  }
  if (bar == n - 1) return true;
  for (int i = 0; i <= bar; i++) {
    if (radi[i] >= 0) {
      empi(i, radi[i]);
      add_event(i);
      rec_cnt += 2;
    }
  }
  for (int i = n; i < n + k; i++) add_event(i);
  sort(s4 + 1, s4 + ev_top + 1, cmp_p4);
  tmp_discr();
  rt.init(seg_wid2);
  for (int i = 1; i <= ev_top; i++) {
    if (pri[s4[i]]) {
      if (pri[s4[i]] > 0) rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], 1); else rt.change(1, ord_p5[s4[i] * 2 - 1], ord_p5[s4[i] * 2], -1);
    } else {
      if (rt.query(1, ord_p5[s4[i] + rec_cnt])) return true;
    }
  }
  return false;
}

int main() {
  scanf("%d %d", &n, &k);
  for (int i = 0; i < n; i++) scanf("%d %d", &x[i], &y[i]);
  for (int i = n; i < n + k; i++) scanf("%d %d", &x[i], &y[i]);
  pro_discr();
  r = (n == 1) ? 0 : 200000000;
  if (k) cal_metro(); else for (int i = 0; i < n; i++) s3[i] = i;
  l = 0;
  while (r - l > 1) {
    m = (l + r) >> 1;
    if (jug_alive(m)) r = m; else l = m;
  }
  printf("%d\n", r);
  return 0;
}


// Codeforces Round #118
// Problem E -- Soap Time! - 2
#include <cstdio>
#include <cstring>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;

const int INF = 1000000000;

struct Point {
    int id, x, y;

    Point(int id = 0, int x = 0, int y = 0): id(id), x(x), y(y) {}
};

bool operator <(const Point &a, const Point &b) {
    if (a.x == b.x) {
        if (a.y == b.y) {
            return a.id > b.id;
        }
        return a.y < b.y;
    }
    return a.x < b.x;
}

const int N = 200000;

int n, m, near[N];
Point points[N], points2[N];

namespace Treap{
    int node_count, key[N], weight[N], value[N], maximum[N], children[N][2];

    void update(int &x) {
        maximum[x] = max(value[x], max(maximum[children[x][0]], maximum[children[x][1]]));
    }

    void rotate(int &x, int t) {
        int y = children[x][t];
        children[x][t] = children[y][1 ^ t];
        children[y][1 ^ t] = x;
        update(x);
        update(y);
        x = y;
    }

    void insert(int &x, int k, int v) {
        if (x == 0) {
            x = ++ node_count;
            key[x] = k;
            value[x] = v;
            weight[x] = rand();
            children[x][0] = children[x][1] = 0;
        } else if (key[x] == k) {
            value[x] = max(value[x], v);
        } else {
            int t = key[x] < k;
            insert(children[x][t], k, v);
            if (weight[children[x][t]] < weight[x]) {
                rotate(x, t);
            }
        }
        update(x);
    }

    int query(int &x, int k) {
        if (x == 0) {
            return -INF;
        }
        if (k < key[x]) {
            return query(children[x][0], k);
        }
        return max(max(maximum[children[x][0]], value[x]), query(children[x][1], k));
    }
}

namespace SegmentTree {
    vector <int> x_values, y_values;

    struct Node {
        int total;
        Node *left, *right;

        Node(int total, Node* left, Node* right): total(total), left(left), right(right) {}

        Node* insert(int l, int r, int k) {
            if (k < y_values[l] || y_values[r] < k) {
                return this;
            }
            if (l == r) {
                return new Node(total + 1, NULL, NULL);
            }
            int m = (l + r) >> 1;
            return new Node(total + 1, left->insert(l, m, k), right->insert(m + 1, r, k));
        }

        int query(int l, int r, int a, int b) {
            if (b < y_values[l] || y_values[r] < a) {
                return 0;
            }
            if (a <= y_values[l] && y_values[r] <= b) {
                return total;
            }
            int m = (l + r) >> 1;
            return left->query(l, m, a, b) + right->query(m + 1, r, a, b);
        }
    };

    Node* build(int l, int r) {
        int m = (l + r) >> 1;
        bool is_leaf = l == r;
        return new Node(0, is_leaf? NULL: build(l, m), is_leaf? NULL: build(m + 1, r));
    }

    Node* roots[N];

    void initilize() {
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id >= n) {
                x_values.push_back(points[i].x);
                y_values.push_back(points[i].y);
            }
        }
        sort(x_values.begin(), x_values.end());
        x_values.erase(unique(x_values.begin(), x_values.end()), x_values.end());
        sort(y_values.begin(), y_values.end());
        y_values.erase(unique(y_values.begin(), y_values.end()), y_values.end());
        if (m > 0) {
            roots[0] = build(0, (int)y_values.size() - 1);
            for (int i = 0, j = 0; i < (n + m); ++ i) {
                if (points[i].id >= n) {
                    if (j == 0 || x_values[j - 1] != points[i].x) {
                        roots[j + 1] = roots[j];
                        j ++;
                    }
                    roots[j] = roots[j]->insert(0, (int)y_values.size() - 1, points[i].y);
                }
            }
        }
    }

    Node* get_tree(int x) {
        return roots[upper_bound(x_values.begin(), x_values.end(), x) - x_values.begin()];
    }

    bool query(int x_1, int x_2, int y_1, int y_2) {
        if (m == 0) {
            return false;
        }
        return get_tree(x_2)->query(0, (int)y_values.size() - 1, y_1, y_2) - get_tree(x_1 - 1)->query(0, (int)y_values.size() - 1, y_1, y_2) > 0;
    }
}

vector <pair <int, Point> > orders;

bool check(int limit) {
    if (orders.back().first <= limit) {
        return true;
    }
    int x_min = INT_MIN;
    int x_max = INT_MAX;
    int y_min = INT_MIN;
    int y_max = INT_MAX;
    for (int i = n - 1; i >= 0; -- i) {
        Point p = orders[i].second;
        x_min = max(x_min, p.x - limit);
        x_max = min(x_max, p.x + limit);
        y_min = max(y_min, p.y - limit);
        y_max = min(y_max, p.y + limit);
        if (x_min > x_max || y_min > y_max) {
            return false;
        }
        if (i == 0) {
            return true;
        }
        if (orders[i - 1].first <= limit) {
            int left = limit - orders[i - 1].first;
            if (SegmentTree::query(x_min - left, x_max + left, y_min - left, y_max + left)) {
                return true;
            }
        }
    }
    return false;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++ i) {
        points[i].id = i;
        scanf("%d%d", &points[i].x, &points[i].y);
    }
    for (int i = 0; i < m; ++ i) {
        points[n + i].id = n + i;
        scanf("%d%d", &points[n + i].x, &points[n + i].y);
    }
    Treap::weight[0] = INT_MAX;
    Treap::maximum[0] = -INF;
    for (int i = 0; i < n; ++ i) {
        near[i] = INF;
    }
    for (int direction = 0; direction < 4; ++ direction) {
        for (int i = 0; i < (n + m); ++ i) {
            points[i] = Point(points[i].id, -points[i].y, points[i].x);
        }
        int root = Treap::node_count = 0;
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id < n) {
                near[points[i].id] = min(near[points[i].id], points[i].x + points[i].y - Treap::query(root, points[i].y));
            } else {
                Treap::insert(root, points[i].y, points[i].x + points[i].y);
            }
        }
    }
    for (int i = 0; i < (n + m); ++ i) {
        points[i] = Point(points[i].id, points[i].x - points[i].y, points[i].x + points[i].y);
        if (points[i].id < n) {
            orders.push_back(make_pair(near[points[i].id], points[i]));
        }
    }
    sort(orders.begin(), orders.end());
    SegmentTree::initilize();
    int lower = 0;
    int upper = 200000000;
    while (lower < upper) {
        int mider = (lower + upper) >> 1;
        if (check(mider)) {
            upper = mider;
        } else {
            lower = mider + 1;
        }
    }
    printf("%d\n", upper);
    return 0;
}


// Codeforces Round #118
// Problem E -- Soap Time! - 2
#include <cstdio>
#include <cstring>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;

const int INF = 1000000000;

struct Point {
    int id, x, y;

    Point(int id = 0, int x = 0, int y = 0): id(id), x(x), y(y) {}
};

bool operator <(const Point &a, const Point &b) {
    if (a.x == b.x) {
        return a.y < b.y;
    }
    return a.x < b.x;
}

const int N = 200000;

int n, m, near[N];
Point points[N], points2[N];

namespace Treap{
    int node_count, key[N], weight[N], value[N], maximum[N], children[N][2];

    void update(int &x) {
        maximum[x] = max(value[x], max(maximum[children[x][0]], maximum[children[x][1]]));
    }

    void rotate(int &x, int t) {
        int y = children[x][t];
        children[x][y] = children[y][1 ^ t];
        children[y][1 ^ t] = x;
        update(x);
        update(y);
        x = y;
    }

    void insert(int &x, int k, int v) {
        if (x == 0) {
            x = ++ node_count;
            key[x] = k;
            value[x] = v;
            weight[x] = rand();
            children[x][0] = children[x][1] = 0;
        } else if (key[x] == k) {
            value[x] = max(value[x], v);
        } else {
            int t = key[x] < k;
            insert(children[x][t], k, v);
            if (weight[children[x][t]] < weight[x]) {
                rotate(x, t);
            }
        }
        update(x);
    }

    int query(int &x, int k) {
        if (x == 0) {
            return -INF;
        }
        if (k < key[x]) {
            return query(children[x][0], k);
        }
        return max(max(maximum[children[x][0]], value[x]), query(children[x][1], k));
    }
}

namespace SegmentTree {
    vector <int> x_values, y_values;

    struct Node {
        int total;
        Node *left, *right;

        Node(int total, Node* left, Node* right): total(total), left(left), right(right) {}

        Node* insert(int l, int r, int k) {
            if (k < y_values[l] || y_values[r] < k) {
                return this;
            }
            if (l == r) {
                return new Node(total + 1, NULL, NULL);
            }
            int m = (l + r) >> 1;
            return new Node(total + 1, left->insert(l, m, k), right->insert(m + 1, r, k));
        }

        int query(int l, int r, int a, int b) {
            if (b < y_values[l] || y_values[r] < a) {
                return 0;
            }
            if (a <= y_values[l] && y_values[r] <= b) {
                return total;
            }
            int m = (l + r) >> 1;
            return left->query(l, m, a, b) + right->query(m + 1, r, a, b);
        }
    };

    Node* build(int l, int r) {
        int m = (l + r) >> 1;
        bool is_leaf = l == r;
        return new Node(0, is_leaf? NULL: build(l, m), is_leaf? NULL: build(m + 1, r));
    }

    Node* roots[N];

    void initilize() {
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id >= n) {
                x_values.push_back(points[i].x);
                y_values.push_back(points[i].y);
            }
        }
        sort(x_values.begin(), x_values.end());
        x_values.erase(unique(x_values.begin(), x_values.end()), x_values.end());
        sort(y_values.begin(), y_values.end());
        y_values.erase(unique(y_values.begin(), y_values.end()), y_values.end());
        if (m > 0) {
            roots[0] = build(0, (int)y_values.size() - 1);
            for (int i = 0, j = 0; i < (n + m); ++ i) {
                if (points[i].id >= n) {
                    if (j == 0 || x_values[j - 1] != points[i].x) {
                        roots[j + 1] = roots[j];
                        j ++;
                    }
                    roots[j] = roots[j]->insert(0, (int)y_values.size() - 1, points[i].y);
                }
            }
        }
    }

    Node* get_tree(int x) {
        return roots[upper_bound(x_values.begin(), x_values.end(), x) - x_values.begin()];
    }

    bool query(int x_1, int x_2, int y_1, int y_2) {
        if (m == 0) {
            return false;
        }
        return get_tree(x_2)->query(0, (int)y_values.size() - 1, y_1, y_2) - get_tree(x_1 - 1)->query(0, (int)y_values.size() - 1, y_1, y_2) > 0;
    }
}

vector <pair <int, Point> > orders;

bool check(int limit) {
    if (orders.back().first <= limit) {
        return true;
    }
    int x_min = INT_MIN;
    int x_max = INT_MAX;
    int y_min = INT_MIN;
    int y_max = INT_MAX;
    for (int i = n - 1; i >= 0; -- i) {
        Point p = orders[i].second;
        x_min = max(x_min, p.x - limit);
        x_max = min(x_max, p.x + limit);
        y_min = max(y_min, p.y - limit);
        y_max = min(y_max, p.y + limit);
        if (x_min > x_max || y_min > y_max) {
            return false;
        }
        if (i == 0) {
            return true;
        }
        if (orders[i - 1].first <= limit) {
            int left = limit - orders[i - 1].first;
            if (SegmentTree::query(x_min - left, x_max + left, y_min - left, y_max + left)) {
                return true;
            }
        }
    }
    return false;
}

int main() {
freopen("E.in", "r", stdin);
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++ i) {
        points[i].id = i;
        scanf("%d%d", &points[i].x, &points[i].y);
    }
    for (int i = 0; i < m; ++ i) {
        points[n + i].id = n + i;
        scanf("%d%d", &points[n + i].x, &points[n + i].y);
    }
    Treap::weight[0] = INT_MAX;
    Treap::maximum[0] = -INF;
    for (int i = 0; i < n; ++ i) {
        near[i] = INF;
    }
    for (int direction = 0; direction < 4; ++ direction) {
        for (int i = 0; i < (n + m); ++ i) {
            points[i] = Point(points[i].id, -points[i].y, points[i].x);
        }
        int root = Treap::node_count = 0;
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id < n) {
                near[points[i].id] = min(near[points[i].id], points[i].x + points[i].y - Treap::query(root, points[i].y));
            } else {
                Treap::insert(root, points[i].y, points[i].x + points[i].y);
            }
        }
    }
    for (int i = 0; i < (n + m); ++ i) {
        points[i] = Point(points[i].id, points[i].x - points[i].y, points[i].x + points[i].y);
        if (points[i].id < n) {
            orders.push_back(make_pair(near[points[i].id], points[i]));
        }
    }
    sort(orders.begin(), orders.end());
    SegmentTree::initilize();
    int lower = 0;
    int upper = 200000000;
check(5);
    while (lower < upper) {
        int mider = (lower + upper) >> 1;
        if (check(mider)) {
            upper = mider;
        } else {
            lower = mider + 1;
        }
    }
    printf("%d\n", upper);
    return 0;
}


// Codeforces Round #118
// Problem E -- Soap Time! - 2
#include <cstdio>
#include <cstring>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;

const int INF = 1000000000;

struct Point {
    int id, x, y;

    Point(int id = 0, int x = 0, int y = 0): id(id), x(x), y(y) {}
};

bool operator <(const Point &a, const Point &b) {
    if (a.x == b.x) {
        return a.y < b.y;
    }
    return a.x < b.x;
}

const int N = 200000;

int n, m, near[N];
Point points[N], points2[N];

namespace Treap{
    int node_count, key[N], weight[N], value[N], maximum[N], children[N][2];

    void update(int &x) {
        maximum[x] = max(value[x], max(maximum[children[x][0]], maximum[children[x][1]]));
    }

    void rotate(int &x, int t) {
        int y = children[x][t];
        children[x][y] = children[y][1 ^ t];
        children[y][1 ^ t] = x;
        update(x);
        update(y);
        x = y;
    }

    void insert(int &x, int k, int v) {
        if (x == 0) {
            x = ++ node_count;
            key[x] = k;
            value[x] = v;
            weight[x] = rand();
            children[x][0] = children[x][1] = 0;
        } else if (key[x] == k) {
            value[x] = max(value[x], v);
        } else {
            int t = key[x] < k;
            insert(children[x][t], k, v);
            if (weight[children[x][t]] < weight[x]) {
                rotate(x, t);
            }
        }
        update(x);
    }

    int query(int &x, int k) {
        if (x == 0) {
            return -INF;
        }
        if (k < key[x]) {
            return query(children[x][0], k);
        }
        return max(max(maximum[children[x][0]], value[x]), query(children[x][1], k));
    }
}

namespace SegmentTree {
    vector <int> x_values, y_values;

    struct Node {
        int total;
        Node *left, *right;

        Node(int total, Node* left, Node* right): total(total), left(left), right(right) {}

        Node* insert(int l, int r, int k) {
            if (k < y_values[l] || y_values[r] < k) {
                return this;
            }
            if (l == r) {
                return new Node(total + 1, NULL, NULL);
            }
            int m = (l + r) >> 1;
            return new Node(total + 1, left->insert(l, m, k), right->insert(m + 1, r, k));
        }

        int query(int l, int r, int a, int b) {
            if (b < y_values[l] || y_values[r] < a) {
                return 0;
            }
            if (a <= y_values[l] && y_values[r] <= b) {
                return total;
            }
            int m = (l + r) >> 1;
            return left->query(l, m, a, b) + right->query(m + 1, r, a, b);
        }
    };

    Node* build(int l, int r) {
        int m = (l + r) >> 1;
        bool is_leaf = l == r;
        return new Node(0, is_leaf? NULL: build(l, m), is_leaf? NULL: build(m + 1, r));
    }

    Node* roots[N];

    void initilize() {
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id >= n) {
                x_values.push_back(points[i].x);
                y_values.push_back(points[i].y);
            }
        }
        sort(x_values.begin(), x_values.end());
        x_values.erase(unique(x_values.begin(), x_values.end()), x_values.end());
        sort(y_values.begin(), y_values.end());
        y_values.erase(unique(y_values.begin(), y_values.end()), y_values.end());
        if (m > 0) {
            roots[0] = build(0, (int)y_values.size() - 1);
            for (int i = 0, j = 0; i < (n + m); ++ i) {
                if (points[i].id >= n) {
                    if (j == 0 || x_values[j - 1] != points[i].x) {
                        roots[j + 1] = roots[j];
                        j ++;
                    }
                    roots[j] = roots[j]->insert(0, (int)y_values.size() - 1, points[i].y);
                }
            }
        }
    }

    Node* get_tree(int x) {
        return roots[upper_bound(x_values.begin(), x_values.end(), x) - x_values.begin()];
    }

    bool query(int x_1, int x_2, int y_1, int y_2) {
        if (m == 0) {
            return false;
        }
        return get_tree(x_2)->query(0, (int)y_values.size() - 1, y_1, y_2) - get_tree(x_1 - 1)->query(0, (int)y_values.size() - 1, y_1, y_2) > 0;
    }
}

vector <pair <int, Point> > orders;

bool check(int limit) {
    if (orders.back().first <= limit) {
        return true;
    }
    int x_min = INT_MIN;
    int x_max = INT_MAX;
    int y_min = INT_MIN;
    int y_max = INT_MAX;
    for (int i = n - 1; i >= 0; -- i) {
        Point p = orders[i].second;
        x_min = max(x_min, p.x - limit);
        x_max = min(x_max, p.x + limit);
        y_min = max(y_min, p.y - limit);
        y_max = min(y_max, p.y + limit);
        if (x_min > x_max || y_min > y_max) {
            return false;
        }
        if (i == 0) {
            return true;
        }
        if (orders[i - 1].first <= limit) {
            int left = limit - orders[i - 1].first;
            if (SegmentTree::query(x_min - left, x_max + left, y_min - left, y_max + left)) {
                return true;
            }
        }
    }
    return false;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++ i) {
        points[i].id = i;
        scanf("%d%d", &points[i].x, &points[i].y);
    }
    for (int i = 0; i < m; ++ i) {
        points[n + i].id = n + i;
        scanf("%d%d", &points[n + i].x, &points[n + i].y);
    }
    Treap::weight[0] = INT_MAX;
    Treap::maximum[0] = -INF;
    for (int i = 0; i < n; ++ i) {
        near[i] = INF;
    }
    for (int direction = 0; direction < 4; ++ direction) {
        for (int i = 0; i < (n + m); ++ i) {
            points[i] = Point(points[i].id, -points[i].y, points[i].x);
        }
        int root = Treap::node_count = 0;
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id < n) {
                near[points[i].id] = min(near[points[i].id], points[i].x + points[i].y - Treap::query(root, points[i].y));
            } else {
                Treap::insert(root, points[i].y, points[i].x + points[i].y);
            }
        }
    }
    for (int i = 0; i < (n + m); ++ i) {
        points[i] = Point(points[i].id, points[i].x - points[i].y, points[i].x + points[i].y);
        if (points[i].id < n) {
            orders.push_back(make_pair(near[points[i].id], points[i]));
        }
    }
    sort(orders.begin(), orders.end());
    SegmentTree::initilize();
    int lower = 0;
    int upper = 200000000;
    while (lower < upper) {
        int mider = (lower + upper) >> 1;
        if (check(mider)) {
            upper = mider;
        } else {
            lower = mider + 1;
        }
    }
    printf("%d\n", upper);
    return 0;
}


// Codeforces Round #118
// Problem E -- Soap Time! - 2
#include <cstdio>
#include <cstring>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;

const int INF = 1000000000;

struct Point {
    int id, x, y;

    Point(int id = 0, int x = 0, int y = 0): id(id), x(x), y(y) {}
};

bool operator <(const Point &a, const Point &b) {
    if (a.x == b.x) {
        return a.y < b.y;
    }
    return a.x < b.x;
}

const int N = 200000;

int n, m, near[N];
Point points[N], points2[N];

namespace Treap{
    int node_count, key[N], weight[N], value[N], maximum[N], children[N][2];

    void update(int &x) {
        maximum[x] = max(value[x], max(maximum[children[x][0]], maximum[children[x][1]]));
    }

    void rotate(int &x, int t) {
        int y = children[x][t];
        children[x][t] = children[y][1 ^ t];
        children[y][1 ^ t] = x;
        update(x);
        update(y);
        x = y;
    }

    void insert(int &x, int k, int v) {
        if (x == 0) {
            x = ++ node_count;
            key[x] = k;
            value[x] = v;
            weight[x] = rand();
            children[x][0] = children[x][1] = 0;
        } else if (key[x] == k) {
            value[x] = max(value[x], v);
        } else {
            int t = key[x] < k;
            insert(children[x][t], k, v);
            if (weight[children[x][t]] < weight[x]) {
                rotate(x, t);
            }
        }
        update(x);
    }

    int query(int &x, int k) {
        if (x == 0) {
            return -INF;
        }
        if (k < key[x]) {
            return query(children[x][0], k);
        }
        return max(max(maximum[children[x][0]], value[x]), query(children[x][1], k));
    }
}

namespace SegmentTree {
    vector <int> x_values, y_values;

    struct Node {
        int total;
        Node *left, *right;

        Node(int total, Node* left, Node* right): total(total), left(left), right(right) {}

        Node* insert(int l, int r, int k) {
            if (k < y_values[l] || y_values[r] < k) {
                return this;
            }
            if (l == r) {
                return new Node(total + 1, NULL, NULL);
            }
            int m = (l + r) >> 1;
            return new Node(total + 1, left->insert(l, m, k), right->insert(m + 1, r, k));
        }

        int query(int l, int r, int a, int b) {
            if (b < y_values[l] || y_values[r] < a) {
                return 0;
            }
            if (a <= y_values[l] && y_values[r] <= b) {
                return total;
            }
            int m = (l + r) >> 1;
            return left->query(l, m, a, b) + right->query(m + 1, r, a, b);
        }
    };

    Node* build(int l, int r) {
        int m = (l + r) >> 1;
        bool is_leaf = l == r;
        return new Node(0, is_leaf? NULL: build(l, m), is_leaf? NULL: build(m + 1, r));
    }

    Node* roots[N];

    void initilize() {
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id >= n) {
                x_values.push_back(points[i].x);
                y_values.push_back(points[i].y);
            }
        }
        sort(x_values.begin(), x_values.end());
        x_values.erase(unique(x_values.begin(), x_values.end()), x_values.end());
        sort(y_values.begin(), y_values.end());
        y_values.erase(unique(y_values.begin(), y_values.end()), y_values.end());
        if (m > 0) {
            roots[0] = build(0, (int)y_values.size() - 1);
            for (int i = 0, j = 0; i < (n + m); ++ i) {
                if (points[i].id >= n) {
                    if (j == 0 || x_values[j - 1] != points[i].x) {
                        roots[j + 1] = roots[j];
                        j ++;
                    }
                    roots[j] = roots[j]->insert(0, (int)y_values.size() - 1, points[i].y);
                }
            }
        }
    }

    Node* get_tree(int x) {
        return roots[upper_bound(x_values.begin(), x_values.end(), x) - x_values.begin()];
    }

    bool query(int x_1, int x_2, int y_1, int y_2) {
        if (m == 0) {
            return false;
        }
        return get_tree(x_2)->query(0, (int)y_values.size() - 1, y_1, y_2) - get_tree(x_1 - 1)->query(0, (int)y_values.size() - 1, y_1, y_2) > 0;
    }
}

vector <pair <int, Point> > orders;

bool check(int limit) {
    if (orders.back().first <= limit) {
        return true;
    }
    int x_min = INT_MIN;
    int x_max = INT_MAX;
    int y_min = INT_MIN;
    int y_max = INT_MAX;
    for (int i = n - 1; i >= 0; -- i) {
        Point p = orders[i].second;
        x_min = max(x_min, p.x - limit);
        x_max = min(x_max, p.x + limit);
        y_min = max(y_min, p.y - limit);
        y_max = min(y_max, p.y + limit);
        if (x_min > x_max || y_min > y_max) {
            return false;
        }
        if (i == 0) {
            return true;
        }
        if (orders[i - 1].first <= limit) {
            int left = limit - orders[i - 1].first;
            if (SegmentTree::query(x_min - left, x_max + left, y_min - left, y_max + left)) {
                return true;
            }
        }
    }
    return false;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++ i) {
        points[i].id = i;
        scanf("%d%d", &points[i].x, &points[i].y);
    }
    for (int i = 0; i < m; ++ i) {
        points[n + i].id = n + i;
        scanf("%d%d", &points[n + i].x, &points[n + i].y);
    }
    Treap::weight[0] = INT_MAX;
    Treap::maximum[0] = -INF;
    for (int i = 0; i < n; ++ i) {
        near[i] = INF;
    }
    for (int direction = 0; direction < 4; ++ direction) {
        for (int i = 0; i < (n + m); ++ i) {
            points[i] = Point(points[i].id, -points[i].y, points[i].x);
        }
        int root = Treap::node_count = 0;
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id < n) {
                near[points[i].id] = min(near[points[i].id], points[i].x + points[i].y - Treap::query(root, points[i].y));
            } else {
                Treap::insert(root, points[i].y, points[i].x + points[i].y);
            }
        }
    }
    for (int i = 0; i < (n + m); ++ i) {
        points[i] = Point(points[i].id, points[i].x - points[i].y, points[i].x + points[i].y);
        if (points[i].id < n) {
            orders.push_back(make_pair(near[points[i].id], points[i]));
        }
    }
    sort(orders.begin(), orders.end());
    SegmentTree::initilize();
    int lower = 0;
    int upper = 200000000;
    while (lower < upper) {
        int mider = (lower + upper) >> 1;
        if (check(mider)) {
            upper = mider;
        } else {
            lower = mider + 1;
        }
    }
    printf("%d\n", upper);
    return 0;
}


#include <cstdio>
#include <cstring>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;

const int INF = 1000000000;

struct Point {
    int id, x, y;

    Point(int id = 0, int x = 0, int y = 0): id(id), x(x), y(y) {}
};

bool operator <(const Point &a, const Point &b) {
    if (a.x == b.x) {
        if (a.y == b.y) {
            return a.id > b.id;
        }
        return a.y < b.y;
    }
    return a.x < b.x;
}

const int N = 200000;

int n, m, near[N];
Point points[N], points2[N];

namespace Treap{
    int node_count, key[N], weight[N], value[N], maximum[N], children[N][2];

    void update(int &x) {
        maximum[x] = max(value[x], max(maximum[children[x][0]], maximum[children[x][1]]));
    }

    void rotate(int &x, int t) {
        int y = children[x][t];
        children[x][t] = children[y][1 ^ t];
        children[y][1 ^ t] = x;
        update(x);
        update(y);
        x = y;
    }

    void insert(int &x, int k, int v) {
        if (x == 0) {
            x = ++ node_count;
            key[x] = k;
            value[x] = v;
            weight[x] = rand();
            children[x][0] = children[x][1] = 0;
        } else if (key[x] == k) {
            value[x] = max(value[x], v);
        } else {
            int t = key[x] < k;
            insert(children[x][t], k, v);
            if (weight[children[x][t]] < weight[x]) {
                rotate(x, t);
            }
        }
        update(x);
    }

    int query(int &x, int k) {
        if (x == 0) {
            return -INF;
        }
        if (k < key[x]) {
            return query(children[x][0], k);
        }
        return max(max(maximum[children[x][0]], value[x]), query(children[x][1], k));
    }
}

namespace SegmentTree {
    vector <int> x_values, y_values;

    struct Node {
        int total;
        Node *left, *right;

        Node(int total, Node* left, Node* right): total(total), left(left), right(right) {}

        Node* insert(int l, int r, int k) {
            if (k < y_values[l] || y_values[r] < k) {
                return this;
            }
            if (l == r) {
                return new Node(total + 1, NULL, NULL);
            }
            int m = (l + r) >> 1;
            return new Node(total + 1, left->insert(l, m, k), right->insert(m + 1, r, k));
        }

        int query(int l, int r, int a, int b) {
            if (b < y_values[l] || y_values[r] < a) {
                return 0;
            }
            if (a <= y_values[l] && y_values[r] <= b) {
                return total;
            }
            int m = (l + r) >> 1;
            return left->query(l, m, a, b) + right->query(m + 1, r, a, b);
        }
    };

    Node* build(int l, int r) {
        int m = (l + r) >> 1;
        bool is_leaf = l == r;
        return new Node(0, is_leaf? NULL: build(l, m), is_leaf? NULL: build(m + 1, r));
    }

    Node* roots[N];

    void initilize() {
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id >= n) {
                x_values.push_back(points[i].x);
                y_values.push_back(points[i].y);
            }
        }
        sort(x_values.begin(), x_values.end());
        x_values.erase(unique(x_values.begin(), x_values.end()), x_values.end());
        sort(y_values.begin(), y_values.end());
        y_values.erase(unique(y_values.begin(), y_values.end()), y_values.end());
        if (m > 0) {
            roots[0] = build(0, (int)y_values.size() - 1);
            for (int i = 0, j = 0; i < (n + m); ++ i) {
                if (points[i].id >= n) {
                    if (j == 0 || x_values[j - 1] != points[i].x) {
                        roots[j + 1] = roots[j];
                        j ++;
                    }
                    roots[j] = roots[j]->insert(0, (int)y_values.size() - 1, points[i].y);
                }
            }
        }
    }

    Node* get_tree(int x) {
        return roots[upper_bound(x_values.begin(), x_values.end(), x) - x_values.begin()];
    }

    bool query(int x_1, int x_2, int y_1, int y_2) {
        if (m == 0) {
            return false;
        }
        return get_tree(x_2)->query(0, (int)y_values.size() - 1, y_1, y_2) - get_tree(x_1 - 1)->query(0, (int)y_values.size() - 1, y_1, y_2) > 0;
    }
}

vector <pair <int, Point> > orders;

bool check(int limit) {
    if (orders.back().first <= limit) {
        return true;
    }
    int x_min = INT_MIN;
    int x_max = INT_MAX;
    int y_min = INT_MIN;
    int y_max = INT_MAX;
    for (int i = n - 1; i >= 0; -- i) {
        Point p = orders[i].second;
        x_min = max(x_min, p.x - limit);
        x_max = min(x_max, p.x + limit);
        y_min = max(y_min, p.y - limit);
        y_max = min(y_max, p.y + limit);
        if (x_min > x_max || y_min > y_max) {
            return false;
        }
        if (i == 0) {
            return true;
        }
        if (orders[i - 1].first <= limit) {
            int left = limit - orders[i - 1].first;
            if (SegmentTree::query(x_min - left, x_max + left, y_min - left, y_max + left)) {
                return true;
            }
        }
    }
    return false;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++ i) {
        points[i].id = i;
        scanf("%d%d", &points[i].x, &points[i].y);
    }
    for (int i = 0; i < m; ++ i) {
        points[n + i].id = n + i;
        scanf("%d%d", &points[n + i].x, &points[n + i].y);
    }
    Treap::weight[0] = INT_MAX;
    Treap::maximum[0] = -INF;
    for (int i = 0; i < n; ++ i) {
        near[i] = INF;
    }
    for (int direction = 0; direction < 4; ++ direction) {
        for (int i = 0; i < (n + m); ++ i) {
            points[i] = Point(points[i].id, -points[i].y, points[i].x);
        }
        int root = Treap::node_count = 0;
        sort(points, points + (n + m));
        for (int i = 0; i < (n + m); ++ i) {
            if (points[i].id < n) {
                near[points[i].id] = min(near[points[i].id], points[i].x + points[i].y - Treap::query(root, points[i].y));
            } else {
                Treap::insert(root, points[i].y, points[i].x + points[i].y);
            }
        }
    }
    for (int i = 0; i < (n + m); ++ i) {
        points[i] = Point(points[i].id, points[i].x - points[i].y, points[i].x + points[i].y);
        if (points[i].id < n) {
            orders.push_back(make_pair(near[points[i].id], points[i]));
        }
    }
    sort(orders.begin(), orders.end());
    SegmentTree::initilize();
    int lower = 0;
    int upper = 200000000;
    while (lower < upper) {
        int mider = (lower + upper) >> 1;
        if (check(mider)) {
            upper = mider;
        } else {
            lower = mider + 1;
        }
    }
    printf("%d\n", upper);
    return 0;
}


#include<cstdio>
#include<cstring>
#include<algorithm>
#define lb lower_bound
#define fo(i,a,b) for(i=a;i<=b;++i)
#define fd(i,a,b) for(i=a;i>=b;--i)
using namespace std;
const int mn=201000,inf=1<<30;
struct node{int x,y;}pt[mn],sub[mn];
struct point{int k,x,y,r;}e[mn*2];
int s1[mn*2],s2[mn*2];
int xs[mn*2],dis[mn],sa[mn],L;
int n,m,i,j,step,lo,hi,mid;
bool cmp(node a,node b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
bool cmp1(point a,point b){return a.x<b.x||(a.x==b.x&&a.k<b.k);}
bool subway(int i,int j){return dis[i]>dis[j];}
struct tree
{
    int tt,rt[mn],s[mn*20],le[mn*20],ri[mn*20];
    int get(const int&p,int l,int r,int x,int y)
    {
        if(x<=l&&y>=r)return s[p];
        int m=(l+r)/2,res=0;
        if(x<=m)res+=get(le[p],l,m,x,y);
        if(y>m)res+=get(ri[p],m+1,r,x,y);
        return res;
    }
    void add(const int&t,int&p,int l,int r,int k)
    {
        s[p=++tt]=s[t]+1;
        if(l==r)return;
        int m=(l+r)/2;
        if(k<=m)add(le[t],le[p],l,m,k),ri[p]=ri[t];
        else add(ri[t],ri[p],m+1,r,k),le[p]=le[t];
    }
    void ins(int i,int v){add(rt[i-1],rt[i],1,L,v);}
    int query(int x,int y,int l,int r)
    {
        if(x>y||l>r||x>m)return 0;
        return get(rt[y],1,L,l,r)-get(rt[x-1],1,L,l,r);
    }
}T;
point eva(int k,int x,int y,int r)
{
    point res;
    res.k=k,res.x=x,res.y=y,res.r=r;
    return res;
}
void cross(int&l,int&r,int&x,int&y,int i,int t)
{
    l=max(l,pt[i].x-t),x=max(x,pt[i].y-t);
    r=min(r,pt[i].x+t),y=min(y,pt[i].y+t);
}
void add(int *s,int p,int v)
{
    for(;p<=L;p+=p&(-p))s[p]=max(s[p],v);
}
int query(int *s,int p)
{
    int res=-inf;
    for(;p;p-=p&(-p))res=max(res,s[p]);
    return res;
}
void get(point p)
{
    int y=lb(xs+1,xs+1+L,p.y)-xs;
    if(p.k==0)
        add(s1,y,p.x+p.y),add(s2,L-y+1,p.x-p.y);
    else
    {
        int t1=p.x+p.y-query(s1,y),t2=p.x-p.y-query(s2,L-y+1);
        dis[p.r]=min(dis[p.r],min(t1,t2));
    }
}
void ugly(node&p)
{
    int X=p.x-p.y,Y=p.x+p.y;
    p.x=X,p.y=Y;
}
void nearest()
{
    int i,tt=0;
    fo(i,1,n)xs[++L]=pt[i].y;
    fo(i,1,m)xs[++L]=sub[i].y;
    sort(xs+1,xs+1+L);L=unique(xs+1,xs+1+L)-xs-1;
    fo(i,1,n)e[++tt]=eva(1,pt[i].x,pt[i].y,i);
    fo(i,1,m)e[++tt]=eva(0,sub[i].x,sub[i].y,i);
    sort(e+1,e+1+tt,cmp1);
    fill(dis+1,dis+1+n,inf);
    fill(s1+1,s1+L+1,-inf),fill(s2+1,s2+1+L,-inf);
    fo(i,1,tt)get(e[i]);
    fo(i,1,tt)e[i].x=-e[i].x;
    sort(e+1,e+1+tt,cmp1);
    fill(s1+1,s1+L+1,-inf),fill(s2+1,s2+1+L,-inf);
    fo(i,1,tt)get(e[i]);
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)
        scanf("%d%d",&pt[i].x,&pt[i].y);
    fo(i,1,m)
        scanf("%d%d",&sub[i].x,&sub[i].y);
    nearest();
    fo(i,1,n)ugly(pt[i]);
    fo(i,1,m)ugly(sub[i]);
    sort(sub+1,sub+1+m,cmp);
    L=0;
    fo(i,1,n)xs[++L]=pt[i].y;
    fo(i,1,m)xs[++L]=sub[i].y;
    sort(xs+1,xs+1+L),L=unique(xs+1,xs+1+L)-xs-1;
    fo(i,1,m)
        T.ins(i,lb(xs+1,xs+1+L,sub[i].y)-xs);
    fo(i,1,n)sa[i]=i;
    sort(sa+1,sa+1+n,subway);
    lo=0,hi=dis[sa[1]];
    int mix,mxx,miy,mxy,ct,l,r,x,y;
    node su;
    while(lo<hi)
    {
        int mid=(lo+hi)/2;
        mix=miy=-inf,mxx=mxy=inf;
        fo(i,1,n)
        {
            if(dis[sa[i]]>mid)
                cross(mix,mxx,miy,mxy,sa[i],mid);
            else
            {
                ct=mid-dis[sa[i]];
                su.y=-inf,su.x=mix-ct;
                l=lb(sub+1,sub+1+m,su,cmp)-sub;
                su.y=inf,su.x=mxx+ct;
                r=lb(sub+1,sub+1+m,su,cmp)-sub-1;
                x=lb(xs+1,xs+1+L,miy-ct)-xs,y=lb(xs+1,xs+1+L,mxy+ct+1)-xs-1;
                if(T.query(l,r,x,y))break;
                else cross(mix,mxx,miy,mxy,sa[i],mid);
            }
            if(mix>mxx||miy>mxy)break;
        }
        if(mix<=mxx&&miy<=mxy)hi=mid;else lo=mid+1;
    }
    printf("%d\n",lo);
    return 0;
}


#define _CRT_SECURE_NO_DEPRECATE
#include <algorithm>
#include <string>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iostream>
#include <iterator>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <ctime>
#include <cstring>
#pragma comment(linker, "/STACK:66777216")
using namespace std;
#define pb push_back
#define ppb pop_back
#define pi 3.1415926535897932384626433832795028841971
#define mp make_pair
#define x first
#define y second
#define pii pair<int,int>
#define pdd pair<double,double>
#define INF 1000000000
#define FOR(i,a,b) for (int _n(b), i(a); i <= _n; i++)
#define FORD(i,a,b) for(int i=(a),_b=(b);i>=_b;i--)
#define all(c) (c).begin(), (c).end()
#define SORT(c) sort(all(c))
#define rep(i,n) FOR(i,1,(n))
#define rept(i,n) FOR(i,0,(n)-1)
#define L(s) (int)((s).size())
#define C(a) memset((a),0,sizeof(a))
#define VI vector <int>
#define ll long long

template<int N> struct rmq {
	int tr[N * 2 + 2];
	int n;
	inline void reset() {
		memset(tr, 63, sizeof(tr));
	}
	inline void upd(int x, int val) {
		x += n;
		tr[x] = min(tr[x], val);
		for (; x /= 2; ) tr[x] = min(tr[x * 2], tr[x * 2 + 1]);
	}
	inline int fnd(int l, int r) {
		int ans = 2 * INF;
		for (l += n, r += n; l <= r; l = (l + 1) / 2, r = (r - 1) / 2) {
			if (l & 1) ans = min(ans, tr[l]);
			if (~r & 1) ans = min(ans, tr[r]);
		}
		return ans;
	}
};
int a,b,c,d,i,j,n,m,k,ans;
pii gn[100001], met[100001];
int ds[100001];

rmq<1 << 18> t1, t2;
int ny[300001];

inline bool cmp(const pair<pii, int> &a, const pair<pii, int> & b) {
	if (a.x.x != b.x.x) return a.x.x < b.x.x; else
	if (a.y != b.y) return a.y < b.y; else
	return a.x.y < b.x.y;
}
void solveMetro(vector<pair<pii, int> > &q) {
	sort(all(q), cmp);
	int k = 0;
	rept(i, L(q)) ny[k++] = q[i].x.y;
	sort(ny, ny + k);
	k = unique(ny, ny + k) - ny;
	t1.n = k; t2.n = k;
	t1.reset(); t2.reset();
	rept(i, L(q)) {
		int cy = lower_bound(ny, ny + k, q[i].x.y) - ny;
		if (q[i].y == -1) {
			t1.upd(cy, -q[i].x.x - q[i].x.y);
			t2.upd(cy, -q[i].x.x + q[i].x.y);
		} else {
			int v1 = t1.fnd(0, cy) + q[i].x.x + q[i].x.y;
			int v2 = t2.fnd(cy, k - 1) + q[i].x.x - q[i].x.y;
			if (v1 < ds[q[i].y]) ds[q[i].y] = v1;
			if (v2 < ds[q[i].y]) ds[q[i].y] = v2;
		}
	}
}
void findClosestMetro() {
	memset(ds, 63, sizeof(ds));
	vector<pair<pii, int> > q;
	rept(i, n) {
		q.pb(mp(gn[i], i));
	}
	rept(i, m) {
		q.pb(mp(met[i], -1));
	}
	solveMetro(q);

	rept(i, L(q)) {
		q[i].x.x = -q[i].x.x;
		q[i].x.y = -q[i].x.y;
	}
	solveMetro(q);
}
inline pii rot(pii t) {
	return mp(t.x + t.y, t.x - t.y);
}

bool check_simple(int d) {
	int x1 = -INF, y1 = -INF, x2 = INF, y2 = INF;
	rept(i, n) {
		x1 = max(x1, gn[i].x - d);
		x2 = min(x2, gn[i].x + d);
		y1 = max(y1, gn[i].y - d);
		y2 = min(y2, gn[i].y + d);
	}
	if (x1 <= x2 && y1 <= y2) return 1; else
	return 0;
}
int simple() {
	int l = -1, r = INF;
	while (r - l > 1) {
		int xx = (r + l) / 2;
		if (check_simple(xx)) r = xx; else
		l = xx;
	}
	return r;
}

pii ord[100001];
pair<pii, pii> rect[100001];
pair<int, pii> ev[300001];
pii bounds[100001];
int fen[300002];
int g;
inline void upd(int x, int val) {
	for (; x <= g; x |= x + 1) fen[x] += val;
}
inline void upd(int l, int r, int val) {
	upd(l, val);
	upd(r + 1, -val);
}
inline int sum(int x) {
	int ans = 0;
	for (; x >= 0; x = (x & (x + 1)) - 1) ans += fen[x];
	return ans;
}
bool inside(int cr) {
	int k = 0, ce = 0;
	rept(i, cr) {
		ny[k++] = rect[i].x.y;
		ny[k++] = rect[i].y.y;
		ev[ce++] = mp(rect[i].x.x, mp(0, i));
		ev[ce++] = mp(rect[i].y.x, mp(2, i));
	}
	rept(i, m) {
		ny[k++] = met[i].y;
		ev[ce++] = mp(met[i].x, mp(1, i));
	}
	sort(ny, ny + k);
	k = unique(ny, ny + k) - ny;
	g = k;
	rept(i, cr) {
		int l = lower_bound(ny, ny + k, rect[i].x.y) - ny;
		int r = lower_bound(ny, ny + k, rect[i].y.y) - ny;
		bounds[i] = mp(l, r);
	}
	sort(ev, ev + ce);
	C(fen);
	rept(i, ce) {
		int v = ev[i].y.y;
		if (ev[i].y.x == 0) {
			upd(bounds[v].x, bounds[v].y, 1);
		} else
		if (ev[i].y.x == 1) {
			int y = lower_bound(ny, ny + k, met[v].y) - ny;
			if (sum(y)) return 1;
		} else {
			upd(bounds[v].x, bounds[v].y, -1);
		}
	}
	return 0;
}
bool check(int d) {
	if (ord[0].x <= d) return 1;
	if (ord[n - 1].x > d) return 0;
	int x1 = -INF, x2 = INF, y1 = -INF, y2 = INF;
	int cr = 0;
	rept(i, n) {
		int v = ord[i].y;
		if (ord[i].x <= d) {
			int left = d - ord[i].x;
			rect[cr++] = mp(mp(x1 - left, y1 - left), mp(x2 + left, y2 + left));
		}

		if (x1 > x2 || y1 > y2) break;
		x1 = max(x1, gn[v].x - d);
		x2 = min(x2, gn[v].x + d);
		y1 = max(y1, gn[v].y - d);
		y2 = min(y2, gn[v].y + d);
	}
	if (!cr) return 0;
	if (inside(cr)) return 1; else
	return 0;
}
int solve_metro() {
	rept(i, n) ord[i] = mp(ds[i], i);
	sort(ord, ord + n); reverse(ord, ord + n);

	int l = -1, r = INF;
	while (r - l > 1) {
		int xx = (r + l) / 2;
		if (check(xx)) r = xx; else
		l = xx;
	}
	return r;
}
int main() {
	//freopen("input.txt","r",stdin);
	//freopen("output.txt","w",stdout);

	scanf("%d%d", &n, &m);
	if (n == 1) {
		printf("0\n");
		exit(0);
	}
	rept(i, n) scanf("%d%d", &gn[i].x, &gn[i].y);
	rept(i, m) scanf("%d%d", &met[i].x, &met[i].y);

	if (m) findClosestMetro();

	rept(i, n) gn[i] = rot(gn[i]);
	rept(i, m) met[i] = rot(met[i]);
	int ans = simple();
	if (!m) {
		printf("%d\n", ans);
		exit(0);
	}

	ans = min(ans, solve_metro());
	printf("%d\n", ans);
}


#define _CRT_SECURE_NO_DEPRECATE
#include <algorithm>
#include <string>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iostream>
#include <iterator>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <ctime>
#include <cstring>
#pragma comment(linker, "/STACK:66777216")
using namespace std;
#define pb push_back
#define ppb pop_back
#define pi 3.1415926535897932384626433832795028841971
#define mp make_pair
#define x first
#define y second
#define pii pair<int,int>
#define pdd pair<double,double>
#define INF 1000000000
#define FOR(i,a,b) for (int _n(b), i(a); i <= _n; i++)
#define FORD(i,a,b) for(int i=(a),_b=(b);i>=_b;i--)
#define all(c) (c).begin(), (c).end()
#define SORT(c) sort(all(c))
#define rep(i,n) FOR(i,1,(n))
#define rept(i,n) FOR(i,0,(n)-1)
#define L(s) (int)((s).size())
#define C(a) memset((a),0,sizeof(a))
#define VI vector <int>
#define ll long long

template<int N> struct rmq {
	int tr[N * 2 + 2];
	int n;
	inline void reset() {
		memset(tr, 63, sizeof(tr));
	}
	inline void upd(int x, int val) {
		x += n;
		tr[x] = min(tr[x], val);
		for (; x /= 2; ) tr[x] = min(tr[x * 2], tr[x * 2 + 1]);
	}
	inline int fnd(int l, int r) {
		int ans = 2 * INF;
		for (l += n, r += n; l <= r; l = (l + 1) / 2, r = (r - 1) / 2) {
			if (l & 1) ans = min(ans, tr[l]);
			if (~r & 1) ans = min(ans, tr[r]);
		}
		return ans;
	}
};
int a,b,c,d,i,j,n,m,k,ans;
pii gn[100001], met[100001];
int ds[100001];

rmq<1 << 18> t1, t2;
int ny[300001];

inline bool cmp(const pair<pii, int> &a, const pair<pii, int> & b) {
	if (a.x.x != b.x.x) return a.x.x < b.x.x; else
	if (a.y != b.y) return a.y < b.y; else
	return a.x.y < b.x.y;
}
void solveMetro(vector<pair<pii, int> > &q) {
	sort(all(q), cmp);
	int k = 0;
	rept(i, L(q)) ny[k++] = q[i].x.y;
	sort(ny, ny + k);
	k = unique(ny, ny + k) - ny;
	t1.n = k; t2.n = k;
	t1.reset(); t2.reset();
	rept(i, L(q)) {
		int cy = lower_bound(ny, ny + k, q[i].x.y) - ny;
		if (q[i].y == -1) {
			t1.upd(cy, -q[i].x.x - q[i].x.y);
			t2.upd(cy, -q[i].x.x + q[i].x.y);
		} else {
			int v1 = t1.fnd(0, cy) + q[i].x.x + q[i].x.y;
			int v2 = t2.fnd(cy, k - 1) + q[i].x.x - q[i].x.y;
			if (v1 < ds[q[i].y]) ds[q[i].y] = v1;
			if (v2 < ds[q[i].y]) ds[q[i].y] = v2;
		}
	}
}
void findClosestMetro() {
	memset(ds, 63, sizeof(ds));
	vector<pair<pii, int> > q;
	rept(i, n) {
		q.pb(mp(gn[i], i));
	}
	rept(i, m) {
		q.pb(mp(met[i], -1));
	}
	solveMetro(q);

	rept(i, L(q)) {
		q[i].x.x = -q[i].x.x;
		q[i].x.y = -q[i].x.y;
	}
	solveMetro(q);
}
inline pii rot(pii t) {
	return mp(t.x + t.y, t.x - t.y);
}

bool check_simple(int d) {
	int x1 = -INF, y1 = -INF, x2 = INF, y2 = INF;
	rept(i, n) {
		x1 = max(x1, gn[i].x - d);
		x2 = min(x2, gn[i].x + d);
		y1 = max(y1, gn[i].y - d);
		y2 = min(y2, gn[i].y + d);
	}
	if (x1 <= x2 && y1 <= y2) return 1; else
	return 0;
}
int simple() {
	int l = -1, r = INF;
	while (r - l > 1) {
		int xx = (r + l) / 2;
		if (check_simple(xx)) r = xx; else
		l = xx;
	}
	return r;
}

pii ord[100001];
pair<pii, pii> rect[100001];
pair<int, pii> ev[300001];
pii bounds[100001];
int fen[300002];
int g;
inline void upd(int x, int val) {
	for (; x <= g; x |= x + 1) fen[x] += val;
}
inline void upd(int l, int r, int val) {
	upd(l, val);
	upd(r + 1, -val);
}
inline int sum(int x) {
	int ans = 0;
	for (; x >= 0; x = (x & (x + 1)) - 1) ans += fen[x];
	return ans;
}
bool inside(int cr) {
	int k = 0, ce = 0;
	rept(i, cr) {
		ny[k++] = rect[i].x.y;
		ny[k++] = rect[i].y.y;
		ev[ce++] = mp(rect[i].x.x, mp(0, i));
		ev[ce++] = mp(rect[i].y.x, mp(2, i));
	}
	rept(i, m) {
		ny[k++] = met[i].y;
		ev[ce++] = mp(met[i].x, mp(1, i));
	}
	sort(ny, ny + k);
	k = unique(ny, ny + k) - ny;
	g = k;
	rept(i, cr) {
		int l = lower_bound(ny, ny + k, rect[i].x.y) - ny;
		int r = lower_bound(ny, ny + k, rect[i].y.y) - ny;
		bounds[i] = mp(l, r);
	}
	sort(ev, ev + ce);
	C(fen);
	rept(i, ce) {
		int v = ev[i].y.y;
		if (ev[i].y.x == 0) {
			upd(bounds[v].x, bounds[v].y, 1);
		} else
		if (ev[i].y.x == 1) {
			int y = lower_bound(ny, ny + k, met[v].y) - ny;
			if (sum(y)) return 1;
		} else {
			upd(bounds[v].x, bounds[v].y, -1);
		}
	}
	return 0;
}
bool check(int d) {
	if (ord[0].x <= d) return 1;
	if (ord[n - 1].x > d) return 0;
	int x1 = -INF, x2 = INF, y1 = -INF, y2 = INF;
	int cr = 0;
	rept(i, n) {
		if (x1 > x2 || y1 > y2) break;
		int v = ord[i].y;
		if (ord[i].x <= d) {
			int left = d - ord[i].x;
			rect[cr++] = mp(mp(x1 - left, y1 - left), mp(x2 + left, y2 + left));
		}

		x1 = max(x1, gn[v].x - d);
		x2 = min(x2, gn[v].x + d);
		y1 = max(y1, gn[v].y - d);
		y2 = min(y2, gn[v].y + d);
	}
	if (!cr) return 0;
	if (inside(cr)) return 1; else
	return 0;
}
int solve_metro() {
	rept(i, n) ord[i] = mp(ds[i], i);
	sort(ord, ord + n); reverse(ord, ord + n);

	int l = -1, r = INF;
	while (r - l > 1) {
		int xx = (r + l) / 2;
		if (check(xx)) r = xx; else
		l = xx;
	}
	return r;
}
int main() {
	//freopen("input.txt","r",stdin);
	//freopen("output.txt","w",stdout);

	scanf("%d%d", &n, &m);
	if (n == 1) {
		printf("0\n");
		exit(0);
	}
	rept(i, n) scanf("%d%d", &gn[i].x, &gn[i].y);
	rept(i, m) scanf("%d%d", &met[i].x, &met[i].y);

	if (m) findClosestMetro();

	rept(i, n) gn[i] = rot(gn[i]);
	rept(i, m) met[i] = rot(met[i]);
	int ans = simple();
	if (!m) {
		printf("%d\n", ans);
		exit(0);
	}

	ans = min(ans, solve_metro());
	printf("%d\n", ans);
}


#define _CRT_SECURE_NO_DEPRECATE
#include <algorithm>
#include <string>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iostream>
#include <iterator>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <ctime>
#include <cstring>
#pragma comment(linker, "/STACK:66777216")
using namespace std;
#define pb push_back
#define ppb pop_back
#define pi 3.1415926535897932384626433832795028841971
#define mp make_pair
#define x first
#define y second
#define pii pair<int,int>
#define pdd pair<double,double>
#define INF 1000000000
#define FOR(i,a,b) for (int _n(b), i(a); i <= _n; i++)
#define FORD(i,a,b) for(int i=(a),_b=(b);i>=_b;i--)
#define all(c) (c).begin(), (c).end()
#define SORT(c) sort(all(c))
#define rep(i,n) FOR(i,1,(n))
#define rept(i,n) FOR(i,0,(n)-1)
#define L(s) (int)((s).size())
#define C(a) memset((a),0,sizeof(a))
#define VI vector <int>
#define ll long long

template<int N> struct rmq {
	int tr[N * 2 + 2];
	int n;
	inline void reset() {
		memset(tr, 63, sizeof(tr));
	}
	inline void upd(int x, int val) {
		x += n;
		tr[x] = min(tr[x], val);
		for (; x /= 2; ) tr[x] = min(tr[x * 2], tr[x * 2 + 1]);
	}
	inline int fnd(int l, int r) {
		int ans = 2 * INF;
		for (l += n, r += n; l <= r; l = (l + 1) / 2, r = (r - 1) / 2) {
			if (l & 1) ans = min(ans, tr[l]);
			if (~r & 1) ans = min(ans, tr[r]);
		}
		return ans;
	}
};
int a,b,c,d,i,j,n,m,k,ans;
pii gn[100001], met[100001];
int ds[100001];

rmq<1 << 18> t1, t2;
int ny[300001];

inline bool cmp(const pair<pii, int> &a, const pair<pii, int> & b) {
	if (a.x.x != b.x.x) return a.x.x < b.x.x; else
	if (a.y != b.y) return a.y < b.y; else
	return a.x.y < b.x.y;
}
void solveMetro(vector<pair<pii, int> > &q) {
	sort(all(q), cmp);
	int k = 0;
	rept(i, L(q)) ny[k++] = q[i].x.y;
	sort(ny, ny + k);
	k = unique(ny, ny + k) - ny;
	t1.n = k; t2.n = k;
	t1.reset(); t2.reset();
	rept(i, L(q)) {
		int cy = lower_bound(ny, ny + k, q[i].x.y) - ny;
		if (q[i].y == -1) {
			t1.upd(cy, -q[i].x.x - q[i].x.y);
			t2.upd(cy, -q[i].x.x + q[i].x.y);
		} else {
			int v1 = t1.fnd(0, cy) + q[i].x.x + q[i].x.y;
			int v2 = t2.fnd(cy, k - 1) + q[i].x.x - q[i].x.y;
			if (v1 < ds[q[i].y]) ds[q[i].y] = v1;
			if (v2 < ds[q[i].y]) ds[q[i].y] = v2;
		}
	}
}
void findClosestMetro() {
	memset(ds, 63, sizeof(ds));
	vector<pair<pii, int> > q;
	rept(i, n) {
		q.pb(mp(gn[i], i));
	}
	rept(i, m) {
		q.pb(mp(met[i], -1));
	}
	solveMetro(q);

	rept(i, L(q)) {
		q[i].x.x = -q[i].x.x;
		q[i].x.y = -q[i].x.y;
	}
	solveMetro(q);
}
inline pii rot(pii t) {
	return mp(t.x + t.y, t.x - t.y);
}

bool check_simple(int d) {
	int x1 = -INF, y1 = -INF, x2 = INF, y2 = INF;
	rept(i, n) {
		x1 = max(x1, gn[i].x - d);
		x2 = min(x2, gn[i].x + d);
		y1 = max(y1, gn[i].y - d);
		y2 = min(y2, gn[i].y + d);
	}
	if (x1 <= x2 && y1 <= y2) return 1; else
	return 0;
}
int simple() {
	int l = -1, r = INF;
	while (r - l > 1) {
		int xx = (r + l) / 2;
		if (check_simple(xx)) r = xx; else
		l = xx;
	}
	return r;
}

pii ord[100001];
pair<pii, pii> rect[100001];
pair<int, pii> ev[300001];
pii bounds[100001];
int fen[300002];
int g;
inline void upd(int x, int val) {
	for (; x <= g; x |= x + 1) fen[x] += val;
}
inline void upd(int l, int r, int val) {
	upd(l, val);
	upd(r + 1, -val);
}
inline int sum(int x) {
	int ans = 0;
	for (; x >= 0; x = (x & (x + 1)) - 1) ans += fen[x];
	return ans;
}
bool inside(int cr) {
	int k = 0, ce = 0;
	rept(i, cr) {
		ny[k++] = rect[i].x.y;
		ny[k++] = rect[i].y.y;
		ev[ce++] = mp(rect[i].x.x, mp(0, i));
		ev[ce++] = mp(rect[i].y.x, mp(2, i));
	}
	rept(i, m) {
		ny[k++] = met[i].y;
		ev[ce++] = mp(met[i].x, mp(1, i));
	}
	sort(ny, ny + k);
	k = unique(ny, ny + k) - ny;
	g = k;
	rept(i, cr) {
		int l = lower_bound(ny, ny + k, rect[i].x.y) - ny;
		int r = lower_bound(ny, ny + k, rect[i].y.y) - ny;
		bounds[i] = mp(l, r);
	}
	sort(ev, ev + ce);
	C(fen);
	rept(i, ce) {
		int v = ev[i].y.y;
		if (ev[i].y.x == 0) {
			upd(bounds[v].x, bounds[v].y, 1);
		} else
		if (ev[i].y.x == 1) {
			int y = lower_bound(ny, ny + k, met[v].y) - ny;
			if (sum(y)) return 1;
		} else {
			upd(bounds[v].x, bounds[v].y, -1);
		}
	}
	return 0;
}
bool check(int d) {
	if (ord[0].x <= d) return 1;
	if (ord[n - 1].x > d) return 0;
	int x1 = -INF, x2 = INF, y1 = -INF, y2 = INF;
	int cr = 0;
	rept(i, n) {
		int v = ord[i].y;
		if (ord[i].x <= d) {
			int left = d - ord[i].x;
			rect[cr++] = mp(mp(x1 - left, y1 - left), mp(x2 + left, y2 + left));
		}

		x1 = max(x1, gn[v].x - d);
		x2 = min(x2, gn[v].x + d);
		y1 = max(y1, gn[v].y - d);
		y2 = min(y2, gn[v].y + d);
	}
	if (!cr) return 0;
	if (inside(cr)) return 1; else
	return 0;
}
int solve_metro() {
	rept(i, n) ord[i] = mp(ds[i], i);
	sort(ord, ord + n); reverse(ord, ord + n);

	int l = -1, r = INF;
	while (r - l > 1) {
		int xx = (r + l) / 2;
		if (check(xx)) r = xx; else
		l = xx;
	}
	return r;
}
int main() {
	//freopen("input.txt","r",stdin);
	//freopen("output.txt","w",stdout);

	scanf("%d%d", &n, &m);
	if (n == 1) {
		printf("0\n");
		exit(0);
	}
	rept(i, n) scanf("%d%d", &gn[i].x, &gn[i].y);
	rept(i, m) scanf("%d%d", &met[i].x, &met[i].y);
	srand(25091992);
	random_shuffle(gn, gn + n);
	random_shuffle(met, met + m);

	if (m) findClosestMetro();

	rept(i, n) gn[i] = rot(gn[i]);
	rept(i, m) met[i] = rot(met[i]);
	int ans = simple();
	if (!m) {
		printf("%d\n", ans);
		exit(0);
	}

	ans = min(ans, solve_metro());
	printf("%d\n", ans);
}