Problem:
Problem E: Immortal Jewels

English text is not available in this practice contest.

ある時ある貴族が，ある貧乏な国のおてんばで勇敢なお姫様に惚れ込み結婚を申し込んだ．お姫様は貴族にある条件を出した．その条件とは「不死の宝石」と呼ばれている宝石を大量に持ってくることであった．不死の宝石は，ある山の特定の場所でしか取ることができない非常に希少な宝石である．しかも大変壊れやすいため，採取するには特別な方法が必要だった．

不死の宝石は円形をしており，二次元空間上に複数存在している．この宝石を取るには，ある特別な金属の棒でそれらを吸着する必要がある．金属の棒は無限の長さを持つ直線であり，太さは無視することができる．宝石は一つ一つ異なる強さの磁力を持っており，金属がその磁力に反応するほど十分に近ければ宝石は吸着される．具体的には，金属と宝石の表面との距離を d ，宝石の磁力の強さを m としたとき，

0 ≤ d ≤ m

であれば宝石は金属に吸着される．逆に，金属の棒と宝石がその磁力よりも離れている場合は吸着できない．また，棒が少しでも宝石を貫通してしまった場合にも，その宝石が壊れてしまうため吸着できない．

例を見てみよう．下の図は二次元空間上に置かれた宝石の例である．宝石1から宝石6まであり，磁力はそれぞれ1, 0, 1, 1, 1, 2であるとする．

図 E-1: 宝石の配置例

上の図に加えて金属の棒を配置した例が下の図である．宝石を吸着した結果も表に示してある．この例の場合，宝石3は磁力の届く範囲より離れており，また宝石4は棒に貫通されているため吸着できないが，残りの4つは全て吸着することができる．

図 E-2: 金属の棒の配置例

宝石名

磁力

金属との距離

吸着できるか

宝石1

1

約0.21

できる

宝石2

0

0

できる

宝石3

1

約5.37

できない

宝石4

1

貫通している

できない

宝石5

1

約0.97

できる

宝石6

2

約0.53

できる

表 E-3: 吸着結果

貴族は全財産を注ぎ込み，特別な金属の棒を必死に探し求めた．しかしながら，この金属も非常に貴重であったため，結局たった一本しか入手することができなかった．したがって吸着のチャンスはただ一回しか無い．

あなたはある貴族に仕えるプログラマーである．あなたの仕事は，与えられた二次元上の宝石の配置に対して，金属の棒をうまく配置したときに，最大で何個の宝石を吸着することができるかを求めるプログラムを書くことである．

Input

入力は複数のデータセットから成り， 1つのデータセットは以下の形式で与えられる．

N

x1 y1 r1 m1

x2 y2 r2 m2

...

xN yN rN mN

データセットの最初の行は宝石の数 N (1 ≤ N ≤ 50) を表している．続く N 行の各行には4つの整数 xi, yi, ri, mi (-1000 ≤ xi, yi ≤ 1000, 1 ≤ ri ≤ 100, 0 ≤ mi ≤ 100) が記述されており，宝石の位置，大きさ，および磁力を表す．すなわち，宝石 i は中心を(xi, yi)とし半径が ri であるような円形をしており，その磁力は mi である．宝石は互いに重なり合うことは無い．

入力の終わりは，0のみからなる行で表される．

Output

各データセットについて， 一度に吸着することができる宝石の最大数を1行に出力せよ．

Sample Input

6
-2 -2 1 1
2 2 2 0
5 7 1 1
8 0 3 1
13 4 1 1
16 1 1 2
3
0 0 2 1
10 0 2 1
0 10 2 1
3
0 0 2 1
10 0 2 1
0 6 2 1
3
0 0 2 1
10 0 2 1
0 4 2 1
1
0 0 1 1
0

Output for the Sample Input

4
2
3
3
1

Solution in C++ (judge verdict: Accepted)

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
using namespace std;

#define dump(a) (cerr << (#a) << " = " << (a) << endl)
#define FOR(it,c) for(__typeof((c).begin())it=(c).begin(); it!=(c).end();++it)
#define RFOR(it,c) for(__typeof((c).rbegin())it=(c).rbegin(); it!=(c).rend();++it)
#define exist(c, v) (find((c).begin(), (c).end(), (v)) != (c).end())

template<class T> inline void chmax(T& a, const T& b) { if(b > a) a = b; }
template<class T> inline void chmin(T& a, const T& b) { if(b < a) a = b; }

template<typename T, typename U> ostream& operator<<(ostream& os, const pair<T, U>& p) {
	os << '(' << p.first << ", " << p.second << ')';
	return os;
}

template<typename T> ostream& operator<<(ostream& os, const vector<T>& v) {
	copy(v.begin(), v.end(), ostream_iterator<T>(os, " "));
	return os;
}

const double EPS = 1e-9;

struct point {
	double x, y;
	point():x(0), y(0) {}
	point(double x, double y):x(x), y(y) {}
	point(const point& p):x(p.x), y(p.y) {}

	point operator+ (const point& p) const {
		return point(x + p.x, y + p.y);
	}

	point operator- (const point& p) const {
		return point(x - p.x, y - p.y);
	}

	point operator* (const double s) const {
		return point(x * s, y * s);
	}

	point operator* (const point& p) const {
		return point(x * p.x - y * p.y, x * p.y + y * p.x);
	}

	point operator/ (const double s) const {
		return point(x / s, y / s);
	}

	bool operator< (const point& p) const {
		return x + EPS < p.x || abs(x - p.x) < EPS && y + EPS < p.y;
	}

	bool operator== (const point& p) const {
		return abs(x - p.x) < EPS && abs(y - p.y) < EPS;
	}
};

inline point rotate90(const point& p) {
	return point(-p.y, p.x);
}

inline point rotate(const point& p, const double theta) {
	const double s = sin(theta), c = cos(theta);
	return point(c * p.x - s * p.y, s * p.x + c * p.y);
}

inline double angle(const point& p) {
	return atan2(p.y, p.x);
}

inline double abs(const point& p) {
	return sqrt(p.x * p.x + p.y * p.y);
}

inline double norm(const point& p) {
	return p.x * p.x + p.y * p.y;
}

inline double dot(const point& l, const point& r) {
	return l.x * r.x + l.y * r.y;
}

inline double cross(const point& l, const point& r) {
	return l.x * r.y - l.y * r.x;
}

struct line {
	point a, b;
	line(point a, point b):a(a), b(b){}
};

struct segment {
	point a, b;
	segment(point a, point b):a(a), b(b){}
};

struct circle {
	point c;
	double r;
	circle(point c, double r):c(c), r(r){}
};

typedef vector<point> polygon;

inline int ccw(const point& a, point b, point c) {
	b = b - a;
	c = c - a;
	const double tmp = cross(b, c);
	if(tmp > EPS) return 1; // ccw
	if(tmp < -EPS) return -1; // cw
	if(dot(b, c) < 0) return 2; // c, a, b 順に一直線上
	if(norm(b) < norm(c)) return -2; // a, b, c 順に一直線上
	return 0; //a, c, b 順で一直線上
}

inline point projection(const line& l, const point& p) {
	const point dif = l.b - l.a;
	const double tmp = dot(p - l.a, dif) / norm(dif);
	return l.a + dif * tmp;
}

inline bool intersect(const line& l, const line& m) {
	return abs(cross(l.b - l.a, m.b - m.a)) > EPS || abs(cross(l.b - l.a, m.b - l.a)) < EPS;
}

inline bool intersect(const line& l, const segment& s) {
	return cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < EPS;
}

inline bool intersect(const line& l, const point& p) {
	return abs(ccw(l.a, l.b, p)) != -1;
}

inline bool intersect(const segment& s, const segment& t) {
	return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
		ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

inline bool intersect(const segment& s, const point& p) {
	return ccw(s.a, s.b, p) == 0;
}

inline bool intersect(const circle& c, const point& p) {
	return abs(c.c - p) <= c.r + EPS;
}

inline bool intersect(const circle& c, const circle& d) {
	return abs(c.c - d.c) <= c.r + d.r && abs(c.c - d.c) >= abs(c.r - d.r);
}

inline double dist(const line& l, const point& p) {
	return abs(cross((l.b - l.a), (p - l.a))) / abs(l.b - l.a);
}

inline double dist(const line& l, const line& m) {
	return intersect(l, m) ? 0 : dist(l, m.a);
}

inline double dist(const line& l, const segment& s) {
	return intersect(l, s) ? 0 : min(dist(l, s.a), dist(l, s.b));
}

inline double dist(const segment& s, const point& p) {
	if(dot((s.b - s.a), (p - s.a)) < 0) return abs(p - s.a);
	if(dot((s.a - s.b), (p - s.b)) < 0) return abs(p - s.b);
	return dist(line(s.a, s.b), p);
}

inline double dist(const segment& s, const segment& t) {
	if(intersect(s, t))
		return 0;
	return min(min(dist(s, t.a), dist(s, t.b)), min(dist(t, s.a), dist(t, s.b)));
}

inline double dist(const point& a, const point& b) {
	return abs(a - b);
}

inline bool intersect(const circle& c, const segment& s) {
	return dist(s, c.c) < c.r + EPS;
}

inline point crosspoint(const line& l, const line& m) {
	const double tmp = cross(l.b - l.a, m.b - m.a);
	if(abs(tmp) < EPS) // 平行
		return l.a;
	return m.a + (m.b - m.a) * cross(l.b - l.a, l.a - m.a) * (1.0 / tmp);
}

inline point crosspoint(const segment& s, const segment& t) {
	if(!intersect(s, t)) // 交点を持たない
		return s.a; // 用改善

	const double tmp = cross(s.b - s.a, t.b - t.a);
	if(abs(tmp) < EPS) { // 一直線上
		if(intersect(s, t.a)) return t.a;
		if(intersect(s, t.b)) return t.b;
		if(intersect(t, s.a)) return s.a;
		return s.b;
	}

	return t.a + (t.b - t.a) * cross(s.b - s.a, s.b - t.a) * (1.0 / tmp);
}

//先に，intersectを用いて交差判定を行うこと
inline vector<point> crosspoint(const circle &c, const circle& d) {
	vector<point> res;
	if(abs(c.c - d.c) < EPS) // 中心の座標が同じ
		return res;

	const double tmp = abs(c.c - d.c);
	const double rc = (tmp * tmp + c.r * c.r - d.r * d.r) / (tmp + tmp);
	const double rs = sqrt(c.r * c.r - rc * rc);
	const point diff = (d.c - c.c) / tmp;
	res.push_back(point(c.c + diff * point(rc, rs)));
	res.push_back(point(c.c + diff * point(rc, -rs)));
	return res;
}

inline vector<point> crosspoint(const circle& c, const line& l) {
	vector<point> res;
	const point h = projection(l, c.c);
	const double d = abs(h - c.c);
	if(d > c.r - EPS) {
		res.push_back(h);
	}
	else if(d <= c.r - EPS) {
		point v = l.b - l.a;
		v = v * sqrt(c.r * c.r - d * d) / abs(v);
		res.push_back(h + v);
		res.push_back(h - v);
	}

	return res;
}

inline vector<point> crosspoint(const circle& c, const segment& s) {
	vector<point> res;
	vector<point> tmp = crosspoint(c, line(s.a, s.b));
	for(int i = 0; i < tmp.size(); ++i)
		if(intersect(s, tmp[i]))
			res.push_back(tmp[i]);

	return res;
}

inline double areaTriangle(point a, point b, const point& c) {
	a = a - c;
	b = b - c;
	return fabs(a.x * b.y - b.x * a.y) / 2.0;
}

inline double area(const polygon& p) {
	const int num = p.size();
	if(num < 3)
		return 0;

	if(num == 3)
		return areaTriangle(p[0], p[1], p[2]);

	double res = cross(p[num - 1], p[0]);
	for(int i = 1; i < num; ++i)
		res += cross(p[i - 1], p[i]);

	return abs(res) * 0.5;
}

// L.aからL.bの方向を見た場合に，点aが左側に来る．
inline line bisector(const point& a, const point& b) {
	return line(point((a.x - a.y + b.x + b.y) / 2.0, (a.y + a.x + b.y - b.x) / 2.0),
				point((a.x + a.y + b.x - b.y) / 2.0, (a.y - a.x + b.y + b.x) / 2.0));
}

// L.aからL.bを向いた時の左側を残して切断する．
inline polygon convex_cut(const polygon& p, const line& l) {
	const int num = p.size();
	polygon res;
	for(int i = 0; i < num; ++i) {
		const int next = (i + 1) % num;
		const int tmp = ccw(l.a, l.b, p[i]);
		if(tmp != -1)
			res.push_back(p[i]);

		if(tmp * ccw(l.a, l.b, p[next]) < 0)
			res.push_back(crosspoint(l, line(p[i], p[next])));
	}

	return res;
}

vector<point> tangent(const circle& c, const point& p) {
	const double x = norm(p - c.c);
	double d = x - c.r * c.r;
	if(d < -EPS) return vector<point>();
	d = max(d, 0.0);
	const point q1 = (p - c.c) * (c.r * c.r / x);
	const point q2 = rotate90((p - c.c) * (-c.r * sqrt(d) / x));
	vector<point> res;
	res.push_back(c.c + q1 - q2);
	res.push_back(c.c + q1 + q2);
	return res;
}

vector<line> tangent(const circle &a, const circle& b) {
	vector<line> res;
	if(abs(a.r - b.r) < EPS) {
		point dir = b.c - a.c;
		dir = rotate90(dir * (a.r / abs(dir)));
		res.push_back(line(a.c + dir, b.c + dir));
		res.push_back(line(a.c - dir, b.c - dir));
	}
	else {
		point p = a.c * (-b.r) + b.c * a.r;
		p = p * (1.0 / (a.r - b.r));
		vector<point> ps = tangent(a, p), qs = tangent(b, p);
		for(int i = 0; i < min<int>(ps.size(), qs.size()); ++i)
			res.push_back(line(ps[i], qs[i]));
	}

	point p = a.c * b.r + b.c * a.r;
	p = p * (1.0 / (a.r + b.r));
	vector<point> ps = tangent(a, p), qs = tangent(b, p);
	for(int i = 0; i < min<int>(ps.size(), qs.size()); ++i)
		res.push_back(line(ps[i], qs[i]));

	return res;
}

// 三角形と点の包含関係，境界は含まない．
// 含める場合は == -1を <= 0とする．
bool contain_triangle(const polygon& triangle, const point& p) {
	const int d1 = ccw(p, triangle[0], triangle[1]), d2 = ccw(p, triangle[1], triangle[2]), d3 = ccw(p, triangle[2], triangle[0]);
	return !(d1 * d2 == -1 || d2 * d3 == -1 || d3 * d1 == -1);
}

// 多角形と点の包含関係．境界を含む．
bool contain(const polygon& P, const point& p) {
	const int n = P.size();
	bool res = false;
	for(int i = 0; i < n; ++i) {
		point v1 = P[i] - p, v2 = P[(i + 1) % n] - p;
		if(v1.y > v2.y)
			swap(v1, v2);

		if(v1.y < EPS && EPS < v2.y && cross(v1, v2) > EPS)
			res = !res;

		if(abs(cross(v1, v2)) < EPS && dot(v1, v2) < EPS)
			return true; // 境界を含まないなら，falseにする．
	}

	return res;
}

int cnt(const vector<circle>& jewels, const vector<circle>& mags, const vector<line>& vl) {
	int res = 0;
	for(int i = 0; i < (int)vl.size(); ++i) {
		int cnt = 0;
		for(int j = 0; j < (int)jewels.size(); ++j) {
			const double d = dist(vl[i], jewels[j].c);
			if(jewels[j].r < d + EPS && d < mags[j].r + EPS)
				++cnt;
		}
		chmax(res, cnt);
	}

	return res;
}

int main() {
	cin.tie(0);
	ios::sync_with_stdio(false);

	for(int n; cin >> n, n;) {
		vector<circle> jewels, mags;
		jewels.reserve(n);
		mags.reserve(n);
		for(int i = 0; i < n; ++i) {
			int x, y, r, m;
			cin >> x >> y >> r >> m;
			const point pos(x, y);
			jewels.push_back(circle(pos, r));
			mags.push_back(circle(pos, r + m));
		}

		int ans = (n > 1 ? 2 : 1);
		for(int i = 0; i < n; ++i) {
			for(int j = i + 1; j < n; ++j) {
				chmax(ans, cnt(jewels, mags, tangent(jewels[i], jewels[j])));
				chmax(ans, cnt(jewels, mags, tangent(jewels[i], mags[j])));
				chmax(ans, cnt(jewels, mags, tangent(mags[i], jewels[j])));
				chmax(ans, cnt(jewels, mags, tangent(mags[i], mags[j])));
			}
		}

		cout << ans << endl;
	}
	

	return EXIT_SUCCESS;
}