using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("4 4");
WillReturn.Add("1 12 6 11");
WillReturn.Add("11 10 2 14");
WillReturn.Add("10 1 9 20");
WillReturn.Add("4 17 19 10");
//11
}
else if (InputPattern == "Input2") {
WillReturn.Add("8 6");
WillReturn.Add("23 23 10 11 16 21");
WillReturn.Add("15 26 19 28 19 20");
WillReturn.Add("25 26 28 16 15 11");
WillReturn.Add("11 8 19 11 15 24");
WillReturn.Add("14 19 15 14 24 11");
WillReturn.Add("10 8 11 7 6 14");
WillReturn.Add("23 5 19 23 17 17");
WillReturn.Add("18 11 21 14 20 16");
//18
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static int[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => int.Parse(pX)).ToArray();
}
static int mMinVal;
static int mMaxVal;
static void Main()
{
List<string> InputList = GetInputList();
int[,] BanArr1 = CreateBanArr(InputList.Skip(1));
mMinVal = BanArr1.Cast<int>().Min();
mMaxVal = BanArr1.Cast<int>().Max();
// 0が達成可能な場合
if (BanArr1.Cast<int>().Distinct().Count() == 0) {
Console.WriteLine(0);
return;
}
int[,] BanArr2 = Kaiten90do<int>(BanArr1);
int[,] BanArr3 = Kaiten90do<int>(BanArr2);
int[,] BanArr4 = Kaiten90do<int>(BanArr3);
int L = 0;
int R = mMaxVal - mMinVal;
while (L + 1 < R) {
int Mid = (L + R) / 2;
if (CanAchieve(BanArr1, Mid) ||
CanAchieve(BanArr2, Mid) ||
CanAchieve(BanArr3, Mid) ||
CanAchieve(BanArr4, Mid)) {
R = Mid;
}
else {
L = Mid;
}
}
Console.WriteLine(R);
}
// MinとMaxとの差を、X以下にできるかを返す
static bool CanAchieve(int[,] pBanArr, int pX)
{
int UB_X = pBanArr.GetUpperBound(0);
int UB_Y = pBanArr.GetUpperBound(1);
int ValMax = mMinVal + pX;
// どこまでX座標を見たか[Y座標]
var XSetDict = new Dictionary<int, int?>();
for (int Y = 0; Y <= UB_Y; Y++) {
XSetDict[Y] = null;
}
int XLimit = UB_X;
for (int Y = 0; Y <= UB_Y; Y++) {
for (int X = 0; X <= XLimit; X++) {
if (pBanArr[X, Y] > ValMax) {
XLimit = X - 1;
break;
}
else {
XSetDict[Y] = X;
}
}
}
// 残った座標のチェック
int ValMin = mMaxVal - pX;
for (int Y = 0; Y <= UB_Y; Y++) {
int StaX = 0;
if (XSetDict[Y].HasValue) {
StaX = XSetDict[Y].Value + 1;
}
for (int X = StaX; X <= UB_X; X++) {
if (pBanArr[X, Y] < ValMin) {
return false;
}
}
}
return true;
}
// 右に90度回転させた盤面を返す
static Type[,] Kaiten90do<Type>(Type[,] pBanArr)
{
Type[,] WillReturn = new Type[pBanArr.GetUpperBound(1) + 1,
pBanArr.GetUpperBound(0) + 1];
for (int X = 0; X <= WillReturn.GetUpperBound(0); X++) {
for (int Y = 0; Y <= WillReturn.GetUpperBound(1); Y++) {
WillReturn[X, Y] = pBanArr[Y, WillReturn.GetUpperBound(0) - X];
}
}
return WillReturn;
}
////////////////////////////////////////////////////////////////
// IEnumerable<string>をintの2次元配列に設定
////////////////////////////////////////////////////////////////
static int[,] CreateBanArr(IEnumerable<string> pStrEnum)
{
var StrList = new List<string>(pStrEnum);
if (StrList.Count == 0) {
return new int[0, 0];
}
int[] IntArr = { };
Action<string> SplitAct = pStr =>
IntArr = pStr.Split(' ').Select(pX => int.Parse(pX)).ToArray();
SplitAct(StrList[0]);
int UB_X = IntArr.GetUpperBound(0);
int UB_Y = StrList.Count - 1;
int[,] WillReturn = new int[UB_X + 1, UB_Y + 1];
for (int Y = 0; Y <= UB_Y; Y++) {
SplitAct(StrList[Y]);
for (long X = 0; X <= UB_X; X++) {
WillReturn[X, Y] = IntArr[X];
}
}
return WillReturn;
}
}