#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <numeric>
#include <functional>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include <thread>
#include <tuple>

using namespace std;

pair<int,int> par[602][602];
bool valid[602][602];
int linkc[602][602];
int colorindex[602][602];
int n, d1, d2;

pair<pair<int, int>, int> findroot(int a, int b) {
  int la = a, lb = b;
  int c = 0;
  while (make_pair(a, b) != par[a][b]) {
    c ^= linkc[a][b];
    tie(a, b) = par[a][b];
  }
  int c2 = 0;
  while (make_pair(la, lb) != par[la][lb]) {
    int x, y;
    tie(x, y) = par[la][lb];
    par[la][lb] = make_pair(a, b);
    int olc = linkc[la][lb];
    linkc[la][lb] = (c ^ c2);
    c2 ^= olc;
    la = x; lb = y;
  }
  return make_pair(make_pair(a, b), c);
}

void merge(int a, int b, int c, int d) {
  auto rr1 = findroot(a, b);
  auto pos1 = rr1.first; auto c1 = rr1.second;
  auto rr2 = findroot(c, d);
  auto pos2 = rr2.first; auto c2 = rr2.second;
  if (pos1 == pos2)
    return;
  par[pos1.first][pos1.second] = pos2;
  linkc[pos1.first][pos1.second] = (c1 ^ c2 ^ 1);
}

void process(int r, int c, int r2, int c2) {
  if (r2 < 0 || c2 < 0 || r2 >= 2 * n || c2 >= 2 * n)
    return;
  if (!valid[r][c]) return;
  if (!valid[r2][c2]) return;
  merge(r, c, r2, c2);
}
void init_mark() {
  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < 2 * n; j++) {
      par[i][j] = make_pair(i, j);
      linkc[i][j] = 0;
    }
  }
}

void color_adj(vector<pair<int,int>> &v1) {
  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < 2 * n; j++) {
      if (!valid[i][j]) continue;
      for (auto v : v1) {
        process(i, j, i + v.first, j + v.second);
        process(i, j, i + v.first, j - v.second);
        process(i, j, i - v.first, j + v.second);
        process(i, j, i - v.first, j - v.second);
      }
    }
  }
}

void erase_valid(vector<pair<int,int>> &v1) {
  int n1 = 0, n2 = 0;
  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < 2 * n; j++) {
      if (!valid[i][j]) continue;
      auto res = findroot(i, j); // compress
      if (res.second == 0) {
        colorindex[i][j] = n1;
        n1++;
      }
      else {
        colorindex[i][j] = n2;
        n2++;
      }
    }
  }
  if (n1 > n2) {
    for (int i = 0; i < 2 * n; i++) {
      for (int j = 0; j < 2 * n; j++) {
        if (!valid[i][j]) continue;
        if (linkc[i][j] == 1) {
          valid[i][j] = false;
        }
      }
    }
  } else {
    for (int i = 0; i < 2 * n; i++) {
      for (int j = 0; j < 2 * n; j++) {
        if (!valid[i][j]) continue;
        if (linkc[i][j] == 0) {
          valid[i][j] = false;
        }
      }
    }
  }
}

int main() {
  scanf("%d%d%d", &n, &d1, &d2);

  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < 2 * n; j++) {
      valid[i][j] = true;
    }
  }
  vector<pair<int, int>> v1, v2;
  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < 2 * n; j++) {
      if (i * i + j * j == d1) {
        v1.emplace_back(i, j);
      }
      if (i * i + j * j == d2) {
        v2.emplace_back(i, j);
      }
    }
  }

  init_mark();
  color_adj(v1);
  erase_valid(v1);
  init_mark();
  color_adj(v2);
  erase_valid(v2);
  vector<pair<int, int>> ans;
  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < 2 * n; j++) {
      if (ans.size() >= n*n) break;
      if (valid[i][j]) {
          ans.emplace_back(i, j);
      }
    }
  }
  for (auto a : ans) {
    printf("%d %d\n", a.first, a.second);
  }
  return 0;
}


Measured memory_bytes: 7796