#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;

// PC: path compression

struct ParityPC {
  // assumption: every root node points itself.
  struct node_t {
    int parent;
    int size;
    char edge_parity;
    node_t() : size(1), edge_parity(0) {}
  };
  vector<node_t> nodes;

  ParityPC() { }
  ParityPC(int size) : nodes(size) {
    for (int i = 0; i < size; i++) {
      nodes[i].parent = i;
    }
  }

  // add more nodes
  void expand(int count) {
    int oldsize = nodes.size();
    nodes.resize(oldsize + count);
    for (int i = oldsize; i < oldsize + count; i++) {
      nodes[i].parent = i;
    }
  }

  void clear() {
    nodes.clear();
  }

  // find root node
  int find(int index) {
    int root = index;
    char sum = 0;
    while (root != nodes[root].parent) {
      sum ^= nodes[root].edge_parity;
      root = nodes[root].parent;
    }
    char partial = 0;
    while (root != nodes[index].parent) {
      int next = nodes[index].parent;
      char edge = nodes[index].edge_parity;
      nodes[index].edge_parity = partial ^ sum;
      nodes[index].parent = root;
      partial ^= edge;
      index = next;
    }
    return root;
  }

  // true if merge was successful
  bool merge(int first, int second, bool parity) {
    int r1 = find(first);
    int r2 = find(second);
    if (r1 == r2) {
      return false;
    }
    if (nodes[r1].size < nodes[r2].size) {
      // careful, first and second not swapped
      swap(r1, r2);
    }
    nodes[r1].size += nodes[r2].size;
    nodes[r2].parent = r1;
    nodes[r2].edge_parity = nodes[first].edge_parity ^ nodes[second].edge_parity ^ (parity ? 1 : 0);
    return true;
  }

  // 0, 1 if related, -1 if not related
  int parity_distance(int first, int second) {
    int root_first = find(first);
    int root_second = find(second);
    if (root_first != root_second) {
      return -1;
    }
    // assumption: nodes[first].parent == nodes[second].parent because of PC(Path Compression)
    return nodes[first].edge_parity ^ nodes[second].edge_parity;
  }

  // parity distance to root
  bool parity(int index) {
    find(index);
    // assumption: nodes[first].parent == root
    return nodes[index].edge_parity;
  }

  int group_size(int index) {
    return nodes[find(index)].size;
  }
};


bool valid[602][602];
int n, d1, d2;
ParityPC uf;

int encode(int r, int c) {
  return r * 2 * n + c;
}

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;
  uf.merge(encode(r, c), encode(r2, c2), 1);
}

void init() {
  uf.clear();
  uf.expand(4 * n * n);
}

void merge_forbidden(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_invalid(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 parity = uf.parity(encode(i, j));
      if (!parity) {
        n1++;
      }
      else {
        n2++;
      }
    }
  }
  for (int i = 0; i < 2 * n; i++) {
    for (int j = 0; j < 2 * n; j++) {
      if (!valid[i][j]) continue;
      bool wanted_parity = (n1 < n2);
      if (uf.parity(encode(i, j)) != wanted_parity) {
        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();
  merge_forbidden(v1);
  erase_invalid(v1);
  init();
  merge_forbidden(v2);
  erase_invalid(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: 6260