Problem:
Problem E: Immortal Jewels

English text is not available in this practice contest.

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

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

0 ≤ d ≤ m

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

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

図 E-1: 宝石の配置例

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

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

宝石名

磁力

金属との距離

吸着できるか

宝石1

1

約0.21

できる

宝石2

0

0

できる

宝石3

1

約5.37

できない

宝石4

1

貫通している

できない

宝石5

1

約0.97

できる

宝石6

2

約0.53

できる

表 E-3: 吸着結果

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

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

Input

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

N

x1 y1 r1 m1

x2 y2 r2 m2

...

xN yN rN mN

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

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

Output

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

Sample Input

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

Output for the Sample Input

4
2
3
3
1

Solution in C++11 (judge verdict: Accepted)

#include <iostream>
#include <cstdio>
#include <iomanip>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <stack>
#include <utility>
#include <numeric>
#include <algorithm>
#include <functional>
#include <cctype>
#include <complex>
#include <string>
#include <sstream>
#include <fstream>
#include <cassert>
using namespace std;

typedef long long ll;
//typedef pair<int, int> P;
#define ISEQ(c) (c).begin(), (c).end()
#define REP(i,n) for(int i=0;i<(int)(n);++i)

#define DUMP(x)  cerr << #x << " = " << (x)
#define DUMPLN(x)  DUMP(x) <<endl
#define DEBUG(x) DUMP(x) << LINE() << " " << __FILE__
#define DEBUGLN(x) DEBUG(x)<<endl
#define LINE()    cerr<< " (L" << __LINE__ << ")"
#define LINELN()    LINE()<<endl
#define CHECK(exp,act)  if(exp!=act){DUMPLN(exp);DEBUGLN(act);}
#define STOP(e)  CHECK(e,true);if(!(e)) exit(1);

namespace Ps{
	typedef  double D;
	typedef complex<D> P;
	const D EPS = 1e-9;
	const D INF = 1e20;

#define X real()
#define Y imag()

	template<typename T> int sig(T a,T b=0){return a<b-EPS?-1:a>b+EPS?1:0;}
	template<typename T> bool eq(T a,T b){ return sig(abs(a-b))==0;}
	template<typename T> D norm(T a){ return a*a;}
	// a×b
	D cross(const P& a,const P& b){
		return imag(conj(a)*b);
	}
	// a・b
	D dot(const P&a,const P& b) {
		return real(conj(a)*b);
	}

	int ccw(const P& a,P b,P c){
		b -= a; c -= a;
		  if (cross(b,c) > 0)   return +1;       // counter clockwise
		  if (cross(b,c) < 0)   return -1;       // clockwise
		  if (dot(b,c) < 0)     return +2;       // c--a--b on line
		  if (norm(b) < norm(c)) return -2;       // a--b--c on line
		  return 0;
	}

	//浮動小数点modで非負の値のみかえす．
	D pfmod(D v,D MOD){
		return fmod(fmod(v,MOD)+MOD,MOD);
	}

	//[0,PI)
	D AbsArg(D a){
	  	D ret=pfmod(max(a,-a),2*M_PI);
	  	return min(ret,2*M_PI-ret);
	}
	
}
using namespace Ps;


namespace Ls{
	struct L : public vector<P> {
	  L(const P &a, const P &b) {
	    push_back(a); push_back(b);
	  }
	};

	//直線へ射影した時の点
	P projection(const L &l, const P &p) {
	  D t = dot(p-l[0], l[0]-l[1]) / norm(l[0]-l[1]);
	  return l[0] + t*(l[0]-l[1]);
	}

	D distanceLP(const L &l, const P &p) {
	  return abs(p - projection(l, p));
	}

}
using namespace Ls;

namespace std{
	bool operator < (const P& a,const P& b){
    		return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  	}
  	bool operator == (const P& a,const P& b){
    		return eq(a,b);
   	}
};

namespace Cs{
	int INF=1<<28;
	struct C {
 		 P o; D r;
 		 C(const P &o,D r) : o(o), r(r) { }
	};

	// 包含関係
	enum RELATION{INCOMPARABLE=0,SAME=1,CONTAIN=2,OVER=4};
	pair<RELATION,int> cRel(const C& c1,const C& c2){
		D d=abs(c1.o-c2.o);
		if(eq(c1.o,c2.o) && eq(c1.r,c2.r))return make_pair(SAME,INF);// c1 == c2
		if(sig(d,c1.r-c2.r)<0)return make_pair(OVER,0); // c2 ⊆ c1
		if(sig(d,c2.r-c1.r)<0)return make_pair(CONTAIN,0); // c1 ⊆ c2
		if(sig(d,c1.r-c2.r)==0)return make_pair(OVER,1);// c2 ⊆ c1,1点で接する
		if(sig(d,c2.r-c1.r)==0)return make_pair(CONTAIN,1);// c1 ⊆ c2,1点で接する
		if(sig(d,c1.r+c2.r)==0)return make_pair(INCOMPARABLE,1);// 1点で接する
		if(sig(d,c1.r+c2.r)<0)return make_pair(INCOMPARABLE,2);//  2点で接する
		return make_pair(INCOMPARABLE,0);// 交わらない
	}

	// 点p を通る 円c の接点
	vector<L> ContactCP(const C& c,const P& p){
		vector<L> res;
		P dir =p-c.o;D d=abs(dir);
		if(sig(c.r,d)==0){
			res.push_back(L(p,p+dir*P(0,1)));return res;
		}
		if(sig(c.r,d)>0)return res;

		D th=acos(c.r/d),ph=arg(dir);
		res.push_back(L(p,c.o+polar(c.r,ph+th)));res.push_back(L(p,c.o+polar(c.r,ph-th)));
		return res;
	}
	//角度 arg の 円c の接線
	L contactCP(const C& c,D arg){
		P p=polar(c.r,arg);return L(c.o+p-p*P(0,1),c.o+p+p*P(0,1));
	}

	//2円 c1,c2の共通接線
	vector<L> ContactCC(const C& c1,const C& c2){
		pair<RELATION,int> rel=cRel(c1, c2);
		vector<L> res;
		if(rel.first==SAME)return res;
		P dir=c2.o - c1.o;D t=arg(dir);
		//external
		if(rel.first==INCOMPARABLE || rel.second>0){
			D a=acos((c2.r-c1.r)/abs(dir));
			res.push_back(contactCP(c1,t+a));
			if(sig(a)!=0)res.push_back(contactCP(c1,t-a));
		}
		//internal
		if(rel.first==INCOMPARABLE && rel.second<2){
			D a=acos((c2.r+c1.r)/abs(dir));
			res.push_back(contactCP(c1,t+a));
			if(sig(a)!=0)res.push_back(contactCP(c1,t-a));
		}
		return res;
	}
}
using namespace Cs;

int main() {
	// ifstream cin("in");
	// ofstream coutf( "out" );
	while(true){
		int N;cin >> N;if(N==0)break;
		vector<C> cs1,cs2,cs;
		REP(i,N){
			D x,y,r,m;cin >> x >> y >>r >> m;
			cs1.push_back(C(P(x,y),r));cs2.push_back(C(P(x,y),r+m));
			cs.push_back(C(P(x,y),r));cs.push_back(C(P(x,y),r+m));
		}

		int Mv=0;if(N==1)Mv=max(Mv,1);
		REP(i,2*N)REP(j,2*N){
			vector<L> ls=ContactCC(cs[i],cs[j]);
			REP(k,ls.size()){
				int c=0;
				REP(s,N){
					D d=distanceLP(ls[k],cs2[s].o);
					if(sig(cs1[s].r,d)<=0 && sig(d,cs2[s].r)<=0){
						c++;
					}
				}
				Mv=max(Mv,c);
			}
		}
		cout << Mv <<endl;
	}	
}