#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 1005

struct Point{
	Point(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	Point(){
		x = y = 0.0;
	}

	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 a){ return Point(a*x,a*y); }
	Point operator / (double a){ return Point(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return x != p.x? x < p.x: y < p.y;
	}

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

	double x,y;
};

struct Line{
public:
	Point p[2];
	Line(Point p1,Point p2){
		p[0] = p1;
		p[1] = p2;
	}
};

typedef Point Vector;


int N;
int boss[NUM],height[NUM];
int ANS[11];
vector<Line> LINE,GROUP[NUM];

int func(double x1,double y1,double x2, double y2, double xp, double yp){
	double naiseki,norm1,norm2,gaiseki;
	norm1 = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
	norm2 = sqrt((xp-x1)*(xp-x1)+(yp-y1)*(yp-y1));
	naiseki = (xp-x1)*(x2-x1)+(yp-y1)*(y2-y1);
	gaiseki = (x2-x1)*(yp-y1)-(xp-x1)*(y2-y1);
	if(gaiseki > EPS){
		return 1;
	}else if(gaiseki < -EPS){
		return -1;
	}
	if(naiseki < -EPS){
		return 2;
	}

	if(norm1 < norm2){
		return -2;
	}
	return 0;
}

bool check_Cross(Line a,Line b){

	if(func(a.p[0].x,a.p[0].y,a.p[1].x,a.p[1].y,b.p[0].x,b.p[0].y)*
			func(a.p[0].x,a.p[0].y,a.p[1].x,a.p[1].y,b.p[1].x,b.p[1].y) <= 0 &&
			func(b.p[0].x,b.p[0].y,b.p[1].x,b.p[1].y,a.p[0].x,a.p[0].y)*
			func(b.p[0].x,b.p[0].y,b.p[1].x,b.p[1].y,a.p[1].x,a.p[1].y) <= 0){
		return true;
	}
	return false;
}

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

double abs(Vector a){
	return sqrt(norm(a));
}

double abs(Line a){

	return sqrt((a.p[0].x-a.p[1].x)*(a.p[0].x-a.p[1].x)+(a.p[0].y-a.p[1].y)*(a.p[0].y-a.p[1].y));
}

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

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

//点と直線の距離
double calc_dist1(Line line,Point point){
    return abs(cross(line.p[1]-line.p[0],point-line.p[0]))/abs(line.p[1]-line.p[0]);
}

double calc_slope(Line A){

	if(fabs(A.p[0].x-A.p[1].x) < EPS){

		return DBL_MAX;

	}else if(fabs(A.p[0].y-A.p[1].y) < EPS){

		return 0;

	}else{

		return (A.p[0].y-A.p[1].y)/(A.p[0].x-A.p[1].x);
	}
}

Point calc_Cross_Point(double x1,double x2,double x3,double x4,double y1,double y2,double y3,double y4){
	Point ret;
	ret.x = ((x2-x1)*(y3*(x4-x3)+x3*(y3-y4))-(x4-x3)*(y1*(x2-x1)+x1*(y1-y2)))/((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1));
	if(x1 != x2){
		ret.y = ((y2-y1)*ret.x+y1*(x2-x1)+x1*(y1-y2))/(x2-x1);
	}else{
		ret.y = ((y4-y3)*ret.x+y3*(x4-x3)+x3*(y3-y4))/(x4-x3);
	}
	return ret;
}

//インタフェース関数
Point calc_Cross_Point(Point a,Point b,Point c,Point d){
	return calc_Cross_Point(a.x,b.x,c.x,d.x,a.y,b.y,c.y,d.y);
}

static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -1;
static const int ON_SEGMENT = 0;

int ccw(Point p0,Point p1,Point p2){

	Vector a = p1 - p0;
	Vector b = p2 - p0;

	if(cross(a,b) > EPS)return COUNTER_CLOCKWISE;
	if(cross(a,b) < -EPS)return CLOCKWISE;
	if(dot(a,b) < -EPS)return ONLINE_BACK;
	if(a.norm() < b.norm())return ONLINE_FRONT;

	return ON_SEGMENT;
}


int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

int is_same(int x,int y){
	return get_boss(x) == get_boss(y);
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[x] > height[y]){

		boss[boss_y] = boss_x;

	}else if(height[x] < height[y]){

		boss[boss_x] = boss_y;

	}else{ //height[x] == height[y]

		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void init(){

	for(int i = 0; i < N; i++){
		boss[i] = i;
		height[i] = 0;
	}
}

void func(){

	for(int i = 0; i <= 10; i++){
		ANS[i] = 0;
	}
	LINE.clear();

	for(int i = 0; i < N; i++){

		GROUP[i].clear();
	}

	for(int loop = 0; loop < N; loop++){

		Point a,b;

		scanf("%lf %lf %lf %lf",&a.x,&a.y,&b.x,&b.y);
		LINE.push_back(Line(a,b));
	}

	init();

	//交差する直線をuniteする
	for(int i = 0; i < N-1; i++){
		for(int k = i+1; k < N; k++){
			if(check_Cross(LINE[i],LINE[k])){
				unite(i,k);
			}
		}
	}

	//線分を、各グループに仕分けする
	for(int i = 0; i < N; i++){

		GROUP[get_boss(i)].push_back(LINE[i]);
	}

	for(int i = 0; i < N; i++){

		if(GROUP[i].size() == 0)continue;

		//交差の回数を数える
		int num_cross = 0;

		for(int a = 0; a < GROUP[i].size()-1; a++){
			for(int b = a+1; b < GROUP[i].size(); b++){
				if(check_Cross(GROUP[i][a],GROUP[i][b])){
					num_cross++;
				}
			}
		}

		bool FLG;

		switch(num_cross){
		case 0:

			ANS[1]++;
			break;
		case 2: //4か7

			FLG = true;
			//交点が全て端点に含まれていれば7
			for(int a = 0; a < 2; a++){
				for(int b = a+1; b < 3; b++){
					if(!check_Cross(GROUP[i][a],GROUP[i][b]))continue;

					Point ret = calc_Cross_Point(GROUP[i][a].p[0],GROUP[i][a].p[1],GROUP[i][b].p[0],GROUP[i][b].p[1]);

					if((ret == GROUP[i][a].p[0]|| ret == GROUP[i][a].p[1]) && (ret == GROUP[i][b].p[0]|| ret == GROUP[i][b].p[1])){

						//Do nothing

					}else{

						FLG = false;
						break;
					}
					if(!FLG)break;
				}
			}

			if(FLG){

				ANS[7]++;

			}else{

				ANS[4]++;
			}


			break;
		case 3:
			;
			ANS[3]++;
			break;
		case 4: //0,2,5,9のいずれか

			if(GROUP[i].size() == 4){ //0か9

				FLG = true;

				//端点以外で交点を持つなら9
				for(int a = 0; a < 3; a++){
					for(int b = a+1; b < 4; b++){
						if(!check_Cross(GROUP[i][a],GROUP[i][b]))continue;

						if(GROUP[i][a].p[0] == GROUP[i][b].p[0] || GROUP[i][a].p[0] == GROUP[i][b].p[1] ||
								GROUP[i][a].p[1] == GROUP[i][b].p[0] || GROUP[i][a].p[1] == GROUP[i][b].p[1]){

							//Do nothing

						}else{

							FLG = false;
							break;
						}
					}
					if(!FLG)break;
				}

				if(FLG){

					ANS[0]++;
				}else{

					ANS[9]++;
				}

			}else{ //2か5

				//交差回数が1の線分を探し、それと交差回数2の線分の、交点ではない端点との位置関係を見る
				int count,index_1,index_2;

				for(int a = 0; a < 5; a++){
					count = 0;
					for(int b = 0; b < 5; b++){
						if(b == a)continue;

						if(check_Cross(GROUP[i][a],GROUP[i][b])){
							count++;
						}
					}
					if(count == 1){

						index_1 = a;
						break;
					}
				}

				for(int a = 0; a < 5; a++){
					if(a == index_1 || check_Cross(GROUP[i][index_1],GROUP[i][a]) == false)continue;

					index_2 = a;
					break;
				}

				Point A,B,C;

				if(GROUP[i][index_1].p[0] == GROUP[i][index_2].p[0]){

					A = GROUP[i][index_1].p[1];
					B = GROUP[i][index_1].p[0];
					C = GROUP[i][index_2].p[1];

				}else if(GROUP[i][index_1].p[0] == GROUP[i][index_2].p[1]){

					A = GROUP[i][index_1].p[1];
					B = GROUP[i][index_1].p[0];
					C = GROUP[i][index_2].p[0];

				}else if(GROUP[i][index_1].p[1] == GROUP[i][index_2].p[0]){

					A = GROUP[i][index_1].p[0];
					B = GROUP[i][index_1].p[1];
					C = GROUP[i][index_2].p[1];


				}else{ //GROUP[i][index_1].p[1] == GROUP[i][index_2].p[1]

					A = GROUP[i][index_1].p[0];
					B = GROUP[i][index_1].p[1];
					C = GROUP[i][index_2].p[0];

				}

				if(ccw(A,B,C) == CLOCKWISE){

					ANS[2]++;

				}else{

					ANS[5]++;
				}
			}

			break;
		case 5:

			ANS[6]++;
			break;
		case 6:

			ANS[8]++;
			break;
		}
	}

	printf("%d",ANS[0]);
	for(int i = 1; i <= 9; i++){

		printf(" %d",ANS[i]);
	}
	printf("\n");
}

int main(){

	while(true){

		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}



Measured memory_bytes: 3372