using System; using static System.Console; using System.Linq; using System.Collections.Generic; using System.Runtime.Intrinsics.Arm; class Program { static int NN => int.Parse(ReadLine()); static int[] NList => ReadLine().Split().Select(int.Parse).ToArray(); static int[][] NArr(long n) => Enumerable.Repeat(0, (int)n).Select(_ => NList).ToArray(); public static void Main() { Solve(); } static void Solve() { var n = NN; var p = NArr(n); var list = new List(n); for (var i = 0; i < n; ++i) list.Add(new Point(p[i][0], p[i][1])); list.Sort(); // WriteLine(string.Join(" ", list)); var ans = 0L; for (var i = 0; i < n; ++i) for (var j = i + 1; j < n; ++j) { if (list[i].CompareTo(list[j]) == 0) continue; var revi = GetReverse(list[i]); if (revi.CompareTo(list[j]) == 0) continue; var revj = GetReverse(list[j]); if (OuterProduct(revi.X, revi.Y, revj.X, revj.Y) > 0) { var posri = UpperBound(revi, list) % n; var posrj = LowerBound(revj, list) % n; // WriteLine($"i:{i}, j;{j}, revi:{revi}, revj:{revj}, posri:{posri}, posrj:{posrj}"); if (posri <= posrj) ans += posrj - posri; else ans += posrj + n - posri; } else { var posrj = UpperBound(revj, list) % n; var posri = LowerBound(revi, list) % n; // WriteLine($"i:{i}, j;{j}, revi:{revi}, revj:{revj}, posri:{posri}, posrj:{posrj}"); if (posrj <= posri) ans += posri - posrj; else ans += posri + n - posrj; } // if (revj.CompareTo(list[posrj]) != 0) --ans; } WriteLine(ans / 3); } static Point GetReverse(Point b) { return new Point(-b.X, -b.Y, b.Dx, b.Dy, (b.Dim + 4) % 8); } class Point : IComparable { public int X; public int Y; public int Dx; public int Dy; public int Dim; public Point(int x, int y) { X = x; Y = y; (Dx, Dy, Dim) = GetRad(x, y); } public Point(int x, int y, int dx, int dy, int dim) { X = x; Y = y; Dx = dx; Dy = dy; Dim = dim; } public int CompareTo(Point b) { var d = Dim.CompareTo(b.Dim); if (d != 0) return d; if (Dim % 2 == 0) return 0; d = ((long)b.Dx * Dy).CompareTo((long)Dx * b.Dy); if (Dim % 4 == 1) return d; else return -d; } public override string ToString() { return $"({X}, {Y})"; } } // 角度を求める // dx,dy: 有理数の角度(絶対値) // dim: 象限 (0: X軸正, 1: X>0,Y>0, 2: Y軸正, 3: X<0,Y>0, 4: X軸負, 5: X<0,Y<0, 6: Y軸負, 7: X>0,Y<0) static (int dx, int dy, int dim) GetRad(int x, int y) { if (x == 0) { if (y > 0) return (0, 1, 2); if (y < 0) return (0, 1, 6); throw new Exception("原点"); } if (y == 0) { if (x > 0) return (1, 0, 0); if (x < 0) return (1, 0, 4); } var gcd = GCD(Math.Abs(x), Math.Abs(y)); if (x > 0 && y > 0) { return (x / gcd, y / gcd, 1); } else if (y > 0) { return (-x / gcd, y / gcd, 3); } else if (x > 0) { return (x / gcd, -y / gcd, 7); } return (-x / gcd, -y / gcd, 5); } static int GCD(int a, int b) { if (a < b) return GCD(b, a); if (a % b == 0) return b; return GCD(b, a % b); } static int LowerBound(T min, IList list) where T: IComparable { var ng = -1; var ok = list.Count; while (ok - ng > 1) { var mid = (ng + ok) / 2; if (list[mid].CompareTo(min) < 0) ng = mid; else ok = mid; } return ok; } static int UpperBound(T min, IList list) where T: IComparable { var ng = -1; var ok = list.Count; while (ok - ng > 1) { var mid = (ng + ok) / 2; if (list[mid].CompareTo(min) <= 0) ng = mid; else ok = mid; } return ok; } /// 外積(Z=0としたとき)を求める /// 正ならベクトルBはベクトルAの進行方向左側にある /// 0なら方向一致、負なら右側 static long OuterProduct(int ax, int ay, int bx, int by) { return (long)ax * by - (long)ay * bx; } }