#include <bits/stdc++.h>

#define db double

const db eps = 1e-9;
inline int sign(db k) { return k < -eps ? -1 : k > eps; }
inline int cmp(db k1, db k2) { return sign(k1 - k2); }

struct P {
  db x, y;
  P() {}
  P(db x, db y): x(x), y(y) {}
  P operator + (const P &p) const { return {x + p.x, y + p.y}; }
  P operator - (const P &p) const { return {x - p.x, y - p.y}; }
  P operator * (db k) const { return {x * k, y * k}; }
  P operator / (db k) const { return {x / k, y / k}; }
  bool operator < (const P &p) const { 
    int c = cmp(x, p.x);
    return c ? c == -1 : cmp(y, p.y) == -1; 
  }
  bool operator == (const P &p) const { 
    return !cmp(x, p.x) && !cmp(y, p.y); 
  }
  db distTo(const P &p) const { return (*this - p).abs(); }
  db alpha() { return atan2(y, x); }
  void read() { scanf("%lf%lf", &x, &y); }
  void print() { printf("%.10f %.10f\n", x, y); }
  db abs() { return sqrt(abs2()); }
  db abs2() { return x * x + y * y; }
  P rot(const db &k) { return P(x * cos(k) - y * sin(k), x * sin(k) + y * cos(k)); }
  P rot90() { return P(-y, x); }
  P unit() { return *this / abs(); }
  P normal() { return rot90() / abs(); }
  int quad() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); }
  db dot(const P &p) { return x * p.x + y * p.y; }
  db det(const P &p) { return x * p.y - y * p.x; }
};

struct L { // ps[0] -> ps[1]
  P ps[2];
  L() {}
  L(const P &p0, const P &p1) {
    ps[0] = p0; ps[1] = p1;
  }
  void read() {
    ps[0].read(); ps[1].read();
  }
  P &operator[](int i) { return ps[i]; }
  P dir() { return ps[1] - ps[0]; }
  bool include(const P &p) { return sign((ps[1] - ps[0]).det(p - ps[0])) > 0; }
  L push() { // push eps outawrd
    const db Eps = 1e-6;
    P delta = (ps[1] - ps[0]).normal() * Eps;
    return {ps[0] - delta, ps[1] - delta};
  }
};

#define cross(p1, p2, p3) ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y))
#define crossOp(p1, p2, p3) sign(cross(p1, p2, p3))

// 判断 p1p2 和 q1q2 是否相交
bool chkLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  return sign(a1 + a2) != 0;
}

bool chkLL(L l0, L l1) {
  return chkLL(l0[0], l0[1], l1[0], l1[1]);
}

// 直线交点
P isLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  assert(chkLL(p1, p2, q1, q2));
  db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  return (p1 * a2 + p2 * a1) / (a1 + a2);
}

P isLL(L l1, L l2) {
  return isLL(l1[0], l1[1], l2[0], l2[1]);
}
// 线段相交
bool intersect(db l1, db r1, db l2, db r2) {
  if (l1 > r1) std::swap(l1, r1); if (l2 > r2) std::swap(l2, r2);
  return !(cmp(r1, l2) == -1 || cmp(r2, l1) == -1);
}
bool isSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  return intersect(p1.x, p2.x, q1.x, q2.x) && intersect(p1.y, p2.y, q1.y, q2.y)
         && crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <= 0
         && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) <= 0;
}
bool isSS_strict(const P &p1, const P &p2, const P &q1, const P &q2) {
  return crossOp(p1, p2, q1) * crossOp(p1, p2, q2) < 0
         && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) < 0;
}

bool isSS(L l0, L l1) {
  return isSS(l0[0], l0[1], l1[0], l1[1]);
}

bool isSS_strict(L l0, L l1) {
  return isSS_strict(l0[0], l0[1], l1[0], l1[1]);
}

// 点在线段上判定
bool isMiddle(db a, db m, db b) {
  return sign(a - m) == 0 || sign(b - m) == 0 || (a < m != b < m);
}
bool isMiddle(const P &a, const P &m, const P &b) {
  return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
}
bool onSeg(const P &p1, const P &p2, const P &q) {
  return crossOp(p1, p2, q) == 0 && isMiddle(p1, q, p2);
}
bool onSeg_strict(const P &p1, const P &p2, const P &q) {
  return crossOp(p1, p2, q) == 0 && sign((q - p1).dot(p1 - p2)) * sign((q - p2).dot(p1 - p2)) < 0;
}

// p2 相对于 p0->p1 的位置
int ccw(P p0, P p1, P p2) {
  int k = crossOp(p0, p1, p2);
  if (k > 0) return 1; // 逆时针
  if (k < 0) return 2; // 顺时针
  int dot = sign((p1 - p0).dot(p2 - p0));
  if (dot < 0) return 3; // 反向延长线
  if (onSeg(p0, p1, p2)) return 5; // 线段上
  return 4; // 延长线
}

// 投影
P proj(const P &p1, const P &p2, const P &q) {
  P dir = p2 - p1;
  return p1 + dir * (dir.dot(q - p1) / dir.abs2());
}

// 反射
P reflect(const P &p1, const P &p2, const P &q) {
  return proj(p1, p2, q) * 2 - q;
}

// 最近点
db nearest(const P &p1, const P &p2, const P &q) {
  P h = proj(p1, p2, q);
  if (isMiddle(p1, h, p2)) return q.distTo(h);
  return std::min(p1.distTo(q), p2.distTo(q));
}

// 线段距离
db disSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  if (isSS(p1, p2, q1, q2)) return 0;
  return std::min(std::min(nearest(p1, p2, q1), nearest(p1, p2, q2)), std::min(nearest(q1, q2, p1), nearest(q1, q2, p2)));
}

// 夹角
db rad(P p1, P p2) {
  return atan2l(p1.det(p2), p1.dot(p2));
}

db incircle(P p1, P p2, P p3) {
  db A = p1.distTo(p2);
  db B = p2.distTo(p3);
  db C = p3.distTo(p1);
  return sqrtl(A * B * C / (A + B + C));
}

// 多边形面积
db area(std::vector<P> ps) {
  db ans = 0;
  for (int i = 0, n = ps.size(); i < n; i++)
    ans += ps[i].det(ps[(i + 1) % n]);
  return std::fabs(ans / 2);
}

// 点包含 2: inside 1: onSeg 0: outside
int contain(std::vector<P> ps, P p) {
  int n = ps.size(), ret = 0;
  for (int i = 0; i < n; i++) {
    P u = ps[i], v = ps[(i + 1) % n];
    if (onSeg(u, v, p)) return 1;
    if (cmp(u.y, v.y) <= 0) std::swap(u, v);
    if (cmp(p.y, u.y) > 0 || cmp(p.y, v.y) <= 0) continue;
    ret ^= crossOp(p, u, v) > 0;
  }
  return ret * 2;
}
// 凸包
std::vector<P> convexHull(std::vector<P> ps) {
  int n = ps.size(); if (n <= 1) return ps;
  std::sort(ps.begin(), ps.end());
  std::vector<P> qs(n * 2); int k = 0;
  for (int i = 0; i < n; qs[k++] = ps[i++])
    while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k;
  for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--])
    while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k;
  qs.resize(k - 1);
  return qs;
}
std::vector<P> convexHullNonStrict(std::vector<P> ps) {
  int n = ps.size(); if (n <= 1) return ps;
  std::sort(ps.begin(), ps.end());
  std::vector<P> qs(n * 2); int k = 0;
  for (int i = 0; i < n; qs[k++] = ps[i++])
    while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k;
  for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--])
    while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k;
  qs.resize(k - 1);
  return qs;
}

// 点集直径
db convexDiameter(const std::vector<P> &ps) {
  int n = ps.size(); if (n <= 1) return 0;
  int is = 0, js = 0;
  for (int k = 1; k < n; k++)
    is = ps[k] < ps[is] ? k : is, js = ps[js] < ps[k] ? k : js;
  int i = is, j = js;
  db ret = ps[i].distTo(ps[j]);
  do {
    if ((ps[(i + 1) % n] - ps[i]).det(ps[(j + 1) % n] - ps[j]) >= 0)
      (++j) %= n;
    else
      (++i) %= n;
    ret = std::max(ret, ps[i].distTo(ps[j]));
  } while (i != is || j != js);
  return ret;
}

// convecCut
std::vector<P> convexCut(const std::vector<P> &ps, const P &q1, const P &q2) {
  std::vector<P> qs;
  int n = ps.size();
  for (int i = 0; i < n; i++) {
    P p1 = ps[i], p2 = ps[(i + 1) % n];
    int d1 = crossOp(q1, q2, p1), d2 = crossOp(q1, q2, p2);
    if (d1 >= 0) qs.push_back(p1);
    if (d1 * d2 < 0) qs.push_back(isLL(p1, p2, q1, q2));
  }
  return qs;
}
// min_dis sort first
db min_dis(std::vector<P> &ps, int l, int r) {
  if (r - l <= 5) {
    db ret = 1e5;
    for (int i = l; i < r; i++)
      for (int j = l; j < i; j++)
        ret = std::min(ret, ps[i].distTo(ps[j]));
    return ret;
  }
  int mid = l + r >> 1;
  db ret = std::min(min_dis(ps, l, mid), min_dis(ps, mid, r));
  std::vector<P> qs;
  for (int i = l; i < r; i++)
    if (cmp(fabs(ps[i].x - ps[mid].x), ret) <= 0) qs.push_back(ps[i]);
  std::sort(qs.begin(), qs.end(), [](P a, P b) -> bool { return cmp(a.y, b.y) < 0; });
  for (int i = 1; i < qs.size(); i++)
    for (int j = i - 1; j >= 0 && cmp(qs[j].y, qs[i].y - ret) >= 0; j--)
      ret = std::min(ret, qs[i].distTo(qs[j]));
  return ret;
}

// 圆的关系
int type(const P &o1, db r1, const P &o2, db r2) {
  db d = o1.distTo(o2);
  if (cmp(d, r1 + r2) == 1) return 4; // 相离
  if (cmp(d, r1 + r2) == 0) return 3; // 外切
  if (cmp(d, fabs(r1 - r2)) == 1) return 2; // 相交
  if (cmp(d, fabs(r1 - r2)) == 0) return 1; // 内切
  return 0;
}

// 圆和线交点
std::vector<P> isCL(P o, db r, P p1, P p2) {
  db x = (p1 - o).dot(p2 - p1), y = (p2 - p1).abs2(), d = x * x - y * ((p1 - o).abs2() - r * r);
  if (sign(d) < 0) return {};
  d = std::max(d, 0.0); P m = p1 - (p2 - p1) * (x / y), dr = (p2 - p1) * (sqrt(d) / y);
  return {m - dr, m + dr}; // along dir: p1->p2
}

// 圆和圆交点 需先判断两圆是否相同
std::vector<P> isCC(P o1, db r1, P o2, db r2) {
  db d = o1.distTo(o2);
  if (cmp(d, r1 + r2) == 1) return {};
  d = std::min(d, r1 + r2);
  db y = (r1 * r1 + d * d - r2 * r2) / (2 * d), x = sqrt(r1 * r1 - y * y);
  P dr = (o2 - o1).unit();
  P q1 = o1 + dr * y, q2 = dr.rot90() * x;
  return {q1 - q2, q1 + q2}; // along circle 1
}

// 点到圆的切点
std::vector<P> tanCP(P o, db r, P p) {
  db x = (p - o).abs2(), d = x - r * r;
  if (sign(d) == 0) return { p };
  if (sign(d) < 0) return {}; // on circle => no tangent
  P q1 = o + (p - o) * (r * r / x);
  P q2 = (p - o).rot90() * (r * sqrt(d) / x);
  return {q1 - q2, q1 + q2}; // counter clock-wise
}

// 外切线
std::vector<L> extanCC(P o1, db r1, P o2, db r2) {
  std::vector<L> ret;
  if (cmp(r1, r2) == 0) {
    P dr = (o2 - o1).unit().rot90() * r1;
    ret.push_back({o1 + dr, o2 + dr});
    ret.push_back({o1 - dr, o2 - dr});
  } else {
    P p = (o2 * r1 - o1 * r2) / (r1 - r2);
    std::vector<P> ps = tanCP(o1, r1, p), qs = tanCP(o2, r2, p);
    for (int i = 0; i < std::min(ps.size(), qs.size()); i++)
      ret.push_back({ps[i], qs[i]}); // c1 counter clock-wise
  }
  return ret;
}

std::vector<L> intanCC(P o1, db r1, P o2, db r2) {
  std::vector<L> ret;
  P p = (o1 * r2 + o2 * r1) / (r1 + r2);
  std::vector<P> ps = tanCP(o1, r1, p), qs = tanCP(o2, r2, p);
  for (int i = 0; i < std::min(ps.size(), qs.size()); i++)
    ret.push_back({ps[i], qs[i]});
  return ret;
}

bool parallel(L l0, L l1) {
  return sign(l0.dir().det(l1.dir())) == 0;
}

bool orth(L l0, L l1) {
  return sign(l0.dir().dot(l1.dir())) == 0;
}

bool sameDir(L l0, L l1) {
  return parallel(l0, l1) && sign(l0.dir().dot(l1.dir())) == 1;
}

bool cmp(P a, P b) {
  if (a.quad() != b.quad()) {
    return a.quad() < b.quad();
  } else {
    return sign(a.det(b)) > 0;
  }
}

bool operator < (L l0, L l1) {
  if (sameDir(l0, l1)) {
    return l1.include(l0[0]);
  } else {
    return cmp(l0.dir(), l1.dir());
  }
}

bool check(L u, L v, L w) {
  return w.include(isLL(u, v));
}

const int N = 1e3 + 7;
L que[N];

std::vector<L> halfPlaneIS(std::vector<L> &l) {
  std::sort(l.begin(), l.end());
  int head = 0, tail = 0;
  for (int i = 0; i < l.size(); i++) {
    if (i && sameDir(l[i], l[i - 1])) continue;
    while (tail - head > 1 && !check(que[tail - 2], que[tail - 1], l[i])) tail--;
    while (tail - head > 1 && !check(que[head + 1], que[head], l[i])) head++;
    que[tail++] = l[i];
  }
  while (tail - head > 2 && !check(que[tail - 2], que[tail - 1], que[0])) tail--;
  while (tail - head > 2 && !check(que[1], que[0], que[tail - 1])) head++;
  std::vector<L> ans;
  for (int i = head; i < tail; i++)
    ans.push_back(que[i]);
  return ans;
}

int main() {
  P o1, o2;
  db r1, r2;
  o1.read(); scanf("%lf", &r1);
  o2.read(); scanf("%lf", &r2);
  std::vector<L> intan, extan;
  intan = intanCC(o1, r1, o2, r2);
  extan = extanCC(o1, r1, o2, r2);
  std::vector<P> ret;
  for (auto it: intan) {
    P p1 = it[0];
    if (sign(p1.x) == 0) p1.x = 0;
    if (sign(p1.y) == 0) p1.y = 0;
    ret.push_back(p1);
  }
  for (auto it: extan) {
    P p1 = it[0];
    if (sign(p1.x) == 0) p1.x = 0;
    if (sign(p1.y) == 0) p1.y = 0;
    ret.push_back(p1);
  }
  std::sort(ret.begin(), ret.end());
  for (int i = 0; i < ret.size(); i++)
    ret[i].print();
  return 0;
}


Measured memory_bytes: 3288