#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <set>
#include <queue>
#include <deque>
using namespace std;
ifstream f("matrice2.in");
ofstream g("matrice2.out");
#define nmax (301*301)
#define ll long long
#define Ceva 17
#define inf (1<<29)
const int dx[] = {0, -1, 0, 1};
const int dy[] = {-1, 0, 1, 0};
const int h = 200;
int n, m, t[nmax],nivel[nmax];
bool viz[nmax];
int rezMin[Ceva][nmax], cine[Ceva][nmax];
int t2[nmax];
vector< pair<int,int> > gf[nmax];
int cnt;
pair< pair<int,int>, int> v[nmax*2];
int a[3][301];
void citeste(){
f >> n >> m;
int k = 0;
for(int i=1; i<=n; ++i){
int linie = 0;
if (i & 1) linie = 1;
for(int j=1; j<=n; ++j){
++k;
f >> a[linie][j];
//fac muchiile
if (k - n > 0) v[++cnt] = make_pair( make_pair(k, k-n), min(a[linie][j], a[1-linie][j]) );
if (j - 1 > 0) v[++cnt] = make_pair( make_pair(k, k-1), min(a[linie][j], a[linie][j-1]) );
}
}
}
void transforma(int x1, int y1, int x2, int y2, int &i, int &j){
i = (n * x1) - (n - y1);
j = (n * x2) - (n - y2);
}
inline bool check(int x, int y){
if (x > 0 && y > 0 && x <= n && y <= n) return 1;
return 0;
}
void fa_graf(){
for(int i=1; i<=n; ++i){
for(int j=1; j<=n; ++j){
for(int k=0; k<4; ++k){
int new_i = i + dx[k];
int new_j = j + dy[k];
if (check(new_i, new_j) ){
int x = 0; int y = 0;
transforma( i, j, new_i, new_j, x, y );
v[++cnt] = make_pair( make_pair(x, y), min(a[i][j], a[new_i][new_j]) );
}
}
}
}
}
bool cmp( pair< pair<int,int>, int> a, pair< pair<int,int>, int > b){
return (a.second > b.second);
}
int afla(int x){
int cpy = x;
for(; t[x] != 0; x=t[x]);
while(cpy != x){
int unde = t[cpy];
t[cpy] = x;
cpy = unde;
}
return x;
}
void uneste(int x, int y){
t[x] = y;
}
void fa_apm(){
//fac apm de cost maxim
sort(v+1, v+cnt+1, cmp);
for(int i=1; i<=cnt; ++i){
int x = v[i].first.first;
int y = v[i].first.second;
int c = v[i].second;
int radx = afla(x); int rady = afla(y);
if (radx == rady) continue;// sunt in aceeasi componenta
uneste(radx, rady);
//cout << x << " " << y << " " << c << "\n";
gf[x].push_back( make_pair(y, c) );
gf[y].push_back( make_pair(x, c) );
}
}
void init(int tata, int nod, int cost){
rezMin[0][nod] = cost;
cine[0][nod] = tata;// cien ii al 2 ^ 0 tata pt nod
}
void dfs(int nod, int n2, int niv){
viz[nod] = 1;
t2[nod] = n2;
if (niv % h == 0) n2 = nod;
nivel[nod] = niv;
for(int i=0; i<gf[nod].size(); ++i){
int vc = gf[nod][i].first;
if (viz[vc] == 0){
init(nod, vc, gf[nod][i].second);//initializez rmq-ul pt muchia minima
t[vc] = nod;
dfs(vc, n2, niv+1);
}
}
}
void fa_muchie(){
//acum aflu muchia minima
for(int i=1; i<17; ++i){
int put = (1<<i);
for(int j=1; j<=n*n; ++j){
int new_x = cine[i-1][j];
cine[i][j] = cine[i-1][new_x];
rezMin[i][j] = min(rezMin[i-1][j], rezMin[i-1][new_x]);
}
}
}
void afla_lca(int &Lca, int x, int y){
while(t2[x] != t2[y]){
if (nivel[x] > nivel[y]){
x = t2[x];
}else y = t2[y];
}
while(x != y){
if (nivel[x]>nivel[y]){
x = t[x];
}else y = t[y];
}
Lca = x;
}
void afla_cost(int nod, int lca, int &cost){
int k = nivel[nod] - nivel[lca];// al cate-lea tata e lca
//cout << " K: " << k << " " << nod << " " << lca << "\n";
for(int i=17; i>=0; --i){
if ( ( k & (1<<i) ) != 0 ){
cost = min(cost, rezMin[i][nod]);
nod = cine[i][nod];
}
}
}
void rezolva(){
//fa_graf();
fa_apm();
for(int i=0; i<nmax; ++i) t[i] = 0;//numai am nevoie de ei; o sa am nevoie la muchia minima
//initializez pt muchia minima
for(int i=0; i<17; ++i){
for(int j=0; j<nmax; ++j) rezMin[i][j] = inf;
}
dfs(1,0,1);
//acum aflu muchia minima de pe drumul x-lca, y-lca
fa_muchie();
//acum raspund la query-uri
for(int i=1; i<=m; ++i){
int x1, y1, x2, y2;
f >> x1 >> y1 >> x2 >> y2;
int X = 0; int Y = 0;
transforma(x1, y1, x2, y2, X, Y);
//aflu lca-ul dintre X si Y;
int Lca = 0;
afla_lca(Lca, X, Y);
int cost_min = inf;
afla_cost(X, Lca, cost_min);
afla_cost(Y, Lca, cost_min);
//cout << X << " " << Y << " " << Lca << " " << nivel[X] << " " << nivel[Lca] << " " << nivel[Y]<< "\n";
cout << cost_min << "\n";
g << cost_min << "\n";
}
}
int main(){
citeste();
rezolva();
f.close();
g.close();
return 0;
}