#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#define MAX_Q 250000
#define MAX_TREE 32768
#define MAX_U 22000
#define NIL -1
#define ll long long
/** Algoritmul lui Euclid. **/
ll int gcd(ll int a, ll int b) {
return b ? gcd(b, a % b) : a;
}
ll int join(ll int l, ll int r) {
ll int val;
if (l == 0) {
val = r;
} else if (r == 0) {
val = l;
} else {
val = gcd(l, r);
}
return val;
}
/** Structura pentru o celula. **/
struct pair {
int l, c;
pair() {
}
pair(int l, int c) {
this -> l = l;
this -> c = c;
}
bool operator == (const pair &other) const {
return this -> l == other.l && this -> c == other.c;
}
bool operator != (const pair &other) const {
return !(*this == other);
}
bool operator < (const pair &other) const {
return this -> c < other.c || (this -> c == other.c && this -> l < other.l);
}
bool operator <= (const pair &other) const {
return *this < other || *this == other;
}
};
inline int get(int x) {
return 1 << ((int)log2(x) + 1);
}
/** Nodul din arborele de intervale principal. **/
class segTree {
public:
int n, shl;
pair *sorted;
ll int *tree;
ll int local;
segTree() {
}
/** Umple arborele de intervale cu "1". **/
void fill(int u, int left, int right) {
this -> tree[u] = 1;
if (left != right) {
int mid = (left + right) / 2;
this -> fill(2 * u, left, mid);
this -> fill(2 * u + 1, mid + 1, right);
}
}
/** Initializarea arborelui cu ajutorul vectorului sortat "take". **/
void init(const int l, const int *take) {
this -> n = take[0];
this -> shl = get(this -> n);
this -> sorted = (pair*)calloc(this -> n, sizeof(pair));
this -> tree = (ll int*)calloc(2 * get(this -> n) + 1, sizeof(ll int));
int i;
for (i = 0; i < this -> n; i++) {
this -> sorted[i] = pair(l, take[i + 1]);
}
}
/** Cautare binara cu proprietatea "set". **/
int find(pair search, int set) {
int pas = this -> shl;
int x = -1;
while (pas) {
if (x + pas < this -> n && this -> sorted[x + pas] <= search) {
x += pas;
}
pas >>= 1;
}
return x + (this -> sorted[x] != search) * set;
}
/** Update normal. **/
void update(int u, int left, int right, const int pos, const ll int val) {
if (left == right) {
this -> tree[u] = val;
return;
}
int mid = (left + right) / 2;
if (pos <= mid) {
update(2 * u, left, mid, pos, val);
} else {
update(2 * u + 1, mid + 1, right, pos, val);
}
this -> tree[u] = join(this -> tree[2 * u], this -> tree[2 * u + 1]);
}
/** Query normal. **/
void query(int u, int left, int right, int a, int b) {
if (a <= left && right <= b) {
if (local == NIL) {
local = this -> tree[u];
} else {
local = join(local, this -> tree[u]);
}
return;
}
int mid = (left + right) / 2;
if (a <= mid) {
query(2 * u, left, mid, a, b);
}
if (b > mid) {
query(2 * u + 1, mid + 1, right, a, b);
}
}
public:
void update(int l, int c, ll int val) {
update(1, 0, this -> n - 1, find(pair(l, c), 0), val);
}
ll int query(int a, int b) {
local = NIL;
if (this -> n) {
query(1, 0, this -> n - 1, find(pair(NIL, a), 1), find(pair(INT_MAX, b), 0));
} else {
local = 0;
}
return local;
}
/** Uneste doua noduri in "result". **/
segTree operator + (const segTree &other) {
int i = 0, j = 0, k = 0;
segTree result;
result.n = this -> n + other.n;
result.shl = get(result.n);
result.sorted = (pair*)calloc(result.n, sizeof(pair));
result.tree = (ll int*)calloc(2 * get(result.n) + 1, sizeof(ll int));
while (i < this -> n && j < other.n) {
if (this -> sorted[i] < other.sorted[j]) {
result.sorted[k++] = this -> sorted[i++];
} else {
result.sorted[k++] = other.sorted[j++];
}
}
while (i < this -> n) {
result.sorted[k++] = this -> sorted[i++];
}
while (j < other.n) {
result.sorted[k++] = other.sorted[j++];
}
return result;
}
};
struct save {
char task;
int a, b, c, d;
ll int val;
};
int N, R, C;
ll int answer, now;
segTree tree[2 * MAX_TREE + 1];
save query[MAX_Q + MAX_U + 2];
int size, shl;
int normalize[MAX_U];
int **list;
int count[MAX_U];
/** quick-sort. **/
template <class data> void sort(data *val, int begin, int end) {
int b = begin, e = end;
data tmp, pivot = val[(b + e) / 2];
while (b <= e) {
while (val[b] < pivot) {
b++;
}
while (pivot < val[e]) {
e--;
}
if (b <= e) {
tmp = val[b];
val[b++] = val[e];
val[e--] = tmp;
}
}
if (begin < e) {
sort(val, begin, e);
}
if (b < end) {
sort(val, b, end);
}
}
/** Elimina duplicatele. **/
void peek() {
int i, buff = 1;
for (i = 1; i < size; i++) {
if (normalize[i] != normalize[buff - 1]) {
normalize[buff++] = normalize[i];
}
}
size = buff;
}
/** Cautare binara cu proprietate. **/
int binSearch(int val, int set) {
int pas = shl;
int x = -1;
while (pas) {
if (x + pas < size && normalize[x + pas] <= val) {
x += pas;
}
pas >>= 1;
}
return x + (val != normalize[x]) * set;
}
/** Construieste blocurile de memorie. **/
void alloc() {
int i;
list = (int**)calloc(size, sizeof(int*));
for (i = 0; i < size; i++) {
list[i] = (int*)calloc(count[i] + 1, sizeof(int));
}
for (i = 0; i < N; i++) {
if (query[i].task == 1) {
list[query[i].a][++list[query[i].a][0]] = query[i].b;
}
}
for (i = 0; i < size; i++) {
if (list[i][0] > 1) {
sort(list[i], 1, list[i][0]);
}
}
}
void build(int u, int left, int right) {
if (left == right) {
tree[u].init(left, list[left]);
return;
}
int mid = (left + right) / 2;
build(2 * u, left, mid);
build(2 * u + 1, mid + 1, right);
tree[u] = tree[2 * u] + tree[2 * u + 1];
}
void update(int u, int left, int right, const int l, const int c, const ll int val) {
tree[u].update(l, c, val);
if (left == right) {
return;
}
int mid = (left + right) / 2;
if (l <= mid) {
update(2 * u, left, mid, l, c, val);
} else {
update(2 * u + 1, mid + 1, right, l, c, val);
}
}
void ask(int u, int left, int right, const int l, const int l0, const int c, const int c0) {
if (l <= left && right <= l0) {
now = tree[u].query(c, c0);
answer = (answer == NIL ? now : join(answer, now));
return;
}
int mid = (left + right) / 2;
if (l <= mid) {
ask(2 * u, left, mid, l, l0, c, c0);
}
if (l0 > mid) {
ask(2 * u + 1, mid + 1, right, l, l0, c, c0);
}
}
int main(void) {
int i;
FILE *f = fopen("game.in", "r");
freopen("game.out", "w", stdout);
fscanf(f, "%d %d %d", &R, &C, &N);
for (i = 0; i < N + 2; i++) {
if (i == N) {
query[i].task = 1;
query[i].a = NIL;
query[i].b = NIL;
} else if (i == N + 1) {
query[i].task = 1;
query[i].a = R + 1;
query[i].b = C + 1;
} else {
fscanf(f, "%d", &query[i].task);
if (query[i].task == 1) {
fscanf(f, "%d %d %lld", &query[i].a, &query[i].b, &query[i].val);
} else {
fscanf(f, "%d %d %d %d", &query[i].a, &query[i].b, &query[i].c, &query[i].d);
}
}
if (query[i].task == 1) {
normalize[size++] = query[i].a;
}
}
N += 2;
fclose(f);
/** Sorteaza si scoate dublurile. **/
sort(normalize, 0, size - 1);
peek();
/** Construieste "shl". **/
shl = get(size);
/** Normalizeaza intrebarile. **/
for (i = 0; i < N; i++) {
if (query[i].task == 1) {
query[i].a = binSearch(query[i].a, 0);
count[query[i].a]++;
} else {
query[i].a = binSearch(query[i].a, 1);
query[i].c = binSearch(query[i].c, 0);
}
}
/** Construieste liniile. **/
alloc();
build(1, 0, size - 1);
/** Raspunde la intrebari. **/
for (i = 0; i < N; i++) {
if (query[i].task == 1) {
update(1, 0, size - 1, query[i].a, query[i].b, query[i].val);
} else {
if (query[i].a <= query[i].c) {
answer = NIL;
ask(1, 0, size - 1, query[i].a, query[i].c, query[i].b, query[i].d);
} else {
answer = 0;
}
fprintf(stdout, "%lld\n", answer);
}
}
fclose(stdout);
return 0;
}