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

Submitted solutions:

code_snippet: #include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <algorithm>

using namespace std;
const int maxn = 100 + 5;
const double PI = acos(-1);

struct Point
{
    double x, y;
    Point(){}
    Point(double x_, double y_):x(x_),y(y_){}
};
typedef Point Vector;
int n;

struct Circle:public Point
{
    Point c;
    double r, m;
    Circle(){}
    Circle(Point c, double r, double m):c(c.x, c.y),r(r),m(m){}
    Point getPoint(double a) {
        return Point(c.x + cos(a) * r, c.y + sin(a) * r);
    }
};
vector<Circle> p;

Vector operator + (Vector a, Vector b)  { return Vector(a.x + b.x, a.y + b.y); }
Vector operator - (Point a, Point b)    { return Point(a.x - b.x, a.y - b.y); }
Vector operator * (Vector a, double p)  { return Vector(a.x * p, a.y * p); }
Vector operator / (Vector a, double p)  { return Vector(a.x / p, a.y / p); }

bool operator < (const Point& a, const Point& b)
{
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

const double eps = 1e-12;
int dcmp(double x)
{
    if(fabs(x) < eps)    return 0;
    else                return x < 0 ? -1 : 1;
}

bool operator == (const Point& a, const Point& b)
{
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}

double dot(Vector a, Vector b)      { return a.x * b.x + a.y * b.y; }
double length(Vector a)             { return sqrt(dot(a, a)); }
double angle(Vector a, Vector b)    { return acos(dot(a, b) / length(a) / length(b)); }
double angle(Vector v)              { return atan2(v.y, v.x); }
double cross(Vector a, Vector b)    { return a.x * b.y - b.x * a.y; }
double dist(Point p1,Point p2)      { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); }

double distToLine(Point p, Point a, Point b) {
	Vector v1 = b - a, v2 = p - a;
	return fabs(cross(v1, v2)) / length(v1);
}

int getTangents(Circle A, Circle B, vector<Point>& a, vector<Point>& b)
{
	int cnt = 0;
	if(A.r < B.r) { swap(A, B); swap(a, b); }
	int d2 = (A.c.x - B.c.x)*(A.c.x - B.c.x) + (A.c.y - B.c.y)*(A.c.y - B.c.y);
	int rdiff = A.r - B.r;
	int rsum = A.r + B.r;
	if(d2 < rdiff * rdiff)	return 0;

	double base = atan2(B.c.y - A.c.y, B.c.x - A.c.x);
	if(d2 == 0 && A.r == B.r)	return -1;
	if(d2 == rdiff * rdiff) {
		a.push_back(A.getPoint(base));
		b.push_back(B.getPoint(base));
		return 1;
	}

	//有共切线
	double ang = acos((A.r - B.r) / sqrt(d2));
	a.push_back(A.getPoint(base + ang));	b.push_back(B.getPoint(base + ang));
	a.push_back(A.getPoint(base - ang));	b.push_back(B.getPoint(base - ang));

	if(d2 == rsum * rsum) {
		a.push_back(A.getPoint(base));
		b.push_back(B.getPoint(PI + base));
	} else if(d2 > rsum * rsum) {
		double ang = acos((A.r + B.r) / sqrt(d2));
		a.push_back(A.getPoint(base + ang));	b.push_back(B.getPoint(PI + base + ang));
		a.push_back(A.getPoint(base - ang));	b.push_back(B.getPoint(PI + base - ang));
	}
	return a.size();
}

int cmp(double a, double b)
{
	const double diff = a - b;
	if (fabs(diff) < eps)
		return 0;
	else if (diff < 0)
		return -1;
	else
		return 1;
}

int calculate(const Point& a, const Point& b)
{
	int res = 0;
	for(int i = 0; i < n; i++) {
		double d = distToLine(p[i].c, a, b);
		if(cmp(d, p[i].r) >= 0 && cmp(d, p[i].r + p[i].m) <= 0)	res++;
		if(cmp(d, p[i].r) < 0)	return -1;
	}
	return res;
}

int main()
{
	//freopen("in.txt", "r", stdin);
    while(~scanf("%d", &n)) {
		if(n == 0)	break;
		p.clear();
		double x, y, r, m;
		for(int i = 0; i < n; i++) {
			scanf("%lf%lf%lf%lf", &x, &y, &r, &m);
			p.push_back(Circle(Point(x, y), r, m));
		}

		vector<Point> a, b;
		for(int i = 0; i < n; i++) {
			for(int j = 0; j < n; j++) if(i != j) {
				Circle A1 = p[i], A2 = p[i];
				Circle B1 = p[j], B2 = p[j];
				B2.r += B2.m;	A2.r += A2.m;

				getTangents(A1, B1, a, b);
				getTangents(A1, B2, a, b);
				getTangents(A2, B1, a, b);
				getTangents(A2, B2, a, b);
			}
		}

		int num = a.size(), ans = 1;
		for(int i = 0; i < num; i++) {
			int res = count_if(p.begin(), p.end(), [&](const Circle& C) {
								return cmp(distToLine(C.c, a[i], b[i]), C.r) >= 0
								&& cmp(distToLine(C.c, a[i], b[i]), C.r + C.m) <= 0; });
			ans = max(ans, res);
		}
		printf("%d\n", ans);
    }
    return 0;
}
accuracy: 1
time_ms: 230
memory_kb: 6524
final_rank: 18

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

using T = double;
const T eps = 1e-12;
using Point = complex<T>;
using Poly = vector<Point>;
#define X real()
#define Y imag()
template <typename T> inline bool eq(const T &a, const T &b) {
    return fabs(a - b) < eps;
}
bool cmp(const Point &a, const Point &b) {
    auto sub = [&](Point a) {
        return (a.Y < 0 ? -1 : (a.Y == 0 && a.X >= 0 ? 0 : 1));
    };
    if (sub(a) != sub(b))
        return sub(a) < sub(b);
    return a.Y * b.X < a.X * b.Y;
}
struct Line {
    Point a, b, dir;
    Line() {}
    Line(Point _a, Point _b) : a(_a), b(_b), dir(b - a) {}
    Line(T A, T B, T C) {
        if (eq(A, .0)) {
            a = Point(0, C / B), b = Point(1 / C / B);
        } else if (eq(B, .0)) {
            a = Point(C / A, 0), b = Point(C / A, 1);
        } else {
            a = Point(0, C / B), b = Point(C / A, 0);
        }
    }
};
struct Segment : Line {
    Segment() {}
    Segment(Point _a, Point _b) : Line(_a, _b) {}
};
struct Circle {
    Point p;
    T r;
    Circle() {}
    Circle(Point _p, T _r) : p(_p), r(_r) {}
};

istream &operator>>(istream &is, Point &p) {
    T x, y;
    is >> x >> y;
    p = Point(x, y);
    return is;
}
ostream &operator<<(ostream &os, Point &p) {
    os << fixed << setprecision(12) << p.X << ' ' << p.Y;
    return os;
}
Point unit(const Point &a) {
    return a / abs(a);
}
T dot(const Point &a, const Point &b) {
    return a.X * b.X + a.Y * b.Y;
}
T cross(const Point &a, const Point &b) {
    return a.X * b.Y - a.Y * b.X;
}
Point rot(const Point &a, const T &theta) {
    return Point(cos(theta) * a.X - sin(theta) * a.Y,
                 sin(theta) * a.X + cos(theta) * a.Y);
}
Point rot90(const Point &a) {
    return Point(-a.Y, a.X);
}
T arg(const Point &a, const Point &b, const Point &c) {
    double ret = acos(dot(a - b, c - b) / abs(a - b) / abs(c - b));
    if (cross(a - b, c - b) < 0)
        ret = -ret;
    return ret;
}

Point Projection(const Line &l, const Point &p) {
    T t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}
Point Projection(const Segment &l, const Point &p) {
    T t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}
Point Reflection(const Line &l, const Point &p) {
    return p + (Projection(l, p) - p) * 2.;
}
int ccw(const Point &a, Point b, Point c) {
    b -= a;
    c -= a;
    if (cross(b, c) > eps)
        return 1; // ccw

    if (cross(b, c) < -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

}
bool isOrthogonal(const Line &a, const Line &b) {
    return eq(dot(a.b - a.a, b.b - b.a), .0);
}
bool isParallel(const Line &a, const Line &b) {
    return eq(cross(a.b - a.a, b.b - b.a), .0);
}
bool isIntersect(const Segment &a, const Segment &b) {
    return ccw(a.a, a.b, b.a) * ccw(a.a, a.b, b.b) <= 0 and
           ccw(b.a, b.b, a.a) * ccw(b.a, b.b, a.b) <= 0;
}
int isIntersect(const Circle &a, const Circle &b) {
    T d = abs(a.p - b.p);
    if (d > a.r + b.r + eps)
        return 4;
    if (eq(d, a.r + b.r))
        return 3;
    if (eq(d, abs(a.r - b.r)))
        return 1;
    if (d < abs(a.r - b.r) - eps)
        return 0;
    return 2;
}
T Dist(const Line &a, const Point &b) {
    Point c = Projection(a, b);
    return abs(c - b);
}
T Dist(const Segment &a, const Point &b) {
    if (dot(a.b - a.a, b - a.a) < eps)
        return abs(b - a.a);
    if (dot(a.a - a.b, b - a.b) < eps)
        return abs(b - a.b);
    return abs(cross(a.b - a.a, b - a.a)) / abs(a.b - a.a);
}
T Dist(const Segment &a, const Segment &b) {
    if (isIntersect(a, b))
        return .0;
    T res = min({Dist(a, b.a), Dist(a, b.b), Dist(b, a.a), Dist(b, a.b)});
    return res;
}
Point Intersection(const Line &a, const Line &b) {
    T d1 = cross(a.b - a.a, b.b - b.a);
    T d2 = cross(a.b - a.a, a.b - b.a);
    if (eq(d1, 0.) and eq(d2, 0.))
        return b.a;
    return b.a + (b.b - b.a) * (d2 / d1);
}
Poly Intersection(const Circle &a, const Line &b) {
    Poly res;
    T d = Dist(b, a.p);
    if (d > a.r + eps)
        return res;
    Point h = Projection(b, a.p);
    if (eq(d, a.r)) {
        res.push_back(h);
        return res;
    }
    Point e = unit(b.b - b.a);
    T ph = sqrt(a.r * a.r - d * d);
    res.push_back(h - e * ph);
    res.push_back(h + e * ph);
    return res;
}
Poly Intersection(const Circle &a, const Segment &b) {
    Line c(b.a, b.b);
    Poly sub = Intersection(a, c);
    double xmi = min(b.a.X, b.b.X), xma = max(b.a.X, b.b.X);
    double ymi = min(b.a.Y, b.b.Y), yma = max(b.a.Y, b.b.Y);
    Poly res;
    rep(i, 0, sub.size()) {
        if (xmi <= sub[i].X + eps and sub[i].X - eps <= xma and
            ymi <= sub[i].Y + eps and sub[i].Y - eps <= yma) {
            res.push_back(sub[i]);
        }
    }
    return res;
}
Poly Intersection(const Circle &a, const Circle &b) {
    Poly res;
    int mode = isIntersect(a, b);
    T d = abs(a.p - b.p);
    if (mode == 4 or mode == 0)
        return res;
    if (mode == 3) {
        T t = a.r / (a.r + b.r);
        res.push_back(a.p + (b.p - a.p) * t);
        return res;
    }
    if (mode == 1) {
        if (b.r < a.r - eps) {
            res.push_back(a.p + (b.p - a.p) * (a.r / d));
        } else {
            res.push_back(b.p + (a.p - b.p) * (b.r / d));
        }
        return res;
    }
    T rc = (a.r * a.r + d * d - b.r * b.r) / d / 2.;
    T rs = sqrt(a.r * a.r - rc * rc);
    if (a.r - abs(rc) < eps)
        rs = 0;
    Point e = unit(b.p - a.p);
    res.push_back(a.p + rc * e + rs * e * Point(0, 1));
    res.push_back(a.p + rc * e + rs * e * Point(0, -1));
    return res;
}
Poly HalfplaneIntersection(vector<Line> &H) {
    sort(ALL(H), [&](Line &l1, Line &l2) { return cmp(l1.dir, l2.dir); });
    auto outside = [&](Line &L, Point p) -> bool {
        return cross(L.dir, p - L.a) < -eps;
    };
    deque<Line> deq;
    int sz = 0;
    rep(i, 0, SZ(H)) {
        while (sz > 1 and
               outside(H[i], Intersection(deq[sz - 1], deq[sz - 2]))) {
            deq.pop_back();
            sz--;
        }
        while (sz > 1 and outside(H[i], Intersection(deq[0], deq[1]))) {
            deq.pop_front();
            sz--;
        }
        if (sz > 0 and fabs(cross(H[i].dir, deq[sz - 1].dir)) < eps) {
            if (dot(H[i].dir, deq[sz - 1].dir) < 0) {
                return {};
            }
            if (outside(H[i], deq[sz - 1].a)) {
                deq.pop_back();
                sz--;
            } else
                continue;
        }
        deq.push_back(H[i]);
        sz++;
    }

    while (sz > 2 and outside(deq[0], Intersection(deq[sz - 1], deq[sz - 2]))) {
        deq.pop_back();
        sz--;
    }
    while (sz > 2 and outside(deq[sz - 1], Intersection(deq[0], deq[1]))) {
        deq.pop_front();
        sz--;
    }
    if (sz < 3)
        return {};
    deq.push_back(deq.front());
    Poly ret;
    rep(i, 0, sz) ret.push_back(Intersection(deq[i], deq[i + 1]));
    return ret;
}

T Area(const Poly &a) {
    T res = 0;
    int n = a.size();
    rep(i, 0, n) res += cross(a[i], a[(i + 1) % n]);
    return fabs(res / 2.);
}
T Area(const Poly &a, const Circle &b) {
    int n = a.size();
    if (n < 3)
        return .0;
    auto rec = [&](auto self, const Circle &c, const Point &p1,
                   const Point &p2) {
        Point va = c.p - p1, vb = c.p - p2;
        T f = cross(va, vb), res = .0;
        if (eq(f, .0))
            return res;
        if (max(abs(va), abs(vb)) < c.r + eps)
            return f;
        if (Dist(Segment(p1, p2), c.p) > c.r - eps)
            return c.r * c.r * arg(vb * conj(va));
        auto u = Intersection(c, Segment(p1, p2));
        Poly sub;
        sub.push_back(p1);
        for (auto &x : u)
            sub.push_back(x);
        sub.push_back(p2);
        rep(i, 0, sub.size() - 1) res += self(self, c, sub[i], sub[i + 1]);
        return res;
    };
    T res = .0;
    rep(i, 0, n) res += rec(rec, b, a[i], a[(i + 1) % n]);
    return fabs(res / 2.);
}
T Area(const Circle &a, const Circle &b) {
    T d = abs(a.p - b.p);
    if (d >= a.r + b.r - eps)
        return .0;
    if (d <= abs(a.r - b.r) + eps) {
        T r = min(a.r, b.r);
        return M_PI * r * r;
    }
    T ath = acos((a.r * a.r + d * d - b.r * b.r) / d / a.r / 2.);
    T res = a.r * a.r * (ath - sin(ath * 2) / 2.);
    T bth = acos((b.r * b.r + d * d - a.r * a.r) / d / b.r / 2.);
    res += b.r * b.r * (bth - sin(bth * 2) / 2.);
    return fabs(res);
}
bool isConvex(const Poly &a) {
    int n = a.size();
    int cur, pre, nxt;
    rep(i, 0, n) {
        pre = (i - 1 + n) % n;
        nxt = (i + 1) % n;
        cur = i;
        if (ccw(a[pre], a[cur], a[nxt]) == -1)
            return 0;
    }
    return 1;
}
int isContained(const Poly &a,
                const Point &b) { // 0:not contain,1:on edge,2:contain

    bool res = 0;
    int n = a.size();
    rep(i, 0, n) {
        Point p = a[i] - b, q = a[(i + 1) % n] - b;
        if (p.Y > q.Y)
            swap(p, q);
        if (p.Y < eps and eps < q.Y and cross(p, q) > eps)
            res ^= 1;
        if (eq(cross(p, q), .0) and dot(p, q) < eps)
            return 1;
    }
    return (res ? 2 : 0);
}
Poly ConvexHull(Poly &a) {
    sort(ALL(a), [](const Point &p, const Point &q) {
        return (eq(p.Y, q.Y) ? p.X < q.X : p.Y < q.Y);
    });
    a.erase(unique(ALL(a)), a.end());
    int n = a.size(), k = 0;
    Poly res(n * 2);
    for (int i = 0; i < n; res[k++] = a[i++]) {
        while (k >= 2 and
               cross(res[k - 1] - res[k - 2], a[i] - res[k - 1]) < eps)
            k--;
    }
    for (int i = n - 2, t = k + 1; i >= 0; res[k++] = a[i--]) {
        while (k >= t and
               cross(res[k - 1] - res[k - 2], a[i] - res[k - 1]) < eps)
            k--;
    }
    res.resize(k - 1);
    return res;
}
T Diam(const Poly &a) {
    int n = a.size();
    int x = 0, y = 0;
    rep(i, 1, n) {
        if (a[i].Y > a[x].Y)
            x = i;
        if (a[i].Y < a[y].Y)
            y = i;
    }
    T res = abs(a[x] - a[y]);
    int i = x, j = y;
    do {
        if (cross(a[(i + 1) % n] - a[i], a[(j + 1) % n] - a[j]) < 0)
            i = (i + 1) % n;
        else
            j = (j + 1) % n;
        chmax(res, abs(a[i] - a[j]));
    } while (i != x or j != y);
    return res;
}
Poly Cut(const Poly &a, const Line &l) {
    int n = a.size();
    Poly res;
    rep(i, 0, n) {
        Point p = a[i], q = a[(i + 1) % n];
        if (ccw(l.a, l.b, p) != -1)
            res.push_back(p);
        if (ccw(l.a, l.b, p) * ccw(l.a, l.b, q) < 0)
            res.push_back(Intersection(Line(p, q), l));
    }
    return res;
}

T Closest(Poly &a) {
    int n = a.size();
    if (n <= 1)
        return 0;
    sort(ALL(a), [&](Point a, Point b) {
        return (eq(a.X, b.X) ? a.Y < b.Y : a.X < b.X);
    });
    Poly buf(n);
    auto rec = [&](auto self, int lb, int rb) -> T {
        if (rb - lb <= 1)
            return (T)INF;
        int mid = (lb + rb) >> 1;
        auto x = a[mid].X;
        T res = min(self(self, lb, mid), self(self, mid, rb));
        inplace_merge(a.begin() + lb, a.begin() + mid, a.begin() + rb,
                      [&](auto p, auto q) { return p.Y < q.Y; });
        int ptr = 0;
        rep(i, lb, rb) {
            if (abs(a[i].X - x) >= res)
                continue;
            rep(j, 0, ptr) {
                auto sub = a[i] - buf[ptr - 1 - j];
                if (sub.Y >= res)
                    break;
                chmin(res, abs(sub));
            }
            buf[ptr++] = a[i];
        }
        return res;
    };
    return rec(rec, 0, n);
}

Circle Incircle(const Point &a, const Point &b, const Point &c) {
    T A = abs(b - c), B = abs(c - a), C = abs(a - b);
    Point p(A * a.X + B * b.X + C * c.X, A * a.Y + B * b.Y + C * c.Y);
    p /= (A + B + C);
    T r = Dist(Line(a, b), p);
    return Circle(p, r);
}
Circle Circumcircle(const Point &a, const Point &b, const Point &c) {
    Line l1((a + b) / 2., (a + b) / 2. + (b - a) * Point(0, 1));
    Line l2((b + c) / 2., (b + c) / 2. + (c - b) * Point(0, 1));
    Point p = Intersection(l1, l2);
    return Circle(p, abs(p - a));
}
Poly tangent(const Point &a, const Circle &b) {
    return Intersection(b, Circle(a, sqrt(norm(b.p - a) - b.r * b.r)));
}
vector<Line> tangent(const Circle &a, const Circle &b) {
    vector<Line> res;
    T d = abs(a.p - b.p);
    if (eq(d, 0.))
        return res;
    Point u = unit(b.p - a.p);
    Point v = u * Point(0, 1);
    for (int t : {-1, 1}) {
        T h = (a.r + b.r * t) / d;
        if (eq(h * h, 1.)) {
            res.push_back(Line(a.p + (h > 0 ? u : -u) * a.r,
                               a.p + (h > 0 ? u : -u) * a.r + v));
        } else if (1 > h * h) {
            Point U = u * h, V = v * sqrt(1 - h * h);
            res.push_back(Line(a.p + (U + V) * a.r, b.p - (U + V) * (b.r * t)));
            res.push_back(Line(a.p + (U - V) * a.r, b.p - (U - V) * (b.r * t)));
        }
    }
    return res;
}

/**
 * @brief Geometry
 */

int COUNT(Line& L, vector<Point>& Center, vector<double>& R, vector<double>& M) {
    int N = Center.size();
    int Ret = 0;
    rep(i,0,N) {
        double D = Dist(L, Center[i]);
        if (R[i] - eps < D && D < R[i] + M[i] + eps) Ret++;
    }
    return Ret;
}

int main() {
while(1) {
    int N;
    cin >> N;
    if (N == 0) return 0;
    vector<double> PX(N), PY(N), R(N), M(N);
    vector<Point> Center(N);
    vector<Circle> C1, C2;
    rep(i,0,N) {
        cin >> PX[i] >> PY[i] >> R[i] >> M[i];
        Center[i] = Point(PX[i],PY[i]);
        Circle c1(Center[i],R[i]),  c2(Center[i],R[i]+M[i]);
        C1.push_back(c1);
        C2.push_back(c1), C2.push_back(c2);
    }
    int ANS = 1;
    rep(i,0,N*2) {
        rep(j,i+1,N*2) {
            if (i / 2 == j / 2) continue;
            vector<Line> LS = tangent(C2[i],C2[j]);
            for (Line L : LS) {
                chmax(ANS, COUNT(L,Center,R,M));
            }
        }
    }
    cout << ANS << endl;
}
}
accuracy: 1
time_ms: 290
memory_kb: 3604
final_rank: 12

code_snippet: #include <bits/stdc++.h>
//#include <ranges>
#define _USE_MATH_DEFINES
//#include <atcoder/all>
//using namespace atcoder;
//using mint = modint998244353;
//using mint = modint1000000007;
//using mint = modint;
using namespace std;

using ll = long long;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define rep2(i, s, n) for (ll i = (s); i < (ll)(n); i++)
#define rrep(i,a,b) for(int i=a;i>=b;i--)
#define fore(i,a) for(auto &i:a)
#define V vector<ll>
#define Vi vector<int>
#define Vd vector<double>
#define Vb vector<bool>
#define Vs vector<string>
#define Vc vector<char>
#define VV vector<V>
using P = pair<ll,ll>;
using G = vector<vector<int>>;
#define VP vector<P>
template<typename T> using min_priority_queue = priority_queue<T, vector<T>, greater<T>>;
#define all(a) (a).begin(),(a).end()
#define rall(a) (a).rbegin(),(a).rend()
#define INF 1LL << 60
#define inf 1e9
template <typename T>
bool chmax(T &a, const T& b) {
  if (a < b) {
    a = b;  // aをbで更新
    return true;
  }
return false;
}
template <typename T>
bool chmin(T &a, const T& b) {
  if (a > b) {
    a = b;  // aをbで更新
    return true;
  }
  return false;
}

long long combi(long long n, long long k) {
  if (n == k || k == 0)
    return 1;
  else {
    return combi(n - 1, k - 1) + combi(n - 1, k);
  }
}
//整数かどうか
bool isNumber(const string& str)
{
  for (const char &c : str) {
        if (std::isdigit(c) == 0) return false;
    }
    return true;
}
///*
//最大公約数
ll gcd(ll a, ll b){
  if(b==0){
    return a;
  }else{
    return gcd(b, a%b);
  }
}
//最小公倍数
ll lcm(ll a, ll b){
  ll g=gcd(a,b);
  return a/g*b;
}
//*/
//int di[] = {-1,0,1,0};
//int dj[] = {0,-1,0,1};

//s = regex_replace(s, regex("あ"), "う");
/*stiring で char を検索するときは
    s.find(c)!=string::npos
 */


/*//各桁の和
 int wa(int n){
  int sum =0;
  while(n>0){
    sum += n%10;
    n/=10;
  }
  return sum;
}
*/
/*
//階乗
 int ki(int i){
   int k = 1;
   for(int j = 1; j<=i; j++){
     k *= j;
   }
   return k;
 }
*/
/*log_x(b)
double logN(double x, double b) {
    return log(x) / log(b);
}
*/
//
/*//エラトステネスの篩 main関数内にsolve();を書き込む!!
const ll N = 101010;//求める範囲
Vb isp(N+1,true);
void solve(){
  isp[0] = false;
  isp[1] = false;
  for(ll i = 2; i+i<=N;i++){
    if(isp[i])for(ll j = 2; i*j<=N;j++)isp[i*j] = false;
  }
}
//
*/
//
/*
//約数列挙　O(√n)
vector<long long> divisor(long long n) {
    vector<long long> ret;
    for (long long i = 1; i * i <= n; i++) {
        if (n % i == 0) {
            ret.push_back(i);
            if (i * i != n) ret.push_back(n / i);
        }
    }
    sort(ret.begin(), ret.end()); // 昇順に並べる
    return ret;
}
//
*/
//
/*
//素因数分解O(√n)
map< ll, ll > prime_factor(ll n) {
  map< ll, ll > ret;
  for(ll i = 2; i * i <= n; i++) {
    while(n % i == 0) {
      ret[i]++;
      n /= i;
    }
  }
  if(n != 1) ret[n] = 1;
  return ret;
}
//
*/

/*
ll modpow(ll x, ll n, ll mod){
 while(n){
   ll resu = 1;
   if(n&1)res = (res * x) %mod;
   x = (x*x)%mod;
   n>>=1;
 }
 return res;
}
*/
/*
//最小二乗法
//aのb乗をmで割ったあまりを返す関数
//変数aはa^1→a^2→a^4→a^8→…と変化
ll power(ll a,ll b, ll m){
  ll p = a,ans = 1;
  rep(i,60){
    ll wari = (1LL<<i);
    if((b/wari)%2==1){
      ans=(ans*p)%m;
    }
    p=(p*p)%m;
  }
  return ans;
}
*/
//
/*
template <typename T> bool next_combination(const T first, const T last, int k) {
    const T subset = first + k;
    // empty container | k = 0 | k == n 
    if (first == last || first == subset || last == subset) {
        return false;
    }
    T src = subset;
    while (first != src) {
        src--;
        if (*src < *(last - 1)) {
            T dest = subset;
            while (*src >= *dest) {
                dest++;
            }
            iter_swap(src, dest);
            rotate(src + 1, dest + 1, last);
            rotate(subset, subset + (last - dest) - 1, last);
            return true;
        }
    }
    // restore
    rotate(first, subset, last);
    return false;
}
//
*/
int ctoi(char c){
  if(c=='1')return 1;
  else if(c=='2')return 2;
  else if(c=='3')return 3;
  else if(c=='4')return 4;
  else if(c=='5')return 5;
  else if(c=='6')return 6;
  else if(c=='7')return 7;
  else if(c=='8')return 8;
  else if(c=='9')return 9;
  else if(c=='0')return 0;
  else return -inf;
}
//vector<int> dx = {1,0,-1,0,1,-1,-1,1};
//vector<int> dy = {0,1,0,-1,1,1,-1,-1};
//int dx[4] = { 0, 1, 0, -1 }, dy[4] = { -1, 0, 1, 0 };
int dx[8]={0,-1,-1,-1,0,1,1,1},dy[8]={1,1,0,-1,-1,-1,0,1};
//#define mod 998244353
//cout << mint.val() << endl;
//cout << fixed << setprecision(15) << y << endl;

//bit s に i番目のビットを立てる
#define bittate(s,i) s | (1LL<<i)
//bit sから i番目のビットを消す
#define bitkeshi(s,i) s^(1LL<<i)
//bit s が i番目のビットを含んでいるか
#define bitcheck(s,i) (s>>i)&1LL
//string str(bitset<32>(value).to_string<char, char_traits<char>, allocator<char> >());
#define yes "Yes"
#define no "No"
#define Yes "YES"
#define No "NO"


#include<complex>


namespace geometry{
    using ld=long double;
    using point=complex<ld>;
    const ld eps=1e-9;
    const ld PI=acos(ld(-1));
    inline bool equal(const ld &a,const ld &b){
        return fabs(a-b)<eps;
    }
    //単位ベクトル
    point unitVector(const point &a){
        return a/abs(a);
    }
    //法線ベクトル
    point normalVector(const point &a){
        return a*point(0,1);
    }
    //内積: a*b=|a||b|cosθ
    ld dot(const point &a, const point &b){
        return (a.real()*b.real()+a.imag()*b.imag());
    }
    //外積: a×b=|a||b|sinθ
    ld cross(const point &a, const point &b){
        return (a.real()*b.imag()-a.imag()*b.real());
    }
    //反時計回りに回転(ラジアン)
    point rotate(const point &p, const ld &theta){
        return point(cos(theta)*p.real()-sin(theta)*p.imag(),
                     sin(theta)*p.real()+cos(theta)*p.imag());
    }
    //ラジアンー＞度
    ld radianTodegree(const ld &radian){return radian *180/PI;}
    //度ー＞ラジアン
    ld degreeToradian(const ld &degree){return degree *PI/180;}
    //点の回転方向
    //点aを基準にしてb,cの位置関係
    int ccw(const point &a, point b,point c){
        b-=a,c-=a;
        //反時計回りのときー＞１
        if(cross(b,c)>eps)return 1;
        //時計回りのときー＞－１
        if(cross(b,c)<-eps)return -1;
        //c,a,bがこの順で同一直線上にあるとき
        if(dot(b,c)<0)return 2;
        //a,b,cがこの順で同一直線上にあるとき
        if(norm(b)<norm(c))return -2;
        //cが線分ab上にあるとき
        return 0;
    }
    //Line:直線
    //b-aで直線・線分
    struct Line{
        point a,b;
        Line()=default;
        Line(point a, point b) : a(a),b(b){}
        //ax+by=c
        Line(ld A, ld B, ld C){
            if(equal(A,0)){
                a=point(0,C/B),b=point(1,C/B);
            }else if(equal(B,0)){
                b=point(C/A,0),b=point(C/A,1);//ホントにb⁇
            }else{
                a=point(0,C/B),b=point(C/A,0);
            }
        }
    };
    //線分
    struct Segment : Line{
        Segment()=default;
        Segment(point a, point b):Line(a,b){}
        ld get_dist(){return abs(a-b);}
    };
    //円
    //pが中心の位置ベクトル，rは半径
    struct circle{
        point p;
        ld r;
        circle()=default;
        circle(point p, ld r) : p(p),r(r){};
    };
    //2直線の直交判定 内積0
    bool isOrthogonal(const Line &a, const Line &b){
        return equal(dot(a.b-a.a,b.b-b.a),0);
    }
    //2直線の平行判定 外積0
    bool isParaleel(const Line &a, const Line &b){
        return equal(cross(a.b-a.a,b.b-b.a),0);
    }
    //点cが直線ab上にあるか
    bool isPointOnLine(const point &a,const point &b, const point &c){
        return isParaleel(Line(a,b),Line(a,c));
    }
    //点cが線分ab上にあるか
    bool isPointOnSegment(const point &a, const point &b, const point &c){
        //|a-c|+|c-b|<=|a-b|なら線分上
        return (abs(a-c)+abs(c-b)<abs(a-b)+eps);
    }
    //直線lと点pのキョリ
    ld distanceBetweenLineAndPoint(const Line &l, const point &p){
        return abs(cross(l.b-l.a,p-l.a))/abs(l.b-l.a);
    }
    //線分lと点pのキョリを求める
    //点pから線分lのどこかへの最短距離
    ld distanceBetweenSegmentAndPoint(const Segment &l, const point &p){
        if(dot(l.b-l.a,p-l.a)<eps)return abs(p-l.a);
        if(dot(l.a-l.b,p-l.b)<eps)return abs(p-l.b);
        return abs(cross(l.b-l.a,p-l.a))/abs(l.b-l.a);
    }
    //直線s,tの交点
    point crossPoint(const Line &s, const Line &t){
        ld d1=cross(s.b-s.a,t.b-t.a);
        ld d2=cross(s.b-s.a,s.b-t.a);
        if(equal(abs(d1),0) and equal(abs(d2),0))return t.a;
        return t.a+(t.b-t.a)*(d2/d1);
    }
    //線分s,tの交点
    point crossPoint(const Segment &s,const Segment &t){
        return crossPoint(Line(s),Line(t));
    }
    //線分sと線分tが交差しているか
    //bound:線分の境界を含むか
    bool isIntersect(const Segment &s, const Segment &t, bool bound){
        return ccw(s.a,s.b,t.a)*ccw(s.a,s.b,t.b)<bound and
                ccw(t.a,t.b,s.a)*ccw(t.a,t.b,s.b)<bound;
    }
    //線分s,t間のキョリ
    ld distanceBetweenSegments(const Segment &s,const Segment &t){
        if(isIntersect(s,t,1))return (ld)(0);
        ld ans=distanceBetweenSegmentAndPoint(s,t.a);
        ans=min(ans,distanceBetweenSegmentAndPoint(s,t.b));
        ans=min(ans,distanceBetweenSegmentAndPoint(t,s.a));
        ans=min(ans,distanceBetweenSegmentAndPoint(t,s.b));
        return ans;
    }
    //射影(projection)
    //直線(線分)lに点pから引いた垂線の足を求める
    point projection(const Line &l, const point &p){
        ld t=dot(p-l.a,l.a-l.b)/norm(l.a-l.b);
        return l.a+(l.a-l.b)*t;
    }
    point projection(const Segment &l, const point &p){
        ld t=dot(p-l.a,l.a-l.b)/norm(l.a-l.b);
        return l.a+(l.a-l.b)*t;
    }
    //反射(reflection)
    //直線lを対象軸として点pと線対称の位置にある点
    point reflection(const Line &l, const point &p){
        return p+(projection(l,p)-p)*(ld)2.0;
    }
    //2円の交差判定
    //返り値は共通接戦の数
    int isIntersect(const circle &c1, const circle &c2){
        ld d=abs(c1.p-c2.p);
        //2円が離れているとき
        if(d>c1.r+c2.r+eps)return 4;
        //外接しているとき
        if(equal(d,c1.r+c2.r))return 3;
        //内接しているとき
        if(equal(d,abs(c1.r-c2.r)))return 1;
        //内包しているとき
        if(d<abs(c1.r-c2.r)-eps)return 0;
        return 2;
    }
    //2円の交点
    vector<point> crossPoint(const circle &c1, const circle &c2){
        vector<point>ret;
        int mode=isIntersect(c1,c2);
        //2円の中心間のキョリ
        ld d=abs(c1.p-c2.p);
        //2円が離れているとき
        if(mode==4)return ret;
        //一方の円が他方の円に内包されているとき
        if(mode==0)return ret;
        //2円が外接するとき
        if(mode==3){
            ld t=c1.r/(c1.r+c2.r);
            ret.emplace_back(c1.p+(c2.p-c1.p)*t);
            return ret;
        }
        //内接しているとき
        if(mode==1){
            if(c2.r<c1.r-eps){
                ret.emplace_back(c1.p+(c2.p-c1.p)*(c1.r/d));
            }else{
                ret.emplace_back(c2.p+(c1.p-c2.p)*(c2.r/d));
            }
            return ret;
        }
        //2円が重なるとき
        ld rc1=(c1.r*c1.r+d*d-c2.r*c2.r)/(2*d);
        ld rs1=sqrt(c1.r*c1.r-rc1*rc1);
        if(c1.r-abs(rc1)<eps)rs1=0;
        point e12=(c2.p-c1.p)/abs(c2.p-c1.p);
        ret.emplace_back(c1.p+rc1*e12+rs1*e12*point(0,1));
        ret.emplace_back(c1.p+rc1*e12+rs1*e12*point(0,-1));
        return ret;
    }
    //点pが円cの内部(円周上も含む)に入っているかどうか
    bool isIncircle(const circle &c, const point &p){
        ld d=abs(c.p-p);
        return (equal(d,c.r) or d<c.r-eps);
    }
    //円cと直線lの交点
    vector<point> crossPoint(const circle &c, const Line &l){
        vector<point> ret;
        ld d=distanceBetweenLineAndPoint(l,c.p);
        //交点を持たない
        if(d>c.r+eps)return ret;
        //接する
        point h=projection(l,c.p);
        if(equal(d,c.r)){
            ret.emplace_back(h);
            return ret;
        }
        point e=unitVector(l.b-l.a);
        ld ph=sqrt(c.r*c.r-d*d);
        ret.emplace_back(h-e*ph);
        ret.emplace_back(h+e*ph);
        return ret;
    }
    //点pを通る円cの接線
    //2本あるので接点のみを返す
    vector<point> tangentToCircle(const point &p, const circle &c){
        return crossPoint(c,circle(p,sqrt(norm(c.p-p)-c.r*c.r)));
    }
    //円の共通接線
    vector<Line> tangent(const circle &a, const circle &b){
        vector<Line>ret;
        //2円の中心間のキョリ
        ld g=abs(a.p-b.p);
        if(equal(g,0))return ret;
        point u=unitVector(b.p-a.p);
        point v=rotate(u,PI/2);
        for(int s : {-1,1}){
            ld h=(a.r+b.r*s)/g;
            if(equal(h*h,1)){
                ret.emplace_back(a.p+(h>0 ? u:-u)*a.r,
                                 a.p+(h>0 ? u:-u)*a.r+v);
            }else if(1-h*h>0){
                point U=u*h,W=v*sqrt(1-h*h);
                ret.emplace_back(a.p+(U+W)*a.r,
                                b.p-(U+W)*(b.r*s));
                ret.emplace_back(a.p+(U-W)*a.r,
                                b.p-(U-W)*(b.r*s));
                
            }
        }
        return ret;
    }
    //多角形の面積を求める
    ld polygonArea(const vector<point> &p){
        ld ret=0;
        int n=p.size();
        for(int i=0; i<n-1; i++)ret+=cross(p[i],p[i+1]);
        ret+=cross(p[n-1],p[0]);
        return ret*0.5;
    }
    //凸多角形かどうか
    bool isConvex(const vector<point> &p){
        int n=p.size();
        int now,pre,nxt;
        for(int i=0;i<n;i++){
            pre=(i-1+n)%n;
            nxt=(i+n)%n;
            now=i;
            if(ccw(p[pre],p[now],p[nxt])==-1)return false;
        }
        return true;
    }
    //凸包O(NlogN)
    vector<point> ConvexHull(vector<point> p){
        int n=(int)p.size(),k=0;
        sort(p.begin(),p.end(),[](const point &a, const point &b){
            return (a.real()!=b.real() ? a.real()<b.real() : a.imag()<b.imag());
        });
        vector<point> ch(2*n);
        //一直線上の3点を含める->(<eps)
        //含めない->(<eps)
        for(int i=0; i<n; ch[k++]=p[i++]){
            while(k>=2 and 
                    cross(ch[k-1]-ch[k-2],p[i]-ch[k-1])<eps)k--;
        }
        for(int i=n-2,t=k+1;i>=0;ch[k++]=p[i--]){
            while(k>=t and 
                    cross(ch[k-1]-ch[k-2],p[i]-ch[k-1])<eps)k--;
        }
        ch.resize(k-1);
        return ch;
    }
    //多角形gに点pが含まれているか
    //含まれる：2，辺上にある：1，含まれない:0
    int isContained(const vector<point> &g, const point &p){
        bool in =false;
        int n=(int)g.size();
        for(int i=0;i<n;i++){
            point a=g[i]-p,b=g[(i+1)%n]-p;
            if(imag(a)>imag(b))swap(a,b);
            if(imag(a)<=eps and eps<imag(b) and cross(a,b)<-eps)in=!in;
            if(cross(a,b)==0 and dot(a,b)<=0)return 1;
        }
        return (in ? 2 : 0);
    }
    // 凸多角形pを直線lで切断し、その左側を返す
    vector<point> convexCut(vector<point> p, Line l){
        vector<point> ret;
        int sz=(int)p.size();
        for(int i=0;i<sz;i++){
            point now=p[i];
            point nxt=p[i==sz-1 ? 0 : i+1];
            if(ccw(l.a,l.b,now)!=-1)ret.emplace_back(now);
            if(ccw(l.a,l.b,now)*ccw(l.a,l.b,nxt)<0){
                ret.emplace_back(crossPoint(Line(now,nxt),l));
            }
        }
        return ret;
    }
}

using namespace geometry;


ll f(vector<point> &po, Line li,vector<ld>& r, vector<ld>& m,int n){
    ll re=0;
    rep(i,n){
        auto dis=distanceBetweenLineAndPoint(li,po[i]);
        if(r[i]<=dis+eps and dis<=r[i]+m[i]+eps)re++;
    }
    return re;
}



int main(){
  int n;
  while(cin >> n){
    if(n==0)break;
    //int n;
    vector<ld> r(n),m(n);
    vector<point> po(n);

    rep(i,n){
      long double x,y;
      cin >> x >> y >> r[i] >> m[i];
      po[i]=point(x,y);
    }
    if(n==1){
        cout << 1 << endl;
        continue;
    }
    ll ma=-1;
    rep(i,n)rep2(j,i+1,n){
      //金属金属
      circle a,b;
      vector<Line> kyotusessenn;
      a.p=po[i],a.r=r[i];
      b.p=po[j],b.r=r[j];
      kyotusessenn=tangent(a,b);
      fore(e,kyotusessenn){
        chmax(ma,f(po,e,r,m,n));
      }
      //金属磁力
      a.p=po[i],a.r=r[i];
      b.p=po[j],b.r=r[j]+m[j];
      kyotusessenn=tangent(a,b);
      fore(e,kyotusessenn){
        chmax(ma,f(po,e,r,m,n));
      }
      //磁力金属
      a.p=po[i],a.r=r[i]+m[i];
      b.p=po[j],b.r=r[j];
      kyotusessenn=tangent(a,b);
      fore(e,kyotusessenn){
        chmax(ma,f(po,e,r,m,n));
      }
      //磁力磁力
      a.p=po[i],a.r=r[i]+m[i];
      b.p=po[j],b.r=r[j]+m[j];
      kyotusessenn=tangent(a,b);
      fore(e,kyotusessenn){
        chmax(ma,f(po,e,r,m,n));
      }
    }
    cout << ma << endl;
  }

  
}
accuracy: 1
time_ms: 460
memory_kb: 3840
final_rank: 14

code_snippet: #line 1 "2201.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/2201"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/TypeAlias.hpp"

#include <cstdint>
#include <cstddef>

namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa
#line 4 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#include <iostream>
#include <iomanip>

namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Real.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Real.hpp"

#include <cmath>
#include <cassert>

namespace zawa {

namespace geometryR2 {

using Real = long double;

namespace internal {

Real EPS{1e-12};
constexpr i32 negative{-1};
constexpr i32 zero{};
constexpr i32 positive{1};

} // namespace internal

Real& Eps() {
    return internal::EPS;
}

i32 Sign(Real value) {
    if (value < -Eps()) return internal::negative;
    if (value > Eps()) return internal::positive;
    return internal::zero;
}

bool Zero(Real value) {
    return Sign(value) == internal::zero;
}

bool Positive(Real value) {
    return Sign(value) == internal::positive;
}

bool Negative(Real value) {
    return Sign(value) == internal::negative;
}

bool Equal(Real a, Real b) {
    return Zero(a - b);
}

bool Smaller(Real a, Real b) {
    return Negative(a - b);
}

bool Bigger(Real a, Real b) {
    return Positive(a - b);
}

Real Square(Real value) {
    return (Zero(value) ? value : value * value);
}

Real Sqrt(Real value) {
    assert(!Negative(value));
    return (Zero(value) ? value : sqrtl(value));
}

Real Abs(Real value) {
    return (Negative(value) ? -value : value);
}

} // namespace geometryR2
 
} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Point.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Angle.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Angle.hpp"

#line 6 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Angle.hpp"

namespace zawa {

namespace geometryR2 {

constexpr Real PI{acosl(-1)};
constexpr Real TAU{static_cast<Real>(2) * PI};

constexpr Real ArcToRadian(Real arc) {
    return (arc * PI) / static_cast<Real>(180);
}

constexpr Real RadianToArc(Real radian) {
    return (radian * static_cast<Real>(180)) / PI;
}

} // namespace geometryR2

} // namespace zawa
#line 5 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Point.hpp"

#line 9 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Point.hpp"

namespace zawa {

namespace geometryR2 {

class Point {
private:
    Real x_{}, y_{};
public:
    /* constructor */
    Point() = default;
    Point(Real x, Real y) : x_{x}, y_{y} {}

    /* getter, setter */
    Real x() const {
        return x_;
    }
    Real& x() {
        return x_;
    }
    Real y() const {
        return y_;
    }
    Real& y() {
        return y_;
    }

    /* operator */
    Point& operator+=(const Point& rhs) {
        x_ += rhs.x();
        y_ += rhs.y();
        return *this;
    }
    friend Point operator+(const Point& lhs, const Point& rhs) {
        return Point{lhs} += rhs;
    }
    Point operator+() const {
        return *this;
    }
    Point& operator-=(const Point& rhs) {
        x_ -= rhs.x();
        y_ -= rhs.y();
        return *this;
    }
    friend Point operator-(const Point& lhs, const Point& rhs) {
        return Point{lhs} -= rhs;
    }
    Point operator-() const {
        return Point{} - *this;
    }
    Point& operator*=(Real k) {
        x_ *= k;
        y_ *= k;
        return *this;
    }
    friend Point operator*(Real k, const Point& p) {
        return Point{p} *= k;
    }
    friend Point operator*(const Point& p, Real k) {
        return Point{p} *= k;
    }
    Point& operator/=(Real k) {
        assert(!Zero(k));
        x_ /= k;
        y_ /= k;
        return *this;
    }
    friend Point operator/(Real k, const Point& p) {
        return Point{p} /= k;
    }
    friend Point operator/(const Point& p, Real k) {
        return Point{p} /= k;
    }
    friend bool operator==(const Point& lhs, const Point& rhs) {
        return Equal(lhs.x(), rhs.x()) and Equal(lhs.y(), rhs.y());
    }
    friend bool operator!=(const Point& lhs, const Point& rhs) {
        return !Equal(lhs.x(), rhs.x()) or !Equal(lhs.y(), rhs.y());
    }
    friend bool operator<(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and Smaller(lhs.y(), rhs.y()));
    }
    friend bool operator<=(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and (Smaller(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend bool operator>(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and Bigger(lhs.y(), rhs.y()));
    }
    friend bool operator>=(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and (Bigger(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend std::istream& operator>>(std::istream& is, Point& p) {
        is >> p.x_ >> p.y_;
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, const Point& p) {
        os << '(' << p.x_ << ',' << p.y_ << ')';
        return os;
    }
    
    /* member function */
    Real normSquare() const {
        return Square(x_) + Square(y_);
    }
    Real norm() const {
        return Sqrt(normSquare());
    }
    void normalize() {
        assert((*this) != Point{});
        (*this) /= norm(); 
    }
    Point normalized() const {
        Point res{*this};
        res.normalize();
        return res;
    }
    Point rotated(Real radian) const {
        return Point{
            x_ * cosl(radian) - y_ * sinl(radian),
            x_ * sinl(radian) + y_ * cosl(radian)
        };
    }
    void rotate(Real radian) {
        *this = rotated(radian); 
    }
    Point rotatedByArc(Real arc) const {
        return rotated(ArcToRadian(arc));
    }
    void rotateByArc(Real arc) {
        *this = rotatedByArc(arc);
    }
    Real argument() const {
        return (Negative(y_) ? TAU : static_cast<Real>(0)) + atan2l(y_, x_);
    }
    Real argumentByArc() const {
        return RadianToArc(argument());
    }

    /* friend function */
    friend Real Dot(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.x() + lhs.y() * rhs.y();
    }
    friend Real Cross(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.y() - lhs.y() * rhs.x();
    }
    friend Real Argument(const Point& lhs, const Point& rhs) {
        return rhs.argument() - lhs.argument();
    }
    friend bool ArgComp(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.argument(), rhs.argument());
    }
};

using Vector = Point;

} // namespace geometryR2

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Line.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Relation.hpp"

#line 5 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Relation.hpp"

namespace zawa {

namespace geometryR2 {

enum RELATION {
    // p0 -> p1 -> p2の順で直線上に並んでいる
    ONLINE_FRONT = -2,
    // (p1 - p0) -> (p2 - p0)が時計回りになっている
    CLOCKWISE,
    // p0 -> p2 -> p1の順で直線上に並んでいる
    ON_SEGMENT,
    // (p1 - p0) -> (p2 - p0)が反時計回りになっている
    COUNTER_CLOCKWISE,
    // p2 -> p0 -> p1、またはp1 -> p0 -> p2の順で直線上に並んでいる
    ONLINE_BACK
};

RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
    Point a{p1 - p0}, b{p2 - p0};
    if (Positive(Cross(a, b))) return COUNTER_CLOCKWISE;
    if (Negative(Cross(a, b))) return CLOCKWISE;
    if (Negative(Dot(a, b))) return ONLINE_BACK;
    if (Smaller(a.normSquare(), b.normSquare())) return ONLINE_FRONT;
    return ON_SEGMENT;
};

} // namespace geometryR2

} // namespace zawa
#line 5 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Line.hpp"

#line 7 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Line.hpp"

namespace zawa {

namespace geometryR2 {

class Line {
private:
    Point p0_{}, p1_{};
public:
    /* constructor */
    Line() = default;
    Line(const Point& p0, const Point& p1) : p0_{p0}, p1_{p1} {}
    // y = ax + b 
    Line(Real a, Real b) : p0_{static_cast<Real>(0), b}, p1_{static_cast<Real>(1), a + b} {}

    /* getter, setter */
    const Point& p0() const {
        return p0_;
    }
    Point& p0() {
        return p0_;
    }
    const Point& p1() const {
        return p1_;
    }
    Point& p1() {
        return p1_;
    }

    /* operator */
    friend bool operator==(const Line& l0, const Line& l1) {
        return Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l1.p0())) and Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l0.p0()));
    }
    friend bool operator!=(const Line& l0, const Line& l1) {
        return !Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l1.p0())) or !Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l0.p0()));
    }

    /* member function */
    bool valid() const {
        return p0_ != p1_;
    }
    Vector slope() const {
        assert(valid());
        return Vector{p1() - p0()}.normalized();
    }
};

} // namespace geometryR2

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Circle.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Distance/PointAndPoint.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Distance/PointAndPoint.hpp"

namespace zawa {

namespace geometryR2 {

Real Distance(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.norm();
}

Real DistanceSquare(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.normSquare();
}

} // namespace geometryR2

} // namespace zawa
#line 7 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Circle.hpp"

#line 9 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Circle.hpp"
#include <utility>

namespace zawa {

namespace geometryR2 {

class Circle {
private:
    Point center_{};
    Real radius_{};
public:
    /* constructor */
    Circle() = default;
    Circle(const Point& center, Real radius) : center_{center}, radius_{radius} {
        assert(!Negative(radius));
    }
    Circle(Real x, Real y, Real r) : center_{x, y}, radius_{r} {
        assert(!Negative(r));
    }

    /* getter setter */
    const Point& center() const {
        return center_;
    }
    Point& center() {
        return center_;
    }
    Real radius() const {
        return radius_;
    }
    Real& radius() {
        return radius_;
    }

    /* operator */
    friend bool operator==(const Circle& lhs, const Circle& rhs) {
        return lhs.center() == rhs.center() and Equal(lhs.radius(), rhs.radius());
    }
    friend bool operator!=(const Circle& lhs, const Circle& rhs) {
        return lhs.center() != rhs.center() or !Equal(lhs.radius(), rhs.radius());
    }

    /* friend function */
    friend u32 NumberCommonTangent(const Circle& c0, const Circle& c1) {
        Real dist{DistanceSquare(c0.center(), c1.center())};
        Real down{Square(Abs(c0.radius() - c1.radius()))};
        if (Smaller(dist, down)) return 0;
        if (Equal(dist, down)) return 1;
        Real up{Square(c0.radius() + c1.radius())};
        if (Smaller(dist, up)) return 2;
        if (Equal(dist, up)) return 3;
        return 4;
    }
};

} // namespace geometryR2

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Distance/LineAndPoint.hpp"

#line 7 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Distance/LineAndPoint.hpp"

#line 9 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Distance/LineAndPoint.hpp"

namespace zawa {

namespace geometryR2 {

Real Distance(const Line& l, const Point& p) {
    assert(l.valid());
    return Abs(Cross(p - l.p0(), l.p1() - l.p0())) / Distance(l.p1(), l.p0());
}

bool PointOnLine(const Line& l, const Point& p) {
    assert(l.valid());
    return Zero(Distance(l, p));
}

} // namespace geometryR2

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Tangent/CommonTangentBetweenCircles.hpp"

#line 8 "/home/zawatin/compro/cp-documentation/Src/GeometryR2/Tangent/CommonTangentBetweenCircles.hpp"

#include <vector>

namespace zawa {

namespace geometryR2 {

    std::vector<Line> CommonTangentBetweenCircles(const Circle& c0, const Circle& c1) {
        std::vector<Line> res;
        if (c0.center() == c1.center()) {
            return res; 
        }
        res.reserve(4);
        Real g{Distance(c0.center(), c1.center())};
        Vector u{Vector{c1.center() - c0.center()}.normalized()};
        Vector v{u.rotated(PI / Real{2})};
        for (i32 s : { -1, 1 }) {
            Real h{(c0.radius() + c1.radius() * s) / g};
            if (Equal(Square(h), Real{1})) {
                res.emplace_back(c0.center() + (Positive(h) ? u : -u) * c0.radius(),
                        c0.center() + (Positive(h) ? u : -u) * c0.radius() + v);
                                    
            }
            else if (Smaller(Square(h), Real{1})) {
                Point U{u * h}, V{v * Sqrt(Real{1} - Square(h))};
                res.emplace_back(c0.center() + (U + V) * c0.radius(),
                        c1.center() - (U + V) * (c1.radius() * s));
                res.emplace_back(c0.center() + (U - V) * c0.radius(),
                        c1.center() - (U - V) * (c1.radius() * s));
            }
        }
        return res;
    }

} // namespace geometryR2

} // namespace zawa
#line 10 "2201.test.cpp"

#line 12 "2201.test.cpp"

using namespace zawa;
using namespace geometryR2;

bool solve() {
    int N;
    std::cin >> N;
    if (N == 0) return false;
    std::vector<Point> P(N);
    std::vector<Real> R(N), M(N);
    for (int i{} ; i < N ; i++) {
        std::cin >> P[i] >> R[i] >> M[i];
    }
    auto check{[&](const Line& L) -> int {
        int res{};
        for (int i{} ; i < N ; i++) {
            Real d{Distance(L, P[i])};
            if (Smaller(d, R[i])) continue;
            if (Bigger(d, R[i] + M[i])) continue;
            res++;
        }
        return res;
    }};
    int ans{1};
    for (int i{} ; i < N ; i++) {
        for (int j{i + 1} ; j < N ; j++) {
            for (int bit{} ; bit < 4 ; bit++) {
                Circle lhs{P[i], R[i] + ((bit & (1 << 0)) ? M[i] : Real{})};
                Circle rhs{P[j], R[j] + ((bit & (1 << 1)) ? M[j] : Real{})};
                for (const Line& L : CommonTangentBetweenCircles(lhs, rhs)) {
                    ans = std::max(ans, check(L));
                }
            }
        }
    }
    std::cout << ans << '\n';
    return true;
}

int main() {
    SetFastIO();
    while (solve()) ;
}
accuracy: 1
time_ms: 220
memory_kb: 3460
final_rank: 9

code_snippet: #include <bits/stdc++.h>




namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa


namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa



namespace zawa {

namespace geometryR2 {

using Real = long double;

namespace internal {

Real EPS{1e-12};
constexpr i32 negative{-1};
constexpr i32 zero{};
constexpr i32 positive{1};

} // namespace internal

Real& Eps() {
    return internal::EPS;
}

i32 Sign(Real value) {
    if (value < -Eps()) return internal::negative;
    if (value > Eps()) return internal::positive;
    return internal::zero;
}

bool Zero(Real value) {
    return Sign(value) == internal::zero;
}

bool Positive(Real value) {
    return Sign(value) == internal::positive;
}

bool Negative(Real value) {
    return Sign(value) == internal::negative;
}

bool Equal(Real a, Real b) {
    return Zero(a - b);
}

bool Smaller(Real a, Real b) {
    return Negative(a - b);
}

bool Bigger(Real a, Real b) {
    return Positive(a - b);
}

Real Square(Real value) {
    return (Zero(value) ? value : value * value);
}

Real Sqrt(Real value) {
    assert(!Negative(value));
    return (Zero(value) ? value : sqrtl(value));
}

Real Abs(Real value) {
    return (Negative(value) ? -value : value);
}

} // namespace geometryR2
 
} // namespace zawa





namespace zawa {

namespace geometryR2 {

constexpr Real PI{acosl(-1)};
constexpr Real TAU{static_cast<Real>(2) * PI};

constexpr Real ArcToRadian(Real arc) {
    return (arc * PI) / static_cast<Real>(180);
}

constexpr Real RadianToArc(Real radian) {
    return (radian * static_cast<Real>(180)) / PI;
}

} // namespace geometryR2

} // namespace zawa


namespace zawa {

namespace geometryR2 {

class Point {
private:
    Real x_{}, y_{};
public:
    /* constructor */
    Point() = default;
    Point(Real x, Real y) : x_{x}, y_{y} {}

    /* getter, setter */
    Real x() const {
        return x_;
    }
    Real& x() {
        return x_;
    }
    Real y() const {
        return y_;
    }
    Real& y() {
        return y_;
    }

    /* operator */
    Point& operator+=(const Point& rhs) {
        x_ += rhs.x();
        y_ += rhs.y();
        return *this;
    }
    friend Point operator+(const Point& lhs, const Point& rhs) {
        return Point{lhs} += rhs;
    }
    Point operator+() const {
        return *this;
    }
    Point& operator-=(const Point& rhs) {
        x_ -= rhs.x();
        y_ -= rhs.y();
        return *this;
    }
    friend Point operator-(const Point& lhs, const Point& rhs) {
        return Point{lhs} -= rhs;
    }
    Point operator-() const {
        return Point{} - *this;
    }
    Point& operator*=(Real k) {
        x_ *= k;
        y_ *= k;
        return *this;
    }
    friend Point operator*(Real k, const Point& p) {
        return Point{p} *= k;
    }
    friend Point operator*(const Point& p, Real k) {
        return Point{p} *= k;
    }
    Point& operator/=(Real k) {
        assert(!Zero(k));
        x_ /= k;
        y_ /= k;
        return *this;
    }
    friend Point operator/(Real k, const Point& p) {
        return Point{p} /= k;
    }
    friend Point operator/(const Point& p, Real k) {
        return Point{p} /= k;
    }
    friend bool operator==(const Point& lhs, const Point& rhs) {
        return Equal(lhs.x(), rhs.x()) and Equal(lhs.y(), rhs.y());
    }
    friend bool operator!=(const Point& lhs, const Point& rhs) {
        return !Equal(lhs.x(), rhs.x()) or !Equal(lhs.y(), rhs.y());
    }
    friend bool operator<(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and Smaller(lhs.y(), rhs.y()));
    }
    friend bool operator<=(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.x(), rhs.x()) or 
            (Equal(lhs.x(), rhs.x()) and (Smaller(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend bool operator>(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and Bigger(lhs.y(), rhs.y()));
    }
    friend bool operator>=(const Point& lhs, const Point& rhs) {
        return Bigger(lhs.x(), rhs.x()) or
            (Equal(lhs.x(), rhs.x()) and (Bigger(lhs.y(), rhs.y()) or Equal(lhs.y(), rhs.y())));
    }
    friend std::istream& operator>>(std::istream& is, Point& p) {
        is >> p.x_ >> p.y_;
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, const Point& p) {
        os << '(' << p.x_ << ',' << p.y_ << ')';
        return os;
    }
    
    /* member function */
    Real normSquare() const {
        return Square(x_) + Square(y_);
    }
    Real norm() const {
        return Sqrt(normSquare());
    }
    void normalize() {
        assert((*this) != Point{});
        (*this) /= norm(); 
    }
    Point normalized() const {
        Point res{*this};
        res.normalize();
        return res;
    }
    Point rotated(Real radian) const {
        return Point{
            x_ * cosl(radian) - y_ * sinl(radian),
            x_ * sinl(radian) + y_ * cosl(radian)
        };
    }
    void rotate(Real radian) {
        *this = rotated(radian); 
    }
    Point rotatedByArc(Real arc) const {
        return rotated(ArcToRadian(arc));
    }
    void rotateByArc(Real arc) {
        *this = rotatedByArc(arc);
    }
    Real argument() const {
        return (Negative(y_) ? TAU : static_cast<Real>(0)) + atan2l(y_, x_);
    }
    Real argumentByArc() const {
        return RadianToArc(argument());
    }

    /* friend function */
    friend Real Dot(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.x() + lhs.y() * rhs.y();
    }
    friend Real Cross(const Point& lhs, const Point& rhs) {
        return lhs.x() * rhs.y() - lhs.y() * rhs.x();
    }
    friend Real Argument(const Point& lhs, const Point& rhs) {
        return rhs.argument() - lhs.argument();
    }
    friend bool ArgComp(const Point& lhs, const Point& rhs) {
        return Smaller(lhs.argument(), rhs.argument());
    }
};

using Vector = Point;

} // namespace geometryR2

} // namespace zawa


namespace zawa {

namespace geometryR2 {

Real Distance(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.norm();
}

Real DistanceSquare(const Point& p0, const Point& p1) {
    return Point{p1 - p0}.normSquare();
}

} // namespace geometryR2

} // namespace zawa


namespace zawa {

namespace geometryR2 {

class Circle {
private:
    Point center_{};
    Real radius_{};
public:
    /* constructor */
    Circle() = default;
    Circle(const Point& center, Real radius) : center_{center}, radius_{radius} {
        assert(!Negative(radius));
    }
    Circle(Real x, Real y, Real r) : center_{x, y}, radius_{r} {
        assert(!Negative(r));
    }

    /* getter setter */
    const Point& center() const {
        return center_;
    }
    Point& center() {
        return center_;
    }
    Real radius() const {
        return radius_;
    }
    Real& radius() {
        return radius_;
    }

    /* operator */
    friend bool operator==(const Circle& lhs, const Circle& rhs) {
        return lhs.center() == rhs.center() and Equal(lhs.radius(), rhs.radius());
    }
    friend bool operator!=(const Circle& lhs, const Circle& rhs) {
        return lhs.center() != rhs.center() or !Equal(lhs.radius(), rhs.radius());
    }

    /* friend function */
    friend u32 NumberCommonTangent(const Circle& c0, const Circle& c1) {
        Real dist{DistanceSquare(c0.center(), c1.center())};
        Real down{Square(Abs(c0.radius() - c1.radius()))};
        if (Smaller(dist, down)) return 0;
        if (Equal(dist, down)) return 1;
        Real up{Square(c0.radius() + c1.radius())};
        if (Smaller(dist, up)) return 2;
        if (Equal(dist, up)) return 3;
        return 4;
    }
};

} // namespace geometryR2

} // namespace zawa




namespace zawa {

namespace geometryR2 {

enum RELATION {
    // p0 -> p1 -> p2の順で直線上に並んでいる
    ONLINE_FRONT = -2,
    // (p1 - p0) -> (p2 - p0)が時計回りになっている
    CLOCKWISE,
    // p0 -> p2 -> p1の順で直線上に並んでいる
    ON_SEGMENT,
    // (p1 - p0) -> (p2 - p0)が反時計回りになっている
    COUNTER_CLOCKWISE,
    // p2 -> p0 -> p1、またはp1 -> p0 -> p2の順で直線上に並んでいる
    ONLINE_BACK
};

RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
    Point a{p1 - p0}, b{p2 - p0};
    if (Positive(Cross(a, b))) return COUNTER_CLOCKWISE;
    if (Negative(Cross(a, b))) return CLOCKWISE;
    if (Negative(Dot(a, b))) return ONLINE_BACK;
    if (Smaller(a.normSquare(), b.normSquare())) return ONLINE_FRONT;
    return ON_SEGMENT;
};

} // namespace geometryR2

} // namespace zawa


namespace zawa {

namespace geometryR2 {

class Line {
private:
    Point p0_{}, p1_{};
public:
    /* constructor */
    Line() = default;
    Line(const Point& p0, const Point& p1) : p0_{p0}, p1_{p1} {}
    // y = ax + b 
    Line(Real a, Real b) : p0_{static_cast<Real>(0), b}, p1_{static_cast<Real>(1), a + b} {}

    /* getter, setter */
    const Point& p0() const {
        return p0_;
    }
    Point& p0() {
        return p0_;
    }
    const Point& p1() const {
        return p1_;
    }
    Point& p1() {
        return p1_;
    }

    /* operator */
    friend bool operator==(const Line& l0, const Line& l1) {
        return Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l1.p0())) and Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l0.p0()));
    }
    friend bool operator!=(const Line& l0, const Line& l1) {
        return !Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l1.p0())) or !Zero(Cross(l0.p1() - l0.p0(), l1.p1() - l0.p0()));
    }

    /* member function */
    bool valid() const {
        return p0_ != p1_;
    }
    Vector slope() const {
        assert(valid());
        return Vector{p1() - p0()}.normalized();
    }
};

} // namespace geometryR2

} // namespace zawa


namespace zawa {

namespace geometryR2 {

    std::vector<Line> CommonTangentBetweenCircles(const Circle& c0, const Circle& c1) {
        std::vector<Line> res;
        if (c0.center() == c1.center()) {
            return res; 
        }
        res.reserve(4);
        Real g{Distance(c0.center(), c1.center())};
        Vector u{Vector{c1.center() - c0.center()}.normalized()};
        Vector v{u.rotated(PI / Real{2})};
        for (i32 s : { -1, 1 }) {
            Real h{(c0.radius() + c1.radius() * s) / g};
            if (Equal(Square(h), Real{1})) {
                res.emplace_back(c0.center() + (Positive(h) ? u : -u) * c0.radius(),
                        c0.center() + (Positive(h) ? u : -u) * c0.radius() + v);
                                    
            }
            else if (Smaller(Square(h), Real{1})) {
                Point U{u * h}, V{v * Sqrt(Real{1} - Square(h))};
                res.emplace_back(c0.center() + (U + V) * c0.radius(),
                        c1.center() - (U + V) * (c1.radius() * s));
                res.emplace_back(c0.center() + (U - V) * c0.radius(),
                        c1.center() - (U - V) * (c1.radius() * s));
            }
        }
        return res;
    }

} // namespace geometryR2

} // namespace zawa



namespace zawa {

namespace geometryR2 {

Real Distance(const Line& l, const Point& p) {
    assert(l.valid());
    return Abs(Cross(p - l.p0(), l.p1() - l.p0())) / Distance(l.p1(), l.p0());
}

bool PointOnLine(const Line& l, const Point& p) {
    assert(l.valid());
    return Zero(Distance(l, p));
}

} // namespace geometryR2

} // namespace zawa
using namespace zawa;
using namespace geometryR2;

bool solve() {
    int N;
    std::cin >> N;
    if (N == 0) return false;
    std::vector<Circle> C(2 * N);
    std::vector<Real> R(N), M(N);
    for (int i{} ; i < N ; i++) {
        Point c;
        Real r, m;
        std::cin >> c >> r >> m;
        R[i] = r;
        M[i] = m;
        C[2 * i + 0] = Circle{ c, r };
        C[2 * i + 1] = Circle{ c, r + m };
    }
    if (N == 1) {
        std::cout << 1 << '\n';
        return true;
    }
    auto check{[&](const Line& L, int oki, int okj) -> int {
        int res{};
        for (int i{} ; i < N ; i++) {
            if (i == oki or i == okj) {
                res++;
                continue;
            }
            Real d{Distance(L, C[2 * i].center())}; 
            if (Smaller(d, R[i])) continue;
            if (Bigger(d, R[i] + M[i])) continue;
            res++;
        }
        return res;
    }};
    int ans{};
    for (int i{} ; i < N ; i++) {
        for (int j{i + 1} ; j < N ; j++) {
            for (int bit{} ; bit < 4 ; bit++) {
                Circle lhs{(bit & (1 << 0)) ? C[2 * i + 1] : C[2 * i + 0]};
                Circle rhs{(bit & (1 << 1)) ? C[2 * j + 1] : C[2 * j + 0]};
                for (const auto& L : CommonTangentBetweenCircles(lhs, rhs)) {
                    ans = std::max(ans, check(L, i, j));
                }
            }
        }
    }
    std::cout << ans << '\n';
    return true;
}

int main() {
    SetFastIO();

    while (solve()) ;
}
accuracy: 1
time_ms: 210
memory_kb: 3400
final_rank: 8

code_snippet: #include <string.h>

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cfloat>
#include <chrono>
#include <ciso646>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <optional>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#define REP_OVERLOAD(arg1, arg2, arg3, arg4, NAME, ...) NAME
#define REP3(i, l, r, s)                                               \
    for (int i = int(l), rep3_r = int(r), rep3_s = int(s); i < rep3_r; \
         i += rep3_s)
#define REP2(i, l, r) REP3(i, l, r, 1)
#define REP1(i, n) REP2(i, 0, n)
#define rep(...) REP_OVERLOAD(__VA_ARGS__, REP3, REP2, REP1, )(__VA_ARGS__)
#define repin(i, l, r) for (int i = int(l), repin_r = int(r); i <= repin_r; ++i)

#define RREP_OVERLOAD(arg1, arg2, arg3, arg4, NAME, ...) NAME
#define RREP3(i, l, r, s)                                                      \
    for (int i = int(r) - 1, rrep3_l = int(l), rrep3_s = int(s); i >= rrep3_l; \
         i -= rrep3_s)
#define RREP2(i, l, r) RREP3(i, l, r, 1)
#define RREP1(i, n) RREP2(i, 0, n)
#define rrep(...) RREP_OVERLOAD(__VA_ARGS__, RREP3, RREP2, RREP1, )(__VA_ARGS__)
#define rrepin(i, l, r) \
    for (int i = int(r), rrepin_l = int(l); i >= rrepin_l; --i)


#include <algorithm>
#include <cmath>
#include <iostream>

#include <algorithm>
#include <cassert>
#include <vector>

namespace rklib {

// a <- max(a, b)
template <class T>
bool chmax(T &a, const T &b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

// a <- min(a, b)
template <class T>
bool chmin(T &a, const T &b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}

// if a < 0: a <- b
// else: a <- min(a, b)
template <class T>
bool chmin_non_negative(T &a, const T &b) {
    if (a < 0 || a > b) {
        a = b;
        return true;
    }
    return false;
}

// floor(num / den)
template <class T>
T div_floor(T num, T den) {
    if (den < 0) num = -num, den = -den;
    return num >= 0 ? num / den : (num + 1) / den - 1;
}

// ceil(num / den)
template <class T>
T div_ceil(T num, T den) {
    if (den < 0) num = -num, den = -den;
    return num <= 0 ? num / den : (num - 1) / den + 1;
}

namespace internal {

template <class T>
T remainder_count(T r, T b, T m) {
    return r / m * b + std::min(b, r % m);
}

}  // namespace internal

// Number of integer x s.t.
// - x in [l, r)
// - x mod m in [a, b)
template <class T>
T remainder_count(T l, T r, T a, T b, T m) {
    assert(l >= 0);
    assert(m >= 1);

    if (l >= r || a >= b) return 0;
    if (m <= a || b < 0) return 0;
    chmax(a, T(0));
    chmin(b, m);

    auto res = internal::remainder_count(r, b, m);
    if (l >= 1) res -= internal::remainder_count(l, b, m);
    if (a >= 1) res -= internal::remainder_count(r, a, m);
    if (l >= 1 && a >= 1) res += internal::remainder_count(l, a, m);

    return res;
}

}  // namespace rklib

#include <vector>

namespace rklib {

namespace geo2d {

using real_num = long double;
real_num eps = 1e-9;
constexpr real_num PI = 3.14159265358979323846264338327950;

void set_eps(real_num new_eps) { eps = new_eps; }

int sgn(real_num x) {
    if (x < -eps) return -1;
    if (x > eps) return 1;
    return 0;
}

bool eq(real_num x, real_num y) { return sgn(x - y) == 0; }

bool ge(real_num x, real_num y) { return sgn(x - y) == 1; }

bool le(real_num x, real_num y) { return sgn(x - y) == -1; }

bool geq(real_num x, real_num y) { return sgn(x - y) >= 0; }

bool leq(real_num x, real_num y) { return sgn(x - y) <= 0; }

struct Point {
    real_num x, y;

    Point(real_num x = 0, real_num y = 0) : x(x), y(y) {}

    bool operator==(const Point& p) { return eq(x, p.x) && eq(y, p.y); }
    bool operator<(const Point& p) const {
        if (eq(x, p.x)) return le(y, p.y);
        return le(x, p.x);
    }

    Point& operator+=(const Point& rhs) {
        this->x += rhs.x;
        this->y += rhs.y;
        return *this;
    }
    Point& operator-=(const Point& rhs) {
        this->x -= rhs.x;
        this->y -= rhs.y;
        return *this;
    }
    Point& operator*=(const real_num& rhs) {
        this->x *= rhs;
        this->y *= rhs;
        return *this;
    }
    Point& operator/=(const real_num& rhs) {
        this->x /= rhs;
        this->y /= rhs;
        return *this;
    }

    friend Point operator+(const Point& lhs, const Point& rhs) {
        return Point(lhs) += rhs;
    }
    friend Point operator-(const Point& lhs, const Point& rhs) {
        return Point(lhs) -= rhs;
    }
    friend Point operator*(const Point& lhs, const real_num& rhs) {
        return Point(lhs) *= rhs;
    }
    friend Point operator*(const real_num& lhs, const Point& rhs) {
        return Point(rhs) *= lhs;
    }
    friend Point operator/(const Point& lhs, const real_num& rhs) {
        return Point(lhs) /= rhs;
    }
    friend real_num operator*(const Point& lhs, const Point& rhs) {
        return lhs.x * rhs.x + lhs.y * rhs.y;
    }
    friend real_num operator^(const Point& lhs, const Point& rhs) {
        return lhs.x * rhs.y - lhs.y * rhs.x;
    }

    friend std::istream& operator>>(std::istream& is, Point& p) {
        is >> p.x >> p.y;
        return is;
    }
};

using Vec = Point;
using Points = std::vector<Point>;
using Polygon = Points;

struct Line {
    Point a;
    Vec v;

    Line() {}
    Line(Point a, Vec v) : a(a), v(v) {}
};

struct Segment {
    Point a, b;

    Segment() {}
    Segment(Point a, Point b) : a(a), b(b) {}
};

real_num norm(Point p) { return p.x * p.x + p.y * p.y; }

real_num abs(Point p) { return std::sqrt(norm(p)); }

real_num arg(Point p) { return std::atan2(p.y, p.x); }

real_num angle(Point a, Point b) { return arg({a * b, a ^ b}); }

Point rotation(Point p, real_num t) {
    return {p.x * std::cos(t) - p.y * std::sin(t),
            p.x * std::sin(t) + p.y * std::cos(t)};
}

Point projection(Line l, Point p) {
    auto [a, v] = l;
    real_num t = v * (p - a) / norm(v);
    return a + v * t;
}

Point reflection(Line l, Point p) { return projection(l, p) * 2 - p; }

constexpr int CCW_COUNTER_CLOCKWISE = 1;
constexpr int CCW_CLOCKWISE = -1;
constexpr int CCW_ONLINE_BACK = -2;  // C->A->B
constexpr int CCW_ONLINE_FRONT = 2;  // A->B->C
constexpr int CCW_ON_SEGMENT = 0;    // A->C->B

int ccw(Point a, Point b, Point c) {
    Vec v = b - a, w = c - a;
    if (ge(v ^ w, 0)) return CCW_COUNTER_CLOCKWISE;
    if (le(v ^ w, 0)) return CCW_CLOCKWISE;
    if (le(v * w, 0)) return CCW_ONLINE_BACK;
    if (le((a - b) * (c - b), 0)) return CCW_ONLINE_FRONT;
    return CCW_ON_SEGMENT;
}

bool is_parallel(Vec v, Vec w) { return eq(v ^ w, 0); }

bool is_orthogonal(Vec v, Vec w) { return eq(v * w, 0); }

bool has_intersection(Line l, Segment s) {
    auto [p, v] = l;
    auto [a, b] = s;
    return sgn(v ^ (a - p)) * sgn(v ^ (b - p)) <= 0;
}

bool has_intersection(Segment s1, Segment s2) {
    auto [a, b] = s1;
    auto [c, d] = s2;
    return ccw(a, b, c) * ccw(a, b, d) <= 0 && ccw(c, d, a) * ccw(c, d, b) <= 0;
}

Point intersection(Line l1, Line l2) {
    auto [a, v] = l1;
    auto [b, w] = l2;
    real_num t = ((b - a) ^ w) / (v ^ w);
    return a + v * t;
}

Point intersection(Segment s1, Segment s2) {
    auto [a, b] = s1;
    auto [c, d] = s2;
    return intersection(Line(a, b - a), Line(c, d - c));
}

real_num distance(Line l, Point p) {
    auto [a, v] = l;
    return std::abs(v ^ (p - a) / abs(v));
}

real_num distance(Segment s, Point p) {
    auto [a, b] = s;
    if (le((b - a) * (p - a), 0)) return abs(p - a);
    if (le((a - b) * (p - b), 0)) return abs(p - b);
    return distance(Line(a, b - a), p);
}

real_num distance(Line l1, Line l2) {
    auto [a, v] = l1;
    auto [b, w] = l2;
    if (is_parallel(v, w)) return distance(l1, b);
    return 0;
}

real_num distance(Line l, Segment s) {
    if (has_intersection(l, s)) return 0;
    return std::min(distance(l, s.a), distance(l, s.b));
}

real_num distance(Segment s1, Segment s2) {
    if (has_intersection(s1, s2)) return 0;
    return std::min({distance(s1, s2.a), distance(s1, s2.b), distance(s2, s1.a),
                     distance(s2, s1.b)});
}

real_num area(Polygon& p) {
    real_num res = 0;
    for (size_t i = 0; i < p.size(); i++) {
        res += p[i] ^ p[(i + 1) % p.size()] / 2;
    }
    return std::abs(res);
}

bool is_convex(Polygon& p) {
    int n = p.size();
    bool flag1 = false, flag2 = false;
    for (int i = 0; i < n; i++) {
        int tmp = ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]);
        if (tmp == CCW_COUNTER_CLOCKWISE) {
            if (flag2) return false;
            flag1 = true;
        } else if (tmp == CCW_CLOCKWISE) {
            if (flag1) return false;
            flag2 = true;
        }
    }
    return true;
}

int point_in_polygon(Point a, Polygon& p) {
    int n = p.size(), wn = 0;
    for (int i = 0; i < n; i++) {
        int j = (i + 1) % n;
        if (distance(Segment(p[i], p[j]), a) == 0)
            return 1;
        else if (p[i].y <= a.y && a.y < p[j].y) {
            wn += (ccw(a, p[i], p[j]) == CCW_COUNTER_CLOCKWISE);
        } else if (p[j].y <= a.y && a.y < p[i].y) {
            wn -= (ccw(a, p[i], p[j]) == CCW_CLOCKWISE);
        }
    }
    return wn == 0 ? 0 : 2;
}

Polygon convex_hull(Points p) {
    int n = p.size();
    sort(p.begin(), p.end());
    Polygon ch(2 * n);
    int k = 0;
    for (int i = 0; i < n; i++) {
        while (k > 1 && le((ch[k - 1] - ch[k - 2]) ^ (p[i] - ch[k - 1]), 0))
            --k;
        ch[k++] = p[i];
    }
    for (int i = n - 2, t = k; i >= 0; --i) {
        while (k > t && le((ch[k - 1] - ch[k - 2]) ^ (p[i] - ch[k - 1]), 0))
            --k;
        ch[k++] = p[i];
    }
    ch.resize(k - 1);
    return ch;
}

real_num convex_cut(Polygon& p, Line l) {
    int n = p.size();
    auto [a, v] = l;
    Polygon q;
    for (int i = 0; i < n; ++i) {
        int j = (i + 1) % n;
        if (geq(v ^ (p[i] - a), 0)) q.push_back(p[i]);
        if (has_intersection(l, Segment(p[i], p[j])) &&
            !is_parallel(v, p[j] - p[i])) {
            q.push_back(intersection(l, {p[i], p[j] - p[i]}));
        }
    }
    return area(q);
}

Point circumcenter(Point a, Point b, Point c) {
    Point m = (a + b) / 2, n = (a + c) / 2;
    Vec v = {-(b - a).y, (b - a).x}, w = {-(c - a).y, (c - a).x};
    return intersection(Line(m, v), Line(n, w));
}

Point incenter(Point a, Point b, Point c) {
    real_num A = abs(b - c), B = abs(c - a), C = abs(a - b);
    return (a * A + b * B + c * C) / (A + B + C);
}

Point orthocenter(Point a, Point b, Point c) {
    Vec v = {-(c - b).y, (c - b).x}, w = {-(c - a).y, (c - a).x};
    return intersection(Line(a, v), Line(b, w));
}

namespace internal {

std::pair<real_num, std::pair<int, int>> closest_pair_rec(
    std::vector<std::pair<Point, int>>& p, int l, int r) {
    if (r - l <= 1)
        return {std::numeric_limits<real_num>::max(), {p.size(), p.size()}};

    int m = (l + r) / 2;
    real_num x = p[m].first.x;
    auto d = std::min(closest_pair_rec(p, l, m), closest_pair_rec(p, m, r));
    auto cmp = [](std::pair<Point, int> a, std::pair<Point, int> b) {
        return a.first.y < b.first.y;
    };
    std::inplace_merge(p.begin() + l, p.begin() + m, p.begin() + r, cmp);

    std::vector<std::pair<Point, int>> q;
    for (int i = l; i < r; ++i) {
        if (ge(std::abs(p[i].first.x - x), d.first)) continue;
        for (int j = int(q.size()) - 1; j >= 0; --j) {
            real_num dy = p[i].first.y - q[j].first.y;
            if (geq(dy, d.first)) break;
            chmin(d,
                  {abs(p[i].first - q[j].first), {p[i].second, q[j].second}});
        }
        q.push_back(p[i]);
    }
    return d;
}

}  // namespace internal

std::pair<real_num, std::pair<int, int>> closest_pair(Points& p) {
    std::vector<std::pair<Point, int>> pid(p.size());
    for (size_t i = 0; i < p.size(); i++) {
        pid[i] = {p[i], i};
    }
    std::sort(pid.begin(), pid.end());
    return internal::closest_pair_rec(pid, 0, int(p.size()));
}

std::pair<real_num, std::pair<int, int>> farthest_pair(Polygon& p) {
    int n = p.size();
    if (n == 2) {
        return {abs(p[0] - p[1]), {0, 1}};
    }
    int i = 0, j = 0;
    for (int k = 0; k < n; ++k) {
        if (le(p[k].x, p[i].x)) i = k;
        if (ge(p[k].x, p[j].x)) j = k;
    }
    real_num d = 0;
    int a = i, b = j, si = i, sj = j;
    while (i != sj || j != si) {
        if (chmax(d, abs(p[i] - p[j]))) a = i, b = j;
        if (le((p[(i + 1) % n] - p[i]) ^ (p[(j + 1) % n] - p[j]), 0)) {
            i = (i + 1) % n;
        } else
            j = (j + 1) % n;
    }
    return {d, {a, b}};
}

void arg_sort(Points& p) {
    auto cmp = [&](Point& a, Point& b) {
        if (le(a.y, 0)) {
            if (le(b.y, 0)) {
                return ge(a ^ b, 0);
            } else {
                return true;
            }
        } else if (eq(a.y, 0)) {
            if (le(b.y, 0)) {
                return false;
            } else if (eq(b.y, 0)) {
                return ge(a.x, b.x);
            } else {
                return geq(a.x, 0);
            }
        } else {
            if (le(b.y, 0)) {
                return false;
            } else if (eq(b.y, 0)) {
                return le(b.x, 0);
            } else {
                return ge(a ^ b, 0);
            }
        }
    };
    std::sort(p.begin(), p.end(), cmp);
}

struct Circle {
    Point c;
    real_num r;
};

int num_of_common_tangent(Circle c1, Circle c2) {
    if (c1.r < c2.r) std::swap(c1, c2);
    real_num d = abs(c1.c - c2.c), r = c1.r + c2.r;
    if (ge(d, r)) return 4;
    if (eq(d, r)) return 3;
    if (eq(d + c2.r, c1.r)) return 1;
    if (le(d + c2.r, c1.r)) return 0;
    return 2;
}

int num_of_intersection(Circle c, Line l) {
    real_num d = distance(l, c.c);
    if (leq(d, c.r)) return 2;
    if (eq(d, c.r)) return 1;
    return 0;
}

bool has_intersection(Circle c, Segment s) {
    auto [a, b] = s;
    return leq(distance(s, c.c), c.r) &&
           geq(std::max(abs(a - c.c), abs(b - c.c)), c.r);
}

Points intersection(Circle c, Line l) {
    Points res;
    if (num_of_intersection(c, l) == 0) return res;
    Point p = projection(l, c.c);
    real_num t = std::sqrt(
        std::max(real_num(0), (c.r * c.r - norm(p - c.c)) / norm(l.v)));
    res.push_back(p + l.v * t);
    if (!eq(t, 0)) res.push_back(p - l.v * t);
    return res;
}

Points intersection(Circle c, Segment s) {
    auto [a, b] = s;
    Points ps = intersection(c, Line(b, a - b));
    Points res;
    for (auto p : ps) {
        if (ccw(a, b, p) == CCW_ON_SEGMENT) res.push_back(p);
    }
    return res;
}

Points intersection(Circle c1, Circle c2) {
    real_num r1 = c1.r, r2 = c2.r;
    Points res;
    Vec v = c2.c - c1.c, w = {-v.y, v.x};
    real_num d = abs(v);
    real_num x = (d * d + r1 * r1 - r2 * r2) / (2 * d);
    real_num y = std::sqrt(std::max(r1 * r1 - x * x, real_num(0)));
    res.push_back(c1.c + v * x / d + w * y / d);
    if (num_of_common_tangent(c1, c2) != 2) return res;
    res.push_back(c1.c + v * x / d - w * y / d);
    return res;
}

namespace internal {

real_num area_of_intersection(Circle c, Segment s) {
    auto [a, b] = s;
    Vec va = a - c.c, vb = b - c.c;
    if (eq(va ^ vb, 0))
        return 0;
    else if (leq(abs(va), c.r) && leq(abs(vb), c.r))
        return (va ^ vb) / 2;
    else if (geq(distance(s, c.c), c.r))
        return c.r * c.r * angle(va, vb) / 2;
    else {
        auto ps = intersection(c, s);
        if (ps.size() == 1)
            return area_of_intersection(c, {a, ps[0]}) +
                   area_of_intersection(c, {ps[0], b});
        else
            return area_of_intersection(c, {a, ps[0]}) +
                   area_of_intersection(c, {ps[0], ps[1]}) +
                   area_of_intersection(c, {ps[1], b});
    }
}

}  // namespace internal

real_num area_of_intersection(Circle c, Polygon& p) {
    int n = p.size();
    real_num res = 0;
    for (int i = 0; i < n; ++i) {
        res += internal::area_of_intersection(c, {p[i], p[(i + 1) % n]});
    }
    return res;
}

real_num area_of_intersection(Circle c1, Circle c2) {
    int num = num_of_common_tangent(c1, c2);
    if (num >= 3) return 0;
    real_num r1 = c1.r, r2 = c2.r;
    if (num <= 1) {
        real_num r = std::min(r1, r2);
        return PI * r * r;
    }
    real_num d = abs(c1.c - c2.c);
    real_num res = 0;
    for (int i = 0; i < 2; ++i) {
        real_num x = (d * d + r1 * r1 - r2 * r2) / (2 * d);
        real_num t = std::acos(x / r1) * 2;
        res += (t - std::sin(t)) * r1 * r1 / 2;
        std::swap(r1, r2);
    }
    return res;
}

Points tangent(Circle c, Point p) {
    Points res;
    real_num d = abs(p - c.c);
    real_num t = std::acos(c.r / d);
    res.push_back(c.c + rotation(p - c.c, t) * c.r / d);
    res.push_back(c.c + rotation(p - c.c, -t) * c.r / d);
    return res;
}

std::tuple<std::vector<Line>, Points, Points> common_tangent(Circle c1,
                                                             Circle c2) {
    std::vector<Line> ls;
    Points ps, qs;
    int num = num_of_common_tangent(c1, c2);
    if (num >= 2) {
        real_num d = abs(c2.c - c1.c);
        real_num t = std::acos(std::abs(c1.r - c2.r) / d);
        if (le(c1.r, c2.r)) t = PI - t;

        ps.push_back(c1.c + rotation(c2.c - c1.c, t) * c1.r / d);
        qs.push_back(c2.c + rotation(c2.c - c1.c, t) * c2.r / d);
        ls.push_back({ps.back(), qs.back() - ps.back()});

        ps.push_back(c1.c + rotation(c2.c - c1.c, -t) * c1.r / d);
        qs.push_back(c2.c + rotation(c2.c - c1.c, -t) * c2.r / d);
        ls.push_back({ps.back(), qs.back() - ps.back()});
    }
    if (num == 4) {
        real_num d = abs(c2.c - c1.c);
        real_num L = d * c1.r / (c1.r + c2.r);
        real_num t = std::acos(c1.r / L);

        ps.push_back(c1.c + rotation(c2.c - c1.c, t) * c1.r / d);
        qs.push_back(c2.c + rotation(c1.c - c2.c, t) * c2.r / d);
        ls.push_back({ps.back(), qs.back() - ps.back()});

        ps.push_back(c1.c + rotation(c2.c - c1.c, -t) * c1.r / d);
        qs.push_back(c2.c + rotation(c1.c - c2.c, -t) * c2.r / d);
        ls.push_back({ps.back(), qs.back() - ps.back()});
    }
    if (num == 3 || num == 1) {
        Points tg = intersection(c1, c2);
        ps.push_back(tg[0]);
        qs.push_back(tg[0]);
        ls.push_back({tg[0], rotation(c2.c - c1.c, PI / 2)});
    }
    return {ls, ps, qs};
}

Circle minimum_bounding_circle(Points& p) {
    if (p.size() == 1) {
        return {p[0], 0};
    } else if (p.size() == 2) {
        Point c = (p[0] + p[1]) / 2;
        return {c, abs(p[0] - c)};
    } else {
        Point c;
        real_num r = std::numeric_limits<real_num>::max();
        Points ch = convex_hull(p);
        int K = ch.size();
        auto check = [&](Point tc, real_num tr) {
            for (int i = 0; i < K; ++i) {
                if (ge(abs(ch[i] - tc), tr)) return false;
            }
            return true;
        };
        for (int i = 0; i < K; ++i) {
            for (int j = i + 1; j < K; ++j) {
                Point tc = (ch[i] + ch[j]) / 2;
                real_num tr = abs(ch[i] - tc);
                if (check(tc, tr) && chmin(r, tr)) c = tc;
                for (int k = j + 1; k < K; ++k) {
                    int ccw_flag = ccw(ch[i], ch[j], ch[k]);
                    if (ccw_flag != CCW_COUNTER_CLOCKWISE &&
                        ccw_flag != CCW_CLOCKWISE)
                        continue;
                    tc = circumcenter(ch[i], ch[j], ch[k]);
                    tr = abs(ch[i] - tc);
                    if (check(tc, tr) && chmin(r, tr)) c = tc;
                }
            }
        }
        return {c, r};
    }
}

}  // namespace geo2d

}  // namespace rklib


using namespace std;
using namespace rklib;

using lint = long long;
using pii = pair<int, int>;
using pll = pair<lint, lint>;

using namespace geo2d;

int main() {
    while (true) {
        int n;
        cin >> n;
        if (n == 0) break;

        vector<Circle> cs(2 * n);
        vector<double> x(n), y(n), r(n), m(n);
        rep(i, n) {
            cin >> x[i] >> y[i] >> r[i] >> m[i];
            cs[i] = {{x[i], y[i]}, r[i]};
            cs[i + n] = {{x[i], y[i]}, r[i] + m[i]};
        }

        if (n == 1) {
            cout << 1 << "\n";
            continue;
        }

        int ans = 0;
        rep(i, cs.size()) {
            rep(j, cs.size()) {
                if (i % n == j % n) continue;

                auto tangent = common_tangent(cs[i], cs[j]);
                auto ls = get<0>(tangent);
                for (auto l : ls) {
                    int cnt = 0;
                    rep(k, n) {
                        auto d = distance(l, cs[k].c);
                        if (geq(d, r[k]) && leq(d, r[k] + m[k])) {
                            ++cnt;
                        }
                    }
                    chmax(ans, cnt);
                }
            }
        }
        cout << ans << "\n";
    }
}
accuracy: 1
time_ms: 350
memory_kb: 3868
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

using T = double;
using Vec = std::complex<T>;

const T PI = std::acos(-1);

constexpr T eps = 1e-10;
inline bool eq(T a, T b) { return std::abs(a - b) <= eps; }
inline bool eq(Vec a, Vec b) { return std::abs(a - b) <= eps; }
inline bool lt(T a, T b) { return a < b - eps; }
inline bool leq(T a, T b) { return a <= b + eps; }

std::istream& operator>>(std::istream& is, Vec& p) {
    T x, y;
    is >> x >> y;
    p = {x, y};
    return is;
}

struct Line {
    Vec p1, p2;
    Line() = default;
    Line(const Vec& p1, const Vec& p2) : p1(p1), p2(p2) {}
    Vec dir() const { return p2 - p1; }
};

struct Segment : Line {
    using Line::Line;
};

struct Circle {
    Vec c;
    T r;
    Circle() = default;
    Circle(const Vec& c, T r) : c(c), r(r) {}
};

using Polygon = std::vector<Vec>;

T dot(const Vec& a, const Vec& b) {
    return (std::conj(a) * b).real();
}

T cross(const Vec& a, const Vec& b) {
    return (std::conj(a) * b).imag();
}

Vec rot(const Vec& a, T ang) {
    return a * Vec(std::cos(ang), std::sin(ang));
}

Vec perp(const Vec& a) {
    return Vec(-a.imag(), a.real());
}

Vec projection(const Line& l, const Vec& p) {
    return l.p1 + dot(p - l.p1, l.dir()) * l.dir() / std::norm(l.dir());
}

Vec reflection(const Line& l, const Vec& p) {
    return T(2) * projection(l, p) - p;
}

// 0: collinear
// 1: counter-clockwise
// -1: clockwise
int ccw(const Vec& a, const Vec& b, const Vec& c) {
    if (eq(cross(b - a, c - a), 0)) return 0;
    if (lt(cross(b - a, c - a), 0)) return -1;
    return 1;
}

void sort_by_arg(std::vector<Vec>& pts) {
    std::sort(pts.begin(), pts.end(), [&](auto& p, auto& q) {
        if ((p.imag() < 0) != (q.imag() < 0)) return (p.imag() < 0);
        if (cross(p, q) == 0) {
            if (p == Vec(0, 0)) return !(q.imag() < 0 || (q.imag() == 0 && q.real() > 0));
            if (q == Vec(0, 0)) return  (p.imag() < 0 || (p.imag() == 0 && p.real() > 0));
            return (p.real() > q.real());
        }
        return (cross(p, q) > 0);
    });
}

// 0: inside
// 1: inscribe
// 2: intersect
// 3: circumscribe
// 4: outside
int intersect(const Circle& c1, const Circle& c2) {
    T d = std::abs(c1.c - c2.c);
    if (lt(d, std::abs(c2.r - c1.r))) return 0;
    if (eq(d, std::abs(c2.r - c1.r))) return 1;
    if (eq(c1.r + c2.r, d)) return 3;
    if (lt(c1.r + c2.r, d)) return 4;
    return 2;
}

std::vector<Vec> intersection(const Circle& c1, const Circle& c2) {
    T d = std::abs(c1.c - c2.c);
    if (lt(c1.r + c2.r, d)) return {};  // outside
    Vec e1 = (c2.c - c1.c) / std::abs(c2.c - c1.c);
    Vec e2 = perp(e1);
    if (lt(d, std::abs(c2.r - c1.r))) return {};  // contain
    if (eq(d, std::abs(c2.r - c1.r))) return {c1.c + c1.r*e1};  // tangent
    T x = (c1.r*c1.r - c2.r*c2.r + d*d) / (2*d);
    T y = std::sqrt(c1.r*c1.r - x*x);
    return {c1.c + x*e1 + y*e2, c1.c + x*e1 - y*e2};
}

std::pair<Vec, Vec> tangent_points(const Circle& c, const Vec& p) {
    auto m = (p + c.c) / T(2);
    auto is = intersection(c, Circle(m, std::abs(p - m)));
    return {is[0], is[1]};
}

std::vector<Line> common_tangents(Circle c1, Circle c2) {
    assert(!eq(c1.c, c2.c) || !eq(c1.r, c2.r));
    int cnt = intersect(c1, c2);  // number of common tangents
    std::vector<Line> ret;
    if (cnt == 0) {
        return ret;
    }

    // external
    if (eq(c1.r, c2.r)) {
        auto d = c2.c - c1.c;
        Vec e(-d.imag(), d.real());
        e = e / std::abs(e) * c1.r;
        ret.push_back(Line(c1.c + e, c1.c + e + d));
        ret.push_back(Line(c1.c - e, c1.c - e + d));
    } else {
        auto p = (-c2.r*c1.c + c1.r*c2.c) / (c1.r - c2.r);
        if (cnt == 1) {
            Vec q(-p.imag(), p.real());
            return {Line(p, q)};
        } else {
            auto [a, b] = tangent_points(c1, p);
            ret.push_back(Line(a, p));
            ret.push_back(Line(b, p));
        }
    }

    // internal
    auto p = (c2.r*c1.c + c1.r*c2.c) / (c1.r + c2.r);
    if (cnt == 3) {
        Vec q(-p.imag(), p.real());
        ret.push_back(Line(p, q));
    } else if (cnt == 4) {
        auto [a, b] = tangent_points(c1, p);
        ret.push_back(Line(a, p));
        ret.push_back(Line(b, p));
    }

    return ret;
}

T dist(const Line& l, const Vec& p) {
    return std::abs(cross(p - l.p1, l.dir())) / std::abs(l.dir());
}


int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    while (true) {
        int N;
        cin >> N;
        if (N == 0) break;
        vector<Vec> c(N);
        vector<T> r(N), m(N);
        vector<Circle> circles(2*N);
        rep(i,0,N) {
            cin >> c[i] >> r[i] >> m[i];
            circles[2*i] = Circle(c[i], r[i]);
            circles[2*i+1] = Circle(c[i], r[i]+m[i]);
        }
        vector<Line> lines;
        rep(i,0,2*N) {
            rep(j,i+1,2*N) {
                if (eq(circles[i].c, circles[j].c) && eq(circles[i].r, circles[j].r)) continue;
                auto res = common_tangents(circles[i], circles[j]);
                for (auto& line : res) lines.push_back(line);
            }
        }
        int ans = 1;
        for (auto line : lines) {
            int cnt = 0;
            rep(i,0,N) {
                auto d = dist(line, c[i]);
                if (leq(r[i], d) && leq(d, r[i]+m[i])) ++cnt;
            }
            chmax(ans, cnt);
        }
        cout << ans << "\n";
    }
}
accuracy: 1
time_ms: 160
memory_kb: 4852
final_rank: 17

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, n) for (int i = 0; i < int(n); i++)
#define per(i, n) for (int i = (n)NOP; 0 <= i; i--)
#define rep2(i, l, r) for (int i = (l); i < int(r); i++)
#define per2(i, l, r) for (int i = (r)NOP; int(l) <= i; i--)
#define MM << " " <<
#define pb push_back
#define eb emplace_back
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
#define sz(x) (int)x.size()

template<typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (v.empty()) cout << '\n';
}

template<class T>
using MaxHeap = priority_queue<T>;
template<class T>
using MinHeap = priority_queue<T, vector<T>, greater<T>>;

using ll  = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;

template<typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template<typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}


using Real = long double;
using Point = complex<Real>;

const Real EPS = 1e-10;
const Real pi = acos(-1.0);

int sgn(Real a) { return (a < -EPS) ? -1 : (a > EPS) ? 1 : 0; }

bool eq(Real a, Real b) { return sgn(b - a) == 0; }

Point operator*(const Point &p, const Real &d) {
    return Point(real(p) * d, imag(p) * d);
}

Point operator/(const Point &p, const Real & d) {
    return p * (1 / d);
}

Point rotate(const Point &p, const Real &t) {
    return p * Point(cos(t), sin(t));
}

Real dot(const Point &p, const Point &q) {
    return (p * conj(q)).real();
}

struct Line {
    Point a, b;
    Line() {}
    Line(Point _a, Point _b) : a(_a), b(_b) {}
};

struct Circle {
    Point p;
    Real r;
    Circle() {}
    Circle(Point _p, Real _r) : p(_p), r(_r) {}
};

vector<Line> tangent(Circle c1, Circle c2) {
    vector<Line> ret;
    if(c1.r < c2.r) swap(c1, c2);
    Real r = abs(c2.p - c1.p);
    if(eq(r, 0.0)) return ret;
    Point u = (c2.p - c1.p) / r;
    Point v = rotate(u, pi * 0.5);
    for(auto &s : {1.0, -1.0}) {
        Real h = (c1.r + c2.r * s) / r;
        if(eq(abs(h), 1.0)) {
            ret.eb(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
        } else if(abs(h) < 1.0) {
            Point uu = u * h, vv = v * sqrt(1.0 - h * h);
            ret.eb(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
            ret.eb(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
        }
    }
    return ret;
}

Point projection(const Line &l, const Point &p) {
    Real t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + (l.b - l.a) * t;
}

Real distance(const Line &l, const Point &p) {
    return abs(p - projection(l, p));
}

int solve(int n) {
    vector<pair<Circle, Circle>> c(n);
    vector<Circle> b;
    rep(i,n) {
        Real x, y, r, m;
        cin >> x >> y >> r >> m;
        c[i].first = Circle({x, y}, r);
        c[i].second = Circle({x, y}, r + m);
        b.eb(c[i].first);
        b.eb(c[i].second);
    }

    if(n == 1) return 1;
    int ans = 0;
    int m = b.size();
    rep(i,m) rep(j,m) {
        for(auto &l : tangent(b[i], b[j])) {
            int now = 0;
            for(auto &k : c) {
                auto p = k.first.p;
                auto d = distance(l, p);
                if(k.first.r - EPS < d && d < k.second.r + EPS) ++now;
            }
            chmax(ans, now);
        }
    }
    return ans;
}

int main() {
    int n;
    vector<int> ans; 
    while(1) {
        cin >> n;
        if(n == 0) break;
        ans.eb(solve(n));
    }
    for(auto &i : ans) cout << i << endl;
}
accuracy: 1
time_ms: 2000
memory_kb: 3800
final_rank: 19

code_snippet: #ifndef HIDDEN_IN_VS // 折りたたみ用

// 警告の抑制
#define _CRT_SECURE_NO_WARNINGS

// ライブラリの読み込み
#include <bits/stdc++.h>
using namespace std;

// 型名の短縮
using ll = long long; // -2^63 ～ 2^63 = 9 * 10^18（int は -2^31 ～ 2^31 = 2 * 10^9）
using pii = pair<int, int>;	using pll = pair<ll, ll>;	using pil = pair<int, ll>;	using pli = pair<ll, int>;
using vi = vector<int>;		using vvi = vector<vi>;		using vvvi = vector<vvi>;
using vl = vector<ll>;		using vvl = vector<vl>;		using vvvl = vector<vvl>;
using vb = vector<bool>;	using vvb = vector<vb>;		using vvvb = vector<vvb>;
using vc = vector<char>;	using vvc = vector<vc>;		using vvvc = vector<vvc>;
using vd = vector<double>;	using vvd = vector<vd>;		using vvvd = vector<vvd>;
template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>;
using Graph = vvi;

// 定数の定義
const double PI = acos(-1);
const vi DX = { 1, 0, -1, 0 }; // 4 近傍（下，右，上，左）
const vi DY = { 0, 1, 0, -1 };
int INF = 1001001001; ll INFL = 4004004004004004004LL;
double EPS = 1e-12;

// 入出力高速化
struct fast_io { fast_io() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(18); } } fastIOtmp;

// 汎用マクロの定義
#define all(a) (a).begin(), (a).end()
#define sz(x) ((int)(x).size())
#define lbpos(a, x) (int)distance((a).begin(), std::lower_bound(all(a), x))
#define ubpos(a, x) (int)distance((a).begin(), std::upper_bound(all(a), x))
#define Yes(b) {cout << ((b) ? "Yes\n" : "No\n");}
#define rep(i, n) for(int i = 0, i##_len = int(n); i < i##_len; ++i) // 0 から n-1 まで昇順
#define repi(i, s, t) for(int i = int(s), i##_end = int(t); i <= i##_end; ++i) // s から t まで昇順
#define repir(i, s, t) for(int i = int(s), i##_end = int(t); i >= i##_end; --i) // s から t まで降順
#define repe(v, a) for(const auto& v : (a)) // a の全要素（変更不可能）
#define repea(v, a) for(auto& v : (a)) // a の全要素（変更可能）
#define repb(set, d) for(int set = 0; set < (1 << int(d)); ++set) // d ビット全探索（昇順）
#define repp(a) sort(all(a)); for(bool a##_perm = true; a##_perm; a##_perm = next_permutation(all(a))) // a の順列全て（昇順）
#define smod(n, m) ((((n) % (m)) + (m)) % (m)) // 非負mod
#define uniq(a) {sort(all(a)); (a).erase(unique(all(a)), (a).end());} // 重複除去
#define EXIT(a) {cout << (a) << endl; exit(0);} // 強制終了

// 汎用関数の定義
template <class T> inline ll pow(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; }
template <class T> inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新（更新されたら true を返す）
template <class T> inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新（更新されたら true を返す）

// 演算子オーバーロード
template <class T, class U> inline istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first >> p.second; return is; }
template <class T> inline istream& operator>>(istream& is, vector<T>& v) { repea(x, v) is >> x; return is; }
template <class T> inline vector<T>& operator--(vector<T>& v) { repea(x, v) --x; return v; }
template <class T> inline vector<T>& operator++(vector<T>& v) { repea(x, v) ++x; return v; }

// 手元環境（Visual Studio）
#ifdef _MSC_VER
#include "local.hpp"
// 提出用（gcc）
#else
inline int popcount(int n) { return __builtin_popcount(n); }
inline int popcount(ll n) { return __builtin_popcountll(n); }
inline int lsb(int n) { return n != 0 ? __builtin_ctz(n) : -1; }
inline int lsb(ll n) { return n != 0 ? __builtin_ctzll(n) : -1; }
inline int msb(int n) { return n != 0 ? (31 - __builtin_clz(n)) : -1; }
inline int msb(ll n) { return n != 0 ? (63 - __builtin_clzll(n)) : -1; }
#define gcd __gcd
#define dump(...)
#define dumpel(v)
#define dump_list(v)
#define input_from_file(f)
#define output_to_file(f)
#define Assert(b) { if (!(b)) while (1) cout << "OLE"; }
#endif

#endif // 折りたたみ用


////--------------AtCoder 専用--------------
//#include <atcoder/all>
//using namespace atcoder;
//
////using mint = modint1000000007;
//using mint = modint998244353;
////using mint = modint; // mint::set_mod(m);
//
//istream& operator>>(istream& is, mint& x) { ll x_; is >> x_; x = x_; return is; }
//ostream& operator<<(ostream& os, const mint& x) { os << x.val(); return os; }
//using vm = vector<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>;
////----------------------------------------


//【平面上の点，二次元ベクトル】
/*
* 平面における点／二次元ベクトルを表す構造体
*
* Point<T>() : O(1)
*	(0, 0) で初期化する．
*
* Point<T>(T x, T y) : O(1)
*	(x, y) で初期化する．
*
* p1 == p2, p1 != p2, p1 < p2, p1 > p2, p1 <= p2, p1 >= p2 : O(1)
*	x 座標優先，次いで y 座標の大小比較を行う．
*
* p1 + p2, p1 - p2, c * p, p * c, p / c : O(1)
*	ベクトルとみなした加算，減算，スカラー倍，スカラー除算を行う．複合代入演算子も使用可．
*
* T sqnorm() : O(1)
*	自身の 2 乗ノルムを返す．
*
* double norm() : O(1)
*	自身のノルムを返す．
*
* Point<double> normalize() : O(1)
*	自身を正規化したベクトルを返す．
*
* T dot(Point<T> p) : O(1)
*	自身と p との内積を返す．
*
* T cross(Point<T> p) : O(1)
*	自身と p との外積を返す．
*
* double angle(Point<T> p) : O(1)
*	自身から p までの成す角度を返す．
*/
template <class T> struct Point {
	// 点の x 座標，y 座標
	T x, y;

	// コンストラクタ
	Point() : x(0), y(0) {}
	Point(T x_, T y_) : x(x_), y(y_) {}

	// 代入
	Point(const Point& old) = default;
	Point& operator=(const Point& other) = default;

	// キャスト
	operator Point<ll>() const { return Point<ll>((ll)x, (ll)y); }
	operator Point<double>() const { return Point<double>((double)x, (double)y); }

	// 入出力
	friend istream& operator>>(istream& is, Point& p) { is >> p.x >> p.y; return is; }
	friend ostream& operator<<(ostream& os, const Point& p) { os << '(' << p.x << ',' << p.y << ')'; return os; }

	// 比較（x 座標優先）
	bool operator==(const Point& p) const { return x == p.x && y == p.y; }
	bool operator!=(const Point& p) const { return !(*this == p); }
	bool operator<(const Point& p) const { return x == p.x ? y < p.y : x < p.x; }
	bool operator>=(const Point& p) const { return !(*this < p); }
	bool operator>(const Point& p) const { return x == p.x ? y > p.y : x > p.x; }
	bool operator<=(const Point& p) const { return !(*this > p); }

	// 加算，減算，スカラー倍，スカラー除算
	Point& operator+=(const Point& p) { x += p.x; y += p.y;	return *this; }
	Point operator+(const Point& p) const { Point q(*this); return q += p; }
	Point& operator-=(const Point& p) { x -= p.x; y -= p.y;	return *this; }
	Point operator-(const Point& p) const { Point q(*this); return q -= p; }
	Point& operator*=(const T& c) { x *= c; y *= c;	return *this; }
	Point operator*(const T& c) const { Point q(*this); return q *= c; }
	Point& operator/=(const T& c) { x /= c; y /= c;	return *this; }
	Point operator/(const T& c) const { Point q(*this); return q /= c; }
	friend Point operator*(const T& sc, const Point& p) { return p * sc; }
	Point operator-() const { Point a = *this; return a *= -1; }

	// 二乗ノルム，ノルム，正規化
	T sqnorm() const { return x * x + y * y; }
	double norm() const { return sqrt((double)x * x + (double)y * y); }
	Point<double> normalize() const { return Point<double>(*this) / norm(); }

	// 内積，外積，成す角度
	T dot(const Point& other) const { return x * other.x + y * other.y; }
	T cross(const Point& other) const { return x * other.y - y * other.x; }
	double angle(const Point& other) const {
		return atan2(this->cross(other), this->dot(other));
	}
};


//【平面内の直線，線分】
/*
* {a, b} : 2 点 a, b を通る a → b 方向の有向直線を表す．
*
* その他，無向直線，有向線分，無向線分などを表すのにも用いる．
*/
template <class T> using Line = pair<Point<T>, Point<T>>;


//【平面内の円】
/*
* {p, r} : 点 p を中心とする半径 r の円を表す．
*/
template <class T> using Circle = pair<Point<T>, T>;


//【二円の共通接線】O(1)
/*
* 2 円 c1, c2 の共通接線の本数を返す．また c1 との接点があればその座標を ts に格納する．
* c1 = c2 のときは -1 を返す．
*/
int common_tangent(const Circle<ll>& c1, const Circle<ll>& c2, vector<Point<double>>& ts) {
	// verify : https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/all/CGL_7_G

	ts.clear();

	if (c1 == c2) return -1;

	// 円 c1, c2 の中心と半径
	Point<ll> o1 = c1.first; ll r1 = c1.second;
	Point<ll> o2 = c2.first; ll r2 = c2.second;

	// o1 から o2 へのベクトル，半径の和と差
	Point<ll> d = o2 - o1;
	ll r_sum = r1 + r2, r_dif = abs(r1 - r2);

	// 中心間距離が円の半径の差より小さい場合 → 共通接線 0 本
	if (d.sqnorm() < r_dif * r_dif) return 0;

	// 中心間距離が円の半径の差に等しい場合 → 共通接線 1 本
	if (d.sqnorm() == r_dif * r_dif) {
		ts.push_back(Point<double>(o1) + (r1 > r2 ? 1 : -1) * Point<double>(d) * (r1 / d.norm()));
		return 1;
	}

	// 共通外接線を追加
	double x = r1 * r_dif / d.norm();
	double y = r1 * sqrt(1 - (double)r_dif * r_dif / d.sqnorm());
	Point<double> nd = Point<double>(d) * (x / d.norm());
	Point<double> nn = Point<double>(-(double)d.y, (double)d.x) * (y / d.norm());
	ts.push_back(Point<double>(o1) + (r1 > r2 ? 1 : -1) * nd + nn);
	ts.push_back(Point<double>(o1) + (r1 > r2 ? 1 : -1) * nd - nn);

	// 中心間距離が円の半径の和より大きい場合 → 共通接線 4 本
	if (d.sqnorm() > r_sum * r_sum) {
		// 共通内接線を追加
		double x = r1 * r_sum / d.norm();
		double y = r1 * sqrt(1 - (double)r_sum * r_sum / d.sqnorm());
		Point<double> nd = Point<double>(d) * (x / d.norm());
		Point<double> nn = Point<double>(-(double)d.y, (double)d.x) * (y / d.norm());
		ts.push_back(Point<double>(o1) + nd + nn);
		ts.push_back(Point<double>(o1) + nd - nn);

		return 4;
	}

	// 中心間距離が円の半径の和に等しい場合 → 共通接線 3 本
	if (d.sqnorm() == r_sum * r_sum) {
		ts.push_back(Point<double>(o1) + Point<double>(d) * (r1 / d.norm()));
		return 3;
	}

	// その他の場合 → 共通接線 2 本
	return 2;
}


//【90°回転】O(1)
/*
* 点 p を点 c を中心に 90° × i だけ回転した点を返す．
*/
template <class T>
inline Point<T> rotate90(const Point<T>& p, const Point<T>& c, int i) {
	Point<T> q;
	switch (smod(i, 4)) {
	case 0:
		q.x = 1 * (p.x - c.x) - 0 * (p.y - c.y) + c.x;
		q.y = 0 * (p.x - c.x) + 1 * (p.y - c.y) + c.y;
		break;
	case 1:
		q.x = 0 * (p.x - c.x) - 1 * (p.y - c.y) + c.x;
		q.y = 1 * (p.x - c.x) + 0 * (p.y - c.y) + c.y;
		break;
	case 2:
		q.x = -1 * (p.x - c.x) - 0 * (p.y - c.y) + c.x;
		q.y = 0 * (p.x - c.x) + (-1) * (p.y - c.y) + c.y;
		break;
	case 3:
		q.x = 0 * (p.x - c.x) - (-1) * (p.y - c.y) + c.x;
		q.y = -1 * (p.x - c.x) + 0 * (p.y - c.y) + c.y;
		break;
	default:;
	}
	return q;
}


//【点と直線の距離】O(1)
/*
* 点 p と直線 l との距離を返す．
*/
template <class T> inline double distance_P_L(const Point<T>& p, const Line<T>& l) {
	Point<double> d = (l.second - l.first).normalize();
	Point<double> n(-d.y, d.x);
	Point<double> p2 = p - l.first;
	return p2.dot(n);
}


bool solve() {
	int n;
	cin >> n;

	if (n == 0) return false;

	using PL = Point<ll>;
	using LL = Line<ll>;
	using CL = Circle<ll>;
	using PD = Point<double>;
	using LD = Line<double>;

	vector<PL> p(n); vl r(n), m(n);
	rep(i, n) cin >> p[i] >> r[i] >> m[i];

	vector<CL> cs(2 * n);
	rep(i, n) {
		cs[2 * i] = CL{ p[i], r[i] };
		cs[2 * i + 1] = CL{ p[i], r[i] + m[i] };
	}

	int res = 1;

	// 2 円の組を全探索
	rep(i1, 2 * n) repi(i2, i1 + 1, 2 * n - 1) {
		vector<PD> tp;
		int K = common_tangent(cs[i1], cs[i2], tp);
		if (K <= 0) continue;
//		dump("---------------------------"); dump("cs[i1], cs[i2]:", cs[i1], cs[i2]);

		rep(k, K) {
			LD tl{tp[k], rotate90(PD(cs[i1].first), tp[k], 1)};
//			dump("tl:", tl);

			int cnt = 0;
			rep(j, n) {
				double d = abs(distance_P_L(PD(p[j]), tl));
//				dump("p[j], d:", p[j], d);

				if (r[j] - 1e-10 < d && d < r[j] + m[j] + 1e-10) cnt++;
			}

//			dump("cnt:", cnt);
			chmax(res, cnt);
		}
	}

	dump("ans:", res);
	cout << res << endl;

	return true;
}


int main() {
	input_from_file("input.txt");
//	output_to_file("output.txt");
	
	while (solve());
}
accuracy: 1
time_ms: 30
memory_kb: 3376
final_rank: 1

code_snippet: #ifndef LOCAL
#pragma GCC optimize ("Ofast")
#pragma GCC optimize ("unroll-loops")
#endif

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

using ll=long long;
#define int ll

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#define si(x) int(x.size())
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> bool chmax(t&a,u b){if(a<b){a=b;return true;}else return false;}
template<class t,class u> bool chmin(t&a,u b){if(b<a){a=b;return true;}else return false;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using pi=pair<int,int>;
using vi=vc<int>;

template<class t,class u>
ostream& operator<<(ostream& os,const pair<t,u>& p){
	return os<<"{"<<p.a<<","<<p.b<<"}";
}

template<class t> ostream& operator<<(ostream& os,const vc<t>& v){
	os<<"{";
	for(auto e:v)os<<e<<",";
	return os<<"}";
}

#define mp make_pair
#define mt make_tuple
#define one(x) memset(x,-1,sizeof(x))
#define zero(x) memset(x,0,sizeof(x))
#ifdef LOCAL
void dmpr(ostream&os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ";
	dmpr(os,args...);
}
#define dmp2(...) dmpr(cerr,__LINE__,##__VA_ARGS__)
#else
#define dmp2(...) void(0)
#endif

using uint=unsigned;
using ull=unsigned long long;

template<class t,size_t n>
ostream& operator<<(ostream&os,const array<t,n>&a){
	return os<<vc<t>(all(a));
}

template<int i,class T>
void print_tuple(ostream&,const T&){
}

template<int i,class T,class H,class ...Args>
void print_tuple(ostream&os,const T&t){
	if(i)os<<",";
	os<<get<i>(t);
	print_tuple<i+1,T,Args...>(os,t);
}

template<class ...Args>
ostream& operator<<(ostream&os,const tuple<Args...>&t){
	os<<"{";
	print_tuple<0,tuple<Args...>,Args...>(os,t);
	return os<<"}";
}

template<class t>
void print(t x,int suc=1){
	cout<<x;
	if(suc==1)
		cout<<"\n";
	if(suc==2)
		cout<<" ";
}

ll read(){
	ll i;
	cin>>i;
	return i;
}

vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}

pi readpi(int off=0){
	int a,b;cin>>a>>b;
	return pi(a+off,b+off);
}

template<class t,class u>
void print(const pair<t,u>&p,int suc=1){
	print(p.a,2);
	print(p.b,suc);
}

template<class t,class u>
void print_offset(const pair<t,u>&p,ll off,int suc=1){
	print(p.a+off,2);
	print(p.b+off,suc);
}

template<class T>
void print(const vector<T>&v,int suc=1){
	rep(i,v.size())
		print(v[i],i==int(v.size())-1?suc:2);
}

template<class T>
void print_offset(const vector<T>&v,ll off,int suc=1){
	rep(i,v.size())
		print(v[i]+off,i==int(v.size())-1?suc:2);
}

template<class T,size_t N>
void print(const array<T,N>&v,int suc=1){
	rep(i,N)
		print(v[i],i==int(N)-1?suc:2);
}

string readString(){
	string s;
	cin>>s;
	return s;
}

template<class T>
T sq(const T& t){
	return t*t;
}

void YES(bool ex=true){
	cout<<"YES\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void NO(bool ex=true){
	cout<<"NO\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void Yes(bool ex=true){
	cout<<"Yes\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void No(bool ex=true){
	cout<<"No\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
//#define CAPITAL
/*
void yes(bool ex=true){
	#ifdef CAPITAL
	cout<<"YES"<<"\n";
	#else
	cout<<"Yes"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void no(bool ex=true){
	#ifdef CAPITAL
	cout<<"NO"<<"\n";
	#else
	cout<<"No"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}*/
void possible(bool ex=true){
	#ifdef CAPITAL
	cout<<"POSSIBLE"<<"\n";
	#else
	cout<<"Possible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void impossible(bool ex=true){
	#ifdef CAPITAL
	cout<<"IMPOSSIBLE"<<"\n";
	#else
	cout<<"Impossible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}

constexpr ll ten(int n){
	return n==0?1:ten(n-1)*10;
}

const ll infLL=LLONG_MAX/3;

#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif

int topbit(signed t){
	return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
	return t==0?-1:63-__builtin_clzll(t);
}
int botbit(signed a){
	return a==0?32:__builtin_ctz(a);
}
int botbit(ll a){
	return a==0?64:__builtin_ctzll(a);
}
int botbit(ull a){
	return a==0?64:__builtin_ctzll(a);
}
int popcount(signed t){
	return __builtin_popcount(t);
}
int popcount(ll t){
	return __builtin_popcountll(t);
}
int popcount(ull t){
	return __builtin_popcountll(t);
}
bool ispow2(int i){
	return i&&(i&-i)==i;
}
ll mask(int i){
	return (ll(1)<<i)-1;
}

bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}

template<class t> void mkuni(vc<t>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.ed);
}

ll rand_int(ll l, ll r) { //[l, r]
	#ifdef LOCAL
	static mt19937_64 gen;
	#else
	static mt19937_64 gen(chrono::steady_clock::now().time_since_epoch().count());
	#endif
	return uniform_int_distribution<ll>(l, r)(gen);
}

template<class t>
void myshuffle(vc<t>&a){
	rep(i,si(a))swap(a[i],a[rand_int(0,i)]);
}

template<class t>
int lwb(const vc<t>&v,const t&a){
	return lower_bound(all(v),a)-v.bg;
}

vvc<int> readGraph(int n,int m){
	vvc<int> g(n);
	rep(i,m){
		int a,b;
		cin>>a>>b;
		//sc.read(a,b);
		a--;b--;
		g[a].pb(b);
		g[b].pb(a);
	}
	return g;
}

vvc<int> readTree(int n){
	return readGraph(n,n-1);
}

vc<ll> presum(const vi&a){
	vc<ll> s(si(a)+1);
	rep(i,si(a))s[i+1]=s[i]+a[i];
	return s;
}

//copied from yosupo's library
//ptARTLY VERIFIED

//USACO 2022 January ptlatinum C

#define GEOF

#ifdef GEOF
using ld=long double;
//using ld=double;
const ld PI=acos(ld(-1));
#else
using ld=ll;
#endif
const ld eps=1e-9;
int sgn(ld a){return a<-eps?-1:(a>eps?1:0);}
int sgn(ld a,ld b){return sgn(a-b);}
/*
using pt=complex<ld>;
#define x real()
#define y imag()
*/
struct pt {
    ld x,y;
    //pt(ld _x = ld(0), ld _y = ld(0)) : x(_x), y(_y) {}
    pt():x(0),y(0){}
    pt(ld xx,ld yy):x(xx),y(yy){}
    pt operator+(const pt& r) const { return {x + r.x, y + r.y}; }
    pt operator-(const pt& r) const { return {x - r.x, y - r.y}; }
    pt operator*(const pt& r) const {
        return {x * r.x - y * r.y, x * r.y + y * r.x};
    }

    pt operator*(const ld& r) const { return {x * r, y * r}; }
    pt operator/(const ld& r) const { return {x / r, y / r}; }

    pt& operator+=(const pt& r) { return *this = *this + r; }
    pt& operator-=(const pt& r) { return *this = *this - r; }
    pt& operator*=(const pt& r) { return *this = *this * r; }
    pt& operator*=(const ld& r) { return *this = *this * r; }
    pt& operator/=(const ld& r) { return *this = *this / r; }

    pt operator-() const { return {-x, -y}; }

    bool operator<(const pt& r) const {
        return 2 * sgn(x, r.x) + sgn(y, r.y) < 0;
    }
    bool operator==(const pt& r) const { return sgn((*this - r).rabs()) == 0; }
    bool operator!=(const pt& r) const { return !(*this == r); }

    ld norm() const { return x * x + y * y; }
    ld rabs() const { return max(std::abs(x), std::abs(y)); }  // robust abs
    pair<ld, ld> to_pair() const { return {x, y}; }
    #ifdef GEOF
    ld abs() const { return sqrt(norm()); }
    ld arg() const { return atan2(y, x); }
    static pt polar(ld le, ld th) { return {le * cos(th), le * sin(th)}; }
	#endif
};
istream& operator>>(istream& is, pt& p){
	return is>>p.x>>p.y;
}
ostream& operator<<(ostream& os, const pt& p) {
    return os << "pt(" << p.x << ", " << p.y << ")";
}
ld norm(const pt&a){
	return a.norm();
}
#ifdef GEOF
ld abs(const pt&a){
	return sqrt(norm(a));
}
//XXII Opencup Gpt of Ural K
pt normalized(const pt&a){
	return a/abs(a);
}
ld arg(const pt&a){return a.arg();}
//normalize to [-PI,PI)
//Contest 2: ptKU Contest 1, ptTZ Summer 2022 Day 4
ld normarg(ld a){
	ld res=fmod(a+PI,2*PI);
	if(res<0)res+=PI;
	else res-=PI;
	return res;
}
//AOJ1183
//arg between ab
//assume given lengths are valid
ld arg(ld a,ld b,ld c){
	return acos(min(max((a*a+b*b-c*c)/(2*a*b),ld(-1)),ld(1)));
}
#endif
ld norm(const pt&a,const pt&b){
	return (a-b).norm();
}
ld dot(const pt&a,const pt&b){return a.x*b.x+a.y*b.y;}
ld crs(const pt& a,const pt& b){return a.x*b.y-a.y*b.x;}
ld crs(const pt& a,const pt& b,const pt& c){return crs(b-a,c-a);}
int ccw(const pt& a,const pt& b){return sgn(crs(a,b));}
int ccw(const pt& a,const pt& b,const pt& c){return ccw(b-a,c-a);}
//(-pi,0](0,pi]
int argtype(const pt&a){
	if(sgn(a.y)==0)return a.x<0?1:0;
	return a.y<0?0:1;
}
int argcmp(const pt&a,const pt&b){
	int at=argtype(a),bt=argtype(b);
	if(at!=bt)return at<bt?-1:1;
	return -ccw(a,b);
};
bool argless(const pt&a,const pt&b){return argcmp(a,b)<0;}
//(-2)[a,-1](0)[b,1](2)
int bet(pt a,pt b,pt c){
	pt d=b-a;
	ld e=dot(d,c-a);
	if(sgn(e)<=0)return sgn(e)-1;
	return sgn(e-norm(d))+1;
}
//(a,b) を結ぶ直線を考え，x 座標との交点の y 座標を求める
//(分子,分母)を返す
pair<ld,ld> xcut(const pt&a,const pt&b,ld x){
	return mp(a.y*(b.x-x)-b.y*(a.x-x),b.x-a.x);
}
//XXII Opencup Gpt of Ural K
pt rot90(pt a){
	return pt(-a.y,a.x);
}
#ifdef GEOF
ld xcutval(const pt&a,const pt&b,ld x){
	auto [p,q]=xcut(a,b,x);
	return p/q;
}
//AOJ1183
//Xmas2010 E
//-+ の 順で返す
//a の符号により，small/large が決まる
int qeq(ld a,ld b,ld c,ld&d,ld&e){
	if(sgn(a)==0){
		if(sgn(b)==0)return 0;
		d=-c/b;
		return 1;
	}
	ld f=b*b-4*a*c;
	if(sgn(f)<0)return 0;
	ld g=sqrt(max(f,ld(0)));
	d=(-b-g)/(2*a);
	e=(-b+g)/(2*a);
	return sgn(f)+1;
}
#endif

using ln=pair<pt,pt>;
pt dir(ln a){return a.b-a.a;}
pt eval(ln a,ld b){return a.a+dir(a)*b;}
ld crs(ln a,pt b){return crs(a.a,a.b,b);}
int ccw(ln a,pt b){return ccw(a.a,a.b,b);}
int bet(ln a,pt b){return bet(a.a,a.b,b);}
pt proj(ln a,pt b){
	pt c=dir(a);
	return a.a+c*dot(c,b-a.a)/norm(c);
}
pt refl(ln a,pt b){
	return proj(a,b)*2-b;
}
//AOJ2201
ld dlp(ln a,pt b){
	return abs(crs(a,b)/abs(dir(a)));
}
//AOJ0153
ld dsp(ln a,pt b){
	pt c=proj(a,b);
	if(abs(bet(a.a,a.b,c))<=1)return abs(b-c);
	return min(abs(b-a.a),abs(b-a.b));
}
//AOJ1157
//0-no,1-yes(endpoint),2-yes(innner),3-overelap
//if the two line touch like this
// x----x----x
//it returns 1
int iss(ln a,ln b){
	int c1=ccw(a.a,a.b,b.a),c2=ccw(a.a,a.b,b.b);
	int d1=ccw(b.a,b.b,a.a),d2=ccw(b.a,b.b,a.b);
	if(c1||c2||d1||d2)return 1-max(c1*c2,d1*d2);
	int f=bet(a.a,a.b,b.a),g=bet(a.a,a.b,b.b);
	if(max(f,g)==-2||min(f,g)==2)return 0;
	if(max(f,g)==-1||min(f,g)==1)return 1;
	return 3;
}
//segment a intersects line b?
//endpoints inclusive
bool isl(ln a,ln b){
	int d1=ccw(b.a,b.b,a.a),d2=ccw(b.a,b.b,a.b);
	return d1*d2<=0;
}
//AOJ1033
pt cll(ln a,ln b){
	return eval(a,crs(b.a,b.b,a.a)/crs(dir(a),dir(b)));
}
//AOJ1157
ld dss(ln a,ln b){
	if(iss(a,b))return 0;
	return min({dsp(a,b.a),dsp(a,b.b),dsp(b,a.a),dsp(b,a.b)});
}
//AOJ2160
//反時計回り方向に伸びる垂直二等分線
ln vbis(pt a,pt b){
	pt c=(a+b)*ld(0.5),d=b-a;
	return ln(c,pt(c.x-d.y,c.y+d.x));
}

//cm,cr を pt,C に書き換えたあとコンパイルだけ通した，verify はしてない(バカ？)
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
struct C{
	pt c;
	ld r;
	pt eval(ld a){
		return c+pt::polar(r,a);
	}
};
istream& operator>>(istream& is,C& c){
	return is>>c.c>>c.r;
}
ostream& operator<<(ostream& os,const C& c){
	return os<<"C{"<<c.c<<","<<c.r<<"}";
}

//AOJ0153
//0-no,1-edge,2-in
int cont(C a,pt b){
	return sgn(a.r-abs(b-a.c))+1;
}
//AOJ0153 円じゃなくて円盤
//0-no,1-touch,2-cross
int ids(C a,ln b){
	return sgn(a.r-dsp(b,a.c))+1;
}
//AOJ0129 (touch以外)
//0-no(in),1-touch(in),2-cross,3-touch(out),4-no(out)
int ics(C a,ln b){
	int c=ids(a,b);
	if(c<=1)return 4-c;
	return sgn(max(abs(b.a-a.c),abs(b.b-a.c))-a.r)+1;
}
//AOJ1183
//eval(b,t) が a と重なる t を c,d に入れる
//解の個数を返す
int ccl(C a,ln b,ld&c,ld&d){
	pt e=dir(b);
	pt f=b.a-a.c;
	return qeq(norm(e),2*dot(e,f),norm(f)-a.r*a.r,c,d);
}
//AOJ0023
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4 (2,7)
//0-apart,1-coincide,2-a<b,3-a<=b,4-a>b,5-a>=b,6-a touch b,7-a cross b
int icc(C a,C b){
	ld c=abs(a.c-b.c);
	if(sgn(c)==0&&sgn(a.r-b.r)==0)return 1;
	int d=sgn(c+a.r-b.r);
	if(d<=0)return d+3;
	int e=sgn(c+b.r-a.r);
	if(e<=0)return e+5;
	int f=sgn(a.r+b.r-c);
	if(f>=0)return f+6;
	return 0;
}
//AOJ2572
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
//args of two intersections r,l seen by a.c
//assume two circles cross
pair<ld,ld> ccc(C a,C b){
	ld c=arg(b.c-a.c);
	ld d=arg(a.r,abs(b.c-a.c),b.r);
	return make_pair(c-d,c+d);
}
//XXI Opencup GP of Siberia 5
vc<pt> ccc_list(C a,C b){
	int v=icc(a,b);
	if(v==0||v==1||v==2||v==4)return {};
	auto z=ccc(a,b);
	if(v==3||v==5||v==6){
		return {a.c+pt::polar(a.r,z.a)};
	}else{
		return {a.c+pt::polar(a.r,z.a),a.c+pt::polar(a.r,z.b)};
	}
}
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
//単位円で偏角 0-x で y*(-dx) を積分した値
ld cutareaarg(ld x){
	return (x-sin(2*x)/2)/2;
}
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
ld cutareaarg(ld l,ld r){
	return cutareaarg(r)-cutareaarg(l);
}
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1) J
ld cutareaarg(C c,ld l,ld r){
	l=normarg(l);
	r=normarg(r);
	return (l<=r?cutareaarg(l,r):cutareaarg(l,PI)+cutareaarg(-PI,r))
			*sq(c.r)-c.c.y*c.r*(cos(r)-cos(l));
}
//円 a の偏角 b の位置で接する直線
ln tanln(C a,ld b){
	pt c=a.eval(b);
	return ln(c,c+pt::polar(1,b+PI/2));
}
//AOJ2201
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1)
//a と b の共通外接線を引く
//a の接点の偏角を返す
pair<ld,ld> tangent_points_ext(C a,C b){
	pt dif=b.c-a.c;
	ld w=acos(max<ld>(-1,min<ld>(1,(a.r-b.r)/abs(dif))));
	ld k=arg(dif);
	return mp(normarg(k-w),normarg(k+w));
}
pair<ln,ln> tangent_line_ext(C a,C b){
	auto [p,q]=tangent_points_ext(a,b);
	return mp(tanln(a,p),tanln(a,q));
}
//AOJ2201
//内接線
pair<ld,ld> tangent_points_in(C a,C b){
	pt dif=b.c-a.c;
	ld w=acos(min<ld>(1,(a.r+b.r)/abs(dif)));
	ld k=arg(dif);
	return mp(normarg(k-w),normarg(k+w));
}
pair<ln,ln> tangent_line_in(C a,C b){
	auto [p,q]=tangent_points_in(a,b);
	return mp(tanln(a,p),tanln(a,q));
}
//AOJ2201(内接外接あたりの verify はできてない)
vc<ln> tangent_lines(C a,C b){
	vc<ln> res;
	int v=icc(a,b);
	if(v==0||v==3||v==5||v==6||v==7){//out
		auto [p,q]=tangent_line_ext(a,b);
		res.pb(p);
		if(v!=3&&v!=5)res.pb(q); //内接しているときは外接線は 1 つ
	}
	if(v==0||v==6){//in
		auto [p,q]=tangent_line_in(a,b);
		res.pb(p);
		if(v!=6)res.pb(q); //外接しているときは内接線は 1 つ
	}
	return res;
}

//極大な円だけ残す，重複も消す
//AOJ1047
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1) J
//Contest 5, PTZ 2021 Summer Day 2, NAC 2021 B
vc<C> simplify_circle_sets(const vc<C>&rw){
	vc<C> cs;
	for(auto cur:rw){
		bool ok=true;
		rep(j,si(cs)){
			int w=icc(cs[j],cur);
			if(w==1||w==4||w==5){
				ok=false;
			}
		}
		if(ok){
			rep(j,si(cs)){
				int w=icc(cs[j],cur);
				if(w==1||w==2||w==3){
					cs.erase(cs.bg+j--);
				}
			}
			cs.pb(cur);
		}
	}
	return cs;
}

//ByteCamp 2022 Day3 A
//原点中心,半径 r の円の x 座標 a 以下の領域の面積
ld cutarea_sub(ld r,ld a){
	ld theta=acos(max(min(a/r,ld(1)),ld(-1)));
	return sq(r)*(PI-theta)+a*r*sin(theta);
}
//2020 Multi-Uni Day5 J
//x座標 b 以上 c 以下の部分だけ取ってきて，その面積
ld cutarea(C a,ld b,ld c){
	chmax(b,a.c.x-a.r);
	chmin(c,a.c.x+a.r);
	b-=a.c.x;
	c-=a.c.x;
	if(sgn(c-b)<=0)return 0;
	return cutarea_sub(a.r,c)-cutarea_sub(a.r,b);
}

//AOJ2423
C circumc(pt a,pt b,pt c){
	b-=a;
	c-=a;
	pt r=c*norm(b)-b*norm(c);
	r=pt(r.y,-r.x)/(2*crs(b,c));
	return {a+r,abs(r)};
}
//AOJ2423
C mindisc(const vc<pt>& p,array<pt,3> q,int i,int j){
	if(i==int(p.size())){
		if(j==0)
			return {{0,0},-1};
		else if(j==1)
			return {q[0],0};
		else if(j==2)
			return {(q[0]+q[1])*ld(0.5),abs(q[0]-q[1])/2};
		else if(j==3)
			return circumc(q[0],q[1],q[2]);
		else
			assert(false);
	}
	C c=mindisc(p,q,i+1,j);
	if(sgn(abs(c.c-p[i])-c.r)==1){
		assert(j<3);
		q[j]=p[i];
		return mindisc(p,q,i+1,j+1);
	}else
		return c;
}
C mindisc(vc<pt> p){
	shuffle(all(p),mt19937());
	return mindisc(p,array<pt,3>(),0,0);
}

//CF773F
//p の中で k 点以上含むような円の最小半径

void slv(){
	int n;cin>>n;if(n==0)exit(0);
	vc<pt> c(n);
	vc<ld> a(n),b(n);
	rep(i,n){
		cin>>c[i]>>a[i]>>b[i];
		b[i]+=a[i];
	}
	int ans=0;
	auto work=[&](ln z){
		int cnt=0;
		rep(i,n){
			ld r=dlp(z,c[i]);
			if(sgn(a[i],r)<=0&&sgn(r,b[i])<=0)cnt++;
		}
		chmax(ans,cnt);
	};
	rep(i,n){
		pt v=c[i];
		v.y-=a[i];
		work(ln(v,pt(v.x+1,v.y)));
	}
	rep(i,n){
		pt v=c[i];
		v.y-=b[i];
		work(ln(v,pt(v.x+1,v.y)));
	}
	rep(i,n)for(auto x:{a[i],b[i]})rng(j,i+1,n)for(auto y:{a[j],b[j]}){
		vc<ln> ls=tangent_lines(C{c[i],x},C{c[j],y});
		for_each(all(ls),work);
	}
	cout<<ans<<endl;
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	
	while(1)slv();
}
accuracy: 1
time_ms: 100
memory_kb: 3572
final_rank: 6

code_snippet: #ifndef LOCAL
#pragma GCC optimize ("Ofast")
#pragma GCC optimize ("unroll-loops")
#endif

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

using ll=long long;
#define int ll

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#define si(x) int(x.size())
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> bool chmax(t&a,u b){if(a<b){a=b;return true;}else return false;}
template<class t,class u> bool chmin(t&a,u b){if(b<a){a=b;return true;}else return false;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using pi=pair<int,int>;
using vi=vc<int>;

template<class t,class u>
ostream& operator<<(ostream& os,const pair<t,u>& p){
	return os<<"{"<<p.a<<","<<p.b<<"}";
}

template<class t> ostream& operator<<(ostream& os,const vc<t>& v){
	os<<"{";
	for(auto e:v)os<<e<<",";
	return os<<"}";
}

#define mp make_pair
#define mt make_tuple
#define one(x) memset(x,-1,sizeof(x))
#define zero(x) memset(x,0,sizeof(x))
#ifdef LOCAL
void dmpr(ostream&os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ";
	dmpr(os,args...);
}
#define dmp2(...) dmpr(cerr,__LINE__,##__VA_ARGS__)
#else
#define dmp2(...) void(0)
#endif

using uint=unsigned;
using ull=unsigned long long;

template<class t,size_t n>
ostream& operator<<(ostream&os,const array<t,n>&a){
	return os<<vc<t>(all(a));
}

template<int i,class T>
void print_tuple(ostream&,const T&){
}

template<int i,class T,class H,class ...Args>
void print_tuple(ostream&os,const T&t){
	if(i)os<<",";
	os<<get<i>(t);
	print_tuple<i+1,T,Args...>(os,t);
}

template<class ...Args>
ostream& operator<<(ostream&os,const tuple<Args...>&t){
	os<<"{";
	print_tuple<0,tuple<Args...>,Args...>(os,t);
	return os<<"}";
}

template<class t>
void print(t x,int suc=1){
	cout<<x;
	if(suc==1)
		cout<<"\n";
	if(suc==2)
		cout<<" ";
}

ll read(){
	ll i;
	cin>>i;
	return i;
}

vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}

pi readpi(int off=0){
	int a,b;cin>>a>>b;
	return pi(a+off,b+off);
}

template<class t,class u>
void print(const pair<t,u>&p,int suc=1){
	print(p.a,2);
	print(p.b,suc);
}

template<class t,class u>
void print_offset(const pair<t,u>&p,ll off,int suc=1){
	print(p.a+off,2);
	print(p.b+off,suc);
}

template<class T>
void print(const vector<T>&v,int suc=1){
	rep(i,v.size())
		print(v[i],i==int(v.size())-1?suc:2);
}

template<class T>
void print_offset(const vector<T>&v,ll off,int suc=1){
	rep(i,v.size())
		print(v[i]+off,i==int(v.size())-1?suc:2);
}

template<class T,size_t N>
void print(const array<T,N>&v,int suc=1){
	rep(i,N)
		print(v[i],i==int(N)-1?suc:2);
}

string readString(){
	string s;
	cin>>s;
	return s;
}

template<class T>
T sq(const T& t){
	return t*t;
}

void YES(bool ex=true){
	cout<<"YES\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void NO(bool ex=true){
	cout<<"NO\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void Yes(bool ex=true){
	cout<<"Yes\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void No(bool ex=true){
	cout<<"No\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
//#define CAPITAL
/*
void yes(bool ex=true){
	#ifdef CAPITAL
	cout<<"YES"<<"\n";
	#else
	cout<<"Yes"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void no(bool ex=true){
	#ifdef CAPITAL
	cout<<"NO"<<"\n";
	#else
	cout<<"No"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}*/
void possible(bool ex=true){
	#ifdef CAPITAL
	cout<<"POSSIBLE"<<"\n";
	#else
	cout<<"Possible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void impossible(bool ex=true){
	#ifdef CAPITAL
	cout<<"IMPOSSIBLE"<<"\n";
	#else
	cout<<"Impossible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}

constexpr ll ten(int n){
	return n==0?1:ten(n-1)*10;
}

const ll infLL=LLONG_MAX/3;

#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif

int topbit(signed t){
	return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
	return t==0?-1:63-__builtin_clzll(t);
}
int botbit(signed a){
	return a==0?32:__builtin_ctz(a);
}
int botbit(ll a){
	return a==0?64:__builtin_ctzll(a);
}
int botbit(ull a){
	return a==0?64:__builtin_ctzll(a);
}
int popcount(signed t){
	return __builtin_popcount(t);
}
int popcount(ll t){
	return __builtin_popcountll(t);
}
int popcount(ull t){
	return __builtin_popcountll(t);
}
bool ispow2(int i){
	return i&&(i&-i)==i;
}
ll mask(int i){
	return (ll(1)<<i)-1;
}

bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}

template<class t> void mkuni(vc<t>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.ed);
}

ll rand_int(ll l, ll r) { //[l, r]
	#ifdef LOCAL
	static mt19937_64 gen;
	#else
	static mt19937_64 gen(chrono::steady_clock::now().time_since_epoch().count());
	#endif
	return uniform_int_distribution<ll>(l, r)(gen);
}

template<class t>
void myshuffle(vc<t>&a){
	rep(i,si(a))swap(a[i],a[rand_int(0,i)]);
}

template<class t>
int lwb(const vc<t>&v,const t&a){
	return lower_bound(all(v),a)-v.bg;
}

vvc<int> readGraph(int n,int m){
	vvc<int> g(n);
	rep(i,m){
		int a,b;
		cin>>a>>b;
		//sc.read(a,b);
		a--;b--;
		g[a].pb(b);
		g[b].pb(a);
	}
	return g;
}

vvc<int> readTree(int n){
	return readGraph(n,n-1);
}

vc<ll> presum(const vi&a){
	vc<ll> s(si(a)+1);
	rep(i,si(a))s[i+1]=s[i]+a[i];
	return s;
}

//copied from yosupo's library
//ptARTLY VERIFIED

//USACO 2022 January ptlatinum C

#define GEOF

#ifdef GEOF
using ld=long double;
//using ld=double;
const ld PI=acos(ld(-1));
#else
using ld=ll;
#endif
const ld eps=1e-9;
int sgn(ld a){return a<-eps?-1:(a>eps?1:0);}
int sgn(ld a,ld b){return sgn(a-b);}
/*
using pt=complex<ld>;
#define x real()
#define y imag()
*/
struct pt {
    ld x,y;
    //pt(ld _x = ld(0), ld _y = ld(0)) : x(_x), y(_y) {}
    pt():x(0),y(0){}
    pt(ld xx,ld yy):x(xx),y(yy){}
    pt operator+(const pt& r) const { return {x + r.x, y + r.y}; }
    pt operator-(const pt& r) const { return {x - r.x, y - r.y}; }
    pt operator*(const pt& r) const {
        return {x * r.x - y * r.y, x * r.y + y * r.x};
    }

    pt operator*(const ld& r) const { return {x * r, y * r}; }
    pt operator/(const ld& r) const { return {x / r, y / r}; }

    pt& operator+=(const pt& r) { return *this = *this + r; }
    pt& operator-=(const pt& r) { return *this = *this - r; }
    pt& operator*=(const pt& r) { return *this = *this * r; }
    pt& operator*=(const ld& r) { return *this = *this * r; }
    pt& operator/=(const ld& r) { return *this = *this / r; }

    pt operator-() const { return {-x, -y}; }

    bool operator<(const pt& r) const {
        return 2 * sgn(x, r.x) + sgn(y, r.y) < 0;
    }
    bool operator==(const pt& r) const { return sgn((*this - r).rabs()) == 0; }
    bool operator!=(const pt& r) const { return !(*this == r); }

    ld norm() const { return x * x + y * y; }
    ld rabs() const { return max(std::abs(x), std::abs(y)); }  // robust abs
    pair<ld, ld> to_pair() const { return {x, y}; }
    #ifdef GEOF
    ld abs() const { return sqrt(norm()); }
    ld arg() const { return atan2(y, x); }
    static pt polar(ld le, ld th) { return {le * cos(th), le * sin(th)}; }
	#endif
};
istream& operator>>(istream& is, pt& p){
	return is>>p.x>>p.y;
}
ostream& operator<<(ostream& os, const pt& p) {
    return os << "pt(" << p.x << ", " << p.y << ")";
}
ld norm(const pt&a){
	return a.norm();
}
#ifdef GEOF
ld abs(const pt&a){
	return sqrt(norm(a));
}
//XXII Opencup Gpt of Ural K
pt normalized(const pt&a){
	return a/abs(a);
}
ld arg(const pt&a){return a.arg();}
//normalize to [-PI,PI)
//Contest 2: ptKU Contest 1, ptTZ Summer 2022 Day 4
ld normarg(ld a){
	ld res=fmod(a+PI,2*PI);
	if(res<0)res+=PI;
	else res-=PI;
	return res;
}
//AOJ1183
//arg between ab
//assume given lengths are valid
ld arg(ld a,ld b,ld c){
	return acos(min(max((a*a+b*b-c*c)/(2*a*b),ld(-1)),ld(1)));
}
#endif
ld norm(const pt&a,const pt&b){
	return (a-b).norm();
}
ld dot(const pt&a,const pt&b){return a.x*b.x+a.y*b.y;}
ld crs(const pt& a,const pt& b){return a.x*b.y-a.y*b.x;}
ld crs(const pt& a,const pt& b,const pt& c){return crs(b-a,c-a);}
int ccw(const pt& a,const pt& b){return sgn(crs(a,b));}
int ccw(const pt& a,const pt& b,const pt& c){return ccw(b-a,c-a);}
//(-pi,0](0,pi]
int argtype(const pt&a){
	if(sgn(a.y)==0)return a.x<0?1:0;
	return a.y<0?0:1;
}
int argcmp(const pt&a,const pt&b){
	int at=argtype(a),bt=argtype(b);
	if(at!=bt)return at<bt?-1:1;
	return -ccw(a,b);
};
bool argless(const pt&a,const pt&b){return argcmp(a,b)<0;}
//(-2)[a,-1](0)[b,1](2)
int bet(pt a,pt b,pt c){
	pt d=b-a;
	ld e=dot(d,c-a);
	if(sgn(e)<=0)return sgn(e)-1;
	return sgn(e-norm(d))+1;
}
//(a,b) を結ぶ直線を考え，x 座標との交点の y 座標を求める
//(分子,分母)を返す
pair<ld,ld> xcut(const pt&a,const pt&b,ld x){
	return mp(a.y*(b.x-x)-b.y*(a.x-x),b.x-a.x);
}
//XXII Opencup Gpt of Ural K
pt rot90(pt a){
	return pt(-a.y,a.x);
}
#ifdef GEOF
ld xcutval(const pt&a,const pt&b,ld x){
	auto [p,q]=xcut(a,b,x);
	return p/q;
}
//AOJ1183
//Xmas2010 E
//-+ の 順で返す
//a の符号により，small/large が決まる
int qeq(ld a,ld b,ld c,ld&d,ld&e){
	if(sgn(a)==0){
		if(sgn(b)==0)return 0;
		d=-c/b;
		return 1;
	}
	ld f=b*b-4*a*c;
	if(sgn(f)<0)return 0;
	ld g=sqrt(max(f,ld(0)));
	d=(-b-g)/(2*a);
	e=(-b+g)/(2*a);
	return sgn(f)+1;
}
#endif

using ln=pair<pt,pt>;
pt dir(ln a){return a.b-a.a;}
pt eval(ln a,ld b){return a.a+dir(a)*b;}
ld crs(ln a,pt b){return crs(a.a,a.b,b);}
int ccw(ln a,pt b){return ccw(a.a,a.b,b);}
int bet(ln a,pt b){return bet(a.a,a.b,b);}
pt proj(ln a,pt b){
	pt c=dir(a);
	return a.a+c*dot(c,b-a.a)/norm(c);
}
pt refl(ln a,pt b){
	return proj(a,b)*2-b;
}
//AOJ2201
ld dlp(ln a,pt b){
	return abs(crs(a,b)/abs(dir(a)));
}
//AOJ0153
ld dsp(ln a,pt b){
	pt c=proj(a,b);
	if(abs(bet(a.a,a.b,c))<=1)return abs(b-c);
	return min(abs(b-a.a),abs(b-a.b));
}
//AOJ1157
//0-no,1-yes(endpoint),2-yes(innner),3-overelap
//if the two line touch like this
// x----x----x
//it returns 1
int iss(ln a,ln b){
	int c1=ccw(a.a,a.b,b.a),c2=ccw(a.a,a.b,b.b);
	int d1=ccw(b.a,b.b,a.a),d2=ccw(b.a,b.b,a.b);
	if(c1||c2||d1||d2)return 1-max(c1*c2,d1*d2);
	int f=bet(a.a,a.b,b.a),g=bet(a.a,a.b,b.b);
	if(max(f,g)==-2||min(f,g)==2)return 0;
	if(max(f,g)==-1||min(f,g)==1)return 1;
	return 3;
}
//segment a intersects line b?
//endpoints inclusive
bool isl(ln a,ln b){
	int d1=ccw(b.a,b.b,a.a),d2=ccw(b.a,b.b,a.b);
	return d1*d2<=0;
}
//AOJ1033
pt cll(ln a,ln b){
	return eval(a,crs(b.a,b.b,a.a)/crs(dir(a),dir(b)));
}
//AOJ1157
ld dss(ln a,ln b){
	if(iss(a,b))return 0;
	return min({dsp(a,b.a),dsp(a,b.b),dsp(b,a.a),dsp(b,a.b)});
}
//AOJ2160
//反時計回り方向に伸びる垂直二等分線
ln vbis(pt a,pt b){
	pt c=(a+b)*ld(0.5),d=b-a;
	return ln(c,pt(c.x-d.y,c.y+d.x));
}

//cm,cr を pt,C に書き換えたあとコンパイルだけ通した，verify はしてない(バカ？)
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
struct C{
	pt c;
	ld r;
	pt eval(ld a){
		return c+pt::polar(r,a);
	}
};
istream& operator>>(istream& is,C& c){
	return is>>c.c>>c.r;
}
ostream& operator<<(ostream& os,const C& c){
	return os<<"C{"<<c.c<<","<<c.r<<"}";
}

//AOJ0153
//0-no,1-edge,2-in
int cont(C a,pt b){
	return sgn(a.r-abs(b-a.c))+1;
}
//AOJ0153 円じゃなくて円盤
//0-no,1-touch,2-cross
int ids(C a,ln b){
	return sgn(a.r-dsp(b,a.c))+1;
}
//AOJ0129 (touch以外)
//0-no(in),1-touch(in),2-cross,3-touch(out),4-no(out)
int ics(C a,ln b){
	int c=ids(a,b);
	if(c<=1)return 4-c;
	return sgn(max(abs(b.a-a.c),abs(b.b-a.c))-a.r)+1;
}
//AOJ1183
//eval(b,t) が a と重なる t を c,d に入れる
//解の個数を返す
int ccl(C a,ln b,ld&c,ld&d){
	pt e=dir(b);
	pt f=b.a-a.c;
	return qeq(norm(e),2*dot(e,f),norm(f)-a.r*a.r,c,d);
}
//AOJ0023
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4 (2,7)
//0-apart,1-coincide,2-a<b,3-a<=b,4-a>b,5-a>=b,6-a touch b,7-a cross b
int icc(C a,C b){
	ld c=abs(a.c-b.c);
	if(sgn(c)==0&&sgn(a.r-b.r)==0)return 1;
	int d=sgn(c+a.r-b.r);
	if(d<=0)return d+3;
	int e=sgn(c+b.r-a.r);
	if(e<=0)return e+5;
	int f=sgn(a.r+b.r-c);
	if(f>=0)return f+6;
	return 0;
}
//AOJ2572
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
//args of two intersections r,l seen by a.c
//assume two circles cross
pair<ld,ld> ccc(C a,C b){
	ld c=arg(b.c-a.c);
	ld d=arg(a.r,abs(b.c-a.c),b.r);
	return make_pair(c-d,c+d);
}
//XXI Opencup GP of Siberia 5
vc<pt> ccc_list(C a,C b){
	int v=icc(a,b);
	if(v==0||v==1||v==2||v==4)return {};
	auto z=ccc(a,b);
	if(v==3||v==5||v==6){
		return {a.c+pt::polar(a.r,z.a)};
	}else{
		return {a.c+pt::polar(a.r,z.a),a.c+pt::polar(a.r,z.b)};
	}
}
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
//単位円で偏角 0-x で y*(-dx) を積分した値
ld cutareaarg(ld x){
	return (x-sin(2*x)/2)/2;
}
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
ld cutareaarg(ld l,ld r){
	return cutareaarg(r)-cutareaarg(l);
}
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1) J
ld cutareaarg(C c,ld l,ld r){
	l=normarg(l);
	r=normarg(r);
	return (l<=r?cutareaarg(l,r):cutareaarg(l,PI)+cutareaarg(-PI,r))
			*sq(c.r)-c.c.y*c.r*(cos(r)-cos(l));
}
//円 a の偏角 b の位置で接する直線
ln tanln(C a,ld b){
	pt c=a.eval(b);
	return ln(c,c+pt::polar(1,b+PI/2));
}
//AOJ2201
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1)
//a と b の共通外接線を引く
//a の接点の偏角を返す
pair<ld,ld> tangent_points_ext(C a,C b){
	pt dif=b.c-a.c;
	ld w=acos((a.r-b.r)/abs(dif));
	ld k=arg(dif);
	return mp(normarg(k-w),normarg(k+w));
}
pair<ln,ln> tangent_line_ext(C a,C b){
	/*auto [p,q]=tangent_points_ext(a,b);
	return mp(tanln(a,p),tanln(a,q));*/
	auto [p,q]=tangent_points_ext(a,b);
	auto [r,s]=tangent_points_ext(b,a);
	return mp(ln(a.eval(p),b.eval(s)),ln(a.eval(q),b.eval(r)));
}
//a>=b で内接しているケース
ln tangent_line_ext2(C a,C b){
	return tanln(a,arg(b.c-a.c));
}
//AOJ2201
//内接線
pair<ld,ld> tangent_points_in(C a,C b){
	pt dif=b.c-a.c;
	ld w=acos((a.r+b.r)/abs(dif));
	ld k=arg(dif);
	return mp(normarg(k-w),normarg(k+w));
}
pair<ln,ln> tangent_line_in(C a,C b){
	/*auto [p,q]=tangent_points_in(a,b);
	return mp(tanln(a,p),tanln(a,q));*/
	auto [p,q]=tangent_points_in(a,b);
	auto [r,s]=tangent_points_in(b,a);
	return mp(ln(a.eval(p),b.eval(r)),ln(a.eval(q),b.eval(s)));
}
//外接しているケース
ln tangent_line_in2(C a,C b){
	return tanln(a,arg(b.c-a.c));
}
//AOJ2201(ext2,in2 あたりの verify はできてない)
vc<ln> tangent_lines(C a,C b){
	vc<ln> res;
	int v=icc(a,b);
	//out
	if(v==0||v==6||v==7){
		auto [p,q]=tangent_line_ext(a,b);
		res.pb(p);
		res.pb(q);
	}
	if(v==5)res.pb(tangent_line_ext2(a,b));
	if(v==3)res.pb(tangent_line_ext2(b,a));
	//in
	if(v==0){
		auto [p,q]=tangent_line_in(a,b);
		res.pb(p);
		res.pb(q);
	}
	if(v==6)res.pb(tangent_line_in2(a,b));
	return res;
}

//極大な円だけ残す，重複も消す
//AOJ1047
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1) J
//Contest 5, PTZ 2021 Summer Day 2, NAC 2021 B
vc<C> simplify_circle_sets(const vc<C>&rw){
	vc<C> cs;
	for(auto cur:rw){
		bool ok=true;
		rep(j,si(cs)){
			int w=icc(cs[j],cur);
			if(w==1||w==4||w==5){
				ok=false;
			}
		}
		if(ok){
			rep(j,si(cs)){
				int w=icc(cs[j],cur);
				if(w==1||w==2||w==3){
					cs.erase(cs.bg+j--);
				}
			}
			cs.pb(cur);
		}
	}
	return cs;
}

//ByteCamp 2022 Day3 A
//原点中心,半径 r の円の x 座標 a 以下の領域の面積
ld cutarea_sub(ld r,ld a){
	ld theta=acos(max(min(a/r,ld(1)),ld(-1)));
	return sq(r)*(PI-theta)+a*r*sin(theta);
}
//2020 Multi-Uni Day5 J
//x座標 b 以上 c 以下の部分だけ取ってきて，その面積
ld cutarea(C a,ld b,ld c){
	chmax(b,a.c.x-a.r);
	chmin(c,a.c.x+a.r);
	b-=a.c.x;
	c-=a.c.x;
	if(sgn(c-b)<=0)return 0;
	return cutarea_sub(a.r,c)-cutarea_sub(a.r,b);
}

//AOJ2423
C circumc(pt a,pt b,pt c){
	b-=a;
	c-=a;
	pt r=c*norm(b)-b*norm(c);
	r=pt(r.y,-r.x)/(2*crs(b,c));
	return {a+r,abs(r)};
}
//AOJ2423
C mindisc(const vc<pt>& p,array<pt,3> q,int i,int j){
	if(i==int(p.size())){
		if(j==0)
			return {{0,0},-1};
		else if(j==1)
			return {q[0],0};
		else if(j==2)
			return {(q[0]+q[1])*ld(0.5),abs(q[0]-q[1])/2};
		else if(j==3)
			return circumc(q[0],q[1],q[2]);
		else
			assert(false);
	}
	C c=mindisc(p,q,i+1,j);
	if(sgn(abs(c.c-p[i])-c.r)==1){
		assert(j<3);
		q[j]=p[i];
		return mindisc(p,q,i+1,j+1);
	}else
		return c;
}
C mindisc(vc<pt> p){
	shuffle(all(p),mt19937());
	return mindisc(p,array<pt,3>(),0,0);
}

//CF773F
//p の中で k 点以上含むような円の最小半径

void slv(){
	int n;cin>>n;if(n==0)exit(0);
	vc<pt> c(n);
	vc<ld> a(n),b(n);
	rep(i,n){
		cin>>c[i]>>a[i]>>b[i];
		b[i]+=a[i];
	}
	int ans=0;
	auto work=[&](ln z){
		int cnt=0;
		rep(i,n){
			ld r=dlp(z,c[i]);
			if(sgn(a[i],r)<=0&&sgn(r,b[i])<=0)cnt++;
		}
		chmax(ans,cnt);
	};
	rep(i,n){
		pt v=c[i];
		v.y-=a[i];
		work(ln(v,pt(v.x+1,v.y)));
	}
	rep(i,n){
		pt v=c[i];
		v.y-=b[i];
		work(ln(v,pt(v.x+1,v.y)));
	}
	rep(i,n)for(auto x:{a[i],b[i]})rng(j,i+1,n)for(auto y:{a[j],b[j]}){
		vc<ln> ls=tangent_lines(C{c[i],x},C{c[j],y});
		for_each(all(ls),work);
	}
	cout<<ans<<endl;
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	
	while(1)slv();
}
accuracy: 1
time_ms: 120
memory_kb: 3508
final_rank: 5

code_snippet: #ifndef LOCAL
#pragma GCC optimize ("Ofast")
#pragma GCC optimize ("unroll-loops")
#endif

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

using ll=long long;
#define int ll

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#define si(x) int(x.size())
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> bool chmax(t&a,u b){if(a<b){a=b;return true;}else return false;}
template<class t,class u> bool chmin(t&a,u b){if(b<a){a=b;return true;}else return false;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using pi=pair<int,int>;
using vi=vc<int>;

template<class t,class u>
ostream& operator<<(ostream& os,const pair<t,u>& p){
	return os<<"{"<<p.a<<","<<p.b<<"}";
}

template<class t> ostream& operator<<(ostream& os,const vc<t>& v){
	os<<"{";
	for(auto e:v)os<<e<<",";
	return os<<"}";
}

#define mp make_pair
#define mt make_tuple
#define one(x) memset(x,-1,sizeof(x))
#define zero(x) memset(x,0,sizeof(x))
#ifdef LOCAL
void dmpr(ostream&os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ";
	dmpr(os,args...);
}
#define dmp2(...) dmpr(cerr,__LINE__,##__VA_ARGS__)
#else
#define dmp2(...) void(0)
#endif

using uint=unsigned;
using ull=unsigned long long;

template<class t,size_t n>
ostream& operator<<(ostream&os,const array<t,n>&a){
	return os<<vc<t>(all(a));
}

template<int i,class T>
void print_tuple(ostream&,const T&){
}

template<int i,class T,class H,class ...Args>
void print_tuple(ostream&os,const T&t){
	if(i)os<<",";
	os<<get<i>(t);
	print_tuple<i+1,T,Args...>(os,t);
}

template<class ...Args>
ostream& operator<<(ostream&os,const tuple<Args...>&t){
	os<<"{";
	print_tuple<0,tuple<Args...>,Args...>(os,t);
	return os<<"}";
}

template<class t>
void print(t x,int suc=1){
	cout<<x;
	if(suc==1)
		cout<<"\n";
	if(suc==2)
		cout<<" ";
}

ll read(){
	ll i;
	cin>>i;
	return i;
}

vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}

pi readpi(int off=0){
	int a,b;cin>>a>>b;
	return pi(a+off,b+off);
}

template<class t,class u>
void print(const pair<t,u>&p,int suc=1){
	print(p.a,2);
	print(p.b,suc);
}

template<class t,class u>
void print_offset(const pair<t,u>&p,ll off,int suc=1){
	print(p.a+off,2);
	print(p.b+off,suc);
}

template<class T>
void print(const vector<T>&v,int suc=1){
	rep(i,v.size())
		print(v[i],i==int(v.size())-1?suc:2);
}

template<class T>
void print_offset(const vector<T>&v,ll off,int suc=1){
	rep(i,v.size())
		print(v[i]+off,i==int(v.size())-1?suc:2);
}

template<class T,size_t N>
void print(const array<T,N>&v,int suc=1){
	rep(i,N)
		print(v[i],i==int(N)-1?suc:2);
}

string readString(){
	string s;
	cin>>s;
	return s;
}

template<class T>
T sq(const T& t){
	return t*t;
}

void YES(bool ex=true){
	cout<<"YES\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void NO(bool ex=true){
	cout<<"NO\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void Yes(bool ex=true){
	cout<<"Yes\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void No(bool ex=true){
	cout<<"No\n";
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
//#define CAPITAL
/*
void yes(bool ex=true){
	#ifdef CAPITAL
	cout<<"YES"<<"\n";
	#else
	cout<<"Yes"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void no(bool ex=true){
	#ifdef CAPITAL
	cout<<"NO"<<"\n";
	#else
	cout<<"No"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}*/
void possible(bool ex=true){
	#ifdef CAPITAL
	cout<<"POSSIBLE"<<"\n";
	#else
	cout<<"Possible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void impossible(bool ex=true){
	#ifdef CAPITAL
	cout<<"IMPOSSIBLE"<<"\n";
	#else
	cout<<"Impossible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}

constexpr ll ten(int n){
	return n==0?1:ten(n-1)*10;
}

const ll infLL=LLONG_MAX/3;

#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif

int topbit(signed t){
	return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
	return t==0?-1:63-__builtin_clzll(t);
}
int botbit(signed a){
	return a==0?32:__builtin_ctz(a);
}
int botbit(ll a){
	return a==0?64:__builtin_ctzll(a);
}
int botbit(ull a){
	return a==0?64:__builtin_ctzll(a);
}
int popcount(signed t){
	return __builtin_popcount(t);
}
int popcount(ll t){
	return __builtin_popcountll(t);
}
int popcount(ull t){
	return __builtin_popcountll(t);
}
bool ispow2(int i){
	return i&&(i&-i)==i;
}
ll mask(int i){
	return (ll(1)<<i)-1;
}

bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}

template<class t> void mkuni(vc<t>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.ed);
}

ll rand_int(ll l, ll r) { //[l, r]
	#ifdef LOCAL
	static mt19937_64 gen;
	#else
	static mt19937_64 gen(chrono::steady_clock::now().time_since_epoch().count());
	#endif
	return uniform_int_distribution<ll>(l, r)(gen);
}

template<class t>
void myshuffle(vc<t>&a){
	rep(i,si(a))swap(a[i],a[rand_int(0,i)]);
}

template<class t>
int lwb(const vc<t>&v,const t&a){
	return lower_bound(all(v),a)-v.bg;
}

vvc<int> readGraph(int n,int m){
	vvc<int> g(n);
	rep(i,m){
		int a,b;
		cin>>a>>b;
		//sc.read(a,b);
		a--;b--;
		g[a].pb(b);
		g[b].pb(a);
	}
	return g;
}

vvc<int> readTree(int n){
	return readGraph(n,n-1);
}

vc<ll> presum(const vi&a){
	vc<ll> s(si(a)+1);
	rep(i,si(a))s[i+1]=s[i]+a[i];
	return s;
}

//copied from yosupo's library
//ptARTLY VERIFIED

//USACO 2022 January ptlatinum C

#define GEOF

#ifdef GEOF
using ld=long double;
//using ld=double;
const ld PI=acos(ld(-1));
#else
using ld=ll;
#endif
const ld eps=1e-9;
int sgn(ld a){return a<-eps?-1:(a>eps?1:0);}
int sgn(ld a,ld b){return sgn(a-b);}
/*
using pt=complex<ld>;
#define x real()
#define y imag()
*/
struct pt {
    ld x,y;
    //pt(ld _x = ld(0), ld _y = ld(0)) : x(_x), y(_y) {}
    pt():x(0),y(0){}
    pt(ld xx,ld yy):x(xx),y(yy){}
    pt operator+(const pt& r) const { return {x + r.x, y + r.y}; }
    pt operator-(const pt& r) const { return {x - r.x, y - r.y}; }
    pt operator*(const pt& r) const {
        return {x * r.x - y * r.y, x * r.y + y * r.x};
    }

    pt operator*(const ld& r) const { return {x * r, y * r}; }
    pt operator/(const ld& r) const { return {x / r, y / r}; }

    pt& operator+=(const pt& r) { return *this = *this + r; }
    pt& operator-=(const pt& r) { return *this = *this - r; }
    pt& operator*=(const pt& r) { return *this = *this * r; }
    pt& operator*=(const ld& r) { return *this = *this * r; }
    pt& operator/=(const ld& r) { return *this = *this / r; }

    pt operator-() const { return {-x, -y}; }

    bool operator<(const pt& r) const {
        return 2 * sgn(x, r.x) + sgn(y, r.y) < 0;
    }
    bool operator==(const pt& r) const { return sgn((*this - r).rabs()) == 0; }
    bool operator!=(const pt& r) const { return !(*this == r); }

    ld norm() const { return x * x + y * y; }
    ld rabs() const { return max(std::abs(x), std::abs(y)); }  // robust abs
    pair<ld, ld> to_pair() const { return {x, y}; }
    #ifdef GEOF
    ld abs() const { return sqrt(norm()); }
    ld arg() const { return atan2(y, x); }
    static pt polar(ld le, ld th) { return {le * cos(th), le * sin(th)}; }
	#endif
};
istream& operator>>(istream& is, pt& p){
	return is>>p.x>>p.y;
}
ostream& operator<<(ostream& os, const pt& p) {
    return os << "pt(" << p.x << ", " << p.y << ")";
}
ld norm(const pt&a){
	return a.norm();
}
#ifdef GEOF
ld abs(const pt&a){
	return sqrt(norm(a));
}
//XXII Opencup Gpt of Ural K
pt normalized(const pt&a){
	return a/abs(a);
}
ld arg(const pt&a){return a.arg();}
//normalize to [-PI,PI)
//Contest 2: ptKU Contest 1, ptTZ Summer 2022 Day 4
ld normarg(ld a){
	ld res=fmod(a+PI,2*PI);
	if(res<0)res+=PI;
	else res-=PI;
	return res;
}
//AOJ1183
//arg between ab
//assume given lengths are valid
ld arg(ld a,ld b,ld c){
	return acos(min(max((a*a+b*b-c*c)/(2*a*b),ld(-1)),ld(1)));
}
#endif
ld norm(const pt&a,const pt&b){
	return (a-b).norm();
}
ld dot(const pt&a,const pt&b){return a.x*b.x+a.y*b.y;}
ld crs(const pt& a,const pt& b){return a.x*b.y-a.y*b.x;}
ld crs(const pt& a,const pt& b,const pt& c){return crs(b-a,c-a);}
int ccw(const pt& a,const pt& b){return sgn(crs(a,b));}
int ccw(const pt& a,const pt& b,const pt& c){return ccw(b-a,c-a);}
//(-pi,0](0,pi]
int argtype(const pt&a){
	if(sgn(a.y)==0)return a.x<0?1:0;
	return a.y<0?0:1;
}
int argcmp(const pt&a,const pt&b){
	int at=argtype(a),bt=argtype(b);
	if(at!=bt)return at<bt?-1:1;
	return -ccw(a,b);
};
bool argless(const pt&a,const pt&b){return argcmp(a,b)<0;}
//(-2)[a,-1](0)[b,1](2)
int bet(pt a,pt b,pt c){
	pt d=b-a;
	ld e=dot(d,c-a);
	if(sgn(e)<=0)return sgn(e)-1;
	return sgn(e-norm(d))+1;
}
//(a,b) を結ぶ直線を考え，x 座標との交点の y 座標を求める
//(分子,分母)を返す
pair<ld,ld> xcut(const pt&a,const pt&b,ld x){
	return mp(a.y*(b.x-x)-b.y*(a.x-x),b.x-a.x);
}
//XXII Opencup Gpt of Ural K
pt rot90(pt a){
	return pt(-a.y,a.x);
}
#ifdef GEOF
ld xcutval(const pt&a,const pt&b,ld x){
	auto [p,q]=xcut(a,b,x);
	return p/q;
}
//AOJ1183
//Xmas2010 E
//-+ の 順で返す
//a の符号により，small/large が決まる
int qeq(ld a,ld b,ld c,ld&d,ld&e){
	if(sgn(a)==0){
		if(sgn(b)==0)return 0;
		d=-c/b;
		return 1;
	}
	ld f=b*b-4*a*c;
	if(sgn(f)<0)return 0;
	ld g=sqrt(max(f,ld(0)));
	d=(-b-g)/(2*a);
	e=(-b+g)/(2*a);
	return sgn(f)+1;
}
#endif

using ln=pair<pt,pt>;
pt dir(ln a){return a.b-a.a;}
pt eval(ln a,ld b){return a.a+dir(a)*b;}
int bet(ln a,pt b){return bet(a.a,a.b,b);}
int crs(ln a,pt b){return crs(a.a,a.b,b);}
int ccw(ln a,pt b){return ccw(a.a,a.b,b);}
pt proj(ln a,pt b){
	pt c=dir(a);
	return a.a+c*dot(c,b-a.a)/norm(c);
}
pt refl(ln a,pt b){
	return proj(a,b)*2-b;
}
//AOJ2201
ld dlp(ln a,pt b){
	return abs(crs(a,b)/abs(dir(a)));
}
//AOJ0153
ld dsp(ln a,pt b){
	pt c=proj(a,b);
	if(abs(bet(a.a,a.b,c))<=1)return abs(b-c);
	return min(abs(b-a.a),abs(b-a.b));
}
//AOJ1157
//0-no,1-yes(endpoint),2-yes(innner),3-overelap
//if the two line touch like this
// x----x----x
//it returns 1
int iss(ln a,ln b){
	int c1=ccw(a.a,a.b,b.a),c2=ccw(a.a,a.b,b.b);
	int d1=ccw(b.a,b.b,a.a),d2=ccw(b.a,b.b,a.b);
	if(c1||c2||d1||d2)return 1-max(c1*c2,d1*d2);
	int f=bet(a.a,a.b,b.a),g=bet(a.a,a.b,b.b);
	if(max(f,g)==-2||min(f,g)==2)return 0;
	if(max(f,g)==-1||min(f,g)==1)return 1;
	return 3;
}
//segment a intersects line b?
//endpoints inclusive
bool isl(ln a,ln b){
	int d1=ccw(b.a,b.b,a.a),d2=ccw(b.a,b.b,a.b);
	return d1*d2<=0;
}
//AOJ1033
pt cll(ln a,ln b){
	return eval(a,crs(b.a,b.b,a.a)/crs(dir(a),dir(b)));
}
//AOJ1157
ld dss(ln a,ln b){
	if(iss(a,b))return 0;
	return min({dsp(a,b.a),dsp(a,b.b),dsp(b,a.a),dsp(b,a.b)});
}
//AOJ2160
//反時計回り方向に伸びる垂直二等分線
ln vbis(pt a,pt b){
	pt c=(a+b)*ld(0.5),d=b-a;
	return ln(c,pt(c.x-d.y,c.y+d.x));
}

//cm,cr を pt,C に書き換えたあとコンパイルだけ通した，verify はしてない(バカ？)
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
struct C{
	pt c;
	ld r;
	pt eval(ld a){
		return c+pt::polar(r,a);
	}
};
istream& operator>>(istream& is,C& c){
	return is>>c.c>>c.r;
}
ostream& operator<<(ostream& os,const C& c){
	return os<<"C{"<<c.c<<","<<c.r<<"}";
}

//AOJ0153
//0-no,1-edge,2-in
int cont(C a,pt b){
	return sgn(a.r-abs(b-a.c))+1;
}
//AOJ0153 円じゃなくて円盤
//0-no,1-touch,2-cross
int ids(C a,ln b){
	return sgn(a.r-dsp(b,a.c))+1;
}
//AOJ0129 (touch以外)
//0-no(in),1-touch(in),2-cross,3-touch(out),4-no(out)
int ics(C a,ln b){
	int c=ids(a,b);
	if(c<=1)return 4-c;
	return sgn(max(abs(b.a-a.c),abs(b.b-a.c))-a.r)+1;
}
//AOJ1183
//eval(b,t) が a と重なる t を c,d に入れる
//解の個数を返す
int ccl(C a,ln b,ld&c,ld&d){
	pt e=dir(b);
	pt f=b.a-a.c;
	return qeq(norm(e),2*dot(e,f),norm(f)-a.r*a.r,c,d);
}
//AOJ0023
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4 (2,7)
//0-apart,1-coincide,2-a<b,3-a<=b,4-a>b,5-a>=b,6-a touch b,7-a cross b
int icc(C a,C b){
	ld c=abs(a.c-b.c);
	if(sgn(c)==0&&sgn(a.r-b.r)==0)return 1;
	int d=sgn(c+a.r-b.r);
	if(d<=0)return d+3;
	int e=sgn(c+b.r-a.r);
	if(e<=0)return e+5;
	int f=sgn(a.r+b.r-c);
	if(f>=0)return f+6;
	return 0;
}
//AOJ2572
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
//args of two intersections r,l seen by a.c
//assume two circles cross
pair<ld,ld> ccc(C a,C b){
	ld c=arg(b.c-a.c);
	ld d=arg(a.r,abs(b.c-a.c),b.r);
	return make_pair(c-d,c+d);
}
//XXI Opencup GP of Siberia 5
vc<pt> ccc_list(C a,C b){
	int v=icc(a,b);
	if(v==0||v==1||v==2||v==4)return {};
	auto z=ccc(a,b);
	if(v==3||v==5||v==6){
		return {a.c+pt::polar(a.r,z.a)};
	}else{
		return {a.c+pt::polar(a.r,z.a),a.c+pt::polar(a.r,z.b)};
	}
}
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
//単位円で偏角 0-x で y*(-dx) を積分した値
ld cutareaarg(ld x){
	return (x-sin(2*x)/2)/2;
}
//Contest 2: PKU Contest 1, PTZ Summer 2022 Day 4
ld cutareaarg(ld l,ld r){
	return cutareaarg(r)-cutareaarg(l);
}
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1) J
ld cutareaarg(C c,ld l,ld r){
	l=normarg(l);
	r=normarg(r);
	return (l<=r?cutareaarg(l,r):cutareaarg(l,PI)+cutareaarg(-PI,r))
			*sq(c.r)-c.c.y*c.r*(cos(r)-cos(l));
}
//円 a の偏角 b の位置で接する直線
ln tanln(C a,ld b){
	pt c=a.eval(b);
	return ln(c,c+pt::polar(1,b+PI/2));
}
//AOJ2201
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1)
//a と b の共通外接線を引く
//a の接点の偏角を返す
pair<ld,ld> tangent_points_ext(C a,C b){
	pt dif=b.c-a.c;
	ld w=acos((a.r-b.r)/abs(dif));
	ld k=arg(dif);
	return mp(normarg(k-w),normarg(k+w));
}
pair<ln,ln> tangent_line_ext(C a,C b){
	auto [p,q]=tangent_points_ext(a,b);
	return mp(tanln(a,p),tanln(a,q));
}
//a>=b で内接しているケース
ln tangent_line_ext2(C a,C b){
	return tanln(a,arg(b.c-a.c));
}
//AOJ2201
//内接線
pair<ld,ld> tangent_points_in(C a,C b){
	pt dif=b.c-a.c;
	ld w=acos((a.r+b.r)/abs(dif));
	ld k=arg(dif);
	return mp(normarg(k-w),normarg(k+w));
}
pair<ln,ln> tangent_line_in(C a,C b){
	auto [p,q]=tangent_points_in(a,b);
	return mp(tanln(a,p),tanln(a,q));
}
//外接しているケース
ln tangent_line_in2(C a,C b){
	return tanln(a,arg(b.c-a.c));
}
//AOJ2201(ext2,in2 あたりの verify はできてない)
vc<ln> tangent_lines(C a,C b){
	vc<ln> res;
	int v=icc(a,b);
	//out
	if(v==0||v==6||v==7){
		auto [p,q]=tangent_line_ext(a,b);
		res.pb(p);
		res.pb(q);
	}
	if(v==5)res.pb(tangent_line_ext2(a,b));
	if(v==3)res.pb(tangent_line_ext2(b,a));
	//in
	if(v==0){
		auto [p,q]=tangent_line_in(a,b);
		res.pb(p);
		res.pb(q);
	}
	if(v==6)res.pb(tangent_line_in2(a,b));
	return res;
}

//極大な円だけ残す，重複も消す
//AOJ1047
//Contest 4, PTZ 2022 Winter Day 6 (ICPC Camp Day 1) J
//Contest 5, PTZ 2021 Summer Day 2, NAC 2021 B
vc<C> simplify_circle_sets(const vc<C>&rw){
	vc<C> cs;
	for(auto cur:rw){
		bool ok=true;
		rep(j,si(cs)){
			int w=icc(cs[j],cur);
			if(w==1||w==4||w==5){
				ok=false;
			}
		}
		if(ok){
			rep(j,si(cs)){
				int w=icc(cs[j],cur);
				if(w==1||w==2||w==3){
					cs.erase(cs.bg+j--);
				}
			}
			cs.pb(cur);
		}
	}
	return cs;
}

//ByteCamp 2022 Day3 A
//原点中心,半径 r の円の x 座標 a 以下の領域の面積
ld cutarea_sub(ld r,ld a){
	ld theta=acos(max(min(a/r,ld(1)),ld(-1)));
	return sq(r)*(PI-theta)+a*r*sin(theta);
}
//2020 Multi-Uni Day5 J
//x座標 b 以上 c 以下の部分だけ取ってきて，その面積
ld cutarea(C a,ld b,ld c){
	chmax(b,a.c.x-a.r);
	chmin(c,a.c.x+a.r);
	b-=a.c.x;
	c-=a.c.x;
	if(sgn(c-b)<=0)return 0;
	return cutarea_sub(a.r,c)-cutarea_sub(a.r,b);
}

//AOJ2423
C circumc(pt a,pt b,pt c){
	b-=a;
	c-=a;
	pt r=c*norm(b)-b*norm(c);
	r=pt(r.y,-r.x)/(2*crs(b,c));
	return {a+r,abs(r)};
}
//AOJ2423
C mindisc(const vc<pt>& p,array<pt,3> q,int i,int j){
	if(i==int(p.size())){
		if(j==0)
			return {{0,0},-1};
		else if(j==1)
			return {q[0],0};
		else if(j==2)
			return {(q[0]+q[1])*ld(0.5),abs(q[0]-q[1])/2};
		else if(j==3)
			return circumc(q[0],q[1],q[2]);
		else
			assert(false);
	}
	C c=mindisc(p,q,i+1,j);
	if(sgn(abs(c.c-p[i])-c.r)==1){
		assert(j<3);
		q[j]=p[i];
		return mindisc(p,q,i+1,j+1);
	}else
		return c;
}
C mindisc(vc<pt> p){
	shuffle(all(p),mt19937());
	return mindisc(p,array<pt,3>(),0,0);
}

//CF773F
//p の中で k 点以上含むような円の最小半径

void slv(){
	int n;cin>>n;if(n==0)exit(0);
	vc<pt> c(n);
	vc<ld> a(n),b(n);
	rep(i,n){
		cin>>c[i]>>a[i]>>b[i];
		b[i]+=a[i];
	}
	int ans=0;
	auto work=[&](ln z){
		int cnt=0;
		rep(i,n){
			ld r=dlp(z,c[i]);
			if(sgn(a[i],r)<=0&&sgn(r,b[i])<=0)cnt++;
		}
		chmax(ans,cnt);
	};
	rep(i,n){
		pt v=c[i];
		v.y-=a[i];
		work(ln(v,pt(v.x+1,v.y)));
	}
	rep(i,n){
		pt v=c[i];
		v.y-=b[i];
		work(ln(v,pt(v.x+1,v.y)));
	}
	rep(i,n)for(auto x:{a[i],b[i]})rng(j,i+1,n)for(auto y:{a[j],b[j]}){
		vc<ln> ls=tangent_lines(C{c[i],x},C{c[j],y});
		for_each(all(ls),work);
	}
	cout<<ans<<endl;
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	
	while(1)slv();
}
accuracy: 1
time_ms: 110
memory_kb: 3664
final_rank: 10

code_snippet: // お借り中
// https://drken1215.hatenablog.com/entry/2018/09/17/195000
// 参考
// https://mayokoex.hatenablog.com/entry/2016/05/21/200343
// http://www.geisya.or.jp/~mwm48961/koukou/kyori04.htm
// https://hiraocafe.com/note/ennosessen.html
// 
// 2 円の共通接線
//
// verified:
//   AOJ 2201 Immortal Jewels
//     http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2201
//


#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>
#include <algorithm>
using namespace std;


////////////////////////////
// 基本要素 (点, 線分, 円)
////////////////////////////

using DD = double;
const DD INF = 1LL << 60;      // to be set appropriately
const DD EPS = 1e-10;        // to be set appropriately
const DD PI = acos(-1.0);
DD torad(int deg) { return (DD)(deg)*PI / 180; }
DD todeg(DD ang) { return ang * 180 / PI; }

/* Point */
struct Point {
    DD x, y;
    Point(DD x = 0.0, DD y = 0.0) : x(x), y(y) {}
    friend ostream& operator << (ostream& s, const Point& p) { return s << '(' << p.x << ", " << p.y << ')'; }
};
inline Point operator + (const Point& p, const Point& q) { return Point(p.x + q.x, p.y + q.y); }
inline Point operator - (const Point& p, const Point& q) { return Point(p.x - q.x, p.y - q.y); }
inline Point operator * (const Point& p, DD a) { return Point(p.x * a, p.y * a); }
inline Point operator * (DD a, const Point& p) { return Point(a * p.x, a * p.y); }
inline Point operator * (const Point& p, const Point& q) { return Point(p.x * q.x - p.y * q.y, p.x * q.y + p.y * q.x); }
inline Point operator / (const Point& p, DD a) { return Point(p.x / a, p.y / a); }
inline Point conj(const Point& p) { return Point(p.x, -p.y); }
inline Point rot(const Point& p, DD ang) { return Point(cos(ang) * p.x - sin(ang) * p.y, sin(ang) * p.x + cos(ang) * p.y); }
inline Point rot90(const Point& p) { return Point(-p.y, p.x); }
inline DD cross(const Point& p, const Point& q) { return p.x * q.y - p.y * q.x; }
inline DD dot(const Point& p, const Point& q) { return p.x * q.x + p.y * q.y; }
inline DD norm(const Point& p) { return dot(p, p); }
inline DD abs(const Point& p) { return sqrt(dot(p, p)); }
inline DD amp(const Point& p) { DD res = atan2(p.y, p.x); if (res < 0) res += PI * 2; return res; }
inline bool eq(const Point& p, const Point& q) { return abs(p - q) < EPS; }
inline bool operator < (const Point& p, const Point& q) { return (abs(p.x - q.x) > EPS ? p.x < q.x : p.y < q.y); }
inline bool operator > (const Point& p, const Point& q) { return (abs(p.x - q.x) > EPS ? p.x > q.x : p.y > q.y); }
inline Point operator / (const Point& p, const Point& q) { return p * conj(q) / norm(q); }

/* Line */
struct Line : vector<Point> {
    Line(Point a = Point(0.0, 0.0), Point b = Point(0.0, 0.0)) {
        this->push_back(a);
        this->push_back(b);
    }
    friend ostream& operator << (ostream& s, const Line& l) { return s << '{' << l[0] << ", " << l[1] << '}'; }
};

/* Circle */
struct Circle : Point {
    DD r;
    Circle(Point p = Point(0.0, 0.0), DD r = 0.0) : Point(p), r(r) {}
    friend ostream& operator << (ostream& s, const Circle& c) { return s << '(' << c.x << ", " << c.y << ", " << c.r << ')'; }
};



///////////////////////
// 接線
///////////////////////

// 点と円
vector<Point> tanline(const Point& p, const Circle& c) {
    vector<Point> res;
    DD d = norm(p - c);
    DD l = d - c.r * c.r;
    if (l < -EPS) return res;
    if (l <= 0.0) l = 0.0;
    Point cq = (p - c) * (c.r * c.r / d);
    Point qs = rot90((p - c) * (c.r * sqrt(l) / d));
    Point s1 = c + cq + qs, s2 = c + cq - qs;
    res.push_back(s1);
    res.push_back(s2);
    return res;
}

// 円と円の共通接線
vector<Line> comtanline(Circle a, Circle b) {
    vector<Line> res;
    if (abs(a - b) > abs(a.r - b.r) + EPS) {
        if (abs(a.r - b.r) < EPS) {
            Point dir = b - a;
            dir = rot90(dir * (a.r / abs(dir)));
            res.push_back(Line(a + dir, b + dir));
            res.push_back(Line(a - dir, b - dir));
        }
        else {
            Point p = a * -b.r + b * a.r;
            p = p * (1.0 / (a.r - b.r));
            vector<Point> bs = tanline(p, a);
            vector<Point> as = tanline(p, b);
            for (int i = 0; i < min(as.size(), bs.size()); ++i) {
                res.push_back(Line(bs[i], as[i]));
            }
        }
    }
    if (abs(a - b) > a.r + b.r + EPS) {
        Point p = a * b.r + b * a.r;
        p = p * (1.0 / (a.r + b.r));
        vector<Point> bs = tanline(p, a);
        vector<Point> as = tanline(p, b);
        for (int i = 0; i < min(as.size(), bs.size()); ++i) {
            res.push_back(Line(bs[i], as[i]));
        }
    }
    return res;
}




///////////////////////
// ソルバー
///////////////////////

// 距離
Point proj(const Point& p, const Line& l) {
    DD t = dot(p - l[0], l[1] - l[0]) / norm(l[1] - l[0]);
    return l[0] + (l[1] - l[0]) * t;
}
DD distancePL(const Point& p, const Line& l) {
    return abs(p - proj(p, l));
}

// カウント
int count(Line l, vector<Circle> vec, vector<DD> d) {
    int res = 0;
    //cout << endl;
    for (int i = 0; i < vec.size(); ++i) {
        DD dis = distancePL(vec[i], l);
        if (dis >= vec[i].r - EPS && dis <= vec[i].r + d[i] + EPS) ++res;
    }
    return res;
}

int main() {
    int N;
    while (cin >> N) {
        if (N == 0) break;

        vector<Circle> vec(N);
        vector<DD> d(N);
        for (int i = 0; i < N; ++i) {
            cin >> vec[i].x >> vec[i].y >> vec[i].r >> d[i];
        }
        if (N == 1) cout << 1 << endl;
        else {
            int res = 0;
            for (int i = 0; i < N; ++i) {
                for (int j = i + 1; j < N; ++j) {
                    vector<Circle> I(2, vec[i]); I[1].r += d[i];
                    vector<Circle> J(2, vec[j]); J[1].r += d[j];
                    for (int p = 0; p < 2; ++p) {
                        for (int q = 0; q < 2; ++q) {
                            vector<Line> L = comtanline(I[p], J[q]);
                            for (int k = 0; k < L.size(); ++k) {
                                int tmp = count(L[k], vec, d);
                                res = max(res, tmp);
                            }
                        }
                    }
                }
            }
            cout << res << endl;
        }
    }
}
accuracy: 1
time_ms: 120
memory_kb: 3544
final_rank: 7

code_snippet: //
// 2 円の共通接線
//
// verified:
//   AOJ 2201 Immortal Jewels
//     http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2201
//
// 以下参考
// https://shogo82148.github.io/homepage/memo/geometry/line-circle.html
// https://mayokoex.hatenablog.com/entry/2016/05/21/200343
// http://shogo82148.github.io/homepage/memo/geometry/circle-circle.html


#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>
#include <algorithm>
using namespace std;


////////////////////////////
// 基本要素 (点, 線分, 円)
////////////////////////////

using DD = double;
const DD INF = 1LL << 60;      // to be set appropriately
const DD EPS = 1e-10;        // to be set appropriately
const DD PI = acos(-1.0);
DD torad(int deg) { return (DD)(deg)*PI / 180; }
DD todeg(DD ang) { return ang * 180 / PI; }

/* Point */
struct Point {
    DD x, y;
    Point(DD x = 0.0, DD y = 0.0) : x(x), y(y) {}
    friend ostream& operator << (ostream& s, const Point& p) { return s << '(' << p.x << ", " << p.y << ')'; }
};
inline Point operator + (const Point& p, const Point& q) { return Point(p.x + q.x, p.y + q.y); }
inline Point operator - (const Point& p, const Point& q) { return Point(p.x - q.x, p.y - q.y); }
inline Point operator * (const Point& p, DD a) { return Point(p.x * a, p.y * a); }
inline Point operator * (DD a, const Point& p) { return Point(a * p.x, a * p.y); }
inline Point operator * (const Point& p, const Point& q) { return Point(p.x * q.x - p.y * q.y, p.x * q.y + p.y * q.x); }
inline Point operator / (const Point& p, DD a) { return Point(p.x / a, p.y / a); }
inline Point conj(const Point& p) { return Point(p.x, -p.y); }
inline Point rot(const Point& p, DD ang) { return Point(cos(ang) * p.x - sin(ang) * p.y, sin(ang) * p.x + cos(ang) * p.y); }
inline Point rot90(const Point& p) { return Point(-p.y, p.x); }
inline DD cross(const Point& p, const Point& q) { return p.x * q.y - p.y * q.x; }
inline DD dot(const Point& p, const Point& q) { return p.x * q.x + p.y * q.y; }
inline DD norm(const Point& p) { return dot(p, p); }
inline DD abs(const Point& p) { return sqrt(dot(p, p)); }
inline DD amp(const Point& p) { DD res = atan2(p.y, p.x); if (res < 0) res += PI * 2; return res; }
inline bool eq(const Point& p, const Point& q) { return abs(p - q) < EPS; }
inline bool operator < (const Point& p, const Point& q) { return (abs(p.x - q.x) > EPS ? p.x < q.x : p.y < q.y); }
inline bool operator > (const Point& p, const Point& q) { return (abs(p.x - q.x) > EPS ? p.x > q.x : p.y > q.y); }
inline Point operator / (const Point& p, const Point& q) { return p * conj(q) / norm(q); }

/* Line */
struct Line : vector<Point> {
    Line(Point a = Point(0.0, 0.0), Point b = Point(0.0, 0.0)) {
        this->push_back(a);
        this->push_back(b);
    }
    friend ostream& operator << (ostream& s, const Line& l) { return s << '{' << l[0] << ", " << l[1] << '}'; }
};

/* Circle */
struct Circle : Point {
    DD r;
    Circle(Point p = Point(0.0, 0.0), DD r = 0.0) : Point(p), r(r) {}
    friend ostream& operator << (ostream& s, const Circle& c) { return s << '(' << c.x << ", " << c.y << ", " << c.r << ')'; }
};



///////////////////////
// 接線
///////////////////////

// 点と円
vector<Point> tanline(const Point& p, const Circle& c) {
    vector<Point> res;
    DD d = norm(p - c);
    DD l = d - c.r * c.r;
    if (l < -EPS) return res;
    if (l <= 0.0) l = 0.0;
    Point cq = (p - c) * (c.r * c.r / d);
    Point qs = rot90((p - c) * (c.r * sqrt(l) / d));
    Point s1 = c + cq + qs, s2 = c + cq - qs;
    res.push_back(s1);
    res.push_back(s2);
    return res;
}

// 円と円の共通接線
vector<Line> comtanline(Circle a, Circle b) {
    vector<Line> res;
    if (abs(a - b) > abs(a.r - b.r) + EPS) {
        if (abs(a.r - b.r) < EPS) {
            Point dir = b - a;
            dir = rot90(dir * (a.r / abs(dir)));
            res.push_back(Line(a + dir, b + dir));
            res.push_back(Line(a - dir, b - dir));
        }
        else {
            Point p = a * -b.r + b * a.r;
            p = p * (1.0 / (a.r - b.r));
            vector<Point> bs = tanline(p, a);
            vector<Point> as = tanline(p, b);
            for (int i = 0; i < min(as.size(), bs.size()); ++i) {
                res.push_back(Line(bs[i], as[i]));
            }
        }
    }
    if (abs(a - b) > a.r + b.r + EPS) {
        Point p = a * b.r + b * a.r;
        p = p * (1.0 / (a.r + b.r));
        vector<Point> bs = tanline(p, a);
        vector<Point> as = tanline(p, b);
        for (int i = 0; i < min(as.size(), bs.size()); ++i) {
            res.push_back(Line(bs[i], as[i]));
        }
    }
    return res;
}




///////////////////////
// ソルバー
///////////////////////

// 距離
Point proj(const Point& p, const Line& l) {
    DD t = dot(p - l[0], l[1] - l[0]) / norm(l[1] - l[0]);
    return l[0] + (l[1] - l[0]) * t;
}
DD distancePL(const Point& p, const Line& l) {
    return abs(p - proj(p, l));
}

// カウント
int count(Line l, vector<Circle> vec, vector<DD> d) {
    int res = 0;
    //cout << endl;
    for (int i = 0; i < vec.size(); ++i) {
        DD dis = distancePL(vec[i], l);
        if (dis >= vec[i].r - EPS && dis <= vec[i].r + d[i] + EPS) ++res;
    }
    return res;
}

int main() {
    int N;
    while (cin >> N) {
        if (N == 0) break;

        vector<Circle> vec(N);
        vector<DD> d(N);
        for (int i = 0; i < N; ++i) {
            cin >> vec[i].x >> vec[i].y >> vec[i].r >> d[i];
        }
        if (N == 1) cout << 1 << endl;
        else {
            int res = 0;
            for (int i = 0; i < N; ++i) {
                for (int j = i + 1; j < N; ++j) {
                    vector<Circle> I(2, vec[i]); I[1].r += d[i];
                    vector<Circle> J(2, vec[j]); J[1].r += d[j];
                    for (int p = 0; p < 2; ++p) {
                        for (int q = 0; q < 2; ++q) {
                            vector<Line> L = comtanline(I[p], J[q]);
                            for (int k = 0; k < L.size(); ++k) {
                                int tmp = count(L[k], vec, d);
                                res = max(res, tmp);
                            }
                        }
                    }
                }
            }
            cout << res << endl;
        }
    }
}
accuracy: 1
time_ms: 120
memory_kb: 3448
final_rank: 4

code_snippet: #include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <utility>
#include <vector>

namespace luz {

  using isize = std::ptrdiff_t;
  using usize = std::size_t;

  using i32 = std::int32_t;
  using i64 = std::int64_t;
  using u32 = std::uint32_t;
  using u64 = std::uint64_t;
  
} // namespace luz

namespace luz {

  struct rep {
    struct itr {
      usize i;
      constexpr itr(const usize i) noexcept : i(i) {}
      void operator++() noexcept { ++i; }
      constexpr usize operator*() const noexcept { return i; }
      constexpr bool operator!=(const itr x) const noexcept { return i != x.i; }
    };
    const itr f, l;
    constexpr rep(const usize f, const usize l) noexcept
      : f(std::min(f, l)), l(l) {}
    constexpr auto begin() const noexcept { return f; }
    constexpr auto end() const noexcept { return l; }
  };

  struct rrep {
    struct itr {
      usize i;
      constexpr itr(const usize i) noexcept : i(i) {}
      void operator++() noexcept { --i; }
      constexpr usize operator*() const noexcept { return i; }
      constexpr bool operator!=(const itr x) const noexcept { return i != x.i; }
    };
    const itr f, l;
    constexpr rrep(const usize f, const usize l) noexcept
      : f(l - 1), l(std::min(f, l) - 1) {}
    constexpr auto begin() const noexcept { return f; }
    constexpr auto end() const noexcept { return l; }
  };

} // namespace luz

namespace luz {

  void set_fast_ios() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
  }

} // namespace luz

namespace luz {

  void io_set(usize precision) {
    std::cout << std::fixed << std::setprecision(precision);
    std::cerr << std::fixed << std::setprecision(precision);
  }

} // namespace luz

namespace luz {

  template< typename T = i64 > T input() {
    T tmp;
    std::cin >> tmp;
    return tmp;
  }

} // namespace luz

namespace luz {

  template< typename T >
  std::ostream &operator<<(std::ostream &os, std::vector< T > &vs) {
    for (usize i: rep(0, vs.size())) {
      os << vs[i] << (i + 1 != vs.size() ? " " : "");
    }
    return os;
  }

  template< typename T >
  std::istream &operator>>(std::istream &is, std::vector< T > &vs) {
    for (T &v: vs) {
      is >> v;
    }
    return is;
  }

} // namespace luz

namespace luz {

  template< typename T1, typename T2 >
  std::ostream &operator<<(std::ostream &os, std::pair < T1, T2 > &p) {
    os << "(" << p.first << ", " << p.second << ")";
    return os;
  }

  template< typename T1, typename T2 >
  std::istream &operator>>(std::istream &is, std::pair< T1, T2 > &p) {
    is >> p.first >> p.second;
    return is;
  }

} // namespace luz

namespace luz {

  template <typename T>
  std::vector<T> make_vector(usize a, T b) {
    return std::vector<T>(a, b);
  }

  template <typename... Ts>
  auto make_vector(usize a, Ts... ts) {
    return std::vector<decltype(make_vector(ts...))>(a, make_vector(ts...));
  }

} // namespace luz

namespace luz {

  template <typename T1, typename T2>
  inline bool chmax(T1 &a, T2 b) {
    return a < b and (a = b, true);
  }

  template <typename T1, typename T2>
  inline bool chmin(T1 &a, T2 b) {
    return a > b and (a = b, true);
  }

} // namespace luz

using namespace std;

#include <complex>
#include <cmath>
#include <istream>
#include <ostream>

// base
namespace geometry {
  using namespace std;
  using real_number = long double;

  const real_number PI = acosl(-1);

  inline static real_number &eps() {
    static real_number EPS = 1e-10;
    return EPS;
  }

  static void set_eps(real_number EPS) {
    eps() = EPS;
  }

  inline int sign(real_number r) {
    set_eps(1e-10);
    if (r < -eps()) return -1;
    if (r > +eps()) return +1;
    return 0;
  }

  inline bool equals(real_number r1, real_number r2) {
    return sign(r1 - r2) == 0;
  }
}

// point
namespace geometry {
  using point = complex< real_number >;
  using points = vector< point >;

  istream &operator>>(istream &is, point &p) {
    real_number x, y;
    is >> x >> y;
    p = point(x, y);
    return is;
  }

  ostream &operator<<(ostream &os, const point &p) {
    return os << p.real() << " " << p.imag();
  }

  point operator*(const point &p, const real_number &k) {
    return point(p.real() * k, p.imag() * k);
  }

  point rotate(const real_number &theta, const point &p) {
    return point(cos(theta) * p.real() + sin(-theta) * p.imag(),
                 sin(theta) * p.real() + cos(-theta) * p.imag());
  }

  bool equals(const point &a, const point &b) {
    return equals(a.real(), b.real()) and equals(a.imag(), b.imag());
  }
}

using geometry::operator>>;
using geometry::operator<<;

// circle
namespace geometry {
  struct circle {
    point p;
    real_number r;
    circle() {}
    circle(point p, real_number r) : p(p), r(r) {}
  };

  using circles = vector< circle >;
}

// line 
namespace geometry {
  struct line {
    point a, b;

    line() = default;
    line(point a, point b) : a(a), b(b) {}
  };

  using lines = vector< line >;
}

namespace geometry {
  lines tangent_cc(circle c1, circle c2) {
    lines ls;
    if (c1.r > c2.r) swap(c1, c2);

    real_number g = norm(c1.p - c2.p);
    if (sign(g) == 0) return ls;

    point u = (c1.p - c2.p) / sqrt(g);
    point v(-u.imag(), u.real());

    for (int s = 1; s >= -1; s -= 2) {
      real_number h = (c1.r * s + c2.r) / sqrt(g);
      if (sign(1 - norm(h)) == 0) {
        ls.emplace_back(c2.p + u * c2.r, c2.p + (u + v) * c2.r);
      } else if (sign(1 - norm(h)) > 0) {
        point uu = u * h;
        point vv = v * sqrt(1 - norm(h));
        ls.emplace_back(c2.p + (uu + vv) * c2.r, c1.p - (uu + vv) * c1.r * s);
        ls.emplace_back(c2.p + (uu - vv) * c2.r, c1.p - (uu - vv) * c1.r * s);
      }
    }

    return ls;
  }
}

// product
namespace geometry {
  real_number cross(const point &a, const point &b) {
    return a.real() * b.imag() - a.imag() * b.real();
  }

  real_number dot(const point &a, const point &b) {
    return a.real() * b.real() + a.imag() * b.imag();
  }
}

// projection
namespace geometry {
  point projection(const line &l, const point &p) {
    real_number t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
  }
}

namespace geometry {
  real_number distance_lp(const line &l, const point &p) {
    point pr = projection(l, p);
    return abs(pr - p);
  }
}

using namespace geometry;

namespace luz {

  void solve(int n) {
    circles originals(n);
    std::vector< real_number > ms(n);
    circles cs;

    for (usize i: rep(0, n)) {
      real_number x, y;
      real_number r, m;
      std::cin >> x >> y >> r >> m;

      cs.emplace_back(point(x, y), r + m);
      cs.emplace_back(point(x, y), r);
      originals[i] = circle(point(x, y), r);
      ms[i] = m;
    }

    int ans = 1;
    for (usize i: rep(0, cs.size())) {
      for (usize j: rep(i + 1, cs.size())) {
        lines ls = tangent_cc(cs[i], cs[j]);

        for (const line &l: ls) {

          int cnt = 0;
          for (usize ci: rep(0, n)) {
            const circle &c = originals[ci];
            real_number d = distance_lp(l, c.p);

            if (sign(d - c.r) >= 0 and sign(c.r + ms[ci] - d) >= 0) {
              cnt++;
            }
          }

          chmax(ans, cnt);
          
        }
      }
    }

    cout << ans << endl;
  }

  void main_() {
    int n;

    while (std::cin >> n, n) {
      solve(n);
    }
  }

} // namespace luz

int main() {
  luz::set_fast_ios();
  luz::io_set(15);

  luz::main_();
}
accuracy: 1
time_ms: 870
memory_kb: 3516
final_rank: 16

code_snippet: #include<bits/stdc++.h>
#define rep(i,a,...) for(int i = (a)*(strlen(#__VA_ARGS__)!=0);i<(int)(strlen(#__VA_ARGS__)?__VA_ARGS__:(a));++i)
#define per(i,a,...) for(int i = (strlen(#__VA_ARGS__)?__VA_ARGS__:(a))-1;i>=(int)(strlen(#__VA_ARGS__)?(a):0);--i)
#define foreach(i, n) for(auto &i:(n))
#define all(x) (x).begin(), (x).end()
#define bit(x) (1ll << (x))
#define lambda(RES_TYPE, ...) (function<RES_TYPE(__VA_ARGS__)>)[&](__VA_ARGS__) -> RES_TYPE
#define method(FUNC_NAME, RES_TYPE, ...) function<RES_TYPE(__VA_ARGS__)> FUNC_NAME = lambda(RES_TYPE, __VA_ARGS__)
using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
//const ll MOD = (ll)1e9+7;
const ll MOD = 998244353;
const int INF = (ll)1e9+7;
const ll INFLL = (ll)1e18;
template<class t>
using vvector = vector<vector<t>>;
template<class t>
using vvvector = vector<vector<vector<t>>>;
template<class t>
using priority_queuer = priority_queue<t, vector<t>, greater<t>>;
template<class t, class u> bool chmax(t &a, u b){if(a<b){a=b;return true;}return false;}
template<class t, class u> bool chmin(t &a, u b){if(a>b){a=b;return true;}return false;}
#ifdef DEBUG
#define debug(x) cout<<"LINE "<<__LINE__<<": "<<#x<<" = "<<x<<endl;
#else
#define debug(x) (void)0
#endif

namespace templates{
    ll modpow(ll x, ll b,ll mod=MOD){
        ll res = 1;
        while(b){
            if(b&1)res = res * x % mod;
            x = x * x % mod;
            b>>=1;
        }
        return res;
    }

    ll modinv(ll x){
        return modpow(x, MOD-2);
    }

    bool was_output = false;

    void print();
    template <class t> void print(const vector<t> &);
    template <class t, class u> void print(const pair<t, u> &);
    template <class t> void print(const t&);
    template <class Head, class... Tail> void print(const Head&, const Tail&...);

    template <class t> void println(const vector<vector<t>>&);
    template <class t> void println(const vector<t>&);
    template <class t> void println(const t&);
    template <class Head, class... Tail> void println(const Head&, const Tail&...);
    void println();
    void newline();

    void print(){
        return;
    }

    template <class t>
    void print(const vector<t>&x){
        for(const t&i:x)print(i);
    }
    template <class t, class u>
    void print(const pair<t,u>&p){
        print(p.first);
        print(p.second);
    }
    template <class t>
    void print(const t&x){
        if(was_output)cout<<" ";
        cout<<x;
        was_output = true;
    }
    template <class Head, class... Tail>
    void print(const Head&head,const Tail&...tail){
        print(head);
        print(tail...);
    }

    template<class t>
    void println(const vector<vector<t>>&x){
        for(vector<t> i:x)println(i);
    }
    template<class t>
    void println(const vector<t>&x){
        for(const t&i:x)print(i);
        println();
    }
    template <class t>
    void println(const t&x){
        print(x);
        println();
    }
    void println(){
        if(was_output){
            cout << endl;
            was_output = false;
        }
    }
    template <class Head, class... Tail>
    void println(const Head&head,const Tail&...tail){
        print(head);
        println(tail...);
    }

    void newline(){
        was_output = true;
        println();
    }

    template<class t>
    istream& operator>>(istream&is, vector<t>&x){
        for(auto &i:x)is >> i;
        return is;
    }

    template<class t, class u>
    istream& operator>>(istream&is, pair<t, u>&x){
        is >> x.first >> x.second;
        return is;
    }

    template<class t>
    ostream& operator<<(ostream&os, vector<t> &v){
        os << "{";
        for(t &i:v){
            os << i << ", ";
        }
        os << "}";
        return os;
    }

    template<class t = long long>
    t in(){
        t res; cin >> res; return res;
    }

    template<class t>
    vector<t> sorted(vector<t> line,function<bool(t,t)> comp=[](t a,t b){return a<b;}){
        sort(line.begin(),line.end(),comp);
        return line;
    }

    template<class t>
    vector<t> reversed(vector<t> line){
        reverse(line.begin(),line.end());
        return line;
    }
    string reversed(string str){
        reverse(str.begin(),str.end());
        return str;
    }

    long long gcd(long long a,long long b){
        while(b){
            a %= b;
            swap(a,b);
        }
        return a;
    }

    long long lcm(long long a,long long b){
        return a / gcd(a,b) * b;
    }

    class output_initializer{
    public:
        output_initializer(){
            ios::sync_with_stdio(false);
            cin.tie(nullptr);
            cout << setprecision(20);
        }
    };output_initializer OUTPUT_INITIALIZER_INSTANCE = output_initializer();
}

using namespace templates;

using P = complex<double>;
using L = pair<P,P>;

P norm(P x){
    return x / abs(x);
}

template<class t>
t square(t x){
    return x;
}

P polar(double x){
    return P(cos(x),sin(x));
}

const double EPS = 1e-5;

int sign(double x){
    if(abs(x) < EPS)return 0;
    if(x < 0)return -1;
    return 1;
}

double abss(P x){
    return abs(x) * abs(x);
}

vector<L> tangent_cc(P p1,P p2,double r1,double r2) {
    vector<L> res;
    if (r1 > r2){
        swap(r1,r2);
        swap(p1,p2);
    }

    double g = abss(p1 - p2);
    if (sign(g) == 0) return res;

    P u = (p1 - p2) / sqrt(g);
    P v(-u.imag(), u.real());

    for (int s = 1; s >= -1; s -= 2) {
        double h = (r1 * s + r2) / sqrt(g);
        if (sign(1 - abss(h)) == 0) {
            res.emplace_back(p2 + u * r2, p2 + (u + v) * r2);
        } else if (sign(1 - abss(h)) > 0) {
            P uu = u * h;
            P vv = v * sqrt(1 - abss(h));
            res.emplace_back(p2 + (uu + vv) * r2, p1 - (uu + vv) * r1 * double(s));
            res.emplace_back(p2 + (uu - vv) * r2, p1 - (uu - vv) * r1 * double(s));
        }
    }
    return res;
}

double dot(P v1,P v2){
    return v1.real() * v2.real() + v1.imag() * v2.imag();
}

double dist(P p,L l){
    P u = norm(l.first - l.second);
    P v(-u.imag(),u.real());
    return abs(dot(p - l.first,v));
}

int func(int n){
    vector<P> poss;
    vector<double> rs;
    vector<double> ms;
    rep(i,n){
        int x=in();
        int y=in();
        int r=in();
        int m=in();
        poss.emplace_back(x,y);
        rs.emplace_back(r);
        ms.emplace_back(r+m);
    }
    method(get_lines,vector<L>,int i,int j){
        vector<L> res;
        vector<L> a1 = tangent_cc(poss[i],poss[j],rs[i],rs[j]);
        vector<L> a2 = tangent_cc(poss[i],poss[j],ms[i],rs[j]);
        vector<L> a3 = tangent_cc(poss[i],poss[j],rs[i],ms[j]);
        vector<L> a4 = tangent_cc(poss[i],poss[j],ms[i],ms[j]);
        res.insert(res.end(),all(a1));
        res.insert(res.end(),all(a2));
        res.insert(res.end(),all(a3));
        res.insert(res.end(),all(a4));
        return res;
    };
    method(get_lines2,vector<L>,int i){
        vector<L> res;
        res.emplace_back(poss[i] + P(1,0) * rs[i],poss[i] + P(1,0) * rs[i] + P(0,1));
        res.emplace_back(poss[i] + P(1,0) * ms[i],poss[i] + P(1,0) * ms[i] + P(0,1));
        return res;
    };
    int res = 0;
    rep(i,n){
        rep(j,i+1,n){
            foreach(l,get_lines(i,j)){
                int sum = 0;
                rep(g,n){
                    double p = dist(poss[g],l);
                    if(rs[g] - EPS <= p and p <= ms[g] + EPS){
                        ++sum;
                    }
                }
                chmax(res,sum);
            }
        }
    }
    rep(i,n){
        foreach(l,get_lines2(i)){
            int sum = 0;
            rep(g,n){
                double p = dist(poss[g],l);
                if(rs[g] - EPS <= p and p <= ms[g] + EPS){
                    ++sum;
                }
            }
            chmax(res,sum);
        }
    }
    return res;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    while(true){
        int n=in();
        if(n==0)break;
        println(func(n));
    }
    return 0;
}
accuracy: 1
time_ms: 150
memory_kb: 3324
final_rank: 3

code_snippet: #include<bits/stdc++.h>
#include <vector>
#define rep(i,a,...) for(long long i = (a)*(strlen(#__VA_ARGS__)!=0);i<(long long)(strlen(#__VA_ARGS__)?__VA_ARGS__:(a));++i)
#define per(i,a,...) for(long long i = (strlen(#__VA_ARGS__)?__VA_ARGS__:(a))-1;i>=(long long)(strlen(#__VA_ARGS__)?(a):0);--i)
#define foreach(i, n) for(auto &i:(n))
#define all(x) (x).begin(), (x).end()
#define bit(x) (1ll << (x))
#define lambda(RES_TYPE, ...) (function<RES_TYPE(__VA_ARGS__)>)[&](__VA_ARGS__) -> RES_TYPE
#define method(FUNC_NAME, RES_TYPE, ...) function<RES_TYPE(__VA_ARGS__)> FUNC_NAME = lambda(RES_TYPE, __VA_ARGS__)
using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
//const ll MOD = (ll)1e9+7;
const ll MOD = 998244353;
const int INF = (ll)1e9+7;
const ll INFLL = (ll)1e18;
const double EPS = 1e-7;
template<class t>
using vvector = vector<vector<t>>;
template<class t>
using vvvector = vector<vector<vector<t>>>;
template<class t>
using priority_queuer = priority_queue<t, vector<t>, greater<t>>;
template<class t, class u> bool chmax(t &a, u b){if(a<b){a=b;return true;}return false;}
template<class t, class u> bool chmin(t &a, u b){if(a>b){a=b;return true;}return false;}
#ifdef DEBUG
#define debug(x) cout<<"LINE "<<__LINE__<<": "<<#x<<" = "<<x<<endl;
#else
#define debug(x) (void)0
#endif


 struct Point{
    double x,y;
    Point(){}
    Point(double x,double y):x(x),y(y){}
    Point operator+(Point p) {return Point(x+p.x,y+p.y);}
    Point operator-(Point p) {return Point(x-p.x,y-p.y);}
    Point operator*(double k){return Point(x*k,y*k);}
    Point operator/(double k){return Point(x/k,y/k);}
    double norm(){return x*x+y*y;}
    double abs(){return sqrt(norm());}

    bool operator<(const Point &p) const{
      return x!=p.x?x<p.x:y<p.y;
      //grid-point only
      //return !equals(x,p.x)?x<p.x:!equals(y,p.y)?y<p.y:0;
    }

    bool operator==(const Point &p) const{
      return fabs(x-p.x)<EPS and fabs(y-p.y)<EPS;
    }
  };
namespace templates{
    ll modpow(ll x, ll b,ll mod=MOD){
        ll res = 1;
        while(b){
            if(b&1)res = res * x % mod;
            x = x * x % mod;
            b>>=1;
        }
        return res;
    }

    ll modinv(ll x){
        return modpow(x, MOD-2);
    }

    bool was_output = false;

    void print();
    template <class t> void print(const vector<t> &);
    template <class t, class u> void print(const pair<t, u> &);
    template <class t> void print(const t&);
    template <class Head, class... Tail> void print(const Head&, const Tail&...);

    template <class t> void println(const vector<vector<t>>&);
    template <class t> void println(const vector<t>&);
    template <class t> void println(const t&);
    template <class Head, class... Tail> void println(const Head&, const Tail&...);
    void println();
    void newline();

    void print(){
        return;
    }

    template <class t>
    void print(const vector<t>&x){
        for(const t&i:x)print(i);
    }
    template <class t, class u>
    void print(const pair<t,u>&p){
        print(p.first);
        print(p.second);
    }
    template <class t>
    void print(const t&x){
        if(was_output)cout<<" ";
        cout<<x;
        was_output = true;
    }
    template <class Head, class... Tail>
    void print(const Head&head,const Tail&...tail){
        print(head);
        print(tail...);
    }

    template<class t>
    void println(const vector<vector<t>>&x){
        for(vector<t> i:x)println(i);
    }
    template<class t>
    void println(const vector<t>&x){
        for(const t&i:x)print(i);
        println();
    }
    template <class t>
    void println(const t&x){
        print(x);
        println();
    }
    void println(){
        if(was_output){
            cout << endl;
            was_output = false;
        }
    }
    template <class Head, class... Tail>
    void println(const Head&head,const Tail&...tail){
        print(head);
        println(tail...);
    }

    void newline(){
        was_output = true;
        println();
    }

    template<class t>
    istream& operator>>(istream&is, vector<t>&x){
        for(auto &i:x)is >> i;
        return is;
    }

    template<class t, class u>
    istream& operator>>(istream&is, pair<t, u>&x){
        is >> x.first >> x.second;
        return is;
    }

    template<class t>
    ostream& operator<<(ostream&os, vector<t> &v){
        os << "{";
        for(t &i:v){
            os << i << ", ";
        }
        os << "}";
        return os;
    }

    template<class t = long long>
    t in(){
        t res; cin >> res; return res;
    }

    template<class t>
    vector<t> sorted(vector<t> line,function<bool(t,t)> comp=[](t a,t b){return a<b;}){
        sort(line.begin(),line.end(),comp);
        return line;
    }

    template<class t>
    vector<t> reversed(vector<t> line){
        reverse(line.begin(),line.end());
        return line;
    }
    string reversed(string str){
        reverse(str.begin(),str.end());
        return str;
    }

    long long gcd(long long a,long long b){
        while(b){
            a %= b;
            swap(a,b);
        }
        return a;
    }

    long long lcm(long long a,long long b){
        return a / gcd(a,b) * b;
    }

    class output_initializer{
    public:
        output_initializer(){
            ios::sync_with_stdio(false);
            cin.tie(nullptr);
            cout << setprecision(20);
        }
    };output_initializer OUTPUT_INITIALIZER_INSTANCE = output_initializer();
}

using namespace templates;

template <class T>
vector<T> make_vector(int n,T init){
    return vector<T>(n,init);
}

template <class... tail>
auto make_vector(int n, tail... args){
    return vector<decltype(make_vector(args...))>(n,make_vector(args...));
}
class Vector2{
public:
  double x;
  double y;
  Vector2():x(0), y(0){}
  Vector2(double a, double b):x(a), y(b){}
  Vector2(const Vector2 &o):x(o.x), y(o.y){}

  Vector2& operator+=(Vector2 o){x+=o.x;y+=o.y;return *this;}
  Vector2& operator-=(Vector2 o){x-=o.x;y-=o.y;return *this; }
  Vector2& operator*=(double o){x*=o;y*=o;return *this;}
  Vector2& operator/=(double o){x/=o;y/=o;return *this;}

  Vector2 operator+(Vector2 o){return Vector2(*this)+=o;}
  Vector2 operator-(Vector2 o){return Vector2(*this)-=o;}
  Vector2 operator-(){return Vector2(-x,-y);}
  Vector2 operator*(double o){return Vector2(*this)*=o;}
    Vector2 operator*(Vector2 o){
        Vector2 res;
        res.x = x * o.x - y * o.y;
        res.y = x * o.y + y * o.x;
        return res;
    }
  Vector2 operator/(double o){return Vector2(*this)/=o;}

  double abs(){
    return sqrt(mag());
  }

  double mag(){
    return x*x+y*y;
  }

  Vector2 normal(){
    return (*this) / abs();
  }

  Vector2 rotate_90(){
    return Vector2(-y,x);
  }

  static double dot(Vector2 a,Vector2 b){
    return a.x * b.x + a.y * b.y;
  }

  static double cross(Vector2 a,Vector2 b){
    return a.x * b.y - a.y * b.x;
  }
};


double dot(Vector2 x, Vector2 y){
  return Vector2::dot(x,y);
}

double cross(Vector2 x, Vector2 y){
  return Vector2::cross(x,y);
}

void output(Vector2 x) { cout << x.x << " " << x.y << endl; }

Vector2 polar(double theta){
    return Vector2(cos(theta),sin(theta));
}


class Line{
public:
  Vector2 f;
  Vector2 s;
  Line(){
    f = Vector2(0,0);
    s = Vector2(1,0);
  }
    Line(Point a,Point b):f(a.x,a.y),s(b.x,b.y){}

  Vector2 projection(Vector2 pos){
    pos -= f;
    Vector2 n = (s-f).normal();
    Vector2 res;
    res = n * dot(pos,n) + f;
    return res;
  }

  Vector2 reflection(Vector2 pos){
    Vector2 n;
    Vector2 res;
    pos -= f;
    double x = f.y-s.y;
    double y = s.x-f.x;
    n = Vector2(x,y).normal();
    res = pos + f - n * dot(pos,n) * 2;
    return res;
  }

  static bool is_cross(Line a,Line b){
    Vector2 ap = a.s - a.f;
    Vector2 bp = b.s - b.f;
    if(abs(Vector2::dot(ap,bp))<EPS){
      return true;
    }
    return false;
  }

  static bool is_cross_segment(Line a,Line b){
    if(Line::is_parallel(a,b)){
      return a.is_online(b.f) || a.is_online(b.s) || b.is_online(a.f) || b.is_online(a.s);
    }
	  if(Vector2::cross(a.s-a.f,b.f-a.f)*Vector2::cross(a.s-a.f,b.s-a.f)>EPS)return false;
	  if(Vector2::cross(b.s-b.f,a.f-b.f)*Vector2::cross(b.s-b.f,a.s-b.f)>EPS)return false;
return true;
  }

  static bool is_parallel(Line a,Line b){
    Vector2 ap = a.s - a.f;
    Vector2 bp = b.s - b.f;
    if(abs(Vector2::cross(ap,bp))<EPS){
      return true;
    }
    return false;
  }

  static Vector2 cul_cross(Line a,Line b){
    if(a.is_online(b.f)){
      return b.f;
    }
    if(a.is_online(b.s)){
      return b.s;
    }
    if(b.is_online(a.f)){
      return a.f;
    }
    if(b.is_online(a.s)){
      return a.s;
    }
    Vector2 an = (a.s-a.f).rotate_90();
    if(Vector2::dot(b.f-a.f,an)<0){
      an = -an;
    }
    double dis_1 = Vector2::dot(b.f-a.f,an);
    double dis_2 = Vector2::dot(b.s-a.f,an);
    return b.f + (b.s - b.f) * dis_1 / (dis_1 - dis_2);
  }

  double cul_distance(Vector2 pos){
    Vector2 v = s-f;
    Vector2 n = v.normal();
    Vector2 n2 = Vector2(-n.y,n.x);
    return abs(dot(n2,pos-f));
  }

  static double cul_distance(Line a,Line b){
    if(Line::is_cross_segment(a,b))return 0;
    return min({a.cul_distance(b.f),a.cul_distance(b.s),
          b.cul_distance(a.f),b.cul_distance(a.s)});
  }

  bool is_online(Vector2 pos){
    return cul_distance(pos) < EPS;
  }
};

Vector2 input_vector2(){
	double x,y;
	cin >> x >> y;
	return Vector2(x,y);
}


  typedef Point Vector;
  double norm(Vector a){
    return a.x*a.x+a.y*a.y;
  }

  Point orth(Point p) { return Point(-p.y, p.x); }
bool equals(double a,double b){
    return abs(a-b) < EPS;
}  
 struct Circle{
    Point c;
    double r;
    Circle(){}
    Circle(Point c,double r):c(c),r(r){}
  };
  vector<Line> tangent(Circle c1,Circle c2){
    vector<Line> ls;
    if(c1.r<c2.r) swap(c1,c2);
    double g=norm(c1.c-c2.c);
    if(equals(g,0)) return ls;
    Point u=(c2.c-c1.c)/sqrt(g);
    Point v=orth(u);
    for(int s=1;s>=-1;s-=2){
      double h=(c1.r+s*c2.r)/sqrt(g);
      if(equals(1-h*h,0)){
        ls.emplace_back(c1.c+u*c1.r,c1.c+(u+v)*c1.r);
      }else if(1-h*h>0){
        Point uu=u*h,vv=v*sqrt(1-h*h);
        ls.emplace_back(c1.c+(uu+vv)*c1.r,c2.c-(uu+vv)*c2.r*s);
        ls.emplace_back(c1.c+(uu-vv)*c1.r,c2.c-(uu-vv)*c2.r*s);
      }
    }

    return ls;
  }

vector<Line> tangents(Vector2 p1,double r1,Vector2 p2,double r2){
    return tangent(Circle(Point(p1.x,p1.y),r1),Circle(Point(p2.x,p2.y),r2));
}


int func(int n){
    vector<Vector2> ps(n);
    vector<double> rs(n);
    vector<double> ms(n);
    rep(i,n){
        ps[i] = input_vector2();
        rs[i] = in<double>();
        ms[i] = in<double>();
    }
    if(n==1)return 1;

    method(calc,int,Line l){
        int res = 0;
        rep(i,n){
            double d = l.cul_distance(ps[i]);
            if(rs[i] - EPS < d and d < EPS + ms[i] + rs[i]){
                ++res;
            }
        }
        return res;
    };

    int res = 0;

    rep(i,n){
        rep(j,i+1,n){
            rep(k,4){
                foreach(l,tangents(ps[i],rs[i]+((1&k)?0:ms[i]),ps[j],rs[j]+((2&k)?0:ms[j]))){
                    chmax(res,calc(l));
                }
            }
        }
    }
    return res;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    while(true){
        int n = in();
        if(n==0)break;
        println(func(n));
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3472
final_rank: 2

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

// template {{{
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

#define  range(i, l, r) for (i64 i = (i64)(l); i < (i64)(r); (i) += 1)
#define rrange(i, l, r) for (i64 i = (i64)(r) - 1; i >= (i64)(l); (i) -= 1)

#define  whole(f, x, ...) ([&](decltype((x)) container) { return (f)(  begin(container),  end(container), ## __VA_ARGS__); })(x)
#define rwhole(f, x, ...) ([&](decltype((x)) container) { return (f)( rbegin(container), rend(container), ## __VA_ARGS__); })(x)

#define debug(x) cerr << "(" << __LINE__ << ")" << #x << ": " << (x) << endl

constexpr i32 inf   = 1001001001;
constexpr i64 infll = 1001001001001001001ll;

constexpr i32 dx[] = {0, -1, 1, 0, -1, 1, -1, 1}; 
constexpr i32 dy[] = {-1, 0, 0, 1, -1, -1, 1, 1};

struct IoSetup { IoSetup(i32 x = 15){ cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(x); cerr << fixed << setprecision(x); } } iosetup;

template <typename T = i64> T input() { T x; cin >> x; return x; }

template <typename T> ostream &operator<<(ostream &os, vector<T> &v) { range(i, 0, v.size()) { os << v[i] << (i + 1 != (int)v.size() ? " " : ""); } return os; } 
template <typename T> istream &operator>>(istream &is, vector<T> &v) { for (T &in : v) is >> in; return is; }

template <typename T1, typename T2> ostream &operator<<(ostream &os, pair<T1, T2> p) { os << "(" << p.first << ", " << p.second << ")"; return os; }
template <typename T1, typename T2> istream &operator>>(istream &is, pair<T1, T2> &p) { is >> p.first >> p.second; return is; }

template <typename T> vector<T> make_vector(size_t a, T b) { return vector<T>(a, b); }
template <typename... Ts> auto make_vector(size_t a, Ts... ts) { return vector<decltype(make_vector(ts...))>(a, make_vector(ts...)); }

template <typename T1, typename T2> inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }
template <typename T1, typename T2> inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }
// }}}

// base
namespace geometry {
  using namespace std;
  using real_number = long double;

  const real_number PI = acosl(-1);

  inline static real_number &eps() {
    static real_number EPS = 1e-10;
    return EPS;
  }

  static void set_eps(real_number EPS) {
    eps() = EPS;
  }

  inline int sign(real_number r) {
    set_eps(1e-10);
    if (r < -eps()) return -1;
    if (r > +eps()) return +1;
    return 0;
  }

  inline bool equals(real_number r1, real_number r2) {
    return sign(r1 - r2) == 0;
  }
}

// point
namespace geometry {
  using point = complex< real_number >;
  using points = vector< point >;

  istream &operator>>(istream &is, point &p) {
    real_number x, y;
    is >> x >> y;
    p = point(x, y);
    return is;
  }

  ostream &operator<<(ostream &os, const point &p) {
    return os << p.real() << " " << p.imag();
  }

  point operator*(const point &p, const real_number &k) {
    return point(p.real() * k, p.imag() * k);
  }

  point rotate(const real_number &theta, const point &p) {
    return point(cos(theta) * p.real() + sin(-theta) * p.imag(),
                 sin(theta) * p.real() + cos(-theta) * p.imag());
  }

  bool equals(const point &a, const point &b) {
    return equals(a.real(), b.real()) and equals(a.imag(), b.imag());
  }
}

// circle
namespace geometry {
  struct circle {
    point p;
    real_number r;
    circle() {}
    circle(point p, real_number r) : p(p), r(r) {}
  };

  using circles = vector< circle >;
}

// line 
namespace geometry {
  struct line {
    point a, b;

    line() = default;
    line(point a, point b) : a(a), b(b) {}
  };

  using lines = vector< line >;
}

namespace geometry {
  lines tangent_cc(circle c1, circle c2) {
    lines ls;
    if (c1.r > c2.r) swap(c1, c2);

    real_number g = norm(c1.p - c2.p);
    if (sign(g) == 0) return ls;

    point u = (c1.p - c2.p) / sqrt(g);
    point v(-u.imag(), u.real());

    for (int s = 1; s >= -1; s -= 2) {
      real_number h = (c1.r * s + c2.r) / sqrt(g);
      if (sign(1 - norm(h)) == 0) {
        ls.emplace_back(c2.p + u * c2.r, c2.p + (u + v) * c2.r);
      } else if (sign(1 - norm(h)) > 0) {
        point uu = u * h;
        point vv = v * sqrt(1 - norm(h));
        ls.emplace_back(c2.p + (uu + vv) * c2.r, c1.p - (uu + vv) * c1.r * s);
        ls.emplace_back(c2.p + (uu - vv) * c2.r, c1.p - (uu - vv) * c1.r * s);
      }
    }

    return ls;
  }
}

// product
namespace geometry {
  real_number cross(const point &a, const point &b) {
    return a.real() * b.imag() - a.imag() * b.real();
  }

  real_number dot(const point &a, const point &b) {
    return a.real() * b.real() + a.imag() * b.imag();
  }
}

// projection
namespace geometry {
  point projection(const line &l, const point &p) {
    real_number t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
  }
}

namespace geometry {
  real_number distance_lp(const line &l, const point &p) {
    point pr = projection(l, p);
    return abs(pr - p);
  }
}

namespace geometry {
  points cross_point_cc(const circle &c1, const circle &c2) {
    real_number d = abs(c1.p - c2.p), r = c1.r + c2.r;
    if (sign(d - r) > 0 or sign(d + c1.r - c2.r) < 0) return {};
    
    real_number a = acos((norm(c1.r) - norm(c2.r) + norm(d)) / (2 * c1.r * d));
    real_number t = arg(c2.p - c1.p);
    point p = c1.p + polar(c1.r, t + a);
    point q = c1.p + polar(c1.r, t - a);
    if (equals(p.real(), q.real()) and equals(p.imag(), q.imag())) return {p};
    return {p, q};
  }
}

using namespace geometry;

void solve(int n) {
  points cs(n);
  vector< real_number > rs(n), ms(n);

  circles cirs;
  range(i, 0, n) {
    cin >> cs[i] >> rs[i] >> ms[i];

    cirs.emplace_back(cs[i], rs[i]);
    cirs.emplace_back(cs[i], rs[i] + ms[i]);
  }

  if (n == 1) {
    cout << 1 << endl;
    return;
  }

  lines ls;
  range(i, 0, cirs.size()) range(j, 0, i) {
    circle c1 = cirs[i], c2 = cirs[j];
    auto ts = tangent_cc(c1, c2);
    for (auto &l : ts) ls.emplace_back(l);

    auto ps = cross_point_cc(c1, c2);
    if ((int)ps.size() == 2) {
      ls.emplace_back(ps[0], ps[1]);
    }
  }

  int ans = 0;
  for (auto &l : ls) {
    int cnt = 0;
    
    range(i, 0, n) {
      real_number d = distance_lp(l, cs[i]);
      if (sign(d - rs[i]) < 0) continue;
      if (sign(rs[i] + ms[i] - d) >= 0) {
        cnt++;
      }
    }

    chmax(ans, cnt);
  }

  cout << ans << endl;
}

signed main() {
  int n;

  while (cin >> n, n) {
    solve(n);
  }
}
accuracy: 1
time_ms: 880
memory_kb: 6900
final_rank: 20

code_snippet: #include <bits/stdc++.h>
//#include <atcoder/all>
//using namespace atcoder;
#pragma GCC target ("avx2")
#pragma GCC optimization ("O3")
#pragma GCC optimization ("unroll-loops")
using namespace std;
 
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<string> VS;
typedef pair<int, int> PII;
typedef pair<int, int> pii;
typedef pair<long long, long long> PLL;
typedef pair<int, PII> TIII;
 
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
 
 
#define FOR(i, s, n) for (int i = s; i < (int)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define rep(i, a, b) for (int i = a; i < (b); ++i)
#define trav(a, x) for (auto &a : x)
#define all(x) x.begin(), x.end()
 
#define MOD 1000000007
 
template<class T1, class T2> inline bool chmax(T1 &a, T2 b) {if (a < b) {a = b; return true;} return false;}
template<class T1, class T2> inline bool chmin(T1 &a, T2 b) {if (a > b) {a = b; return true;} return false;}
const double EPS = 1e-8, PI = acos(-1);
const double pi = 3.141592653589793238462643383279;
//ここから編集    
typedef string::const_iterator State;
ll GCD(ll a, ll b){
  return (b==0)?a:GCD(b, a%b);
}
ll LCM(ll a, ll b){
  return a/GCD(a, b) * b;
}
template< int mod >
struct ModInt {
  int x;
 
  ModInt() : x(0) {}
 
  ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
 
  ModInt &operator+=(const ModInt &p) {
    if((x += p.x) >= mod) x -= mod;
    return *this;
  }
 
  ModInt &operator-=(const ModInt &p) {
    if((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }
 
  ModInt &operator*=(const ModInt &p) {
    x = (int) (1LL * x * p.x % mod);
    return *this;
  }
 
  ModInt &operator/=(const ModInt &p) {
    *this *= p.inverse();
    return *this;
  }
 
  ModInt operator-() const { return ModInt(-x); }
 
  ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
 
  ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
 
  ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
 
  ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
 
  bool operator==(const ModInt &p) const { return x == p.x; }
 
  bool operator!=(const ModInt &p) const { return x != p.x; }
 
  ModInt inverse() const {
    int a = x, b = mod, u = 1, v = 0, t;
    while(b > 0) {
      t = a / b;
      swap(a -= t * b, b);
      swap(u -= t * v, v);
    }
    return ModInt(u);
  }
 
  ModInt pow(int64_t n) const {
    ModInt ret(1), mul(x);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }
 
  friend ostream &operator<<(ostream &os, const ModInt &p) {
    return os << p.x;
  }
 
  friend istream &operator>>(istream &is, ModInt &a) {
    int64_t t;
    is >> t;
    a = ModInt< mod >(t);
    return (is);
  }
 
  static int get_mod() { return mod; }
};
 
using modint = ModInt< 1000000007 >;
template< typename T >
struct Combination {
  vector< T > _fact, _rfact, _inv;
 
  Combination(int sz) : _fact(sz + 1), _rfact(sz + 1), _inv(sz + 1) {
    _fact[0] = _rfact[sz] = _inv[0] = 1;
    for(int i = 1; i <= sz; i++) _fact[i] = _fact[i - 1] * i;
    _rfact[sz] /= _fact[sz];
    for(int i = sz - 1; i >= 0; i--) _rfact[i] = _rfact[i + 1] * (i + 1);
    for(int i = 1; i <= sz; i++) _inv[i] = _rfact[i] * _fact[i - 1];
  }
 
  inline T fact(int k) const { return _fact[k]; }
 
  inline T rfact(int k) const { return _rfact[k]; }
 
  inline T inv(int k) const { return _inv[k]; }
 
  T P(int n, int r) const {
    if(r < 0 || n < r) return 0;
    return fact(n) * rfact(n - r);
  }
 
  T C(int p, int q) const {
    if(q < 0 || p < q) return 0;
    return fact(p) * rfact(q) * rfact(p - q);
  }
 
  T H(int n, int r) const {
    if(n < 0 || r < 0) return (0);
    return r == 0 ? 1 : C(n + r - 1, r);
  }
};
 
ll modpow(ll x, ll n, ll mod) {
  ll res = 1;
  x %= mod;
  if(x == 0) return 0;
  while(n) {
    if(n&1) res = (res * x) % mod;
    x = (x * x) % mod;
    n >>= 1;
  }
  return res;
} 
inline long long mod(long long a, long long m) {
    return (a % m + m) % m;
}
template<typename T> 
struct BIT{
  int N;
  std::vector<T> node;
  BIT(){}
  BIT(int n){
    N = n;
    node.resize(N+10);
  }
  void build(int n) {
    N = n;
    node.resize(N+10);
  }
  /* a: 1-indexed */
  void add(int a, T x){
    for(int i=a; i<(int)node.size(); i += i & -i) node[i] += x;
  }

  /* [1, a] */
  T sum(int a){
    T res = 0;
    for(int x=a; x>0; x -= x & -x) res += node[x];
    return res;
  }

  /* [l, r] */
  T rangesum(int l, int r){
    return sum(r) - sum(l-1);
  }

  /* 
    a1+a2+...+aw >= valとなるような最小のwを返す
    @verify https://codeforces.com/contest/992/problem/E
  */
  int lower_bound(T val) {
    if(val < 0) return 0;

    int res = 0;
    int n = 1; 
    while (n < N) n *= 2;

    T tv=0;
    for (int k = n; k > 0; k /= 2) {
      if(res + k < N && node[res + k] < val){
        val -= node[res+k];
        res += k;
      }
    }
    return res+1; 
  }
};

struct UnionFind{
  std::vector<int> par;
  std::vector<int> siz;

  UnionFind(int sz_): par(sz_), siz(sz_) {
    for(int i=0; i<sz_; ++i) par[i] = i, siz[i] = 1;
  }

  void init(int sz_){
    par.resize(sz_);
    siz.resize(sz_);
    for(int i=0; i<sz_; ++i) par[i] = i, siz[i] = 1;
  }

  int root(int x){
    if(x == par[x]) return x;
    return par[x] = root(par[x]);
  }

  bool merge(int x, int y){
    x = root(x), y = root(y);
    if(x == y) return false;
    if(siz[x] < siz[y]) std::swap(x, y);
    siz[x] += siz[y];
    par[y] = x;
    return true;
  }

  bool issame(int x, int y){
    return root(x) == root(y);
  }

  int size(int x){
    return siz[root(x)];
  }
};
struct RollingHash{

    using ull = unsigned long long;
    const ull mod = (1ULL << 61) - 1;
    const ull MASK30 = (1ULL << 30) - 1;
    const ull MASK31 = (1ULL << 31) - 1;

    const ull MASK61 = mod;

    ull base;
    int n;
    vector<ull> hash, pow;

    RollingHash(const string &s)
    {
        random_device rnd;
        mt19937_64 mt(rnd());
        base = 1001;
        
        n = (int)s.size();
        hash.assign(n+1, 0);
        pow.assign(n+1, 1);
        
        for(int i=0; i<n; i++){
            hash[i+1] = calc_mod(mul(hash[i], base) + s[i]);
            pow[i+1] = calc_mod(mul(pow[i], base));
        }
    }

    ull calc_mod(ull x){
        ull xu = x >> 61;
        ull xd = x & MASK61;
        ull res = xu + xd;
        if(res >= mod) res -= mod;
        return res;
    }

    ull mul(ull a, ull b){
        ull au = a >> 31;
        ull ad = a & MASK31;
        ull bu = b >> 31;
        ull bd = b & MASK31;
        ull mid = ad * bu + au * bd;
        ull midu = mid >> 30;
        ull midd = mid & MASK30;
        return calc_mod(au * bu * 2 + midu + (midd << 31) + ad * bd);
    }

    //[l,r)のハッシュ値
    inline ull get(int l, int r){
        ull res = calc_mod(hash[r] + mod*3-mul(hash[l], pow[r-l]));
        return res;
    }
    //string tのハッシュ値
    inline ull get(string t){
        ull res = 0;
        for(int i=0; i<t.size(); i++){
            res = calc_mod(mul(res, base)+t[i]);
        }
        return res;
    }
    //string s中のtの数をカウント
    inline int count(string t) {
        if(t.size() > n) return 0;
        auto hs = get(t);
        int res = 0;
        for(int i=0; i<n-t.size()+1; i++){
            if(get(i, i+t.size()) == hs) res++; 
        }
        return res;
    }

    /* 
        concat 
        @verify https://codeforces.com/problemset/problem/514/C
    */
    inline ull concat(ull h1, ull h2, int h2len){
      return calc_mod(h2 + mul(h1, pow[h2len]));
    }

    // LCPを求める S[a:] T[b:]
    inline int LCP(int a, int b){
        int len = min((int)hash.size()-a, (int)hash.size()-b);
        
        int lb = -1, ub = len;
        while(ub-lb>1){
            int mid = (lb+ub)/2;

            if(get(a, a+mid) == get(b, b+mid)) lb = mid;
            else ub = mid;
        }
        return lb;
    }
};
using Point = complex< double >;
bool eq(double a, double b){ return fabs(a-b) < EPS; }
namespace std {
  bool operator<(const Point a, const Point b) {
    if(eq(a.real(),b.real())) return a.imag() < b.imag();
    return a.real() < b.real();
  }
}

istream &operator>> (istream &is, Point &p) {
  double a, b;
  is >> a >> b;
  p = Point(a, b);
  return is;
}

ostream &operator<< (ostream &os, Point &p) {
  return os << fixed << setprecision(10) << p.real() << " " << p.imag();
}

bool operator<(const Point &a, const Point &b) {
  return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
}

// rotate Φ(rad)
// x = r * cos(θ + Φ)
//   = r * cos(θ) * cos(Φ) - r * sin(θ) * sin(Φ)
//   = x * cos(Φ) - y * sin(Φ) (∵ cos(θ) = x/r, sin(θ) = y/r)  
Point rotate(double phi, const Point &p) {
  double x = p.real(), y = p.imag();
  return Point(x * cos(phi) - y * sin(phi), x * sin(phi) + y * cos(phi));
}

double radian_to_degree(double r) {
  return (r * 180.0 / PI);
}

double degree_to_radian(double d) {
  return (d * PI / 180.0);
}

struct Line{
  Point a, b;

  Line() = default;

  Line(Point a, Point b) : a(a), b(b){}

  Line(double A, double B, double C){
    //ax + by = c
    if(eq(A, 0)){
      a = Point(0, C/B), b = Point(1, C/B);
    }else if(eq(B, 0)){
      a = Point(C/A, 0), b = Point(C/A, 1);
    }else{
      a = Point(0, C/B), b = Point(C/A, 0);
    }
  }

  friend istream &operator>>(istream &is, Line &a) {
    return is >> a.a >> a.b;
  }
  friend ostream &operator<<(ostream &os, Line &a) {
    return os << a.a << " to " << a.b;
  }
};

struct Segment: Line {
  Segment() = default;

  Segment(Point a, Point b) : Line(a, b) {}
};

struct Circle {
  Point p;
  double r;

  Circle() = default;

  Circle(Point p, double r): p(p), r(r){}
};

using Points = vector<Point>;
using Polygon = vector<Point>;
using Segments = vector<Segment>;
using Lines = vector<Line>;
using Circles = vector<Circle>;

double cross(const Point &a, const Point &b) {
  return a.real() * b.imag() - a.imag() * b.real();
}

double dot(const Point& a, const Point &b) {
  return a.real() * b.real() + a.imag() * b.imag();
}

//https://mathtrain.jp/projection
Point projection(const Line &l, const Point &p){
  double t = dot(p - l.a, l.a-l.b) / norm(l.a - l.b);
  return l.a + (l.a - l.b) * t;
}

Point projection(const Segment &l, const Point &p){
  double t = dot(p - l.a, l.b-l.a) / norm(l.a - l.b);
  return l.a + (l.b - l.a) * t;
}

Point reflection(const Line &l, const Point &p){
  return p + (projection(l, p) - p) * 2.0;
}

int ccw(const Point &a, const Point &b, const Point &c) {
  if(cross(b-a, c-a) > EPS) return 1;   // "COUNTER_CLOCKWISE"
  if(cross(b-a, c-a) < -EPS) return -1; // "CLOCKWISE"
  if(dot(b-a, c-a) < -EPS) return 2;    // "ONLINE_BACK" c-a-b
  if(norm(b-a) < norm(c-a) - EPS) return -2;  // "ONLINE_FRONT" a-b-c
  return 0;                         // "ON_SEGMENT" a-c-b
}

bool parallel(const Line &a, const Line &b){
  return eq(cross(a.a-a.b, b.a-b.b), 0.0);
}
bool orthogonal(const Line &a, const Line &b){
  return eq(dot(a.a-a.b, b.a-b.b), 0.0);
}
enum { OUT, ON, IN };

int contains(const Polygon& Q, const Point& p){
  bool in = false;
  for(int i=0; i<Q.size(); i++){
    Point a = Q[i] - p, b = Q[(i+1)%Q.size()]-p;
    if(a.imag() > b.imag()) swap(a, b);
    if(a.imag() <= 0 && 0 < b.imag() && cross(a, b) < 0) in = !in;
    if(cross(a, b) == 0 && dot(a, b) <= 0) return ON;
  }
  return in ? IN : OUT;
}
bool intersect(const Segment &s, const Point &p){
  return ccw(s.a, s.b, p) == 0;
}

//直線の交差判定
bool intersect(const Line &a, const Line &b) {
  if(parallel(a, b)) return 0;
  return 1;
}

//線分が重なる/端点で交わるときtrue
bool intersect(const Segment &a, const Segment &b) {
  return ccw(a.a, a.b, b.a) * ccw(a.a, a.b, b.b) <= 0 && ccw(b.a, b.b, a.a) * ccw(b.a, b.b, a.b) <= 0;
}


Point crosspoint(const Line &a, const Line &b){
  double d = cross(b.b-b.a, a.b-a.a);
  if(eq(d, 0.0)) return Point(1e9, 1e9); 
  
  return a.a + (a.b - a.a) * cross(b.b-b.a, b.b-a.a) / d;
}

Point crosspoint(const Segment &a, const Segment &b){
  return crosspoint(Line(a.a, a.b), Line(b.a, b.b));
}
double distance(const Point &a, const Point &b){
  return abs(a - b);
}
double distance(const Line &l, const Point &p){
  return abs( cross(p - l.a, l.b-l.a) / abs(l.b-l.a) );
}
double distance(const Segment &s, const Point &p){
  Point r = projection(s, p);
  if(intersect(s, r)) return abs(r-p);
  return min(abs(s.a - p), abs(s.b - p));
}
// double distance(const Line &a, const Line &b){

// }
// double distance(const Line &a, const Segment &b){

// }
double distance(const Segment &a, const Segment &b) {
  return intersect(a, b) ? 0 : min({distance(a, b.a), distance(a, b.b), distance(b, a.a), distance(b, a.b)});
}

/* 2円の交点 */
vector<Point> crosspointCC(Circle C1, Circle C2){
  vector<Point> ps;
  Point ab = C2.p - C1.p;
  double d = abs(ab);
  double rc = (C1.r * C1.r + d * d - C2.r * C2.r) / (2 * d);
  if(eq(d, 0) || C1.r < abs(rc)) return ps;

  double rs = sqrt(C1.r * C1.r - rc*rc);

  Point abN = ab * Point(0, rs/d);
  Point cp = C1.p + rc / d * ab;
  ps.push_back(cp + abN);
  if(!eq(norm(abN), 0))ps.push_back(cp-abN);
  return ps;
}

vector<Point> crosspointCL(Circle C, Line l){
  Point p = projection(l, C.p);

  Point e = (l.b-l.a)/abs(l.b-l.a);
  if(eq(distance(l, C.p), C.r)) {
    return vector<Point>{p, p};
  }
  double base = sqrt(C.r*C.r-norm(p-C.p));
  
  return vector<Point>{p+e*base, p-e*base};
}

/* 円同士の共通部分の面積を求める */
// verify: http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_I date: 2020/10/11
long double AreaofCC(Circle& C1, Circle& C2){
  if(C1.r > C2.r) swap(C1, C2);
  long double nor = norm(C1.p - C2.p);
  long double dist = sqrtl(nor);
  
  if(C1.r + C2.r < dist+EPS) return 0;

  if(dist + C1.r < C2.r + EPS) return C1.r * C1.r * PI;

  long double theta1 = acosl((nor + C1.r*C1.r - C2.r * C2.r)/(2*C1.r*dist));

  long double theta2 = acosl((nor + C2.r*C2.r - C1.r * C1.r)/(2*C2.r*dist));
  
  return (theta1 - sinl(theta1+ theta1) *0.5) * C1.r * C1.r + (theta2 - sinl(theta2+theta2) *0.5) * C2.r * C2.r;
}


Polygon convex_hull(Polygon &p)
{
    int n = (int)p.size(), k = 0;
    if (n <= 2)
        return p;
    sort(p.begin(), p.end());
    vector<Point> ch(n * 2);

    for (int i = 0; i < n; ch[k++] = p[i++])
    {
        while (k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0)
            --k;
    }

    for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--])
    {
        while (k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0)
            --k;
    }
    ch.resize(k - 1);
    return ch;
}
double convex_diameter(const Polygon &p)
{
    int n = (int)p.size();
    if (n == 2)
        return abs(p[0] - p[1]);

    int is = 0, js = 0;
    for (int i = 1; i < n; ++i)
    {
        if (imag(p[i]) > imag(p[is]))
            is = i;
        if (imag(p[i]) < imag(p[js]))
            js = i;
    }

    double res = abs(p[is] - p[js]);
    int i, maxi, j, maxj;
    i = maxi = is;
    j = maxj = js;
    do
    {
        if (cross(p[(i + 1) % n] - p[i], p[(j + 1) % n] - p[j]) >= 0)
            j = (j + 1) % n;
        else
            i = (i + 1) % n;
        res = max(res, abs(p[i] - p[j]));
    } while (i != is || j != js);
    return res;
}

Polygon convex_cut(const Polygon &p, const Line l)
{
    Polygon ret;
    for (int i = 0; i < p.size(); ++i)
    {
        Point now = p[i], nxt = p[(i + 1) % p.size()];
        if (ccw(l.a, l.b, now) != -1) //交点が線分l上にあるとき
            ret.push_back(now);
        if (ccw(l.a, l.b, now) * ccw(l.a, l.b, nxt) < 0)
        {
            ret.push_back(crosspoint(Line(now, nxt), l));
        }
    }
    return (ret);
}
double closestpair(Points &a, int l, int r){
  if(r-l<=1) return 1e20;
  int mid = (l+r)/2;

  double X = a[mid].real();
  double d = min(closestpair(a, l, mid), closestpair(a, mid, r));
  inplace_merge(a.begin()+l, a.begin()+mid, a.begin()+r, [](const Point& pa, const Point& pb){
    return pa.imag() < pb.imag();
  });

  Points b;
  for(int i=l; i<r; i++){
    if(abs(a[i].real()-X) >= d) continue;
    for(int j=b.size()-1; j>=0; j--){
      if(abs((a[i]-b[j]).imag()) >= d) break;
      d = min(d, abs(a[i]-b[j]));
    }
    b.push_back(a[i]);
  }
  return d;
}

/* 円の交差判定 */
/* verify: http://judge.u-aizu.ac.jp/onlinejudge/finder.jsp?course=CGL date:2020/10/11 */
int intersectionCC(Circle c1, Circle c2){
  if(c1.r > c2.r) swap(c1, c2);
  double d1 = abs(c1.p-c2.p); 
  double d2 = c1.r + c2.r;
  if(d1 > d2) return 4; /* 互いに交点を持たない */
  else if(d1 == d2) return 3; /* 外接する場合 */
  else{
    if(c2.r == c1.r + d1) return 1; /* 内接する場合 */
    else if(c2.r > c1.r + d1) return 0; /* 包含 */
    else return 2; /* 交点を2つ持つ */
  }
}

/* 点集合が反時計回りに与えられる場合のみ */
double Area(Points &g){
  double res = 0;
  int n = g.size();
  REP(i,n){
    res += cross(g[i], g[(i+1)%n]);
  }
  return res/2.0;
}

/* 内接円 */
/* verify: http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_B&lang=ja date: 2020/10/11 */
pair<Point, double> incircle_of_a_Triangle(Points &p){

  double a = abs(p[1]-p[2]), b = abs(p[2]-p[0]), c = abs(p[0]-p[1]);
  double s = (a+b+c) / 2.0;
  double S = sqrtl(s*(s-a)*(s-b)*(s-c));
  double r = S/s; 
  
  Point pp = (a*p[0]+b*p[1]+c*p[2])/(a+b+c);
  return make_pair(pp, r);
} 

/* 外接円 */
pair<Point, double> circumscribed_circle_of_a_triangle(Points &p){

  Point m1((p[0]+p[1])/2.0), m2((p[1]+p[2])/2.0);
  Point v((p[1]-p[0]).imag(), (p[0]-p[1]).real()), w((p[1]-p[2]).imag(), (p[2]-p[1]).real());

  Line l1(m1, Point(v+m1)), l2(m2, Point(w+m2));

  Point x = crosspoint(l1, l2);
  double r = abs(x-p[0]);
  return make_pair(x, r);
}

/* ある点pを通る円cの接線 */
Points tangent_to_a_circle(Point p, Circle C){
  Points ps;
  double d = abs(C.p-p);
  if(eq(d,0)){
    ps.push_back(p);
  }else if(d>EPS){
    
    double d2 = sqrt(d*d-C.r*C.r);
    
    long double theta = acosl(d2/d);
    //cout << theta << endl;
    Point pp = C.p - p;
    Point p2 = rotate(-theta, pp);

    Point e = p2/abs(p2);
    Point ppp = e*d2;
    ps.push_back(p + ppp);
    p2 = rotate(theta, pp);
    e = p2/abs(p2);
    ppp = e*d2;
    ps.push_back(p + ppp);
    
  }
  return ps;
}

Lines tangent(Circle C1, Circle C2){
  Lines ls;
  if(C1.r < C2.r) swap(C1, C2);
  double d = norm(C1.p - C2.p);
  if(eq(d, 0)) return ls;
  Point u = (C2.p - C1.p) / sqrt(d);
  Point v = rotate(pi/2, u);

  for(double s: {-1, 1}) {
    double h = (C1.r + s * C2.r) / sqrt(d);
    if(eq(1-h*h, 0)) {
      ls.emplace_back(C1.p + u * C1.r, C1.p + (u+v) * C1.r);
    }else if(1-h*h>0){
      Point uu = u*h, vv = v*sqrt(1-h*h);
      ls.emplace_back(C1.p + (uu+vv) * C1.r, C2.p - (uu+vv) * C2.r * s);
      ls.emplace_back(C1.p + (uu-vv) * C1.r, C2.p - (uu-vv) * C2.r * s);
    }
  }
  return ls;
}
Line bisector(Point a, Point b){
    Point A = (a+b)*Point(0.5, 0); 
    return Line(A, A+(b-a)*Point(0, PI/2));
}

Polygon voronoi_cell(Polygon Q, Points P, int s){
    REP(i,P.size()){
        if(i!=s){
            Q = convex_cut(Q, bisector(P[s], P[i]));
        }
    }
    return Q;
}
int main() {  
  cin.tie(0);
  ios::sync_with_stdio(false);
  cout << fixed << setprecision(12);
  
  int n;
  while(cin >> n, n) {
    vector<double> x(n), y(n), r(n), m(n);
    REP(i,n) cin >> x[i] >> y[i] >> r[i] >> m[i];

    vector<Point> vp;
    int ans = 1;
    REP(i,n) {
      for(int j=i+1; j<n; j++) {
        {
          auto ls = tangent(Circle(Point(x[i], y[i]), r[i]+m[i]), Circle(Point(x[j], y[j]), r[j]+m[j]));
          for(auto l: ls) {
            int cnt = 0;
            REP(k,n) {
              double dist = distance(l, Point(x[k], y[k]));
              if(r[k] <= dist+EPS && dist <= r[k] + m[k] + EPS) cnt++;
            }
            ans = max(ans, cnt);
          }
        }
        {
          auto ls = tangent(Circle(Point(x[i], y[i]), r[i]), Circle(Point(x[j], y[j]), r[j]+m[j]));
          for(auto l: ls) {
            int cnt = 0;
            REP(k,n) {
              double dist = distance(l, Point(x[k], y[k]));
              if(r[k] <= dist+EPS && dist <= r[k] + m[k] + EPS) cnt++;
            }
            ans = max(ans, cnt);
          }
        }
        {
          auto ls = tangent(Circle(Point(x[i], y[i]), r[i]+m[i]), Circle(Point(x[j], y[j]), r[j]));
          for(auto l: ls) {
            int cnt = 0;
            REP(k,n) {
              double dist = distance(l, Point(x[k], y[k]));
              if(r[k] <= dist+EPS && dist <= r[k] + m[k] + EPS) cnt++;
            }
            ans = max(ans, cnt);
          }
        }
        {
          auto ls = tangent(Circle(Point(x[i], y[i]), r[i]), Circle(Point(x[j], y[j]), r[j]));
          for(auto l: ls) {
            int cnt = 0;
            REP(k,n) {
              double dist = distance(l, Point(x[k], y[k]));
              if(r[k] <= dist+EPS && dist <= r[k] + m[k] + EPS) cnt++;
            }
            ans = max(ans, cnt);
          }
        }
      }
    }

    cout << ans << '\n';
  }
  return 0;
}
accuracy: 1
time_ms: 130
memory_kb: 3760
final_rank: 11

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

#define rep(i,a,b) for(int i=(int)(a);i<(int)(b);i++)
#define ALL(v) (v).begin(),(v).end()
using ll=long long int;
const int inf = 0x3fffffff; const ll INF = 0x1fffffffffffffff; const double eps=1e-12;
template<typename T>inline bool chmax(T& a,T b){if(a<b){a=b;return 1;}return 0;}
template<typename T>inline bool chmin(T& a,T b){if(a>b){a=b;return 1;}return 0;}

using T=double;
using P=complex<T>;
using Poly=vector<P>;
#define X real()
#define Y imag()
inline bool eq(const T& a,const T& b){
   return fabs(a-b)<eps;
}
inline bool cmp(const P& a,const P& b){
   return (eq(a.X,b.X)?a.Y<b.Y:a.X<b.X);
}
struct Line{
   P a,b;
   Line(){}
   Line(P _a,P _b):a(_a),b(_b){}
   Line(T A,T B,T C){
      if(eq(A,.0)){
         a=P(0,C/B),b=P(1/C/B);
      }
      else if(eq(B,.0)){
         a=P(C/A,0),b=P(C/A,1);
      }
      else{
         a=P(0,C/B),b=P(C/A,0);
      }
   }
};
struct Segment:Line{
   Segment(){}
   Segment(P _a,P _b):Line(_a,_b){}
};
struct Circle{
   P p; T r;
   Circle(){}
   Circle(P _p,T _r):p(_p),r(_r){}
};

istream& operator>>(istream& is,P& p){
   T x,y; is>>x>>y; p=P(x,y);
   return is;
}
ostream& operator<<(ostream& os,P& p){
   os<<fixed<<setprecision(12)<<p.X<<' '<<p.Y;
   return os;
}
P unit(const P& a){return a/abs(a);}
T dot(const P& a,const P& b){
   return a.X*b.X+a.Y*b.Y;
}
T cross(const P& a,const P& b){
   return a.X*b.Y-a.Y*b.X;
}
P rot(const P& a,const T& theta){
   return P(cos(theta)*a.X-sin(theta)*a.Y,
      sin(theta)*a.X+cos(theta)*a.Y);
}
T arg(const P& a,const P& b,const P& c){
   return acos(dot(a-b,c-b)/abs(a-b)/abs(c-b));
}

P Projection(const Line&l,const P& p){
   T t=dot(p-l.a,l.a-l.b)/norm(l.a-l.b);
   return l.a+(l.a-l.b)*t;
}
P Projection(const Segment&l,const P& p){
   T t=dot(p-l.a,l.a-l.b)/norm(l.a-l.b);
   return l.a+(l.a-l.b)*t;
}
P Reflection(const Line& l,const P& p){
   return p+(Projection(l,p)-p)*2.;
}
int ccw(const P& a,P b,P c){
   b-=a; c-=a;
   if(cross(b,c)>eps)return 1; //ccw
   if(cross(b,c)<-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
}
bool isOrthogonal(const Line& a,const Line& b){
   return eq(dot(a.b-a.a,b.b-b.a),.0);
}
bool isParallel(const Line& a,const Line& b){
   return eq(cross(a.b-a.a,b.b-b.a),.0);
}
bool isIntersect(const Segment& a,const Segment& b){
   return ccw(a.a,a.b,b.a)*ccw(a.a,a.b,b.b)<=0 and
      ccw(b.a,b.b,a.a)*ccw(b.a,b.b,a.b)<=0;
}
int isIntersect(const Circle& a,const Circle& b){
   T d=abs(a.p-b.p);
   if(d>a.r+b.r+eps)return 4;
   if(eq(d,a.r+b.r))return 3;
   if(eq(d,abs(a.r-b.r)))return 1;
   if(d<abs(a.r-b.r)-eps)return 0;
   return 2;
}
T Dist(const Line& a,const P& b){
   P c=Projection(a,b);
   return abs(c-b);
}
T Dist(const Segment& a,const P& b){
   if(dot(a.b-a.a,b-a.a)<eps)return abs(b-a.a);
   if(dot(a.a-a.b,b-a.b)<eps)return abs(b-a.b);
   return abs(cross(a.b-a.a,b-a.a))/abs(a.b-a.a);
}
T Dist(const Segment& a,const Segment& b){
   if(isIntersect(a,b))return .0;
   T res=min({Dist(a,b.a),Dist(a,b.b),Dist(b,a.a),Dist(b,a.b)});
   return res;
}
P Intersection(const Line& a,const Line& b){
   T d1=cross(a.b-a.a,b.b-b.a);
   T d2=cross(a.b-a.a,a.b-b.a);
   if(eq(d1,0) and eq(d2,0))return b.a;
   return b.a+(b.b-b.a)*(d2/d1);
}
Poly Intersection(const Circle& a,const Line& b){
   Poly res;
   T d=Dist(b,a.p);
   if(d>a.r+eps)return res;
   P h=Projection(b,a.p);
   if(eq(d,a.r)){
      res.push_back(h);
      return res;
   }
   P e=unit(b.b-b.a);
   T ph=sqrt(a.r*a.r-d*d);
   res.push_back(h-e*ph);
   res.push_back(h+e*ph);
   return res;
}
Poly Intersection(const Circle& a,const Segment& b){
   Line c(b.a,b.b);
   Poly sub=Intersection(a,c);
   double xmi=min(b.a.X,b.b.X),xma=max(b.a.X,b.b.X);
   double ymi=min(b.a.Y,b.b.Y),yma=max(b.a.Y,b.b.Y);
   Poly res;
   rep(i,0,sub.size()){
      if(xmi<=sub[i].X+eps and sub[i].X-eps<=xma and
         ymi<=sub[i].Y+eps and sub[i].Y-eps<=yma){
         res.push_back(sub[i]);
      }
   }
   return res;
}
Poly Intersection(const Circle& a,const Circle& b){
   Poly res;
   int mode=isIntersect(a,b);
   T d=abs(a.p-b.p);
   if(mode==4 or mode==0)return res;
   if(mode==3){
      T t=a.r/(a.r+b.r);
      res.push_back(a.p+(b.p-a.p)*t);
      return res;
   }
   if(mode==1){
      if(b.r<a.r-eps){
         res.push_back(a.p+(b.p-a.p)*(a.r/d));
      }
      else{
         res.push_back(b.p+(a.p-b.p)*(b.r/d));
      }
      return res;
   }
   T rc=(a.r*a.r+d*d-b.r*b.r)/d/2.;
   T rs=sqrt(a.r*a.r-rc*rc);
   if(a.r-abs(rc)<eps)rs=0;
   P e=unit(b.p-a.p);
   res.push_back(a.p+rc*e+rs*e*P(0,1));
   res.push_back(a.p+rc*e+rs*e*P(0,-1));
   return res;
}
T Area(const Poly& a){
   T res=0;
   int n=a.size();
   rep(i,0,n)res+=cross(a[i],a[(i+1)%n]);
   return fabs(res/2.);
}
T Area(const Poly& a,const Circle& b){
   int n=a.size();
   if(n<3)return .0;
   auto rec=[&](auto self,const Circle& c,const P& p1,const P& p2){
      P va=c.p-p1,vb=c.p-p2;
      T f=cross(va,vb),res=.0;
      if(eq(f,.0))return res;
      if(max(abs(va),abs(vb))<c.r+eps)return f;
      if(Dist(Segment(p1,p2),c.p)>c.r-eps)return c.r*c.r*arg(vb*conj(va));
      auto u=Intersection(c,Segment(p1,p2));
      Poly sub;
      sub.push_back(p1);
      for(auto& x:u)sub.push_back(x);
      sub.push_back(p2);
      rep(i,0,sub.size()-1)res+=self(self,c,sub[i],sub[i+1]);
      return res;
   };
   T res=.0;
   rep(i,0,n)res+=rec(rec,b,a[i],a[(i+1)%n]);
   return fabs(res/2.);
}
T Area(const Circle& a,const Circle& b){
   T d=abs(a.p-b.p);
   if(d>=a.r+b.r-eps)return .0;
   if(d<=abs(a.r-b.r)+eps){
      T r=min(a.r,b.r);
      return M_PI*r*r;
   }
   T ath=acos((a.r*a.r+d*d-b.r*b.r)/d/a.r/2.);
   T res=a.r*a.r*(ath-sin(ath*2)/2.);
   T bth=acos((b.r*b.r+d*d-a.r*a.r)/d/b.r/2.);
   res+=b.r*b.r*(bth-sin(bth*2)/2.);
   return fabs(res);
}
bool isConvex(const Poly& a){
   int n=a.size();
   int cur,pre,nxt;
   rep(i,0,n){
      pre=(i-1+n)%n;
      nxt=(i+1)%n;
      cur=i;
      if(ccw(a[pre],a[cur],a[nxt])==-1)return 0;
   }
   return 1;
}
int isContained(const Poly& a,const P& b){
   bool res=0;
   int n=a.size();
   rep(i,0,n){
      P p=a[i]-b,q=a[(i+1)%n]-b;
      if(p.Y>q.Y)swap(p,q);
      if(p.Y<eps and eps<q.Y and cross(p,q)>eps)res^=1;
      if(eq(cross(p,q),.0) and dot(p,q)<eps)return 1;
   }
   return (res?2:0);
}
Poly ConvexHull(Poly& a){
   int n=a.size(),k=0;
   sort(ALL(a),[](const P& p,const P& q){
      return (eq(p.Y,q.Y)?p.X<q.X:p.Y<q.Y);
   });
   Poly res(n*2);
   for(int i=0;i<n;res[k++]=a[i++]){
      while(k>=2 and cross(res[k-1]-res[k-2],a[i]-res[k-1])<-eps)k--;
   }
   for(int i=n-2,t=k+1;i>=0;res[k++]=a[i--]){
      while(k>=t and cross(res[k-1]-res[k-2],a[i]-res[k-1])<-eps)k--;
   }
   res.resize(k-1); return res;
}
T Diam(const Poly& a){
   int n=a.size();
   int x=0,y=0;
   rep(i,1,n){
      if(a[i].Y>a[x].Y)x=i;
      if(a[i].Y<a[y].Y)y=i;
   }
   T res=abs(a[x]-a[y]);
   int i=x,j=y;
   do{
      if(cross(a[(i+1)%n]-a[i],a[(j+1)%n]-a[j])<0)i=(i+1)%n;
      else j=(j+1)%n;
      chmax(res,abs(a[i]-a[j]));
   }while(i!=x or j!=y);
   return res;
}
Poly Cut(const Poly& a,const Line& l){
   int n=a.size(); Poly res;
   rep(i,0,n){
      P p=a[i],q=a[(i+1)%n];
      if(ccw(l.a,l.b,p)!=-1)res.push_back(p);
      if(ccw(l.a,l.b,p)*ccw(l.a,l.b,q)<0)res.push_back(Intersection(Line(p,q),l));
   }
   return res;
}

T Closest(Poly& a){
   int n=a.size();
   if(n<=1)return 0;
   sort(ALL(a),cmp);
   Poly buf(n);
   auto rec=[&](auto self,int lb,int rb)->T{
      if(rb-lb<=1)return (T)INF;
      int mid=(lb+rb)>>1;
      auto x=a[mid].X;
      T res=min(self(self,lb,mid),self(self,mid,rb));
      inplace_merge(a.begin()+lb,a.begin()+mid,a.begin()+rb,
         [&](auto p,auto q){return p.Y<q.Y;});
      int ptr=0;
      rep(i,lb,rb){
         if(abs(a[i].X-x)>=res)continue;
         rep(j,0,ptr){
            auto sub=a[i]-buf[ptr-1-j];
            if(sub.Y>=res)break;
            chmin(res,abs(sub));
         }
         buf[ptr++]=a[i];
      }
      return res;
   };
   return rec(rec,0,n);
}

Circle Incircle(const P& a,const P& b,const P& c){
   T A=abs(b-c),B=abs(c-a),C=abs(a-b);
   P p(A*a.X+B*b.X+C*c.X,A*a.Y+B*b.Y+C*c.Y);
   p/=(A+B+C);
   T r=Dist(Line(a,b),p);
   return Circle(p,r);
}
Circle Circumcircle(const P& a,const P& b,const P& c){
   Line l1((a+b)/2.,(a+b)/2.+(b-a)*P(0,1));
   Line l2((b+c)/2.,(b+c)/2.+(c-b)*P(0,1));
   P p=Intersection(l1,l2);
   return Circle(p,abs(p-a));
}
Poly tangent(const P& a,const Circle& b){
   return Intersection(b,Circle(a,sqrt(norm(b.p-a)-b.r*b.r)));
}
vector<Line> tangent(const Circle& a,const Circle& b){
   vector<Line> res;
   T d=abs(a.p-b.p);
   if(eq(d,0))return res;
   P u=unit(b.p-a.p);
   P v=u*P(0,1);
   for(int t:{-1,1}){
      T h=(a.r+b.r*t)/d;
      if(eq(h*h,1)){
         res.push_back(Line(a.p+(h>0?u:-u)*a.r,
            a.p+(h>0?u:-u)*a.r+v));
      }
      else if(1>h*h){
         P U=u*h,V=v*sqrt(1-h*h);
         res.push_back(Line(a.p+(U+V)*a.r,b.p-(U+V)*(b.r*t)));
         res.push_back(Line(a.p+(U-V)*a.r,b.p-(U-V)*(b.r*t)));
      }
   }
   return res;
}

void solve(){
    int n;
    cin>>n;
    if(n==0)exit(0);
    vector<Circle> in(n),out(n),all;
    rep(i,0,n){
        int x,y,r1,r2;
        cin>>x>>y>>r1>>r2;
        r2+=r1;
        in[i]=Circle(P(x,y),r1);
        out[i]=Circle(P(x,y),r2);
        all.push_back(in[i]);
        all.push_back(out[i]);
    }
    
    if(n==1){
        puts("1");
        return;
    }
    int res=0;
    rep(i,0,n*2)rep(j,0,i){
        vector<Line> cand=tangent(all[i],all[j]);
        for(auto& l:cand){
            int sub=0;
            rep(x,0,n){
                if(Intersection(in[x],l).size()>1)continue;
                if(Intersection(out[x],l).size()<1)continue;
                sub++;
            }
            chmax(res,sub);
        }
    }
    cout<<res<<'\n';
}

int main(){
    for(;;)solve();
    return 0;
}
accuracy: 1
time_ms: 700
memory_kb: 3516
final_rank: 15