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


typedef complex<double> P; //Point
typedef pair<P,P> L; //Line, Segment

const double EPS = 1e-8;
const double PI = 6.0 * asin(0.5);

namespace std {
  bool operator < (const P& a, const P& b){
    return fabs(real(a)-real(b)) < EPS ? imag(a) < imag(b) : real(a) < real(b);
  }
  bool operator == (const P&a , const P& b) {
    return abs(a-b)<EPS;
  }  
}

double dot(P a, P b){ return a.real() * b.real() + a.imag() * b.imag(); }
double cross(P a, P b){ return   a.real() * b.imag() - a.imag() * b.real(); }

int ccw(P a, P b, P c){
  b -= a;
  c -= a;
  if(cross(b,c) > EPS) return 1; //テ・ツ渉催ヲツ卍づィツィツ暗・ツ崢榲」ツつ?
  if(cross(b,c) < -EPS) return -1; // テヲツ卍づィツィツ暗・ツ崢榲」ツつ?
  if(dot(b,c) < -EPS) return 2; // c -- a -- b テ」ツ?ョテ、ツクツ?ァツ崢エテァツキツ?
  if(norm(b) < norm(c)) return -2; // a -- b -- c テ」ツ?ョテ、ツクツ?ァツ崢エテァツキツ?
  return 0; // a -- c -- b テ」ツ?ョテ、ツクツ?ァツ崢エテァツキツ?
}

bool isIntersect(L s1, L s2){ 
  if(max(real(s1.first), real(s1.second)) + EPS < min(real(s2.first), real(s2.second)) ||
     max(imag(s1.first), imag(s1.second)) + EPS < min(imag(s2.first), imag(s2.second)) ||
     max(real(s2.first), real(s2.second)) + EPS < min(real(s1.first), real(s1.second)) ||
     max(imag(s2.first), imag(s2.second)) + EPS < min(imag(s1.first), imag(s1.second))) return false;

  return ( ccw(s1.first,s1.second,s2.first) * ccw(s1.first,s1.second,s2.second) <= 0 &&
           ccw(s2.first,s2.second,s1.first) * ccw(s2.first,s2.second,s1.second) <= 0 );
}

P crossPoint(L l, L m){
  double A = cross(l.second - l.first, m.second - m.first);
  double B = cross(l.second - l.first, l.second - m.first);
  if(fabs(A) < EPS && fabs(B) < EPS) return m.first;
  else if(fabs(A) >= EPS) return m.first + B / A * (m.second - m.first);
}

vector<int> G[1111];
bool used[1111];

void dfs(int id,vector<int> &v){
  if( used[id] ) return;
  used[id] = true;
  for( int to : G[id] ) dfs( to, v );
  v.push_back( id );
}



bool zorq(const vector<int> &v,const vector<L>& ss){
  for(int i : v ){
    for(int j : v ){
      if( i == j ) continue;
      if( isIntersect( ss[i], ss[j] )  ){
        P p = crossPoint( ss[i], ss[j] );        
        if( p != ss[i].first && p != ss[i].second ) return true;
      }
    }
  }
  return false;
}

int torf(const vector<int> &v, const vector<L>& ss ){
  for( int i : v ){
    if( G[i].size() == 1 ){
      int to = G[i][0];
      int nx = G[to][0];
      if( nx == i ) nx = G[to][1];

      P p1 = ss[i].first;
      P p2 = crossPoint( ss[i], ss[to] );
      P p3 = crossPoint( ss[to],ss[nx] );
      if( p1 == p2 ) p1 = ss[i].second;
      if( ccw( p1, p2, p3 ) == 1 ) return 5;
      else return 2;
    }
  }
  return -1;
}

int num(int id,const vector<L>& ss){
  vector<int> v;
  dfs( id, v );

  if( v.size() == 1 ) return 1;
  int sum[11]={};
  for( int i : v ){
    sum[G[i].size()]++;
  }

  if( sum[1] == 3 && sum[3] == 1 ) return 3;
  if( sum[1] == 1 && sum[2] == 3 && sum[3] == 1 ) return 6;
  if( sum[2] == 3 && sum[3] == 2 ) return 8;
  if( sum[2] == 4 ) {
    // 0 or 9
    if( zorq( v, ss ) ) return 9;
    return 0;
  }
  if( sum[1] == 2 && sum[2] == 1 ) {
    // 7 or 4
    if( zorq( v, ss ) ) return 4;
    return 7;
  }
  return torf(v,ss);
  
  
}

int main(){
  int n;
  while( cin >> n && n ){
    vector<L> s;
    for(int i=0;i<n;i++) G[i].clear();
    memset( used,0,sizeof(used) );
    
    for(int i=0;i<n;i++){
      int x,y,x2,y2; cin>> x >>y >> x2 >> y2;
      s.push_back( L( P(x,y) , P(x2,y2) ) );      
    }

    for(int i=0;i<n;i++){
      for(int j=0;j<n;j++){
        if( i==j ) continue;
        if( isIntersect( s[i], s[j] ) ) {
          G[i].push_back( j );
        }
      }
    }
    

    int res[11]={};
    for(int i=0;i<n;i++){
      if( used[i] ) continue;
      int st = num(i,s) ;
      res[st]++;
    }
    for(int i=0;i<10;i++) {
      if( i ) cout << " ";
      cout << res[i];
    }
    cout << endl;
    
  }
}