#include <bits/stdc++.h>
using namespace std;
struct UnionFindSet {
  vector<int> f;
  int n;
  UnionFindSet(int n) : f(n) { clear(n); }
  void clear(int n) {
    this->n = n;
    for (int i = 0; i < n; i++) {
      f[i] = i;
    }
  }
  int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
  void Union(int x, int y) { f[find(y)] = find(x); }
};
using db = double;
const db EPS = 1e-6;
const db PI = acos(-1.0);
int sgn(db x) {
  if (x < -EPS) return -1;
  return x > EPS;
}
int cmp(db a, db b) { return sgn(a - b); }
struct db_less : public binary_function<db, db, bool> {
  db_less(db eps = EPS) : eps(eps) {}
  bool operator()(const db &a, const db &b) const { return cmp(a, b) == -1; }
  double eps;
};
db dtor(db d) { return d * PI / 180; }
db rtod(db r) { return r * 180 / PI; }
struct Point2 {
  db x, y;
  db angle;
  int color;
  int id;
  Point2() {}
  Point2(db x, db y) : x(x), y(y) {}
  using p2 = Point2;
  p2 operator+(const p2 b) const { return p2(x + b.x, y + b.y); }
  p2 operator-(const p2 b) const { return p2(x - b.x, y - b.y); }
  p2 operator*(const db k) const { return p2(x * k, y * k); }
  p2 operator/(const db k) const { return p2(x / k, y / k); }
  bool operator<(const p2 b) const {
    if (cmp(x, b.x) != 0) return cmp(x, b.x) < 0;
    return cmp(y, b.y) < 0;
  }
  bool operator==(const p2 b) const {
    return cmp(x, b.x) == 0 && cmp(y, b.y) == 0;
  }
  bool operator>(const p2 b) const { return !operator<(b) && !operator==(b); }
  db norm2() { return x * x + y * y; }
  db norm() { return sqrt(norm2()); }
  p2 unit() { return (*this) / norm(); }
  p2 rot90() { return p2(-y, x); }
  p2 rotate(db a) {
    return p2(x * cos(a) - y * sin(a), x * sin(a) + y * cos(a));
  }
};
using p2 = Point2;
using v2 = p2;
istream &operator>>(istream &in, p2 &p) { return (in >> p.x >> p.y); }
ostream &operator<<(ostream &out, p2 &p) {
  return (out << '(' << p.x << ',' << p.y << ')');
}
struct Hashp2 {
  size_t operator()(const p2 &p) const {
    return (hash<int>()((int)(p.x * 1e6)) << 13) ^
           hash<int>()((int)(p.y * 1e6));
  }
};
db dis(p2 a, p2 b) { return (a - b).norm(); }
db dot(p2 a, p2 b) { return a.x * b.x + a.y * b.y; }
db det(p2 a, p2 b) { return a.x * b.y - a.y * b.x; }
p2 point_proj_line(p2 p, p2 a, p2 b) {
  return a + (b - a) * dot(p - a, b - a) / dot(b - a, b - a);
}
db rad(p2 a, p2 b) { return atan2(det(a, b), dot(a, b)); }
struct Line2 {
  p2 a, b;
  Line2() {}
  Line2(p2 a, p2 b) : a(a), b(b) {}
  using l2 = Line2;
};
using l2 = Line2;
inline bool is_range_cross(db a1, db a2, db b1, db b2) {
  return cmp(max(min(a1, a2), min(b1, b2)), min(max(a1, a2), max(b1, b2))) <= 1;
}
inline bool is_rect_cross(const p2 &a1, const p2 &a2, const p2 &b1,
                          const p2 &b2) {
  return is_range_cross(a1.x, a2.x, b1.x, b2.x) &&
         is_range_cross(a1.y, a2.y, b1.y, b2.y);
}
inline bool is_same_side(l2 l, p2 a, p2 b) {
  return sgn(det(l.b - l.a, a - l.a)) * sgn(det(l.b - l.a, b - l.a)) <= 0;
}
inline bool is_segment_cross(l2 a, l2 b) {
  return is_rect_cross(a.a, a.b, b.a, b.b) && is_same_side(a, b.a, b.b) &&
         is_same_side(b, a.a, a.b);
}
p2 intersection(l2 a, l2 b) {
  db t = det(b.b - b.a, a.a - b.a) / det(a.b - a.a, b.b - b.a);
  return a.a + (a.b - a.a) * t;
}
using Polygon = vector<p2>;
db area(Polygon p) {
  db s = 0;
  for (int i = 0; i < (int)p.size(); i++) s += det(p[i], p[(i + 1) % p.size()]);
  return s / 2;
}
bool is_in(p2 a, Polygon p) {
  int side = sgn(det(p[0] - p.back(), a - p.back()));
  for (int i = 0; i < (int)p.size() - 1; i++)
    if (side != sgn(det(p[i + 1] - p[i], a - p[i]))) return false;
  return true;
}
Polygon convex(Polygon p) {
  int k = 0;
  for (int i = 1; i < (int)p.size(); i++)
    if (p[i] < p[k]) k = i;
  swap(p[k], p[0]);
  auto cmp = [&](p2 a, p2 b) { return sgn(det(a - p[0], b - p[0])) > 0; };
  sort(p.begin() + 1, p.end(), cmp);
  Polygon res;
  res.push_back(p[0]);
  for (int i = 1; i < (int)p.size(); i++) {
    while (res.size() >= 2 &&
           sgn(det(res.back() - *(res.rbegin() + 1), p[i] - res.back())) <= 0)
      res.pop_back();
    res.push_back(p[i]);
  }
  return res;
}
int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  int n;
  cin >> n;
  vector<p2> pts(n);
  for (int i = 0; i < n; i++) {
    cin >> pts[i].x >> pts[i].y >> pts[i].color;
    pts[i].id = i;
  }
  Polygon hull = convex(pts);
  int cnt = 0;
  for (int i = 0; i < (int)hull.size() - 1; i++)
    cnt += hull[i].color ^ hull[i + 1].color;
  cnt += hull.back().color ^ hull[0].color;
  if (cnt > 2) {
    cout << "Impossible";
    return 0;
  }
  vector<pair<int, int>> ans;
  UnionFindSet cpnt(n);
  auto combine = [&](int u, int v) {
    if (cpnt.find(u) != cpnt.find(v)) {
      ans.emplace_back(u, v);
      cpnt.Union(u, v);
    }
  };
  auto get_st = [&](vector<p2> p) {
    sort(p.begin(), p.end());
    for (int i = 0; i < (int)p.size() - 1; i++) combine(p[i].id, p[i + 1].id);
  };
  function<void(Polygon, vector<p2>)> solve = [&](Polygon p, vector<p2> range) {
    int strange_color = p[0].color;
    vector<p2> inner;
    int strange_id = -1;
    for (auto &pt : range) {
      if (pt.id == p[0].id || pt.id == p[1].id || pt.id == p[2].id) continue;
      if (!is_in(pt, p)) continue;
      inner.push_back(pt);
      if (pt.color == strange_color) {
        strange_id = pt.id;
      }
    }
    if (strange_id != -1) {
      inner.push_back(p[0]);
      inner.push_back(p[1]);
      inner.push_back(p[2]);
      solve({pts[strange_id], p[1], p[2]}, inner);
      solve({p[1], p[0], pts[strange_id]}, inner);
      solve({p[2], p[0], pts[strange_id]}, inner);
    } else {
      inner.push_back(p[1]);
      inner.push_back(p[2]);
      get_st(inner);
    }
  };
  if (cnt == 0) {
    int strange_color = 1 - hull[0].color;
    vector<int> is_on_hull(n);
    for (auto &p : hull) is_on_hull[p.id] = 1;
    int strange_id = -1;
    for (auto &pt : pts) {
      if (is_on_hull[pt.id]) continue;
      if (pt.color == strange_color) {
        strange_id = pt.id;
        break;
      }
    }
    if (strange_id != -1) {
      for (int i = 0; i < (int)hull.size() - 1; i++)
        solve({pts[strange_id], hull[i], hull[i + 1]}, pts);
      solve({pts[strange_id], hull.back(), hull[0]}, pts);
    } else {
      get_st(pts);
    }
  } else {
    int start = 0;
    array<vector<p2>, 2> half_hull;
    for (int i = 1; i < (int)hull.size(); i++) {
      if (hull[i].color != hull[i - 1].color) {
        start = i;
        break;
      }
    }
    for (int i = 0; i < (int)hull.size(); i++) {
      p2 pt = hull[(start + i) % hull.size()];
      half_hull[pt.color].push_back(pt);
    }
    for (int i = 0; i < (int)half_hull[0].size() - 1; i++)
      solve({half_hull[1][0], half_hull[0][i], half_hull[0][i + 1]}, pts);
    for (int i = 0; i < (int)half_hull[1].size() - 1; i++)
      solve({half_hull[0][0], half_hull[1][i], half_hull[1][i + 1]}, pts);
  }
  cout << ans.size() << endl;
  for (auto &e : ans) {
    int u = e.first, v = e.second;
    cout << u << " " << v << endl;
  }
}
