Problem:
輪ゴム

n 本の釘を平板上の座標 P1 (x1, y1), P2 (x2, y2), P3 (x3, y3),..., Pn (xn, yn) に１本ずつ打ち、輪ゴムの輪の中に全ての釘が入るように 1 本の輪ゴムで囲みます。このとき、輪ゴムが交差してはいけません。

釘の座標を読み込んで、上記のように釘を輪ゴムで囲んだときに輪ゴムに接していない釘の本数を出力するプログラムを作成してください。輪ゴムは充分に伸び縮みするものとします。同じ座標に 2 本以上の釘を打つことはないものとします。また、輪ゴムがかかった釘と釘の間は直線で結ばれるものとし、その直線上に 3 本以上の釘が並ぶことはないものとします。例えば、図 1 に示すような入力はありえません。図 2 に示すように輪ゴムがかかっていない釘が 1 直線上に並ぶことはありえます。

図１

図２

ただし、それぞれの座標値は -1000.0 以上1000.0 以下の実数です。また、n は 3 以上 100 以下の整数です。

Input

複数のデータセットが与えられます。各データセットは以下のような形式です与えられます。

n
x1, y1
x2, y2
... 　　　　
...
xn, yn

n が 0 の時、入力の最後を示します。データセットの数は 50 を超えません。

Output

データセットごとに、ゴムと接していない釘の本数を出力してください。

例えば、図 3 に示す４つの釘を表す入力があった場合、図 4 のように囲まれるので、輪ゴムに接していない釘の本数は 1 本です。

図３

図４

Sample Input

4
1.0,0.0
0.0,1.0
2.0,1.0
1.0,2.0
9
-509.94,892.63
567.62,639.99
-859.32,-64.84
-445.99,383.69
667.54,430.49
551.12,828.21
-940.2,-877.2
-361.62,-970
-125.42,-178.48
0

Output for the Sample Input

0
3

Hint

以下は２つめのサンプル入力に対する図です。

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
namespace aoj {
	struct Point {
		private double _x, _y;
		public Point(double x, double y) {
			_x = x;
			_y = y;
		}
		public double x { get { return _x; } set { _x = value; } }
		public double y { get { return _y; } set { _y = value; } }
		public static Point operator+(Point r, Point l) { return new Point(r.x + l.x, r.y + l.y); }
		public static Point operator-(Point r, Point l) { return new Point(r.x - l.x, r.y - l.y); }
		public static double Cross(Point p1, Point p2) { return p1.x * p2.y - p1.y * p2.x; }
	}
	class _0068 {
		static bool Turn2Right(Point p0, Point p1, Point p2) { return Point.Cross(p2 - p0, p1 - p0) > 0; }
		static int GrahamScan(Point[] ps) {
			Array.Sort(ps, (a, b) => (a.x < b.x) ? -1 : (a.x > b.x) ? 1 : 0);
			var p0 = ps[0];

			var args = new List<KeyValuePair<Point,double>>();
			for (int i = 1; i < ps.Length; i++) {
				var tp = ps[i] - p0;
				args.Add(new KeyValuePair<Point, double>(ps[i], Math.Atan2(tp.y, tp.x)));
			}
			args.Sort((a, b) => (a.Value < b.Value) ? -1 : (a.Value > b.Value) ? 1 : 0);
			for (int i = 1; i < ps.Length; i++)
				ps[i] = args[i - 1].Key;

			var s = new Stack<Point>();
			s.Push(ps[0]); s.Push(ps[1]); s.Push(ps[2]);
			for(int i = 3; i < ps.Length; i++) {
				var top = s.Pop();
				var n2t = s.Pop();
				while (Turn2Right(n2t, top, ps[i])) {
					top = n2t;
					n2t = s.Pop();
				}
				s.Push(n2t); s.Push(top); s.Push(ps[i]);
			}
			return ps.Length - s.Count;
		}
		public static void Main() {
			string str;
			while ((str = Console.ReadLine()) != "0") {
				var n = int.Parse(str);
				var points = new Point[n];
				for (int i = 0; i < n; i++) {
					var pos = Console.ReadLine().Split(',').Take(2).Select(double.Parse).ToArray();
					points[i].x = pos[0];	points[i].y = pos[1];
				}
				Console.WriteLine(GrahamScan(points));
			}
		}
	}
}