#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], d[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[nmax];
set< pair<int,int> > S;
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[i][j];
f >> a[k];
//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){
if (x > 0 && x <= n*n) return 1;
return 0;
}
void fa_apm(){
//fac apm de cost maxim
int N = n*n;
for(int i=2; i<nmax; ++i) d[i] = 0;
S.insert( make_pair(inf, 1) );
const int dnod[] = {-1, -n, n, 1};
for(; S.size(); ){
set<pair<int,int> > ::iterator it;
it = S.end();
--it;
int nod = (*it).second;
S.erase(it);
if (viz[nod] == 1) continue;
viz[nod] = 1;
for(int k=0; k<4; ++k){
int new_nod = nod + dnod[k];
if (check(new_nod) && d[new_nod] < min(a[nod], a[new_nod]) && viz[new_nod] == 0){
d[new_nod] = min(a[nod], a[new_nod]);
t[new_nod] = nod;
S.insert( make_pair( d[new_nod], new_nod));
}
}
}
for(int i=2; i<=n*n; ++i){
//cout << i << " " << t[i] << " " << d[i] << "\n";
gf[i].push_back( make_pair(t[i], d[i]));
gf[ t[i] ].push_back( make_pair(i, d[i]));
}
}
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){
//cout << nod << "\n";
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;
//cout << vc << "\n";
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) viz[i] = 0,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;
}