import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Closeable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
  public static void main(String[] args) throws Exception {
    Thread thread = new Thread(null, new TaskAdapter(), "", 1 << 27);
    thread.start();
    thread.join();
  }

  static class TaskAdapter implements Runnable {
    @Override
      public void run() {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastInput in = new FastInput(inputStream);
        FastOutput out = new FastOutput(outputStream);
        FElongatedMatrix solver = new FElongatedMatrix();
        solver.solve(1, in, out);
        out.close();
      }
  }

  static class FElongatedMatrix {
    public void solve(int testNumber, FastInput in, FastOutput out) {
      int n = in.readInt();
      int m = in.readInt();
      int[][] mat = new int[n][m];
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
          mat[i][j] = in.readInt();
        }
      }

      int[][] minDist = new int[n][n];
      SequenceUtils.deepFill(minDist, (int) 1e9);
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
          for (int k = 0; k < m; k++) {
            minDist[i][j] = Math.min(minDist[i][j], Math.abs(mat[i][k] - mat[j][k]));
          }
        }
      }
      int[][] minDistBetweenHeadAndTail = new int[n][n];
      SequenceUtils.deepFill(minDistBetweenHeadAndTail, (int) 1e9);
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
          for (int k = 1; k < m; k++) {
            minDistBetweenHeadAndTail[i][j] = Math.min(minDistBetweenHeadAndTail[i][j], Math.abs(mat[i][k] - mat[j][k - 1]));
          }
        }
      }

      Log2 log2 = new Log2();
      BitOperator bo = new BitOperator();
      int[][][] dp = new int[1 << n][n][n];
      for (int i = 1; i < (1 << n); i++) {
        if (i == Integer.lowestOneBit(i)) {
          dp[i][log2.floorLog(i)][log2.floorLog(i)] = (int) 1e9;
          continue;
        }
        for (int j = 0; j < n; j++) {
          for (int k = 0; k < n; k++) {
            if (bo.bitAt(i, j) == 0) {
              continue;
            }
            for (int t = 0; t < n; t++) {
              dp[i][j][k] = Math.max(dp[i][j][k],
                  Math.min(dp[bo.setBit(i, j, false)][t][k],
                    minDist[j][t]));
            }
          }
        }
      }

      int ans = 0;
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
          ans = Math.max(ans, Math.min(dp[(1 << n) - 1][i][j], minDistBetweenHeadAndTail[j][i]));
        }
      }
      out.println(ans);
    }

  }

  static class SequenceUtils {
    public static void deepFill(Object array, int val) {
      if (!array.getClass().isArray()) {
        throw new IllegalArgumentException();
      }
      if (array instanceof int[]) {
        int[] intArray = (int[]) array;
        Arrays.fill(intArray, val);
      } else {
        Object[] objArray = (Object[]) array;
        for (Object obj : objArray) {
          deepFill(obj, val);
        }
      }
    }

  }

  static class Log2 {
    public int floorLog(int x) {
      return 31 - Integer.numberOfLeadingZeros(x);
    }

  }

  static class FastInput {
    private final InputStream is;
    private byte[] buf = new byte[1 << 13];
    private int bufLen;
    private int bufOffset;
    private int next;

    public FastInput(InputStream is) {
      this.is = is;
    }

    private int read() {
      while (bufLen == bufOffset) {
        bufOffset = 0;
        try {
          bufLen = is.read(buf);
        } catch (IOException e) {
          bufLen = -1;
        }
        if (bufLen == -1) {
          return -1;
        }
      }
      return buf[bufOffset++];
    }

    public void skipBlank() {
      while (next >= 0 && next <= 32) {
        next = read();
      }
    }

    public int readInt() {
      int sign = 1;

      skipBlank();
      if (next == '+' || next == '-') {
        sign = next == '+' ? 1 : -1;
        next = read();
      }

      int val = 0;
      if (sign == 1) {
        while (next >= '0' && next <= '9') {
          val = val * 10 + next - '0';
          next = read();
        }
      } else {
        while (next >= '0' && next <= '9') {
          val = val * 10 - next + '0';
          next = read();
        }
      }

      return val;
    }

  }

  static class BitOperator {
    public int bitAt(int x, int i) {
      return (x >> i) & 1;
    }

    public int setBit(int x, int i, boolean v) {
      if (v) {
        x |= 1 << i;
      } else {
        x &= ~(1 << i);
      }
      return x;
    }

  }

  static class FastOutput implements AutoCloseable, Closeable {
    private StringBuilder cache = new StringBuilder(10 << 20);
    private final Writer os;

    public FastOutput(Writer os) {
      this.os = os;
    }

    public FastOutput(OutputStream os) {
      this(new OutputStreamWriter(os));
    }

    public FastOutput println(int c) {
      cache.append(c).append('\n');
      return this;
    }

    public FastOutput flush() {
      try {
        os.append(cache);
        os.flush();
        cache.setLength(0);
      } catch (IOException e) {
        throw new UncheckedIOException(e);
      }
      return this;
    }

    public void close() {
      flush();
      try {
        os.close();
      } catch (IOException e) {
        throw new UncheckedIOException(e);
      }
    }

    public String toString() {
      return cache.toString();
    }

  }
}


import java.io.*;
import java.util.*;

public class F
{
	static Scanner in = new Scanner(System.in);
	static PrintWriter out = new PrintWriter(System.out);

	static int n,m,res=0;
	static int[][] a=new int[20][10005],Min=new int[20][20],Min1=new int[20][20];
	static int[][][] f=new int[1<<16][20][20];

	static int GetBit(int x,int k)
	{
		return (x>>k)&1;
	}

	static int TurnBit(int x,int k)
	{
		return x^(1<<k);
	}

	public static void main(String[] args)
	{
		n=in.nextInt();
		m=in.nextInt();
		for(int i=0;i<n;i++)
			for(int j=1;j<=m;j++)
				a[i][j]=in.nextInt();
		if(n==1)
		{
			res=(int)1e9;
			for(int i=1;i<m;i++)
				res=Math.min(res,Math.abs(a[0][i]-a[0][i+1]));
			out.print(res);
			out.close();
			return;
		}
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
			{
				Min[i][j]=Min1[i][j]=(int)1e9;
				for(int t=1;t<=m;t++)
					Min[i][j]=Math.min(Min[i][j],Math.abs(a[i][t]-a[j][t]));
				for(int t=1;t<m;t++)
					Min1[i][j]=Math.min(Min1[i][j],Math.abs(a[i][t]-a[j][t+1]));
			}
		for(int i=0;i<n;i++)
			f[1<<i][i][i]=(int)1e9;
		for(int mask=0;mask<(1<<n);mask++)
			if(Integer.bitCount(mask)>1)
				for(int i=0;i<n;i++)
					if(GetBit(mask,i)==1)
						for(int j=0;j<n;j++)
							if(i!=j&&GetBit(mask,j)==1)
							{
								for(int t=0;t<n;t++)
									if(j!=t&&GetBit(mask,t)==1)
										f[mask][i][j]=Math.max(f[mask][i][j],Math.min(f[TurnBit(mask,j)][i][t],Min[j][t]));
								if(mask==(1<<n)-1)
									res=Math.max(res,Math.min(f[mask][i][j],Min1[j][i]));
							}
		out.print(res);
		out.close();
	}
}


import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;

public class Main {

	static class Task {

		int NN = 1000006;
		int MOD = 998244353;
		int INF = 2000000000;
		long INFINITY = 1000000000000000000L;

		long [][] a;
		long [][] w, w1;
		long [][] dp;

		int countBit(int num) {
			int ret = 0;
			while(num > 0) {
				if((num&1)!=0)
					++ret;
				num >>= 1;
			}
			return ret;
		}

		long rec(int at, int mask, int n, int start) {
			long ans = -INFINITY;
			if(dp[at][mask] != -1)
				return dp[at][mask];
			if(countBit(mask) == n) {
				return dp[at][mask] = w1[start][at];
			}
			for(int i=0;i < n;++i) {
				if(((mask>>i)&1)==0) {
					ans = Math.max(ans,
							Math.min(w[at][i], rec(i, mask | (1<<i), n, start)));
				}
			}
			return dp[at][mask] = ans;
		}

		public void solve(InputReader in, PrintWriter out) {
			int n  = in.nextInt(), m = in.nextInt();
			dp = new long[n][1<<n];
			a = new long[n][m];
			w = new long[n][n];
			w1 = new long[n][n];
			for(int i=0;i<n;++i) {
				for(int j=0;j<m;++j) {
					a[i][j] = in.nextLong();
				}
			}
			for(int i=0;i<n;++i) {
				for(int j=0;j<n;++j) {
					w[i][j] = INFINITY;
					if(i == j)
						continue;
					for(int k=0;k<m;++k) {
						w[i][j] = Math.min(w[i][j], Math.abs(a[j][k] - a[i][k]));
					}
				}
			}
			for(int i=0;i<n;++i) {
				for(int j=0;j<n;++j) {
					w1[i][j] = INFINITY;
					for(int k=1;k<m;++k) {
						w1[i][j] = Math.min(w1[i][j], Math.abs(a[i][k] - a[j][k - 1]));
					}
				}
			}
			long ans = 0;
			for(int start = 0;start < n;++start) {
				for(int i=0;i<n;++i) {
					for(int j=0;j<(1<<n);++j)
						dp[i][j] = -1;
				}
				ans = Math.max(ans, rec(start, 1<<start, n, start));
			}
			out.println(ans);
		}

		class Pair {
			Integer first, second;
			public Pair() {
			}
			public Pair(int first, int second) {
				this.first = first;
				this.second = second;
			}
			@Override
			public int hashCode() {
				final int prime = 31;
				int result = 1;
				result = prime * result + getOuterType().hashCode();
				result = prime * result + first;
				result = prime * result + second;
				return result;
			}
			@Override
			public boolean equals(Object obj) {
				if (this == obj)
					return true;
				if (obj == null)
					return false;
				if (getClass() != obj.getClass())
					return false;
				Pair other = (Pair) obj;
				if (!getOuterType().equals(other.getOuterType()))
					return false;
				if (first != other.first)
					return false;
				if (second != other.second)
					return false;
				return true;
			}
			private Task getOuterType() {
				return Task.this;
			}

		}

	}

	static void prepareIO(boolean isFileIO) {
		Task solver = new Task();
		// Standard IO
		if(!isFileIO) {
			InputStream inputStream = System.in;
	        OutputStream outputStream = System.out;
	        InputReader in = new InputReader(inputStream);
	        PrintWriter out = new PrintWriter(outputStream);
	        solver.solve(in, out);
	        out.close();
		}
        // File IO
		else {
			String IPfilePath = System.getProperty("user.home") + "/Downloads/ip.in";
	        String OPfilePath = System.getProperty("user.home") + "/Downloads/op.out";
	        InputReader fin = new InputReader(IPfilePath);
	        PrintWriter fout = null;
	        try {
				fout = new PrintWriter(new File(OPfilePath));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
	        solver.solve(fin, fout);
	        fout.close();
		}
	}

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

	static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public InputReader(String filePath) {
        	File file = new File(filePath);
            try {
				reader = new BufferedReader(new FileReader(file));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            tokenizer = null;
        }

        public String nextLine() {
        	String str = "";
        	try {
				str = reader.readLine();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	return str;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public double nextDouble() {
        	return Double.parseDouble(next());
        }

    }

}


import java.io.*;
import java.util.*;

public class CF1102F {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		int n = Integer.parseInt(st.nextToken());
		int m = Integer.parseInt(st.nextToken());
		int[][] aa = new int[n][m];
		for (int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			for (int j = 0; j < m; j++)
				aa[i][j] = Integer.parseInt(st.nextToken());
		}
		int[][] dd = new int[n][n];
		for (int i = 0; i < n; i++)
			for (int j = i + 1; j < n; j++) {
				int d = Integer.MAX_VALUE;
				for (int h = 0; h < m; h++)
					d = Math.min(d, Math.abs(aa[i][h] - aa[j][h]));
				dd[i][j] = dd[j][i] = d;
			}
		int[][] dd_ = new int[n][n];
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++) {
				int d = Integer.MAX_VALUE;
				for (int h = 0; h < m - 1; h++)
					d = Math.min(d, Math.abs(aa[i][h] - aa[j][h + 1]));
				dd_[i][j] = d;
			}
		if (n == 1) {
			System.out.println(dd_[0][0]);
			return;
		}
		int[] ii = new int[1 << n];
		for (int i = 0; i < n; i++)
			ii[1 << i] = i;
		int[][][] dp = new int[1 << n][n][n];
		for (int b = 0; b < 1 << n; b++)
			for (int u = b; u > 0; u &= u - 1) {
				int i = ii[u & -u];
				for (int v = b ^ 1 << i; v > 0; v &= v - 1) {
					int j = ii[v & -v];
					if (b == (1 << i ^ 1 << j))
						dp[b][i][j] = dd[i][j];
					else {
						int x = 0;
						for (int w = b ^ 1 << i ^ 1 << j; w > 0; w &= w - 1) {
							int k = ii[w & -w];
							x = Math.max(x, Math.min(dp[b ^ 1 << j][i][k], dd[k][j]));
						}
						dp[b][i][j] = x;
					}
				}
			}
		int b = (1 << n) - 1;
		int x = 0;
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				if (i != j)
					x = Math.max(x, Math.min(dp[b][i][j], dd_[i][j]));
		System.out.println(x);
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;

public class f {

	static int n, m;
	static int start;
	static int[][] memo;
	static int[][] arr;
	static int[][] costs;
	static int[][] wrapCosts;

	public static void main(String[] args) throws IOException {
		FastScanner in = new FastScanner(System.in);
		PrintWriter out = new PrintWriter(System.out);

		n = in.nextInt();
		m = in.nextInt();
		arr = new int[n][m];
		costs = new int[n][n];
		wrapCosts = new int[n][n];
		for (int r = 0; r < n; r++) {
			for (int c = 0; c < m; c++) {
				arr[r][c] = in.nextInt();
			}
		}
		for (int i = 0; i < n; i++) {
			for (int j = i + 1; j < n; j++) {
				costs[i][j] = Integer.MAX_VALUE;
				for (int c = 0; c < m; c++) {
					costs[i][j] = Math.min(costs[i][j], Math.abs(arr[i][c] - arr[j][c]));
				}
				costs[j][i] = costs[i][j];
			}
		}
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				wrapCosts[i][j] = Integer.MAX_VALUE;
				for (int c = 0; c < m - 1; c++) {
					wrapCosts[i][j] = Math.min(wrapCosts[i][j], Math.abs(arr[i][c + 1] - arr[j][c]));
				}
			}
		}
		memo = new int[n][1 << n];
		long best = 0;
		for (start = 0; start < n; start++) {
			for (int[] a : memo) Arrays.fill(a, -1);
			best = Math.max(best, go(start, (1 << n) - 1 - (1 << start)));
		}
		out.println(best);
		out.close();
	}
	static int go(int cur, int mask) {
		if (mask == 0) {
			return wrapCosts[start][cur];
		}
		if (memo[cur][mask] != -1) return memo[cur][mask];
		int ans = 0;
		for (int next = 0; next < n; next++) {
			int bit = 1 << next;
			if ((mask & bit) == 0) continue;
			ans = Math.max(ans, Math.min(costs[cur][next], go(next, mask ^ bit)));
		}
		return memo[cur][mask] = ans;
	}

	//@
	static class FastScanner {
	    BufferedReader br;
	    StringTokenizer st;

	    public FastScanner(InputStream i) {
	        br = new BufferedReader(new InputStreamReader(i));
	        st = new StringTokenizer("");
	    }

	    public String next() throws IOException {
	        if(st.hasMoreTokens())
	            return st.nextToken();
	        else
	            st = new StringTokenizer(br.readLine());
	        return next();
	    }

	    public int nextInt() throws IOException {
	        return Integer.parseInt(next());
	    }
	    //#
	    public long nextLong() throws IOException {
	        return Long.parseLong(next());
	    }
	    public double nextDouble() throws IOException {
	        return Double.parseDouble(next());
	    }
	    //$
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class ElongatedMatrix2 {
    public static void main(String[] args) {
        FastScanner scanner = new FastScanner();
        int N = scanner.nextInt();
        int M = scanner.nextInt();
        int[][] arr = new int[N][M];
        for(int i = 0; i < N; i++) {
            for(int j = 0; j < M; j++) {
                arr[i][j] = scanner.nextInt();
            }
        }
        int[][] distRow = new int[N][N];
        int[][] distTop = new int[N][N];
//        for(int i = 0; i < N; i++) {
//            Arrays.fill(distTop, Integer.MAX_VALUE);
//            Arrays.fill(distRow, Integer.MAX_VALUE);
//        }
        //compute row distances
        for(int i = 0; i < N; i++) {
            for(int j = i+1; j < N; j++) {
                int curMin = Integer.MAX_VALUE;
                for(int k = 0; k < M; k++) {
                    curMin = Math.min(curMin, Math.abs(arr[i][k] - arr[j][k]));
                }
                distRow[i][j] = distRow[j][i] = curMin;
            }
        }
        //compute bottom/top distances
        for(int i = 0; i < N; i++) {
            for(int j = 0; j < N; j++) {
                int curMin = Integer.MAX_VALUE;
                for(int k = 0; k+1 < M; k++) {
                    curMin = Math.min(curMin, Math.abs(arr[i][k] - arr[j][k+1]));
                }
                distTop[i][j] = curMin;
            }
        }
        int maxMask = 1 << N;
        int[][][] dp = new int[maxMask][N][N];
        for(int i = 0; i < maxMask; i++) {
            for(int j = 0; j < N; j++) {
                Arrays.fill(dp[i][j], Integer.MAX_VALUE);
            }
        }
        for(int mask = 1; mask < maxMask; mask++) {
            for (int j = 0; j < N; j++) {
                if ((mask & ( 1 << j)) == 0) continue;
                for(int k = 0; k < N; k++) {
                    if ((mask &(1 << k)) == 0) continue;
                    if (j == k && mask - (1 << k) != 0) continue;
                    for (int i = 0; i < N; i++) {
                        if ((mask & (1 << i)) > 0) continue;
                        int curMask = mask | (1 << i);
                        if (dp[curMask][i][k] != Integer.MAX_VALUE)
                            dp[curMask][i][k] = Math.max(dp[curMask][i][k], Math.min(dp[mask][j][k], distRow[i][j]));
                        else
                            dp[curMask][i][k] = Math.min(dp[mask][j][k], distRow[i][j]);
                    }
                }
            }
        }
        maxMask--;
        int max = 0;
        for(int i= 0; i < N; i++) {
            for(int j = 0; j < N; j++) {
                if (i==j && N != 1) continue;
                max = Math.max(max, Math.min(dp[maxMask][i][j], distTop[i][j]));
            }
        }
        System.out.println(max);
    }
    public static class FastScanner {
        BufferedReader br;
        StringTokenizer st;
        public FastScanner(Reader in) {
            br = new BufferedReader(in);
        }
        public FastScanner() {
            this(new InputStreamReader(System.in));
        }
        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }
        int nextInt() {
            return Integer.parseInt(next());
        }
        long nextLong() {
            return Long.parseLong(next());
        }
        double nextDouble() {
            return Double.parseDouble(next());
        }
        String readNextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
        int[] readIntArray(int n) {
            int[] a = new int[n];
            for (int idx = 0; idx < n; idx++) {
                a[idx] = nextInt();
            }
            return a;
        }
    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        F solver = new F();
        solver.solve(1, in, out);
        out.close();
    }

    static class F {
        public void solve(int testNumber, FastScanner in, PrintWriter out) {
            int n = in.ni(), m = in.ni();
            int[][] a = new int[n][m];
            for (int i = 0; i < n; i++) {
                a[i] = in.na(m);
            }
            if (n == 1) {
                int ans = Integer.MAX_VALUE;
                for (int i = 1; i < m; i++) {
                    ans = Math.min(ans, Math.abs(a[0][i] - a[0][i - 1]));
                }
                out.println(ans);
                return;
            }
            int[][] mk = new int[n][n];
            int[][] mk1 = new int[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = i + 1; j < n; j++) {
                    int minK = Integer.MAX_VALUE;
                    int minK1 = Integer.MAX_VALUE;
                    int minK2 = Integer.MAX_VALUE;
                    for (int l = 0; l < m; l++) {
                        minK = Math.min(minK, Math.abs(a[i][l] - a[j][l]));
                        if (l > 0) {
                            minK1 = Math.min(minK1, Math.abs(a[i][l] - a[j][l - 1]));
                            minK2 = Math.min(minK2, Math.abs(a[i][l - 1] - a[j][l]));
                        }
                    }
                    mk[i][j] = mk[j][i] = minK;
                    mk1[i][j] = minK1;
                    mk1[j][i] = minK2;
                }
            }

            int ans = 0;
            for (int first = 0; first < n; first++) {
                int[][] dp = new int[1 << n][n];
                for (int mask = 1; mask < (1 << n) - 1; mask++) {
                    int bc = Integer.bitCount(mask);
                    if ((mask & (1 << first)) != 0) {
                        if (bc == 1) {
                            dp[mask][first] = Integer.MAX_VALUE;
                        }
                        for (int i = 0; i < n; i++) {
                            if ((mask & (1 << i)) != 0) {
                                for (int j = 0; j < n; j++) {
                                    if ((mask & (1 << j)) == 0) {
                                        dp[mask | (1 << j)][j] = Math.max(dp[mask | (1 << j)][j], Math.min(dp[mask][i], mk[i][j]));
                                    }
                                }
                            }
                        }
                    }
                }
                for (int i = 0; i < n; i++) {
                    if (i != first) {
                        ans = Math.max(ans, Math.min(dp[(1 << n) - 1][i], mk1[first][i]));
                    }
                }
            }
            out.println(ans);
        }

    }

    static class FastScanner {
        private BufferedReader in;
        private StringTokenizer st;

        public FastScanner(InputStream stream) {
            in = new BufferedReader(new InputStreamReader(stream));
        }

        public String ns() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    String rl = in.readLine();
                    if (rl == null) {
                        return null;
                    }
                    st = new StringTokenizer(rl);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return st.nextToken();
        }

        public int ni() {
            return Integer.parseInt(ns());
        }

        public int[] na(int n) {
            int[] a = new int[n];
            for (int i = 0; i < n; i++) a[i] = ni();
            return a;
        }

    }
}


import java.util.*;
import java.io.*;

public class F531 {
    public static void main(String[] args) {
        MyScanner sc = new MyScanner();
        PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
        int n = sc.nextInt(); int m = sc.nextInt();
        long [][] mn1 = new long[n][n];
        long [][] mn2 = new long[n][n];
        long [][] grid = new long[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) grid[i][j] = sc.nextInt();
        }
        if (n == 1) {
            long ans = Integer.MAX_VALUE;
            for (int i = 0; i < m - 1; i++) ans = Math.min(ans, Math.abs(grid[0][i] - grid[0][i + 1]));
            out.println(ans);
            out.close();
            return;
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (i == j) continue;
                long min = Long.MAX_VALUE;
                for (int k = 0; k < m; k++) min = Math.min(min, Math.abs(grid[i][k] - grid[j][k]));
                mn1[i][j] = min;
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (i == j) continue;
                long min = Long.MAX_VALUE;
                for (int k = 0; k < m - 1; k++) min = Math.min(min, Math.abs(grid[i][k] - grid[j][k + 1]));
                mn2[i][j] = min;
            }
        }
        long [][] dp = new long[1 << n][n];

        // start vertex
        long ans = 0;
        for (int i = 0; i < n; i++) {
            for (long [] a: dp) Arrays.fill(a, -1);
            for (int j = 0; j < n; j++) {
                if (j == i) dp[1 << j][j] = Long.MAX_VALUE;
                else dp[1 << j][j] = 0;
            }
            for (int mask = 1; mask < (1 << n); mask++) {
                for (int last = 0; last < n; last++) {
                    if (dp[mask][last] != -1) continue;
                    for (int prev = 0; prev < n; prev++) {
                        if (prev == last) continue;
                        if (((mask >> prev) & 1) == 1) {
                            dp[mask][last] = Math.max(dp[mask][last], Math.min(mn1[prev][last], dp[mask ^ (1 << last)][prev]));
                        }
                    }
                }
            }
            // end vertex
            for (int j = 0; j < n; j++) {
                // step from the last vertex to the first
                long end = mn2[j][i];
                ans = Math.max(ans, Math.min(dp[(1 << n) - 1][j], end));
            }
        }
        out.println(ans);
        out.close();
    }

    //-----------MyScanner class for faster input----------
    public static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }

    }

}


import java.util.*;
import java.io.*;

public class main{

	static int max = 5000+1;
	static FastReader in = new FastReader();
	static PrintWriter out = new PrintWriter(System.out);
	static int N = 18;
	static int[][] mn1 = new int[N][N];
	static int[][] mn2 = new int[N][N];
	static int[][] dp = new int[1<<N][N];
	static int n,m;

	static void solve(){
		n = in.nextInt(); m = in.nextInt();
		int[][] a = new int[n][m];
		for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j] = in.nextInt();
		for(int i=0;i<n;i++){
			Arrays.fill(mn1[i],Integer.MAX_VALUE);
			Arrays.fill(mn2[i],Integer.MAX_VALUE);
		}

		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				for(int k=0;k<m;k++){
					mn1[i][j] = Math.min(mn1[i][j],Math.abs(a[i][k]-a[j][k]));
					if(k<=m-2)
						mn2[i][j] = Math.min(mn2[i][j],Math.abs(a[i][k]-a[j][k+1]));
				}
		int ans = 0;
		for(int i=0;i<n;i++){
			for(int x=0;x<1<<n;x++)Arrays.fill(dp[x],-1);
			for(int j=0;j<n;j++)dp[1<<j][j] = 0;
			dp[1<<i][i] = Integer.MAX_VALUE;
			for(int j=0;j<n;j++)
				ans = Math.max(ans,Math.min(mn2[j][i],calc((1 << n) - 1, j)));
		}
		out.println(ans);
	}

	static int calc(int mask, int v){
		if (dp[mask][v] != -1)
			return dp[mask][v];
		dp[mask][v] = 0;
		for(int u=0;u<n;u++) if (v != u && (((mask >> u) & 1)>0))
			dp[mask][v] = Math.max(dp[mask][v], Math.min(mn1[u][v], calc(mask ^ (1 << v), u)));
		return dp[mask][v];
	}

	public static void main(String[] args){
		solve();
		out.close();
	}

	static class FastReader{
        BufferedReader br;
        StringTokenizer st;
        public FastReader(){
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        public FastReader(String s) throws Exception{
            br = new BufferedReader(new FileReader(s));
        }

        String next(){
            while (st == null || !st.hasMoreElements()){
                try{
                    st = new StringTokenizer(br.readLine());
                }catch (IOException  e){
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        String nextLine(){
            String str = "";
            try{
                str = br.readLine();
            }catch (IOException e){
                e.printStackTrace();
            }
            return str;
        }
        int nextInt(){return Integer.parseInt(in.next());}
    	long nextLong(){return Long.parseLong(in.next());}
    	double nextDouble(){return Double.parseDouble(in.next());}
    }
}

// |xvcxv|vcv[cvcvc|cxv||]vcvx:v|c|vxc|[|cvx:cxvx||||]


import java.lang.*;
import java.math.*;
import java.util.*;
import java.io.*;
public class Main {
    void solve() {
        int n=ni(),m=ni();
        int a[][]=new int[n+1][m+1];
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=ni();

        if(n==1){
            int mn=Integer.MAX_VALUE;
            for(int i=1;i<m;i++) mn=Math.min(mn,Math.abs(a[1][i]-a[1][i+1]));
            pw.println(mn) ;
            return ;
        }
        mn1=new int[n+1][n+1];
        mn2=new int[n+1][n+1];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){

                if(i==j) continue;

                mn1[i][j]=mn2[i][j]=Integer.MAX_VALUE;
                for(int k=1;k<=m;k++){
                    mn1[i][j]=Math.min(mn1[i][j],Math.abs(a[i][k]-a[j][k]));
                   // if(i==3 && j==4) pw.println(a[i][k]+" "+a[j][k]);
                }
                for(int k=1;k<m;k++){
                    mn2[i][j]=Math.min(mn2[i][j],Math.abs(a[i][k+1]-a[j][k]));
                }
                //if(m==1) mn2[i][j]=0;

            }
        }
      //  pw.println(mn1[3][4]);
        dp=new int[17][1<<16][17];
        for(int i=1;i<17;i++) for(int j=0;j<(1<<16);j++) Arrays.fill(dp[i][j],-1);

        int ans=0;
        for(int i=1;i<=n;i++){
            ans=Math.max(ans,go(2,1<<(i-1),i,i,n));
        }
        pw.println(ans);

    }
    int mn1[][],mn2[][];
    int dp[][][];
    int go(int i,int mask,int prev,int first,int n){
        if(i>n){
       //   if(mn2[first][prev]==0)  pw.println(prev+" "+first+" "+mn2[first][prev]);
            return mn2[first][prev];
        }
       if(dp[first][mask][prev]!=-1) return dp[first][mask][prev];
        int cc=0;
        for(int k=1;k<=n;k++){
            if((mask&(1<<(k-1)))==0){
                cc=Math.max(cc,Math.min(mn1[prev][k],go(i+1,mask|(1<<(k-1)),k,first,n)));
               //if(mn1[prev][k]==0)  pw.println(mn1[prev][k]+" "+prev+" "+k);
            }
        }
        dp[first][mask][prev]=cc;
        return cc;
    }

    long M= (long)1e9+7;
    InputStream is;
    PrintWriter pw;
    String INPUT = "";
    void run() throws Exception {
        is = INPUT.isEmpty() ? System.in : new ByteArrayInputStream(INPUT.getBytes());
        pw = new PrintWriter(System.out);
        long s = System.currentTimeMillis();
        solve();
        pw.flush();
        if(!INPUT.isEmpty())tr(System.currentTimeMillis()-s+"ms");

    }
    public static void main(String[] args) throws Exception { new Main().run(); }

    private byte[] inbuf = new byte[1024];
    public int lenbuf = 0, ptrbuf = 0;

    private int readByte() {
        if(lenbuf == -1)throw new InputMismatchException();
        if(ptrbuf >= lenbuf){
            ptrbuf = 0;
            try { lenbuf = is.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
            if(lenbuf <= 0)return -1;
        }
        return inbuf[ptrbuf++];
    }

    private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
    private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; }

    private double nd() { return Double.parseDouble(ns()); }
    private char nc() { return (char)skip(); }

    private String ns() {
        int b = skip();
        StringBuilder sb = new StringBuilder();
        while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }

    private char[] ns(int n) {
        char[] buf = new char[n];
        int b = skip(), p = 0;
        while(p < n && !(isSpaceChar(b))){
            buf[p++] = (char)b;
            b = readByte();
        }
        return n == p ? buf : Arrays.copyOf(buf, p);
    }

    private char[][] nm(int n, int m) {
        char[][] map = new char[n][];
        for(int i = 0;i < n;i++)map[i] = ns(m);
        return map;
    }

    private int[] na(int n) {
        int[] a = new int[n];
        for(int i = 0;i < n;i++)a[i] = ni();
        return a;
    }

    private int ni() {
        int num = 0, b;
        boolean minus = false;
        while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if(b == '-'){
            minus = true;
            b = readByte();
        }

        while(true){
            if(b >= '0' && b <= '9'){
                num = num * 10 + (b - '0');
            }else{
                return minus ? -num : num;
            }
            b = readByte();
        }
    }

    private long nl() {
        long num = 0;
        int b;
        boolean minus = false;
        while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if(b == '-'){
            minus = true;
            b = readByte();
        }

        while(true){
            if(b >= '0' && b <= '9'){
                num = num * 10 + (b - '0');
            }else{
                return minus ? -num : num;
            }
            b = readByte();
        }
    }
    private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
    private void tr(Object... o) { if(INPUT.length() > 0)System.out.println(Arrays.deepToString(o)); }

}


import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class A {
    static MyScanner sc;
    static PrintWriter pw;

    public static void main(String[] args) throws Throwable {
        sc = new MyScanner();
        pw = new PrintWriter(System.out);

        n = sc.nextInt();
        int m = sc.nextInt();
        int[][] a = new int[n][m];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                a[i][j] = sc.nextInt();
        val = new int[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(val[i], Integer.MAX_VALUE);
        for (int i = 0; i < n; i++)
            for (int j = i; j < n; j++) {
                for (int k = 0; k < m; k++)
                    val[i][j] = val[j][i] = Math.min(val[i][j], Math.abs(a[i][k] - a[j][k]));
            }

        val2 = new int[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(val2[i], Integer.MAX_VALUE);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < m - 1; k++)
                    val2[i][j] = Math.min(val2[i][j], Math.abs(a[i][k] - a[j][k + 1]));
            }
        mem = new Long[n][n][1 << n];
        long ans = 0;
        for (int i = 0; i < n; i++) {
            ans = Math.max(ans, dp(i, i, 1 << i));
        }
        if (n == 1)
            pw.println(val2[0][0]);
        else
            pw.println(ans);

        pw.flush();
        pw.close();
    }

    static int n;
    static int[][] val, val2;
    static Long[][][] mem;

    static long dp(int st, int lst, int msk) {
        int bits = Integer.bitCount(msk);
        if (mem[st][lst][msk] != null)
            return mem[st][lst][msk];
        long ans = 0;
        for (int i = 0; i < n; i++)
            if ((msk & (1 << i)) == 0) {
                int newMsk = (msk | (1 << i));
                if (bits < n - 1)
                    ans = Math.max(ans, Math.min(val[lst][i], dp(st, i, newMsk)));
                else
                    ans = Math.max(ans, Math.min(val[lst][i], val2[i][st]));
            }
        return mem[st][lst][msk] = ans;
    }

    static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author beginner1010
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskF solver = new TaskF();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskF {
        final int INF = (int) 1e9 + 5;
        int n;
        int m;
        int[][][] dp;
        int[][] diff;
        int[][] diffStartLast;

        int two(int x) {
            return 1 << x;
        }

        boolean contain(int mask, int x) {
            return (mask & two(x)) > 0;
        }

        int rec(int start, int pre, int mask) {
            if (mask == two(n) - 1)
                return INF;
            int res = dp[start][pre][mask];
            if (res != -1)
                return res;
            res = 0;

            for (int i = 0; i < n; i++)
                if (contain(mask, i) == false) {
                    int diffPre = mask == 0 ? INF : diff[pre][i]; // mask == 0 should never happen
                    int diffLast = (mask | two(i)) == two(n) - 1 ? diffStartLast[start][i] : INF;
                    res = Math.max(res, Math.min(rec(start, i, mask | two(i)), Math.min(diffLast, diffPre)));
                }

            dp[start][pre][mask] = res;
            return res;
        }

        public void solve(int testNumber, InputReader in, PrintWriter out) {
            n = in.nextInt();
            m = in.nextInt();
            int[][] grid = new int[n][m];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < m; j++)
                    grid[i][j] = in.nextInt();

            if (n == 1) {
                int res = INF;
                for (int i = 0; i + 1 < m; i++) {
                    res = Math.min(res, Math.abs(grid[0][i] - grid[0][i + 1]));
                }
                out.println(res);
                return;
            }

            diff = new int[n][n];
            diffStartLast = new int[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    diff[i][j] = INF;
                    diffStartLast[i][j] = INF;
                    for (int k = 0; k < m; k++) {
                        diff[i][j] = Math.min(diff[i][j], Math.abs(grid[i][k] - grid[j][k]));
                        if (k + 1 < m) {
                            diffStartLast[i][j] = Math.min(diffStartLast[i][j], Math.abs(grid[i][k + 1] - grid[j][k]));
                        }
                    }
                }
            }

            dp = new int[n][n][two(n)];
            for (int[][] aux : dp)
                for (int[] aux2 : aux)
                    Arrays.fill(aux2, -1);

            int ans = 0;
            for (int start = 0; start < n; start++) {
                ans = Math.max(ans, rec(start, start, two(start)));
            }
            out.println(ans);
        }

    }

    static class InputReader {
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputStream stream;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        private boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        private int read() {
            if (numChars == -1)
                throw new InputMismatchException();
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public int nextInt() {
            int c = read();
            while (isWhitespace(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isWhitespace(c));
            return res * sgn;
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Scanner in = new Scanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        FElongatedMatrix solver = new FElongatedMatrix();
        solver.solve(1, in, out);
        out.close();
    }

    static class FElongatedMatrix {
        int n;
        int m;
        int[][] arr;
        int[][] memo;
        int[][][] memo2;
        int first;

        public void readInput(Scanner sc) {
            n = sc.nextInt();
            m = sc.nextInt();
            arr = new int[n][m];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < m; j++)
                    arr[i][j] = sc.nextInt();
        }

        public void solve(int testNumber, Scanner sc, PrintWriter pw) {
            int tc = 1;
            while (tc-- > 0) {
                readInput(sc);
                int max = 0;
                memo2 = new int[2][n][n];
                for (int[][] x : memo2)
                    for (int[] y : x)
                        Arrays.fill(y, -1);
                for (int i = 0; i < n; i++) {
                    memo = new int[n][1 << n];
                    for (int[] y : memo)
                        Arrays.fill(y, -1);
                    first = i;
                    max = Math.max(max, dp(1 << i, i));
                }
                pw.println(max);
            }
        }

        private int dp(int msk, int prev) {
            if (msk == (1 << n) - 1)
                return getLast(first, prev);
            if (memo[prev][msk] != -1)
                return memo[prev][msk];
            int max = 0;
            for (int i = 0; i < n; i++) {
                if ((msk & 1 << i) == 0)
                    max = Math.max(max, Math.min(getDiff(prev, i), dp(msk | 1 << i, i)));
            }
            return memo[prev][msk] = max;
        }

        private int getLast(int i, int j) {
            if (memo2[0][i][j] != -1)
                return memo2[0][i][j];
            int min = Integer.MAX_VALUE;
            for (int k = 0; k < m - 1; k++)
                min = Math.min(min, Math.abs(arr[i][k] - arr[j][k + 1]));
            return memo2[0][i][j] = min;
        }

        private int getDiff(int i, int j) {
            if (memo2[1][i][j] != -1)
                return memo2[1][i][j];
            int min = Integer.MAX_VALUE;
            for (int k = 0; k < m; k++)
                min = Math.min(min, Math.abs(arr[i][k] - arr[j][k]));
            return memo2[1][i][j] = min;
        }

    }

    static class Scanner {
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream s) {
            br = new BufferedReader(new InputStreamReader(s));
        }

        public String next() {
            try {
                while (st == null || !st.hasMoreTokens())
                    st = new StringTokenizer(br.readLine());
                return st.nextToken();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Closeable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(null, new TaskAdapter(), "", 1 << 27);
        thread.start();
        thread.join();
    }

    static class TaskAdapter implements Runnable {
        @Override
        public void run() {
            InputStream inputStream = System.in;
            OutputStream outputStream = System.out;
            FastInput in = new FastInput(inputStream);
            FastOutput out = new FastOutput(outputStream);
            FElongatedMatrix solver = new FElongatedMatrix();
            solver.solve(1, in, out);
            out.close();
        }
    }

    static class FElongatedMatrix {
        public void solve(int testNumber, FastInput in, FastOutput out) {
            int n = in.readInt();
            int m = in.readInt();
            int[][] mat = new int[n][m];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    mat[i][j] = in.readInt();
                }
            }

            int[][] minDist = new int[n][n];
            SequenceUtils.deepFill(minDist, (int) 1e9);
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    for (int k = 0; k < m; k++) {
                        minDist[i][j] = Math.min(minDist[i][j], Math.abs(mat[i][k] - mat[j][k]));
                    }
                }
            }
            int[][] minDistBetweenHeadAndTail = new int[n][n];
            SequenceUtils.deepFill(minDistBetweenHeadAndTail, (int) 1e9);
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    for (int k = 1; k < m; k++) {
                        minDistBetweenHeadAndTail[i][j] = Math.min(minDistBetweenHeadAndTail[i][j], Math.abs(mat[i][k] - mat[j][k - 1]));
                    }
                }
            }

            Log2 log2 = new Log2();
            BitOperator bo = new BitOperator();
            int[][][] dp = new int[1 << n][n][n];
            for (int i = 1; i < (1 << n); i++) {
                if (i == Integer.lowestOneBit(i)) {
                    dp[i][log2.floorLog(i)][log2.floorLog(i)] = (int) 1e9;
                    continue;
                }
                for (int j = 0; j < n; j++) {
                    for (int k = 0; k < n; k++) {
                        if (bo.bitAt(i, j) == 0) {
                            continue;
                        }
                        for (int t = 0; t < n; t++) {
                            dp[i][j][k] = Math.max(dp[i][j][k],
                                    Math.min(dp[bo.setBit(i, j, false)][t][k],
                                            minDist[j][t]));
                        }
                    }
                }
            }

            int ans = 0;
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    ans = Math.max(ans, Math.min(dp[(1 << n) - 1][i][j], minDistBetweenHeadAndTail[j][i]));
                }
            }
            out.println(ans);
        }

    }

    static class SequenceUtils {
        public static void deepFill(Object array, int val) {
            if (!array.getClass().isArray()) {
                throw new IllegalArgumentException();
            }
            if (array instanceof int[]) {
                int[] intArray = (int[]) array;
                Arrays.fill(intArray, val);
            } else {
                Object[] objArray = (Object[]) array;
                for (Object obj : objArray) {
                    deepFill(obj, val);
                }
            }
        }

    }

    static class Log2 {
        public int floorLog(int x) {
            return 31 - Integer.numberOfLeadingZeros(x);
        }

    }

    static class FastInput {
        private final InputStream is;
        private byte[] buf = new byte[1 << 13];
        private int bufLen;
        private int bufOffset;
        private int next;

        public FastInput(InputStream is) {
            this.is = is;
        }

        private int read() {
            while (bufLen == bufOffset) {
                bufOffset = 0;
                try {
                    bufLen = is.read(buf);
                } catch (IOException e) {
                    bufLen = -1;
                }
                if (bufLen == -1) {
                    return -1;
                }
            }
            return buf[bufOffset++];
        }

        public void skipBlank() {
            while (next >= 0 && next <= 32) {
                next = read();
            }
        }

        public int readInt() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            int val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

    }

    static class BitOperator {
        public int bitAt(int x, int i) {
            return (x >> i) & 1;
        }

        public int setBit(int x, int i, boolean v) {
            if (v) {
                x |= 1 << i;
            } else {
                x &= ~(1 << i);
            }
            return x;
        }

    }

    static class FastOutput implements AutoCloseable, Closeable {
        private StringBuilder cache = new StringBuilder(10 << 20);
        private final Writer os;

        public FastOutput(Writer os) {
            this.os = os;
        }

        public FastOutput(OutputStream os) {
            this(new OutputStreamWriter(os));
        }

        public FastOutput println(int c) {
            cache.append(c).append('\n');
            return this;
        }

        public FastOutput flush() {
            try {
                os.append(cache);
                os.flush();
                cache.setLength(0);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
            return this;
        }

        public void close() {
            flush();
            try {
                os.close();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        public String toString() {
            return cache.toString();
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        F solver = new F();
        solver.solve(1, in, out);
        out.close();
    }

    static class F {
        public void solve(int testNumber, FastScanner in, PrintWriter out) {
            int n = in.ni(), m = in.ni();
            int[][] a = new int[n][m];
            for (int i = 0; i < n; i++) {
                a[i] = in.na(m);
            }
            if (n == 1) {
                int ans = Integer.MAX_VALUE;
                for (int i = 1; i < m; i++) {
                    ans = Math.min(ans, Math.abs(a[0][i] - a[0][i - 1]));
                }
                out.println(ans);
                return;
            }
            int[][] mk = new int[n][n];
            int[][] mk1 = new int[n][n];
            for (int i = 0; i < n; i++) {
                for (int j = i + 1; j < n; j++) {
                    int minK = Integer.MAX_VALUE;
                    int minK1 = Integer.MAX_VALUE;
                    int minK2 = Integer.MAX_VALUE;
                    for (int l = 0; l < m; l++) {
                        minK = Math.min(minK, Math.abs(a[i][l] - a[j][l]));
                        if (l > 0) {
                            minK1 = Math.min(minK1, Math.abs(a[i][l] - a[j][l - 1]));
                            minK2 = Math.min(minK2, Math.abs(a[i][l - 1] - a[j][l]));
                        }
                    }
                    mk[i][j] = mk[j][i] = minK;
                    mk1[i][j] = minK1;
                    mk1[j][i] = minK2;
                }
            }

            int ans = 0;
            for (int first = 0; first < n; first++) {
                int[][] dp = new int[1 << n][n];
                for (int mask = 1; mask < (1 << n) - 1; mask++) {
                    int bc = Integer.bitCount(mask);
                    if ((mask & (1 << first)) != 0) {
                        if (bc == 1) {
                            dp[mask][first] = Integer.MAX_VALUE;
                        }
                        for (int i = 0; i < n; i++) {
                            if ((mask & (1 << i)) != 0) {
                                for (int j = 0; j < n; j++) {
                                    if ((mask & (1 << j)) == 0) {
                                        dp[mask | (1 << j)][j] = Math.max(dp[mask | (1 << j)][j], Math.min(dp[mask][i], mk[i][j]));
                                    }
                                }
                            }
                        }
                    }
                }
                for (int i = 0; i < n; i++) {
                    if (i != first) {
                        ans = Math.max(ans, Math.min(dp[(1 << n) - 1][i], mk1[first][i]));
                    }
                }
            }
            out.println(ans);
        }

    }

    static class FastScanner {
        private BufferedReader in;
        private StringTokenizer st;

        public FastScanner(InputStream stream) {
            in = new BufferedReader(new InputStreamReader(stream));
        }

        public String ns() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    String rl = in.readLine();
                    if (rl == null) {
                        return null;
                    }
                    st = new StringTokenizer(rl);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return st.nextToken();
        }

        public int ni() {
            return Integer.parseInt(ns());
        }

        public int[] na(int n) {
            int[] a = new int[n];
            for (int i = 0; i < n; i++) a[i] = ni();
            return a;
        }

    }
}


import java.util.*;
import java.io.*;

public class main{

	static int max = 5000+1;
	static FastReader in = new FastReader();
	static PrintWriter out = new PrintWriter(System.out);
	static int N = 18;
	static int[][] mn1 = new int[N][N];
	static int[][] mn2 = new int[N][N];
	static int[][] dp = new int[1<<N][N];
	static int n,m;

	static void solve(){
		n = in.nextInt(); m = in.nextInt();
		int[][] a = new int[n][m];
		for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j] = in.nextInt();
		for(int i=0;i<n;i++){
			Arrays.fill(mn1[i],Integer.MAX_VALUE);
			Arrays.fill(mn2[i],Integer.MAX_VALUE);
		}

		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				for(int k=0;k<m;k++){
					mn1[i][j] = Math.min(mn1[i][j],Math.abs(a[i][k]-a[j][k]));
					if(k<=m-2)
						mn2[i][j] = Math.min(mn2[i][j],Math.abs(a[i][k]-a[j][k+1]));
				}
		int ans = 0;
		for(int i=0;i<n;i++){
			for(int x=0;x<1<<n;x++)Arrays.fill(dp[x],-1);
			for(int j=0;j<n;j++)dp[1<<j][j] = 0;
			dp[1<<i][i] = Integer.MAX_VALUE;
			for(int j=0;j<n;j++)
				ans = Math.max(ans,Math.min(mn2[j][i],calc((1 << n) - 1, j)));
		}
		out.println(ans);
	}

	static int calc(int mask, int v){
		if (dp[mask][v] != -1)
			return dp[mask][v];
		dp[mask][v] = 0;
		for(int u=0;u<n;u++) if (v != u && (((mask >> u) & 1)>0))
			dp[mask][v] = Math.max(dp[mask][v], Math.min(mn1[u][v], calc(mask ^ (1 << v), u)));
		return dp[mask][v];
	}

	public static void main(String[] args){
		solve();
		out.flush();
		out.close();
	}

	static class FastReader{
        BufferedReader br;
        StringTokenizer st;
        public FastReader(){
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        public FastReader(String s) throws Exception{
            br = new BufferedReader(new FileReader(s));
        }

        String next(){
            while (st == null || !st.hasMoreElements()){
                try{
                    st = new StringTokenizer(br.readLine());
                }catch (IOException  e){
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        String nextLine(){
            String str = "";
            try{
                str = br.readLine();
            }catch (IOException e){
                e.printStackTrace();
            }
            return str;
        }
        int nextInt(){return Integer.parseInt(in.next());}
    	long nextLong(){return Long.parseLong(in.next());}
    	double nextDouble(){return Double.parseDouble(in.next());}
    }
}

// |xvcxv|vcv[cvcvc|cxv||]vcvx:v|c|vxc|[|cvx:cxvx||||]


import java.io.*;
import java.util.*;

public class D{

	static int bot;
	static int n,m;
	static int [][]a;
	static int [][]Min;
	static int [][]memo;
	static int K;
	static int dp(int msk,int ones,int last) {
		if(ones==n) {
			return Min[last][bot]>=K?1:0;
		}
		if(memo[last][msk]!=-1)
			return memo[last][msk];
		int ans=0;
		for(int nxt=0;nxt<n;nxt++)
			if((msk & (1<<nxt)) ==0 && Min[last][nxt]>=K)
			{
				ans|=dp(msk|1<<nxt,ones+1,nxt);
			}
		return memo[last][msk]= ans;
	}
	static boolean check(int top,int bottom) {
		for(int j=0;j+1<m;j++)
		{
			int diff=Math.abs(a[bottom][j]-a[top][j+1]);
			if(diff<K)
				return false;
		}
		return true;
	}
	public static void main(String[] args) throws IOException {
		Scanner sc=new Scanner();
		PrintWriter out=new PrintWriter(System.out);
		n=sc.nextInt();
		m=sc.nextInt();
		a=new int [n][m];
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
				a[i][j]=sc.nextInt();
		Min=new int [n][n];
		if(n==1) {
			int lo=0,hi=(int)1e9;
			int ans=0;
			while(lo<=hi) {
				K=lo+hi>>1;

				if(check(0, 0))
				{
					ans=K;
					lo=K+1;
				}
				else
					hi=K-1;
			}
			System.out.println(ans);
			return;
		}
		for(int i1=0;i1<n;i1++)
			for(int i2=0;i2<n;i2++)
			{
				if(i1==i2)
					continue;
				int min=(int) 1e9;
				for(int j=0;j<m;j++)
					min=Math.min(Math.abs(a[i1][j]-a[i2][j]), min);
				Min[i1][i2]=min;
			}
		memo=new int [n][1<<n];
		int ans=0;
		int lo=0,hi=(int)1e9;
		while(lo<=hi) {
			K=lo+hi>>1;
		for(int []x:memo)
			Arrays.fill(x, -1);
		int ok=0;
		for(int top=0;top<n && ok==0;top++)
			for(int bottom=0;bottom<n && ok==0 ;bottom++) {
				bot=bottom;
				if(top==bottom || !check(top, bottom))
					continue;

				int dp=dp(1<<top | 1<<bottom, 2, top);
				ok|=dp;

			}
		if(ok==1)
		{
			ans=K;
			lo=K+1;

		}
		else
			hi=K-1;
		}
		out.println(ans);

		out.close();

	}
	static class Scanner
	{
		BufferedReader br;
		StringTokenizer st;
		Scanner(){
			br=new BufferedReader(new InputStreamReader(System.in));
		}
		Scanner(String fileName) throws FileNotFoundException{
			br=new BufferedReader(new FileReader(fileName));
		}
		String next() throws IOException {
			while(st==null || !st.hasMoreTokens())
				st=new StringTokenizer(br.readLine());
			return st.nextToken();
		}
		String nextLine() throws IOException {
			return br.readLine();
		}
		int nextInt() throws IOException{
			return Integer.parseInt(next());
		}
		long nextLong()  throws NumberFormatException, IOException {
			return Long.parseLong(next());
		}
		double nextDouble() throws NumberFormatException, IOException {
			return Double.parseDouble(next());
		}
	}
}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Scanner;
import java.util.Vector;

public class Main {

	private static final int SIM = 1;
	private static final int NAO = 2;

	public static void main(String[] args) {

		Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in)));

		int n = in.nextInt();
		int m = in.nextInt();
		int[][] a = new int[n][m];
		int[][] graphVerticial = null;
		int[][] graphDiagonal = null;

		for(int i = 0; i < n; i++) {
			for(int j = 0; j < m; j++) {
				a[i][j] = in.nextInt();
			}
		}

		graphVerticial = createGraphVertical(n, m, a);
		graphDiagonal = createGraphDiagonal(n, m, a);

//		print(graphVerticial);
//		System.out.println("##########################");
//		print(graphDiagonal);

		int result = 0;
		int k = 1;
		int piso = 0;
		int teto = 1000000000;

		while(true) {

			k = (int) Math.ceil((teto - piso) / 2.0) + piso;

			if(isOk(n, k, graphVerticial, graphDiagonal)) {
				result = Math.max(result, k);
				piso = k;
			}
			else{
				teto = k - 1;
			}

			//System.out.println(piso + ", " + teto);

			if(teto <= piso) break;

		}

		System.out.println(result);

	}

	public static int[][] createGraphVertical(int n, int m, int[][] a){

		int[][] graph = new int[n][n];

		for(int i = 0; i < n; i++) {

			for(int j = 0; j  < n; j++) {

				if(i == j) continue;
				if(i > j) {graph[i][j] = graph[j][i]; continue;}

				graph[i][j] = Integer.MAX_VALUE;

				for(int k = 0; k < m; k++) {
					graph[i][j] = Math.min(graph[i][j], Math.abs(a[i][k] - a[j][k]));
				}
			}
		}

		return graph;
	}

	public static int[][] createGraphDiagonal(int n, int m, int[][] a){

		int[][] graph = new int[n][n];

		for(int i = 0; i < n; i++) {

			for(int j = 0; j  < n; j++) {

				graph[i][j] = Integer.MAX_VALUE;

				for(int k = 0; k < m - 1; k++) {
					graph[i][j] = Math.min(graph[i][j], Math.abs(a[j][k] - a[i][k + 1]));
				}
			}
		}

		return graph;
	}

	public static int hasPath(int n, int k, int origem, int destino, int conjunto, int[][] graph, int[][][] pd) {

		//System.out.println(origem + ", " + destino);
		//print(conjunto);

		if(pd[origem][destino][conjunto] != 0) return pd[origem][destino][conjunto];

		if(conjunto == 0) {
			return origem == destino ? SIM : NAO;
		}
		else if(origem == destino){
			return NAO;
		}

		for(int i = 0; i < n; i++) {

			if(i == origem) continue;

			int novoConjunto = conjunto - (1 << i);
			boolean pertenceConjunto = ((conjunto >> i) % 2) == 1;

			if(pertenceConjunto && graph[origem][i] >= k && hasPath(n, k, i, destino, novoConjunto, graph, pd) == SIM) {
				pd[origem][destino][conjunto] = SIM;
				return pd[origem][destino][conjunto];
			}
		}

		pd[origem][destino][conjunto] = NAO;

		return pd[origem][destino][conjunto];

	}

	public static boolean isOk(int n, int k, int[][] graphVertical, int[][] graphDiagonal) {

		int conjunto = (int) Math.pow(2, n) - 1;

		int[][][] pd = new int[n][n][(int)Math.pow(2, n)];

		for(int i = 0; i < n; i++) {

			for(int j = 0; j < n; j++) {

				if(i == j && n > 1) continue;

				int novoConjunto = conjunto - (1 << i);

				if(graphDiagonal[i][j] >= k && hasPath(n, k, i, j, novoConjunto, graphVertical, pd) == SIM) {
					return true;
				}

			}
		}

		return false;

	}

	public static void print(int[][] graph) {

		for(int i = 0; i < graph.length; i++) {

			for(int j = 0; j < graph.length; j++) {

//				if(graph[i][j] >= 3 ) System.out.print("1 ");
//				else System.out.print("0 ");
				System.out.print(graph[i][j] + " ");

			}

			System.out.println();

		}

	}

	public static void print(int n) {

		List<Integer> bits = new Vector<>();

		while(n > 0) {

			bits.add(n % 2);
			n /= 2;
		}

		for(int i = bits.size() - 1; i >= 0; i--) {
			System.out.print(bits.get(i));
		}

		System.out.println();

	}

}


import java.io.*;
import java.util.*;

import static java.lang.Math.*;
import static java.util.Arrays.*;

public class cf1102f {

    public static void main(String[] args) throws IOException {
        int n = rni(), m = ni(), a[][] = new int[n][];
        for (int i = 0; i < n; ++i) {
            a[i] = ria(m);
        }
        int delta[][] = new int[n][n], end_delta[][] = new int[n][n], dp[][][] = new int[n][1 << n][n];
        for (int i = 0; i < n; ++i) {
            fill(delta[i], IBIG);
            fill(end_delta[i], IBIG);
            delta[i][i] = 0;
        }
        for (int i = 0; i < n - 1; ++i) {
            for (int j = i + 1; j < n; ++j) {
                for (int k = 0; k < m; ++k) {
                    delta[i][j] = delta[j][i] = min(delta[i][j], abs(a[i][k] - a[j][k]));
                }
            }
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                for (int k = 1; k < m; ++k) {
                    end_delta[i][j] = min(end_delta[i][j], abs(a[j][k] - a[i][k - 1]));
                }
            }
        }
        for (int[][] layer : dp) {
            for (int[] row : layer) {
                fill(row, IBIG);
            }
        }
        for (int i = 1; i < 1 << n; ++i) {
            boolean one_bit = Integer.bitCount(i) == 1;
            for (int j = 0; j < n; ++j) {
                if ((i & (1 << j)) > 0) {
                    for (int l = 0; l < n; ++l) {
                        if ((i & (1 << l)) == 0) {
                            int max = 0;
                            for (int k = 0; k < n; ++k) {
                                if ((one_bit || j != k) && (i & (1 << k)) > 0) {
                                    max = max(max, min(dp[j][i][k], delta[k][l]));
                                }
                            }
                            // pr(j + " ");
                            // pr(Integer.toBinaryString(i) + " ");
                            // prln(l, max);
                            dp[j][i | (1 << l)][l] = max;
                        }
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (i != j) {
                    // prln(i, j, dp[i][(1 << n) - 1][j]);
                    ans = max(ans, min(dp[i][(1 << n) - 1][j], end_delta[j][i]));
                }
            }
        }
        if (n == 1) {
            ans = maxof(end_delta[0]);
        }
        prln(ans);
        close();
    }

    static BufferedReader __in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter __out = new PrintWriter(new OutputStreamWriter(System.out));
    static StringTokenizer input;
    static Random __rand = new Random();

    // references
    // IBIG = 1e9 + 7
    // IMAX ~= 2e9
    // LMAX ~= 9e18

    // constants
    static final int IBIG = 1000000007;
    static final int IMAX = 2147483647;
    static final int IMIN = -2147483648;
    static final long LMAX = 9223372036854775807L;
    static final long LMIN = -9223372036854775808L;
    // math util
    static int minof(int a, int b, int c) {return min(a, min(b, c));}
    static int minof(int... x) {if (x.length == 1) return x[0]; if (x.length == 2) return min(x[0], x[1]); if (x.length == 3) return min(x[0], min(x[1], x[2])); int min = x[0]; for (int i = 1; i < x.length; ++i) if (x[i] < min) min = x[i]; return min;}
    static long minof(long a, long b, long c) {return min(a, min(b, c));}
    static long minof(long... x) {if (x.length == 1) return x[0]; if (x.length == 2) return min(x[0], x[1]); if (x.length == 3) return min(x[0], min(x[1], x[2])); long min = x[0]; for (int i = 1; i < x.length; ++i) if (x[i] < min) min = x[i]; return min;}
    static int maxof(int a, int b, int c) {return max(a, max(b, c));}
    static int maxof(int... x) {if (x.length == 1) return x[0]; if (x.length == 2) return max(x[0], x[1]); if (x.length == 3) return max(x[0], max(x[1], x[2])); int max = x[0]; for (int i = 1; i < x.length; ++i) if (x[i] > max) max = x[i]; return max;}
    static long maxof(long a, long b, long c) {return max(a, max(b, c));}
    static long maxof(long... x) {if (x.length == 1) return x[0]; if (x.length == 2) return max(x[0], x[1]); if (x.length == 3) return max(x[0], max(x[1], x[2])); long max = x[0]; for (int i = 1; i < x.length; ++i) if (x[i] > max) max = x[i]; return max;}
    static int powi(int a, int b) {if (a == 0) return 0; int ans = 1; while (b > 0) {if ((b & 1) > 0) ans *= a; a *= a; b >>= 1;} return ans;}
    static long powl(long a, int b) {if (a == 0) return 0; long ans = 1; while (b > 0) {if ((b & 1) > 0) ans *= a; a *= a; b >>= 1;} return ans;}
    static int fli(double d) {return (int) d;}
    static int cei(double d) {return (int) ceil(d);}
    static long fll(double d) {return (long) d;}
    static long cel(double d) {return (long) ceil(d);}
    static int gcf(int a, int b) {return b == 0 ? a : gcf(b, a % b);}
    static long gcf(long a, long b) {return b == 0 ? a : gcf(b, a % b);}
    static int lcm(int a, int b) {return a * b / gcf(a, b);}
    static long lcm(long a, long b) {return a * b / gcf(a, b);}
    static int randInt(int min, int max) {return __rand.nextInt(max - min + 1) + min;}
    static long mix(long x) {x += 0x9e3779b97f4a7c15L; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9L; x = (x ^ (x >> 27)) * 0x94d049bb133111ebL; return x ^ (x >> 31);}
    // array util
    static void reverse(int[] a) {for (int i = 0, n = a.length, half = n / 2; i < half; ++i) {int swap = a[i]; a[i] = a[n - i - 1]; a[n - i - 1] = swap;}}
    static void reverse(long[] a) {for (int i = 0, n = a.length, half = n / 2; i < half; ++i) {long swap = a[i]; a[i] = a[n - i - 1]; a[n - i - 1] = swap;}}
    static void reverse(double[] a) {for (int i = 0, n = a.length, half = n / 2; i < half; ++i) {double swap = a[i]; a[i] = a[n - i - 1]; a[n - i - 1] = swap;}}
    static void reverse(char[] a) {for (int i = 0, n = a.length, half = n / 2; i < half; ++i) {char swap = a[i]; a[i] = a[n - i - 1]; a[n - i - 1] = swap;}}
    static void shuffle(int[] a) {int n = a.length - 1; for (int i = 0; i < n; ++i) {int ind = randInt(i, n); int swap = a[i]; a[i] = a[ind]; a[ind] = swap;}}
    static void shuffle(long[] a) {int n = a.length - 1; for (int i = 0; i < n; ++i) {int ind = randInt(i, n); long swap = a[i]; a[i] = a[ind]; a[ind] = swap;}}
    static void shuffle(double[] a) {int n = a.length - 1; for (int i = 0; i < n; ++i) {int ind = randInt(i, n); double swap = a[i]; a[i] = a[ind]; a[ind] = swap;}}
    static void rsort(int[] a) {shuffle(a); sort(a);}
    static void rsort(long[] a) {shuffle(a); sort(a);}
    static void rsort(double[] a) {shuffle(a); sort(a);}
    static int[] copy(int[] a) {int[] ans = new int[a.length]; for (int i = 0; i < a.length; ++i) ans[i] = a[i]; return ans;}
    static long[] copy(long[] a) {long[] ans = new long[a.length]; for (int i = 0; i < a.length; ++i) ans[i] = a[i]; return ans;}
    static double[] copy(double[] a) {double[] ans = new double[a.length]; for (int i = 0; i < a.length; ++i) ans[i] = a[i]; return ans;}
    static char[] copy(char[] a) {char[] ans = new char[a.length]; for (int i = 0; i < a.length; ++i) ans[i] = a[i]; return ans;}
    // input
    static void r() throws IOException {input = new StringTokenizer(rline());}
    static int ri() throws IOException {return Integer.parseInt(rline());}
    static long rl() throws IOException {return Long.parseLong(rline());}
    static double rd() throws IOException {return Double.parseDouble(rline());}
    static int[] ria(int n) throws IOException {int[] a = new int[n]; r(); for (int i = 0; i < n; ++i) a[i] = ni(); return a;}
    static int[] riam1(int n) throws IOException {int[] a = new int[n]; r(); for (int i = 0; i < n; ++i) a[i] = ni() - 1; return a;}
    static long[] rla(int n) throws IOException {long[] a = new long[n]; r(); for (int i = 0; i < n; ++i) a[i] = nl(); return a;}
    static double[] rda(int n) throws IOException {double[] a = new double[n]; r(); for (int i = 0; i < n; ++i) a[i] = nd(); return a;}
    static char[] rcha() throws IOException {return rline().toCharArray();}
    static String rline() throws IOException {return __in.readLine();}
    static String n() {return input.nextToken();}
    static int rni() throws IOException {r(); return ni();}
    static int ni() {return Integer.parseInt(n());}
    static long rnl() throws IOException {r(); return nl();}
    static long nl() {return Long.parseLong(n());}
    static double rnd() throws IOException {r(); return nd();}
    static double nd() {return Double.parseDouble(n());}
    // output
    static void pr(int i) {__out.print(i);}
    static void prln(int i) {__out.println(i);}
    static void pr(long l) {__out.print(l);}
    static void prln(long l) {__out.println(l);}
    static void pr(double d) {__out.print(d);}
    static void prln(double d) {__out.println(d);}
    static void pr(char c) {__out.print(c);}
    static void prln(char c) {__out.println(c);}
    static void pr(char[] s) {__out.print(new String(s));}
    static void prln(char[] s) {__out.println(new String(s));}
    static void pr(String s) {__out.print(s);}
    static void prln(String s) {__out.println(s);}
    static void pr(Object o) {__out.print(o);}
    static void prln(Object o) {__out.println(o);}
    static void prln() {__out.println();}
    static void pryes() {prln("yes");}
    static void pry() {prln("Yes");}
    static void prY() {prln("YES");}
    static void prno() {prln("no");}
    static void prn() {prln("No");}
    static void prN() {prln("NO");}
    static boolean pryesno(boolean b) {prln(b ? "yes" : "no"); return b;};
    static boolean pryn(boolean b) {prln(b ? "Yes" : "No"); return b;}
    static boolean prYN(boolean b) {prln(b ? "YES" : "NO"); return b;}
    static void prln(int... a) {for (int i = 0, len = a.length - 1; i < len; pr(a[i]), pr(' '), ++i); if (a.length > 0) prln(a[a.length - 1]); else prln();}
    static void prln(long... a) {for (int i = 0, len = a.length - 1; i < len; pr(a[i]), pr(' '), ++i); if (a.length > 0) prln(a[a.length - 1]); else prln();}
    static void prln(double... a) {for (int i = 0, len = a.length - 1; i < len; pr(a[i]), pr(' '), ++i); if (a.length > 0) prln(a[a.length - 1]); else prln();}
    static <T> void prln(Collection<T> c) {int n = c.size() - 1; Iterator<T> iter = c.iterator(); for (int i = 0; i < n; pr(iter.next()), pr(' '), ++i); if (n >= 0) prln(iter.next()); else prln();}
    static void h() {prln("hlfd"); flush();}
    static void flush() {__out.flush();}
    static void close() {__out.close();}
}


import java.util.Scanner;

public class Main {

	private static boolean check(int n , int m , int k) {

		for (int i = 0;i < n;i ++) {
			for (int j = 0;j < n;j ++) {
				for (int l = 0;l < (1 << n);l ++) {
					dp[i][j][l] = - 1;
				}
			}
		}
		for (int i = 0;i < n;i ++) {
			if (dfs(i , i , n , m , k , 0)) {
				return true;
			}
		}
		return false;

	}

	private static boolean dfs(int first , int current , int n , int m , int k , int bitmap) {

		bitmap |= (1 << current);
		if (bitmap == (1 << n) - 1) {
			// check first and current
			if (n == 1) {
				if (m > 1) {
					if (rowMinDist[current] >= k) {
						return true;
					} else {
						return false;
					}
				} else {
					return true;
				}
			} else {
				if (m > 1) {
					if (minDistBetweenHeadAndTail[first][current] >= k) {
						return true;
					} else {
						return false;
					}
				} else {
					return true;
				}
			}
		} else {
			if (dp[first][current][bitmap] >= 0) {
				if (dp[first][current][bitmap] > 0) {
					return true;
				} else {
					return false;
				}
			}

			short ans = 0;
			for (int i = 0;i < n;i ++) {
				if ((bitmap & (1 << i)) == 0 && minDistBetweenRow[current][i] >= k) {
					if (dfs(first , i , n , m , k , bitmap)) {
						ans = 1;
						break;
					}
				}
			}

			dp[first][current][bitmap] = ans;
			if (ans > 0) {
				return true;
			} else {
				return false;
			}
		}

	}

	private static short[][][] dp = new short[20][20][(1 << 17)];
	private static int[][] input = new int[20][10010];
	private static int[][] minDistBetweenRow = new int[20][20];
	private static int[][] minDistBetweenHeadAndTail = new int[20][20];
	private static int[] rowMinDist = new int[20];

	public static void main(String[] args) {

		Scanner scan = new Scanner(System.in);

		int i , j , k , n , m;
		n = scan.nextInt();
		m = scan.nextInt();
		for (i = 0;i < n;i ++) {
			for (j = 0;j < m;j ++) {
				input[i][j] = scan.nextInt();
			}
		}
		for (i = 0;i < n;i ++) {
			for (j = i + 1;j < n;j ++) {
				int minDist = - 1;
				for (k = 0;k < m;k ++) {
					int dist = Math.abs(input[i][k] - input[j][k]);
					if (dist < minDist || minDist < 0) {
						minDist = dist;
					}
				}
				minDistBetweenRow[i][j] = minDistBetweenRow[j][i] = minDist;
			}
		}
		for (i = 0;i < n;i ++) {
			for (j = 0;j < n;j ++) {
				if (i != j) {
					// i head , j tail
					int minDist = - 1;
					for (k = 0;k < m - 1;k ++) {
						int dist = Math.abs(input[j][k] - input[i][k + 1]);
						if (dist < minDist || minDist < 0) {
							minDist = dist;
						}
					}
					minDistBetweenHeadAndTail[i][j] = minDist;
				}
			}
		}
		for (i = 0;i < n;i ++) {
			int minDist = - 1;
			for (j = 0;j < m - 1;j ++) {
				int dist = Math.abs(input[i][j] - input[i][j + 1]);
				if (dist < minDist || minDist < 0) {
					minDist = dist;
				}
			}
			rowMinDist[i] = minDist;
		}
		int low = 0 , high = 1000000010;
		while (low < high) {
			int mid = (low + high) / 2;
			if (check(n , m , mid)) {
				low = mid + 1;
			} else {
				high = mid;
			}
		}
		System.out.println(high - 1);

	}

}


// Don't place your source in a package
import java.lang.reflect.Array;
import java.text.DecimalFormat;
import java.util.*;
import java.lang.*;
import java.io.*;
import java.math.*;

// Please name your class Main
public class Main {
    static FastScanner fs=new FastScanner();
    static class FastScanner {//scanner from SecondThread
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st=new StringTokenizer("");
        public String next() {
            while (!st.hasMoreElements())
                try {
                    st=new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            return st.nextToken();
        }
        int Int() {
            return Integer.parseInt(next());
        }

        long Long() {
            return Long.parseLong(next());
        }

        String Str(){
            return next();
        }
    }

    public static void main (String[] args) throws java.lang.Exception {
        PrintWriter out = new PrintWriter(System.out);

        int T=1;
        for(int t=0;t<T;t++){
            int n=Int();int m=Int();
            int A[][]=new int[n][m];

            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    A[i][j]=Int();
                }
            }

            Sol sol=new Sol();
            sol.solution(out,A);
        }
        out.flush();

    }

    public static int Int(){
        return fs.Int();
    }
    public static long Long(){
        return fs.Long();
    }
    public static String Str(){
        return fs.Str();
    }

}

class Sol{
    int dif[][];
    int dp[][][];
    public void solution(PrintWriter out,int A[][]){
        int n=A.length;int m=A[0].length;
        int res=0;
        dif=new int[n][n];
        for(int i=0;i<n;i++){
            for(int j=i+1;j<A.length;j++){
                int mn=Integer.MAX_VALUE;
                for(int k=0;k<m;k++){//different ordering pair
                    mn=Math.min(mn,Math.abs(A[i][k]-A[j][k]));
                }
                dif[i][j]=mn;
                dif[j][i]=mn;
                //System.out.println(i+"  "+j+"  "+mn);
            }
        }

        int state=(1<<n)-1;
        dp=new int[state+5][n+1][n+1];
        for(int i=0;i<dp.length;i++){
            for(int j=0;j<dp[0].length;j++){
                Arrays.fill(dp[i][j],-1);
            }
        }

        for(int i=0;i<n;i++){
            res=Math.max(res,dfs(A,state^(1<<i),i,i));
        }

        out.println(res);

    }

    public int dfs(int A[][],int state,int pre,int start){

        if(state==0){
            int mn=Integer.MAX_VALUE;
            for(int i=1;i<A[0].length;i++){
                mn=Math.min(mn,Math.abs(A[start][i]-A[pre][i-1]));
            }
            return mn;
        }

        if(dp[state][pre][start]!=-1){
            return dp[state][pre][start];
        }

        int res=0;
        for(int i=0;i<A.length;i++){
            if((state&(1<<i))!=0){
                int di=dif[pre][i];
                res=Math.max(res,Math.min(di,dfs(A,state^(1<<i),i,start)));
            }
        }

        //System.out.println(Integer.toBinaryString(state)+"  "+res);
        dp[state][pre][start]=res;
        return res;
    }

}


import java.io.*;
import java.util.*;

public class f{

   public static void main(String[] args) {
      MyScanner sc = new MyScanner();
      out = new PrintWriter(new BufferedOutputStream(System.out));

      int n = sc.nextInt();
      int m = sc.nextInt();
      int[][] arr = new int[n][m];
      for(int i=0; i<n; i++) {
          for(int j=0; j<m; j++) {
              arr[i][j] = sc.nextInt();
          }
      }
      if(n==1) {
          int min = Integer.MAX_VALUE;
          for(int i=0; i<m-1; i++) {
              min = Math.min(min, Math.abs(arr[0][i]-arr[0][i+1]));
          }
          out.println(min);
          out.close();
      }
      int[][] adj = new int[n][n];
      int[][] edgeadj = new int[n][n];
      for(int i=0; i<n; i++) {
          for(int j=i+1; j<n; j++) {
              int min = Integer.MAX_VALUE;
              for(int k=0; k<m; k++) {
                  min = Math.min(min, Math.abs(arr[i][k]-arr[j][k]));
              }
              adj[i][j]=min;
              adj[j][i]=min;
              int min1 = Integer.MAX_VALUE;
              int min2 = Integer.MAX_VALUE;
              for(int k=0; k<m-1; k++) {
                  min1 = Math.min(min1, Math.abs(arr[i][k]-arr[j][k+1]));
                  min2 = Math.min(min2, Math.abs(arr[i][k+1]-arr[j][k]));
              }
              edgeadj[i][j]=min1;
              edgeadj[j][i]=min2;
          }
      }
      int power = (int)Math.pow(2,n);
      int[][][] dp = new int[power][n][n];
      for(int i=0; i<n; i++) {
          dp[(int)Math.pow(2,i)][i][i] = Integer.MAX_VALUE;
      }
      for(int bit=0; bit<power; bit++) {
          for(int j=0; j<n; j++) {
              for(int k=0; k<n; k++) {
                  if((bit & (1<<j))>0 && (bit & (1<<k))>0 && j!=k) {
                      int temp = bit;
                      temp &= ~(1<<k);
                      int ans = 0;
                      for(int l=0; l<n; l++) {
                          if((temp & (1<<l))>0) {
                              int min = Math.min(dp[temp][j][l], adj[l][k]);
                              ans = Math.max(ans, min);
                          }
                      }
                      if(j!=k) {
                          dp[bit][j][k] = ans;
                      }
                    // out.println(bit + " " + j + " " + k + " " + dp[bit][j][k]);
                  }
              }
          }
      }
      int answer = 0;
      for(int i=0; i<n; i++) {
          for(int j=0; j<n; j++) {
              if(i!=j) {
                int ans = Math.min(dp[power-1][i][j], edgeadj[i][j]);
                answer = Math.max(answer, ans);
              }
          }
      }
    //   for(int i=0; i<n; i++) {
    //       for(int j=0; j<n; j++) {
    //           out.println(dp[power-1][i][j]+" "+edgeadj[i][j]);
    //       }
    //   }
      out.println(answer);

      // Start writing your solution here. -------------------------------------

      /*
      int n      = sc.nextInt();        // read input as integer
      long k     = sc.nextLong();       // read input as long
      double d   = sc.nextDouble();     // read input as double
      String str = sc.next();           // read input as String
      String s   = sc.nextLine();       // read whole line as String

      int result = 3*n;
      out.println(result);                    // print via PrintWriter
      */

      // Stop writing your solution here. -------------------------------------
      out.close();
   }

   //-----------PrintWriter for faster output---------------------------------
   public static PrintWriter out;

   //-----------MyScanner class for faster input----------
   public static class MyScanner {
      BufferedReader br;
      StringTokenizer st;

      public MyScanner() {
         br = new BufferedReader(new InputStreamReader(System.in));
      }

      String next() {
          while (st == null || !st.hasMoreElements()) {
              try {
                  st = new StringTokenizer(br.readLine());
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          return st.nextToken();
      }

      int nextInt() {
          return Integer.parseInt(next());
      }

      long nextLong() {
          return Long.parseLong(next());
      }

      double nextDouble() {
          return Double.parseDouble(next());
      }

      String nextLine(){
          String str = "";
	  try {
	     str = br.readLine();
	  } catch (IOException e) {
	     e.printStackTrace();
	  }
	  return str;
      }

   }
   //--------------------------------------------------------
}


import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class A {
    static MyScanner sc;
    static PrintWriter pw;

    public static void main(String[] args) throws Throwable {
        sc = new MyScanner();
        pw = new PrintWriter(System.out);

        n = sc.nextInt();
        int m = sc.nextInt();
        int[][] a = new int[n][m];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                a[i][j] = sc.nextInt();
        val = new int[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(val[i], Integer.MAX_VALUE);
        for (int i = 0; i < n; i++)
            for (int j = i; j < n; j++) {
                for (int k = 0; k < m; k++)
                    val[i][j] = val[j][i] = Math.min(val[i][j], Math.abs(a[i][k] - a[j][k]));
            }

        val2 = new int[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(val2[i], Integer.MAX_VALUE);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < m - 1; k++)
                    val2[i][j] = Math.min(val2[i][j], Math.abs(a[i][k] - a[j][k + 1]));
            }
        mem = new Integer[n][n][1 << n];
        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans = Math.max(ans, dp(i, i, 1 << i));
        }
        if (n == 1)
            pw.println(val2[0][0]);
        else
            pw.println(ans);

        pw.flush();
        pw.close();
    }

    static int n;
    static int[][] val, val2;
    static Integer[][][] mem;

    static int dp(int st, int lst, int msk) {
        int bits = Integer.bitCount(msk);
        if (mem[st][lst][msk] != null)
            return mem[st][lst][msk];
        int ans = 0;
        for (int i = 0; i < n; i++)
            if ((msk & (1 << i)) == 0) {
                int newMsk = (msk | (1 << i));
                if (bits < n - 1)
                    ans = Math.max(ans, Math.min(val[lst][i], dp(st, i, newMsk)));
                else
                    ans = Math.max(ans, Math.min(val[lst][i], val2[i][st]));
            }
        return mem[st][lst][msk] = ans;
    }

    static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        public MyScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
}