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

const double eps = 1E-6;//誤差限

struct Line


{
double a;
double b;
};

struct Point


{
double x;
double y;
};

void readReal(double &v)


{
scanf("%lf",&v);
}

bool operator < (Line const &l1,Line const &l2)


{ //斜率為主序(從小到大)
if(l1.a < l2.a)
return true;
if(l1.a > l2.a)
return false;
//截距為次序(從大倒小,也即從高到低)
return l1.b > l2.b;
}

double y_at_x(Line const &line,double x)


{//返回直線line在x處的y值
return line.a*x + line.b;
}

Point intersection(Line const &l1,Line const &l2 )


{//求直線l1和l2的交點
Point p;
p.x = (l1.b - l2.b)/(l2.a - l1.a);
p.y = y_at_x(l1,p.x);
return p;
}

int count(vector<Line> &L)


{//統計有多少條可見彩虹
if(L.size() < 2) // 處理平凡情形
return L.size();
// 處理非平凡情形
// 首先對直線按斜率遞增排序(平行時截距大者優先)
sort(L.begin(),L.end());
stack<Line> lines;// 半平面所交凸域的邊所在直線
stack<Point> points;// 半平面所交凸域的頂點
lines.push(L.front());// 添加第一條直線作為約束
for(vector<Line>::iterator iter = L.begin() + 1;iter != L.end();iter++)

{// 不斷添加直線約束
while(!points.empty())// 去除多余約束 -- 注意誤差處理

{// 先處理出現平行直線的情形
if(fabs(iter->a - lines.top().a) < eps)
break;
// 處理不含平行直線的情形
Point tmp = points.top();
if(y_at_x(*iter,tmp.x) < tmp.y - eps)
break;
else

{
lines.pop();
points.pop();
}
}
// 添加當前非平行直線作為新的約束
if(fabs(iter->a - lines.top().a) > eps)

{
points.push(intersection(*iter,lines.top()));
lines.push(*iter);
}
}
// 凸域的邊數即是題目要求解的“可見”彩虹條數
return lines.size();
}

int main()


{
int text;
cin>>text;
while(text--)

{
int n;
int i;
scanf("%d",&n);
vector<Line> L(n);//直線集合
for(i = 0;i < n;i++)

{
readReal(L[i].a);
readReal(L[i].b);
}
printf("%d\n",count(L));
}
return 0;
}