#include <bits/stdc++.h>
using namespace std;
class SegmentTree {
 public:
  vector<int> A, st;
  int n;
  SegmentTree(vector<int> v) {
    n = v.size();
    A = v;
    st.assign(4 * n, 0);
    build(1, 0, n - 1);
  }
  int REQ(int i, int j) { return req(1, 0, n - 1, i, j); }
  int UPDATE(int i, int x) {
    A[i] = x;
    return update(1, 0, n - 1, i);
  }

 private:
  int left(int p) { return (p << 1); }
  int right(int p) { return (p << 1) + 1; }
  int point(int x) { return x; }
  int compare(int x1, int x2) { return (A[x1] <= A[x2]) ? x1 : x2; }
  void build(int p, int L, int R) {
    if (L == R)
      st[p] = point(L);
    else {
      build(left(p), L, (L + R) / 2);
      build(right(p), (L + R) / 2 + 1, R);
      int x1 = st[left(p)];
      int x2 = st[right(p)];
      st[p] = compare(x1, x2);
    }
  }
  int req(int p, int L, int R, int i, int j) {
    if (i > R || j < L) return -1;
    if (i <= L && j >= R) return st[p];
    int x1 = req(left(p), L, (L + R) / 2, i, j);
    int x2 = req(right(p), (L + R) / 2 + 1, R, i, j);
    if (x1 == -1) return x2;
    if (x2 == -1) return x1;
    return compare(x1, x2);
  }
  int update(int p, int L, int R, int i) {
    if (L == R)
      st[p] = point(L);
    else {
      if (i <= (L + R) / 2)
        update(left(p), L, (L + R) / 2, i);
      else
        update(right(p), (L + R) / 2 + 1, R, i);
      int x1 = st[left(p)];
      int x2 = st[right(p)];
      st[p] = compare(x1, x2);
    }
  }
};
class FenwickTree {
 public:
  vector<int> ft;
  FenwickTree(int n) { ft.assign(n + 1, 0); }
  FenwickTree(int n, vector<int> v) {
    ft.assign(n + 1, 0);
    for (int i = 0; i < v.size(); i++) ADJUST(v[i], 1);
  }
  int REQ(int b) {
    int sum = 0;
    for (; b; b -= (b & -b)) sum += ft[b];
    return sum;
  }
  int REQ(int a, int b) { return REQ(b) - (a == 1 ? 0 : REQ(a - 1)); }
  int ADJUST(int b, int x) {
    for (; b < ft.size(); b += (b & -b)) ft[b] += x;
  }

 private:
};
class UnionFind {
 public:
  vector<int> p, rank, sizeset;
  int numberSet;
  UnionFind(int n) {
    numberSet = n;
    rank.assign(n, 0);
    p.resize(n);
    for (int i = 0; i < p.size(); i++) p[i] = i;
    sizeset.assign(n, 1);
  }
  int sizeSet(int i) {
    int j = findSet(i);
    return sizeset[j];
  }
  int findSet(int i) { return (p[i] == i) ? i : (p[i] = findSet(p[i])); }
  bool sameSet(int i, int j) { return (findSet(i) == findSet(j)); }
  void unionSet(int i, int j) {
    if (!sameSet(i, j)) {
      numberSet--;
      int x = findSet(i);
      int y = findSet(j);
      if (rank[x] > rank[y]) {
        p[y] = x;
        sizeset[x] += sizeset[y];
      } else {
        p[x] = y;
        sizeset[y] += sizeset[x];
      }
      if (rank[x] == rank[y]) rank[y]++;
    }
  }

 private:
};
int total = 0;
set<int> vert, hor;
int myrand() { return (rand() << 14) + rand(); }
int absolu(int x) { return max(x, -x); }
vector<int> realvert;
vector<int> realhor;
int example(int x, int y) {
  int ans = 1000000000;
  for (int i = 0; i < realvert.size(); i++) {
    ans = min(ans, absolu(x - realvert[i]));
  }
  for (int i = 0; i < realhor.size(); i++) {
    ans = min(ans, absolu(y - realhor[i]));
  }
  return ans;
}
int R = 0;
int startx, starty;
int demande(int x, int y) {
  int z;
  total++;
  printf("0 %d %d\n", x, y);
  fflush(stdout);
  scanf("%d", &z);
  if (z == -1) exit(0);
  if (z > R) {
    startx = x;
    starty = y;
    R = z;
  }
  return z;
}
void investigatex(int x1, int x2) {
  int z;
  if (total == 299998) return;
  if (x1 > x2) return;
  if (x1 == x2) {
    z = demande(x1, starty);
    if (z == 0) vert.insert(x1);
    return;
  }
  int c = (x1 + x2) / 2;
  z = demande(c, starty);
  if (z == 0) vert.insert(c);
  investigatex(x1, c - max(z, 1));
  investigatex(c + max(z, 1), x2);
}
void investigatey(int y1, int y2) {
  int z;
  if (total == 299998) return;
  if (y1 > y2) return;
  if (y1 == y2) {
    z = demande(startx, y1);
    if (z == 0) hor.insert(y1);
    return;
  }
  int c = (y1 + y2) / 2;
  z = demande(startx, c);
  if (z == 0) hor.insert(c);
  investigatey(y1, c - max(z, 1));
  investigatey(c + max(z, 1), y2);
}
int main() {
  srand(42);
  int x, y, z;
  vector<int> souviens;
  for (int i = int(1); i <= int(100); i++) {
    x = myrand() % 200000001 - 100000000;
    y = myrand() % 200000001 - 100000000;
    souviens.push_back(x);
    souviens.push_back(y);
    demande(x, y);
  }
  investigatex(-100000000, 100000000);
  investigatey(-100000000, 100000000);
  if (vert.size() == 0 || hor.size() == 0) {
    printf("%d %d %d\n%d\n", 1, 1, souviens.size(), RAND_MAX);
    for (int i = 0; i < souviens.size(); i++) printf("%d ", souviens[i]);
    printf("\n");
    return 0;
  } else if (vert.size() > 10000 || hor.size() > 10000) {
    printf("%d %d %d\n%d %d\n%d %d\n", 1, 2, 2, 1, 2, 1, 2);
    return 0;
  }
  printf("%d %d %d\n", 1, (int)vert.size(), (int)hor.size());
  set<int>::iterator it;
  for (it = vert.begin(); it != vert.end(); it++) {
    printf("%d ", *it);
  }
  printf("\n");
  for (it = hor.begin(); it != hor.end(); it++) {
    printf("%d ", *it);
  }
  printf("\n");
  return 0;
}
