#include <bits/stdc++.h>
using namespace std;

struct F {
	long long a, b;
	F() {}
	F(long long _a, long long _b) : a(_a), b(_b) {}
	F operator + (const F &f) {
		long long x = a * f.a, y = b * f.a + a * f.b;
		long long g = __gcd(x, y);
		return F(x / g, y / g);
	}
	F operator - (const F &f) {
		long long x = a * f.a, y = b * f.a - a * f.b;
		long long g = __gcd(x, y);
		return F(x / g, y / g);
	}
	F operator * (const F &f) {
		long long x = a * f.a, y = b * f.b;
		long long g = __gcd(x, y);
		return F(x / g, y / g);
	}
	F operator / (const F &f) {
		long long x = a * f.b, y = b * f.a;
		long long g = __gcd(x, y);
		return F(x / g, y / g);
	}
};
const double pi = acos(-1);
const double eps = 1e-8;
inline int sign(double a) {
	return a < -eps ? -1 : a > eps;
}
inline int cmp(double a, double b) {
	return sign(a - b);
}
double add(double a, double b) {
	if (abs(a + b) < eps * (abs(a) + abs(b))) return 0;
	return a + b;
}
typedef double data;
struct P {
	data x, y;
	P() {}
	P(data x, data y) : x(x), y(y) {}
	P operator + (const P &p) { return P(x + p.x, y + p.y); }
	P operator - (const P &p) { return P(x - p.x, y - p.y); }
	P operator * (data d) { return P(x * d, y * d); }
	P operator / (data d) { return P(x / d, y / d); }
	bool operator  == (const P &p) {
		return cmp(x, p.x) == 0 && cmp(y, p.y) == 0;
	}
	data dot(const P &p) { return x * p.x + y * p.y; }
	data det(const P &p) { return x * p.y - y * p.x; }
	double disto(const P &p) { return (*this - p).abs(); }
	double alpha() { return atan2(y, x); }
	double abs() { return sqrt(abs2()); }
	data abs2() { return x * x + y * y; }
	P rot90() { return P(-y, x); }
	P unit() { return *this / abs(); }
	bool operator < (P p) const {
		int c = cmp(x, p.x);
		if (c) return c == -1;
		return cmp(y, p.y) == -1;
	}
	friend istream& operator>>(istream &in, P &p) {
		in >> p.x >> p.y;
		return in;
	}
	friend ostream& operator<<(ostream& out, P &p) {
		out << p.x << ' ' << p.y;
		return out;
	}
};
struct L {
	P ps[2];
	L() {}
	L(P a, P b) {
		ps[0] = a, ps[1] = b;
	}
	P& operator[] (int i) { return ps[i]; }
	P dir() { return ps[1] - ps[0]; }
	bool include(P p) { return sign((ps[1] - ps[0]).det(p - ps[0])) > 0; }
	L push() {
		P delta = (ps[1] - ps[0]).rot90().unit() * eps;
		return {ps[0] - delta, ps[1] - delta};
	}
};
P projection(P p1, P p2, P q) {
	P dir = p2 - p1;
	return p1 + dir * (dir.dot(q - p1) / dir.abs2());
}
P reflection(P p1, P p2, P q) {
	return projection(p1, p2, q) * 2 - q;
}
bool is_midddle(data a, data m, data b) {
	return sign(a - m) == 0 || sign(b - m) == 0 || (a < m != b < m);
}
bool is_midddle(P a, P m, P b) {
	return is_midddle(a.x, m.x, b.x) && is_midddle(a.y, m.y, b.y);
}
bool on_seg(P p1, P p2, P q) {
	return sign((p1 - q).det(p2 - q)) == 0 && (p1 - q).dot(p2 - q) <= 0;
	// return (p1 - q).det(p2 - q) == 0 && is_middle(p1, q, p2);
}
bool intersect(data l1, data r1, data l2, data r2) {
	if (l1 > r1) swap(l1, r1);
	if (l2 > r2) swap(l2, r2);
	return cmp(r1, l2) != -1 && cmp(r2, l1) != -1;
}
bool intersect(P p1, P p2, P q1, P q2) {
	return intersect(p1.x, p2.x, q1.x, q2.x) && intersect(p1.y, p2.y, q1.y, q2.y)
	       && sign((p2 - p1).det(q1 - p1)) * sign((p2 - p1).det(q2 - p1)) <= 0
	       && sign((q2 - q1).det(p1 - q1)) * sign((q2 - q1).det(q2 - q1)) <= 0;
}
P seg_intersection(P p1, P p2, P q1, P q2) {
	data a1 = (q2 - q1).det(p1 - q1), a2 = -(q2 - p1).det(p2 - q1);
	return (p1 * a2 + p2 * a1) / (a1 + a2);
}
P line_intersection(P p1, P p2, P q1, P q2) {
	return p1 + (p2 - p1) * ((q2 - q1).det(q1 - p1) / (q2 - q1).det(p2 - p1));
}
data nearest(P p1, P p2, P q) {
	P h = projection(p1, p2, q);
	if (is_midddle(p1, h, p2)) return q.disto(h);
	return min(p1.disto(q), p2.disto(q));
}
data seg_dist(P p1, P p2, P q1, P q2) {
	if (intersect(p1, p2, q1, q2)) return 0;
	return min(min(nearest(p1, p2, q1), nearest(p1, p2, q2)), min(nearest(q1, q2, p1), nearest(q1, q2, p2)));
}
data area(vector<P> &ps) {
	data res = 0;
	int n = ps.size();
	for (int i = 0; i < n; i++) {
		res += ps[i].det(ps[(i + 1) % n]);
	}
	return abs(res / 2);
}
bool is_convex(vector<P> &ps) {
	int n = ps.size();
	for (int i = 0; i < n; i++) {
		data a1 = (ps[(i + 1) % n] - ps[i]).det(ps[(i + 2) % n] - ps[(i + 1) % n]);
		data a2 = (ps[(i + 2) % n] - ps[(i + 1) % n]).det(ps[(i + 3) % n] - ps[(i + 2) % n]);
		if (a1 * a2 < 0) {
			return false;
		}
	}
	return true;
}
int contain(vector<P> &ps, P p) {
	int n = ps.size(), res = 0;
	for (int i = 0; i < n; i++) {
		P u = ps[i], v = ps[(i + 1) % n];
		if (on_seg(u, v, p)) return 1;
		if (cmp(u.y, v.y) <= 0) swap(u, v);
		if (cmp(p.y, u.y) > 0 || cmp(p.y, v.y) <= 0) continue;
		res ^= (u - p).det(v - p) > 0;
	}
	return res * 2;
	//2:inside 1:on_seg 0:outside
}
vector<P> convex_hull(vector<P> &ps) {
	int n = ps.size();
	sort(ps.begin(), ps.end());
	int k = 0;
	vector<P> qs(n << 1);
	for (int i = 0; i < n; i++) {
		while (k > 1 && sign((qs[k - 1] - qs[k - 2]).det(ps[i] - qs[k - 1])) <= 0) k--;
		qs[k++] = ps[i];
	}
	for (int i = n - 2, t = k; i >= 0; i--) {
		while (k > t && sign((qs[k - 1] - qs[k - 2]).det(ps[i] - qs[k - 1])) <= 0) k--;
		qs[k++] = ps[i];
	}
	qs.resize(k - 1);
	return qs;
}
vector<P> convex_hull_nonstrict(vector<P> &ps) {
	int n = ps.size();
	sort(ps.begin(), ps.end());
	int k = 0;
	vector<P> qs(n << 1);
	for (int i = 0; i < n; i++) {
		while (k > 1 && sign((qs[k - 1] - qs[k - 2]).det(ps[i] - qs[k - 1])) < 0) k--;
		qs[k++] = ps[i];
	}
	for (int i = n - 2, t = k; i >= 0; i--) {
		while (k > t && sign((qs[k - 1] - qs[k - 2]).det(ps[i] - qs[k - 1])) < 0) k--;
		qs[k++] = ps[i];
	}
	qs.resize(k - 1);
	return qs;
}
data convex_diameter(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;
	double res = ps[i].disto(ps[j]);
	do {
		if ((ps[(i + 1) % n] - ps[i]).det(ps[(j + 1) % n] - ps[j]) >= 0) {
			(++j) %= n;
		} else {
			(++i) %= n;
		}
		res = max(res, ps[i].disto(ps[j]));
	} while (i != is || j != js);
	return res;
}
vector<P> convex_cut(vector<P> &ps, P q1, P q2) {
	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 = sign((q2 - q1).det(p1 - q1)), d2 = sign((q2 - q1).det(p2 - q1));
		if (d1 >= 0) qs.push_back(p1); // left-half
		if (d1 * d2 < 0) qs.push_back(line_intersection(p1, p2, q1, q2));
	}
	return qs;
}
data closest_pair(vector<P> &ps, int l, int r) {
	// ps should be sorted before this
	if (r - l <= 5) {
		double res = 1e100;
		for (int i = l; i < r; i++) {
			for (int j = l; j < i; j++) {
				res = min(res, ps[i].disto(ps[j]));
			}
		}
		return res;
	}
	int m = (l + r) >> 1;
	double res = min(closest_pair(ps, l, m), closest_pair(ps, m, r));
	vector<P> qs;
	for (int i = l; i < r; i++) {
		if (abs(ps[i].x - ps[m].x) <= res) {
			qs.push_back(ps[i]);
		}
	}
	sort(qs.begin(), qs.end(), [](const P & a, const P & b) { return a.y < b.y; });
	for (int i = 1, sz = qs.size(); i < sz; i++) {
		for (int j = i - 1; j >= 0 && qs[j].y >= qs[i].y - res; j--) {
			res = min(res, qs[i].disto(qs[j]));
		}
	}
	return res;
}
P circum_center(P p0, P p1, P p2) {
	double a1 = p1.x - p0.x, b1 = p1.y - p0.y, c1 = (a1 * a1 + b1 * b1) / 2;
	double a2 = p2.x - p0.x, b2 = p2.y - p0.y, c2 = (a2 * a2 + b2 * b2) / 2;
	double d = a1 * b2 - a2 * b1;
	return P(p0.x + (c1 * b2 - c2 * b1) / d, p0.y + (a1 * c2 - a2 * c1) / d);
}
int circle_intersect(P o1, data r1, P o2, data r2) {
	data d = (o1 - o2).abs2();
	if (cmp(d, (r1 + r2) * (r1 + r2)) == 1) return 4;
	if (cmp(d, (r1 + r2) * (r1 + r2)) == 0) return 3;
	if (cmp(d, abs(r1 - r2) * abs(r1 - r2)) == 1) return 2;
	if (cmp(d, abs(r1 - r2) * abs(r1 - r2)) == 0) return 1;
	return 0;
}
vector<P> circle_intersection(P o, data r, P p1, P p2) {
	data 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 = max(d, 0.0);
	P m = p1 - (p2 - p1) * (x / y), dr = (p2 - p1) * (sqrt(d) / y);
	return {m - dr, m + dr};
}
vector<P> circle_intersection(P o1, data r1, P o2, data r2) {
	// need to check whether two circles are the same
	data d = o1.disto(o2);
	int tmp = circle_intersect(o1, r1, o2, r2);
	if (tmp == 4 || tmp == 0) return {};
	d = min(d, r1 + r2);
	double 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};
}
vector<P> tangent(P o, data r, P p) {
	data x = (p - o).abs2(), d = x - r * r;
	if (sign(d) <= 0) return {};
	P q1 = o + (p - o) * (r * r / x);
	P q2 = (p - o).rot90() * (r * sqrt(d) / x);
	return {q1 - q2, q1 + q2};
}
vector<L> extangent(P o1, data r1, P o2, data r2) {
	vector<L> res;
	if (cmp(r1, r2) == 0) {
		P dr = (o2 - o1).unit().rot90() * r1;
		res.emplace_back(o1 + dr, o2 + dr);
		res.emplace_back(o1 - dr, o2 - dr);
	} else {
		P p = (o2 * r1 - o1 * r2) / (r1 - r2);
		vector<P> ps = tangent(o1, r1, p), qs = tangent(o2, r2, p);
		int sz = min((int)ps.size(), (int)qs.size());
		for (int i = 0; i < sz; i++) {
			res.emplace_back(ps[i], qs[i]);
		}
	}
	return res;
}
vector<L> intangent(P o1, data r1, P o2, data r2) {
	vector<L> res;
	P p = (o2 * r1 + o1 * r2) / (r1 + r2);
	vector<P> ps = tangent(o1, r1, p), qs = tangent(o2, r2, p);
	int sz = min((int)ps.size(), (int)qs.size());
	for (int i = 0; i < sz; i++) {
		res.emplace_back(ps[i], qs[i]);
	}
	return res;
}

int main() {
#ifdef LOCAL_DEFINE
	// freopen("data.in", "rt", stdin);
	//freopen("data.out", "w", stdout);
	auto _start = chrono::high_resolution_clock::now();
#endif

	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(15);
	P c1, c2; double r1, r2;
	cin >> c1 >> r1 >> c2 >> r2;
	vector<L> line = extangent(c1, r1, c2, r2);
	vector<P> ans;
	for(L &l : line) {
		ans.push_back(l.ps[0]);
	}
	line = intangent(c1, r1, c2, r2);
	for (L &l : line) {
		ans.push_back(l.ps[0]);
	}
	int tmp = circle_intersect(c1, r1, c2, r2);
	if (tmp == 3 || tmp == 1) {
		vector<P> tmp = circle_intersection(c1, r1, c2, r2);
		for(P p : tmp) {
			ans.push_back(p);
		}
	}
	sort(ans.begin(), ans.end());
	ans.erase(unique(ans.begin(), ans.end()), ans.end());
	for (P &p : ans) {
		cout << p << '\n';
	}

#ifdef LOCAL_DEFINE
	auto _end = chrono::high_resolution_clock::now();
	cerr << "elapsed time: " << chrono::duration<double, milli>(_end - _start).count() << " ms\n";
#endif
	return 0;
}


Measured memory_bytes: 3308