#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
typedef long long ll;
using namespace __gnu_pbds;
using namespace std;
template <typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
struct Query {
int a, b, c, d, f, s;
};
struct DSU {
vector<int> rep, sajz;
vector<pair<int, int>> mx;
DSU(int n) {
rep.resize(n+1);
sajz.assign(n+1,1);
mx.assign(n+1, {});
for(int i=0; i<=n; ++i) rep[i] = i;
}
void ustaw(int x, int val) {
mx[x] = {val, x};
}
int Find(int v) {
return rep[v] == v ? v : rep[v] = Find(rep[v]);
}
bool Union(int a, int b) {
a=Find(a);
b=Find(b);
if(a==b) return 0;
if(sajz[b] > sajz[a]) swap(a,b);
rep[b] = a;
sajz[a] += sajz[b];
mx[a] = max(mx[a], mx[b]);
return 1;
}
};
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int h,w,L;
cin >> h >> w >> L;
vector<vector<int>> T(h, vector<int>(w));
for(int i=0; i<h; ++i) {
for(int j=0; j<w; ++j) cin >> T[i][j];
}
auto trans = [&](int a, int b) { return a*w + b; };
auto tb = [&](int x) { return make_pair(x/w, x%w); };
int q; cin >> q;
vector<Query> zap(q);
for(int i=0; i<q; ++i) {
cin >> zap[i].a >> zap[i].b >> zap[i].c >> zap[i].d;
zap[i].a--; zap[i].b--; zap[i].c--; zap[i].d--;
zap[i].f = trans(zap[i].a, zap[i].b);
zap[i].s = trans(zap[i].c, zap[i].d);
}
int n = w*h + 10;
DSU mst(n), dsu(n), tree(n);
map<int, vector<int>> mapa;
for(int i=0; i<h; ++i) {
for(int j=0; j<w; ++j) {
mapa[T[i][j]].push_back(trans(i,j));
}
}
auto proba = [&](int x, int a, int b, DSU &D) {
auto[c,d] = tb(x);
if(a < 0 || a >= h || b < 0 || b >= w || T[a][b] > T[c][d]) return;
D.Union(x, trans(a,b));
};
auto connect = [&](int x, DSU &D) {
auto[a,b] = tb(x);
proba(x, a+1, b, D);
proba(x, a-1, b, D);
proba(x, a, b+1, D);
proba(x, a, b-1, D);
};
for(auto[t, vec] : mapa) {
for(auto x : vec) connect(x, mst);
map<int, int> prio;
for(auto x : vec) {
if(prio.find(mst.Find(x)) == prio.end()) prio[mst.Find(x)] = x;
else dsu.Union(x, prio[mst.Find(x)]);
}
}
vector<int> edge(n);
vector<int> heights;
for(int i=0; i<h; ++i) for(int j=0; j<w; ++j) heights.push_back(T[i][j]);
sort(heights.begin(), heights.end());
map<int, vector<int>> check;
DSU mxdsu(n);
for(int i=0; i<h; ++i) for(int j=0; j<w; ++j) {
mxdsu.ustaw(trans(i,j), T[i][j]);
auto it = upper_bound(heights.begin(), heights.end(), T[i][j] + L);
if(it==heights.end()) edge[trans(i,j)] = trans(i,j);
else check[*it].push_back(trans(i,j));
}
for(auto[t, vec] : mapa) {
for(auto x : check[t]) edge[x] = mxdsu.mx[mxdsu.Find(x)].second;
for(auto x : vec) connect(x, mxdsu);
}
for(auto &x : edge) x = dsu.Find(x);
for(int i=0; i<h; ++i) for(int j=0; j<w; ++j) {
auto[a,b] = tb(dsu.Find(trans(i,j)));
// cout << "(" << i << " " << j << "): " << a << " " << b << "\n";
auto[c,d] = tb(edge[trans(i,j)]);
// cout << "(" << i << " " << j << "): " << c << " " << d << "\n";
}
;vector<int> depth(n);
for(auto it=mapa.rbegin(); it!=mapa.rend(); ++it) {
for(auto x : it->second) {
if(dsu.Find(x) != x) continue;
tree.Union(x, edge[x]);
depth[x] = depth[edge[x]] + 1;
}
}
vector<int> lo(q,0), hi(q, (int)mapa.size() - 1), mid(q);
while(1) {
int ile = 0;
vector<vector<int>> sweep(mapa.size());
for(int x=0; x<q; ++x) {
if(lo[x] < hi[x]) {
ile++;
mid[x] = (lo[x] + hi[x]) / 2;
sweep[mid[x]].push_back(x);
}
}
if(!ile) break;
DSU dsu2(n);
for(int i=0; i<h; ++i) for(int j=0; j<w; ++j) dsu2.ustaw(trans(i,j), T[i][j]);
int idx = 0;
for(auto[t, vec] : mapa) {
for(auto x : vec) connect(x, dsu2);
for(auto x : sweep[idx]) {
if(dsu2.Find(zap[x].f) == dsu2.Find(zap[x].s)) hi[x] = mid[x];
else lo[x] = mid[x] + 1;
}
idx++;
}
}
vector<vector<int>> sweep(mapa.size());
vector<int> target(q);
for(int x=0; x<q; ++x) sweep[lo[x]].push_back(x);
DSU dsu2(n);
for(int i=0; i<h; ++i) for(int j=0; j<w; ++j) dsu2.ustaw(trans(i,j), T[i][j]);
int idx = 0;
for(auto[t, vec] : mapa) {
for(auto x : vec) connect(x, dsu2);
for(auto i : sweep[idx]) target[i] = dsu.Find(dsu2.mx[dsu2.Find(zap[i].f)].second);
idx++;
}
for(int i=0; i<q; ++i) {
if(tree.Find(zap[i].f) != tree.Find(target[i])) cout << "-1\n";
else cout << max(1, depth[dsu.Find(zap[i].f)] - depth[target[i]]) << "\n";
}
return 0;
}