#include <bits/stdc++.h>
using namespace std;
const int Nmax = 2005;
map<int, int> mp1, mp2;
map<pair<int, int>, int> MP;
int a[Nmax][Nmax];
int n, N, M, i, j, c, x, y, z, Xul[Nmax], Yul[Nmax];
int X[Nmax], Y[Nmax], Down[Nmax][Nmax], Right[Nmax][Nmax], EDown[Nmax][Nmax],
    ERight[Nmax][Nmax];
struct W {
  int x, y, z;
};
vector<W> e1, e2;
class MonGraph {
  vector<int> v[Nmax];
  bool used[Nmax], seen1[Nmax], seen2[Nmax];
  int L[Nmax], R[Nmax];
  int n, m;
  void dfs(int node) {
    seen1[node] = 1;
    for (auto it : v[node])
      if (it != L[node] && !seen2[it]) {
        seen2[it] = 1;
        dfs(R[it]);
      }
  }
  bool cupleaza(int x) {
    used[x] = 1;
    for (auto it : v[x])
      if (R[it] == -1) {
        L[x] = it;
        R[it] = x;
        return 1;
      }
    for (auto it : v[x])
      if (!used[R[it]] && cupleaza(R[it])) {
        L[x] = it;
        R[it] = x;
        return 1;
      }
    return 0;
  }

 public:
  bool used1[Nmax];
  bool used2[Nmax];
  int cnt1, cnt2;
  int cuplaj() {
    bool done = 1;
    int i;
    for (i = 0; i < n; ++i) L[i] = -1;
    for (i = 0; i < m; ++i) R[i] = -1;
    while (done) {
      done = 0;
      memset(used, 0, sizeof(used));
      for (i = 0; i < n; ++i)
        if (!used[i] && L[i] == -1) done |= cupleaza(i);
    }
    int cnt = 0;
    for (i = 0; i < n; ++i)
      if (L[i] != -1) ++cnt;
    return cnt;
  }
  void minimum_vertex_cover() {
    int i;
    for (i = 0; i < n; ++i)
      if (L[i] == -1) dfs(i);
    for (i = 0; i < n; ++i)
      if (seen1[i])
        used1[i] = 1, ++cnt1;
      else
        used1[i] = 0;
    for (i = 0; i < m; ++i)
      if (!seen2[i])
        used2[i] = 1, ++cnt2;
      else
        used2[i] = 0;
  }
  void add_edge(int x, int y) { v[x].push_back(y); }
  void set_dim(int N, int M) { n = N, m = M; }
} graph;
bool intersect(W a, W b) {
  if (!(a.x > b.y && a.x < b.z)) return 0;
  if (!(b.x > a.y && b.x < a.z)) return 0;
  return 1;
}
int main() {
  cin.sync_with_stdio(false);
  cin >> n;
  for (i = 1; i <= n; ++i) {
    cin >> X[i] >> Y[i];
    mp1[X[i]] = 1;
    mp2[Y[i]] = 1;
  }
  int cnt = 0;
  for (auto &it : mp1) it.second = ++cnt, Xul[cnt] = it.first;
  N = cnt;
  cnt = 0;
  for (auto &it : mp2) it.second = ++cnt, Yul[cnt] = it.first;
  M = cnt;
  for (i = 1; i <= n; ++i) a[mp1[X[i]]][mp2[Y[i]]] = 1;
  for (i = 1; i <= N; ++i) {
    c = 0;
    for (j = 1; j <= M; ++j)
      if (a[i][j]) {
        if (c)
          e1.push_back({i, c, j}), Right[i][c] = j,
                                   ERight[i][c] = e1.size() - 1;
        c = j;
      }
  }
  for (i = 1; i <= M; ++i) {
    c = 0;
    for (j = 1; j <= N; ++j)
      if (a[j][i]) {
        if (c)
          e2.push_back({i, c, j}), Down[i][c] = j, EDown[i][c] = e2.size() - 1;
        c = j;
      }
  }
  for (i = 0; i < e1.size(); ++i)
    for (j = 0; j < e2.size(); ++j)
      if (intersect(e1[i], e2[j])) graph.add_edge(i, j);
  graph.set_dim(e1.size(), e2.size());
  graph.cuplaj();
  graph.minimum_vertex_cover();
  cout << n - graph.cnt2 << '\n';
  for (i = 1; i <= M; ++i) {
    for (j = 1; j <= N; ++j)
      if (a[j][i]) {
        c = j;
        while (Down[i][c] && graph.used2[EDown[i][c]]) c = Down[i][c];
        cout << Xul[j] << ' ' << Yul[i] << ' ' << Xul[c] << ' ' << Yul[i]
             << '\n';
        j = c;
      }
  }
  cout << n - graph.cnt1 << '\n';
  for (i = 1; i <= N; ++i) {
    for (j = 1; j <= M; ++j)
      if (a[i][j]) {
        c = j;
        while (Right[i][c] && graph.used1[ERight[i][c]]) c = Right[i][c];
        cout << Xul[i] << ' ' << Yul[j] << ' ' << Xul[i] << ' ' << Yul[c]
             << '\n';
        j = c;
      }
  }
  return 0;
}
