#include <fstream>
#include <queue>
#include <cstring>
using namespace std;
ifstream f ("matrice2.in");
ofstream g ("matrice2.out");
const int inf = 1999999;
bool used[305][305];
int n, m, maxim, minim, a[305][305];
int di[4] = {1, 0, -1, 0};
int dj[4] = {0, 1, 0, -1};
queue < pair < int, int > > coada;
void read ()
{
int i, j;
f >> n >> m;
maxim = -inf, minim = inf;
for (i=1; i<=n; i++)
{
for (j=1; j<=n; j++)
{
f >> a[i][j];
maxim = max(maxim, a[i][j]);
minim = min(minim, a[i][j]);
}
}
}
bool ok (int i, int j)
{
if (i<1 || j<1 || i>n || j>n)
return false;
return true;
}
bool check (int x, int y, int xx, int yy, int val)
{
int i, j, i_nou, j_nou, k;
if (a[x][y] < val || a[xx][yy] < val)
return false;
memset(used, 0, sizeof(used));
coada.push(make_pair(x, y));
while (!coada.empty())
{
i = coada.front().first;
j = coada.front().second;
coada.pop();
for (k=0; k<=3; k++)
{
i_nou = i + di[k];
j_nou = j + dj[k];
if (ok(i_nou, j_nou))
{
if (a[i_nou][j_nou] >= val && !used[i_nou][j_nou])
{
used[i_nou][j_nou] = 1;
coada.push(make_pair(i_nou, j_nou));
}
}
}
}
if (used[xx][yy])
return true;
return false;
}
void querry (int x, int y, int xx, int yy)
{
int st = minim, dr = maxim, mij, rez;
while (st <= dr)
{
mij = (st + dr) / 2;
if (check(x, y, xx, yy, mij))
{
rez = mij;
st = mij + 1;
}
else
dr = mij - 1;
}
g << rez << '\n';
}
int main()
{
read();
int i, x, y, xx, yy;
for (i=1; i<=m; i++)
{
f >> x >> y >> xx >> yy;
querry(x, y, xx, yy);
}
return 0;
}