#include <bits/stdc++.h>
using namespace std;
template <typename T>
void _R(T &x) {
  cin >> x;
}
void _R(int &x) { scanf("%d", &x); }
void _R(long long &x) { scanf("%" PRId64, &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { scanf(" %c", &x); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template <typename T, typename... U>
void R(T &head, U &...tail) {
  _R(head);
  R(tail...);
}
template <typename T>
void _W(const T &x) {
  cout << x;
}
void _W(const int &x) { printf("%d", x); }
void _W(const long long &x) { printf("%" PRId64, x); }
template <typename T>
void _W(const vector<T> &x) {
  for (auto i = x.cbegin(); i != x.cend(); i++) {
    if (i != x.cbegin()) putchar(' ');
    _W(*i);
  }
}
void W() {}
template <typename T, typename... U>
void W(const T &head, const U &...tail) {
  _W(head);
  putchar(sizeof...(tail) ? ' ' : '\n');
  W(tail...);
}
struct SplitMix32 {
  uint32_t s;
  SplitMix32(uint32_t _s = 0) : s(_s) {}
  uint32_t operator()() {
    uint32_t z = (s += 0x9e3779b1);
    z = (z ^ (z >> 16)) * 0x85ebca6b;
    z = (z ^ (z >> 13)) * 0xc2b2ae35;
    return z ^ (z >> 16);
  }
  int i(int l, int r) {
    auto x = (*this)();
    return l + x % (r - l + 1);
  }
} rng;
const int L = 1e8;
const int INF = 1e9;
struct NPC {
  vector<int> vx, vy;
  void init(int n, int m) {
    vx.clear();
    vy.clear();
    for (int i = 0; i < int(n); i++) vx.push_back(rng.i(-L, +L));
    for (int i = 0; i < int(m); i++) vy.push_back(rng.i(-L, +L));
    sort(begin(vx), end(vx));
    sort(begin(vy), end(vy));
  }
  static int ask(const vector<int> &v, int x) {
    int mi = INF;
    auto it = lower_bound(begin(v), end(v), x);
    if (it != v.end()) mi = min(mi, *it - x);
    if (it != v.begin()) {
      it--;
      mi = min(mi, x - *it);
    }
    assert(mi != INF);
    return mi;
  }
  int ask(int x, int y) {
    int mi = INF;
    mi = min(mi, ask(vx, x));
    mi = min(mi, ask(vy, y));
    return mi;
  }
} npc;
int ask_cnt;
int ask(int x, int y) {
  ask_cnt++;
  printf("0 %d %d\n", x, y);
  fflush(stdout);
  int res;
  scanf("%d", &res);
  if (res == -1) exit(0);
  return res;
}
struct Range {
  int l, r;
  int mid() { return l + (r - l) / 2; }
  int len() const { return r - l + 1; }
  bool operator<(const Range &rhs) const { return len() < rhs.len(); }
};
template <typename F>
vector<int> solve(F f) {
  vector<int> v;
  priority_queue<Range> q;
  q.push({-L, +L});
  while (!q.empty()) {
    auto t = q.top();
    q.pop();
    int m = t.mid();
    int d = f(m);
    if (d == 0) {
      v.push_back(m);
      d++;
    }
    int l1 = t.l;
    int r1 = m - d;
    int l2 = m + d;
    int r2 = t.r;
    if (l1 <= r1) q.push({l1, r1});
    if (l2 <= r2) q.push({l2, r2});
  }
  return v;
}
int main() {
  npc.init(10000, 10000);
  int mx = -1, ox = 0, oy = 0;
  for (int i = 0; i < int(2000); i++) {
    int x = rng.i(-L, +L);
    int y = rng.i(-L, +L);
    int now = ask(x, y);
    if (now > mx) tie(mx, ox, oy) = tie(now, x, y);
  }
  assert(mx > 0);
  ;
  auto vx = solve([&](int m) { return ask(m, oy); });
  auto vy = solve([&](int m) { return ask(ox, m); });
  sort(begin(vx), end(vx));
  sort(begin(vy), end(vy));
  W(1, ((int)(vx).size()), ((int)(vy).size()));
  W(vx);
  W(vy);
  return 0;
}
