Problem:
Problem D: Digits on the Floor

Taro attempts to tell digits to Hanako by putting straight bars on the floor. Taro wants to
express each digit by making one of the forms shown in Figure 1.

Since Taro may not have bars of desired lengths, Taro cannot always make forms exactly as
shown in Figure 1. Fortunately, Hanako can recognize a form as a digit if the connection
relation between bars in the form is kept. Neither the lengths of bars nor the directions of forms
affect Hanako’s perception as long as the connection relation remains the same. For example,
Hanako can recognize all the awkward forms in Figure 2 as digits. On the other hand, Hanako
cannot recognize the forms in Figure 3 as digits. For clarity, touching bars are slightly separated
in Figures 1, 2 and 3. Actually, touching bars overlap exactly at one single point.

Figure 1: Representation of digits

Figure 2: Examples of forms recognized as digits

In the forms, when a bar touches another, the touching point is an end of at least one of them.
That is, bars never cross. In addition, the angle of such two bars is always a right angle.

To enable Taro to represent forms with his limited set of bars, positions and lengths of bars can
be changed as far as the connection relations are kept. Also, forms can be rotated.

Keeping the connection relations means the following.

Figure 3: Forms not recognized as digits (these kinds of forms are not contained in the dataset)

Separated bars are not made to touch.

Touching bars are not made separate.

When one end of a bar touches another bar, that end still touches the same bar. When it
touches a midpoint of the other bar, it remains to touch a midpoint of the same bar on
the same side.

The angle of touching two bars is kept to be the same right angle (90 degrees and -90
degrees are considered different, and forms for 2 and 5 are kept distinguished).

Your task is to find how many times each digit appears on the floor.

The forms of some digits always contain the forms of other digits. For example, a form for
9 always contains four forms for 1, one form for 4, and two overlapping forms for 7. In this
problem, ignore the forms contained in another form and count only the digit of the “largest”
form composed of all mutually connecting bars. If there is one form for 9, it should be interpreted
as one appearance of 9 and no appearance of 1, 4, or 7.

Input

The input consists of a number of datasets. Each dataset is formatted as follows.

n

x1a y1a x1b y1b

x2a y2a x2b y2b

.

.

.

xna yna xnb ynb

In the first line, n represents the number of bars in the dataset. For the rest of the lines, one
line represents one bar. Four integers xa, ya , xb , yb , delimited by single spaces, are given in
each line. xa and ya are the x- and y-coordinates of one end of the bar, respectively. xb and
yb are those of the other end. The coordinate system is as shown in Figure 4. You can assume
1 ≤ n ≤ 1000 and 0 ≤ xa , ya , xb , yb ≤ 1000.

The end of the input is indicated by a line containing one zero.

Figure 4: The coordinate system

You can also assume the following conditions.

More than two bars do not overlap at one point.

Every bar is used as a part of a digit. Non-digit forms do not exist on the floor.

A bar that makes up one digit does not touch nor cross any bar that makes up another
digit.

There is no bar whose length is zero.

Output

For each dataset, output a single line containing ten integers delimited by single spaces. These
integers represent how many times 0, 1, 2, . . . , and 9 appear on the floor in this order. Output
lines must not contain other characters.

Sample Input

9
60 140 200 300
300 105 330 135
330 135 250 215
240 205 250 215
298 167 285 154
30 40 30 90
30 90 150 90
150 90 150 20
30 40 150 40
8
320 20 300 60
320 20 380 50
380 50 240 330
10 50 40 20
10 50 110 150
110 150 180 80
40 20 37 17
37 17 27 27
20
72 222 132 182
204 154 204 54
510 410 520 370
404 54 204 54
530 450 410 450
204 68 404 68
80 110 120 30
130 160 180 60
520 370 320 320
310 360 320 320
120 30 180 60
60 100 80 110
404 154 204 154
80 60 60 100
430 550 590 550
510 410 310 360
430 450 430 550
404 54 404 154
232 202 142 262
142 262 102 202
0

Output for the Sample Input

0 1 0 1 0 0 0 0 0 1
0 0 0 0 0 1 0 1 0 0
1 0 1 0 2 0 0 0 1 0

Solution in JAVA (judge verdict: Wrong Answer)

import java.util.*;
import java.awt.geom.*;

public class Main{
	
	//2332 start
	
	double EPS = 1.0e-08;
	
	private void doit() {
		Scanner sc = new Scanner(System.in);
		while(true){
			int n = sc.nextInt();
			if(n == 0) break;
			Line2D [] data = new Line2D[n];
			for(int i = 0; i < n; i++){
				Point2D p1 = new Point2D.Double(sc.nextInt(), sc.nextInt());
				Point2D p2 = new Point2D.Double(sc.nextInt(), sc.nextInt());
				data[i] = new Line2D.Double(p1, p2);
			}
			UnionFind uf = new UnionFind(n);
			for(int i = 0; i < n; i++){
				for(int j = i + 1; j < n; j++){
					if(data[i].intersectsLine(data[j])){
						uf.unite(i, j);
					}
				}
			}
			ArrayList<ArrayList<Line2D>> digits = new ArrayList<ArrayList<Line2D>>();
			HashMap<Integer, Integer> toind = new HashMap<Integer, Integer>();
			for(int i = 0; i < n; i++){
				int res = uf.par[i];
				if(toind.containsKey(res)){
				}
				else{
					digits.add(new ArrayList<Line2D>());
					toind.put(res, toind.size());
				}
				digits.get(toind.get(res)).add(data[i]);
			}
			
			int [] ans = new int[10];
			for(int i = 0; i < digits.size(); i++){
				int res  = solve(digits.get(i));
				if(res == -1) continue;
				ans[res]++;
			}
			
			for(int i = 0; i < 9; i++){
				System.out.print(ans[i] + " ");
			}
			System.out.println(ans[9]);
		}
	}
	
	private int solve(ArrayList<Line2D> list) {
		int size = list.size();
		if(size == 1){
			return 1;
		}
		if(size == 3){
			//4 or 7
			for(int i = 0; i < size; i++){
				for(int j = i + 1; j < size; j++){
					Line2D l1 = list.get(i), l2 = list.get(j);
					
					if(l1.intersectsLine(l2)){
						Point2D p = intersectPtSS(l1, l2);
						boolean cand = 
								((p.distance(l1.getP1()) < EPS || p.distance(l1.getP2()) < EPS) && 
								(p.distance(l2.getP1()) < EPS || p.distance(l2.getP2()) < EPS));
						if(! cand){
							return 4;
						}
					}
				}
			}
			return 7;
		}
		if(size == 4){
			//0,3,9
			int count = 0;
			for(int i = 0; i < size; i++){
				for(int j = 0; j < size; j++){
					if(i == j) continue;
					Line2D l1 = list.get(i), l2 = list.get(j);
					if(l1.intersectsLine(l2)){
						count++;
					}
				}
			}
			if(count == 6){
				return 3;
			}
			
			for(int i = 0; i < size; i++){
				for(int j = i + 1; j < size; j++){
					Line2D l1 = list.get(i), l2 = list.get(j);
					
					if(l1.intersectsLine(l2)){
						Point2D p = intersectPtSS(l1, l2);
						boolean cand = 
								((p.distance(l1.getP1()) < EPS || p.distance(l1.getP2()) < EPS) && 
								(p.distance(l2.getP1()) < EPS || p.distance(l2.getP2()) < EPS));
						if(! cand){
							return 9;
						}
					}
				}
			}
			return 0;
		}
		else{
			//2, 5, 8
			int count = 0;
			for(int i = 0; i < size; i++){
				for(int j = 0; j < size; j++){
					if(i == j) continue;
					Line2D l1 = list.get(i), l2 = list.get(j);
					if(l1.intersectsLine(l2)){
						count++;
					}
				}
			}
			if(count == 12){
				return 8;
			}
			if(count == 10){
				return 6;
			}
			ArrayList<Line2D> list2 = new ArrayList<Line2D>();
			for(int i = 0; i < size; i++){
				int c = 0;
				for(int j = i + 1; j < size; j++){
					Line2D l1 = list.get(i), l2 = list.get(j);
					
					if(l1.intersectsLine(l2)){
						c++;
					}
				}
				if(c == 1){
					Line2D now = list.get(i);
					for(int j = 0; j < size; j++){
						if(i == j) continue;
						if(now.intersectsLine(list.get(j))){
							Point2D p1 = list.get(j).getP1();
							Point2D p2 = list.get(j).getP2();
							if(now.ptLineDist(p1) > EPS){
								Point2D temp = p1;
								p1 = p2;
								p2 = temp;
							}
							
							int res = ccw2(p1, p2, now.getP1());
							if(res == 0){
								res = ccw2(p1, p2, now.getP2());
							}
							if(res > 0){
								return 5;
							}
							else{
								return 2;
							}
						}
					}
				}
			}
			return -1;
		}
	}

	class UnionFind{
		int [] par, rank;
		int num;
		public UnionFind(int n) {
			par = new int[n];
			rank = new int[n];
			for(int i = 0 ; i < n; i++){
				par[i] = i;
				//rank[i] = 0;
			}
			num = n;
		}
		int find(int x){
			if(par[x] == x)return x;
			else return par[x] = find(par[x]);
		}
		void unite(int x, int y){
			x = find(x);
			y = find(y);
			if(x == y) return ;
			if(rank[x] < rank[y]) par[x] = y;
			else{
				par[y] = x;
				if(rank[x] == rank[y]) rank[x]++;
			}
			num--;
		}
		boolean same(int x, int y){
			return find(x) == find(y);
		}
	}
	
	private Point2D intersectPtSS(Line2D l, Line2D m) {
		Point2D lVec = sub(l.getP2(), l.getP1());
		Point2D mVec = sub(m.getP2(), m.getP1());
		Point2D m1l1Vec = sub(m.getP1(), l.getP1());
		double a = cross(m1l1Vec, lVec);
		double b = cross(lVec, mVec);
		if(Math.abs(a) < EPS && Math.abs(b) < EPS){
			//平行な直線同士の場合
			if(l.getP1().distance(m.getP1()) < EPS) return l.getP1();
			if(l.getP1().distance(m.getP2()) < EPS) return l.getP1();
			return l.getP2();
		}
		double t = a / b;
		double resx = m.getX1() + t * mVec.getX();
		double resy = m.getY1() + t * mVec.getY();
		return new Point2D.Double(resx, resy);
	}
	
	private double cross(Point2D p1, Point2D p2) {
		double res = p1.getX() * p2.getY() - p1.getY() * p2.getX();
		return res;
	}
	
	private Point2D sub(Point2D p2, Point2D p1) {
		double x = p2.getX() - p1.getX();
		double y = p2.getY() - p1.getY();
		return new Point2D.Double(x, y);
	}
	
	private int ccw2(Point2D p1, Point2D p2, Point2D p3){
		Point2D p3p1 = sub(p3, p1);
		Point2D p3p2 = sub(p3, p2);
		double res = cross(p3p1, p3p2);
		if(Math.abs(res) < EPS) return 0;
		else if(res < EPS) return -1;
		else return 1;
	}
	
	public static void main(String[] args) {
		new Main().doit();
	}
}