a3a6ab5e9a865a75fd9e0ce95c98ed0cfd2aba9d43f666df8714a34b602914df
// https://szkopul.edu.pl/problemset/problem/1M2UtUN-WqIVZu5YBXvVvrJw/site/?key=statement
#include <bits/stdc++.h>
using namespace std;
struct Point {
long long x, y;
};
struct Segment {
int u, v;
long long dx, dy;
long long rdx, rdy;
long long h;
long long l;
long long r;
};
int n;
vector<Point> points;
vector<Segment> segments;
vector<int> bit;
int bit_n;
void bit_update(int idx, int val) {
for (; idx <= bit_n; idx += idx & -idx)
bit[idx] += val;
}
int bit_query(int idx) {
int s = 0;
for (; idx > 0; idx -= idx & -idx)
s += bit[idx];
return s;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n;
points.resize(n);
for (int i = 0; i < n; ++i) {
cin >> points[i].x >> points[i].y;
}
if (n < 4) {
cout << 0 << endl;
return 0;
}
segments.reserve(n * (n - 1));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (i == j) continue;
long long dx = points[j].x - points[i].x;
long long dy = points[j].y - points[i].y;
long long g = std::gcd(std::abs(dx), std::abs(dy));
segments.push_back({i, j, dx, dy, dx / g, dy / g, 0, 0, 0});
}
}
sort(segments.begin(), segments.end(), [](const Segment& a, const Segment& b) {
if (a.rdx != b.rdx) return a.rdx < b.rdx;
return a.rdy < b.rdy;
});
long long total_z = 0;
int seg_count = segments.size();
int i = 0;
while (i < seg_count) {
int j = i;
while (j < seg_count && segments[j].rdx == segments[i].rdx && segments[j].rdy == segments[i].rdy) {
j++;
}
int group_size = j - i;
if (group_size < 2) {
i = j;
continue;
}
long long Vx = segments[i].rdx;
long long Vy = segments[i].rdy;
vector<Segment*> grp;
grp.reserve(group_size);
vector<long long> coords;
coords.reserve(group_size * 2);
for (int k = i; k < j; ++k) {
Segment& s = segments[k];
s.h = points[s.u].x * Vy - points[s.u].y * Vx;
s.l = points[s.u].x * Vx + points[s.u].y * Vy;
s.r = points[s.v].x * Vx + points[s.v].y * Vy;
coords.push_back(s.l);
coords.push_back(s.r);
grp.push_back(&s);
}
sort(coords.begin(), coords.end());
coords.erase(unique(coords.begin(), coords.end()), coords.end());
auto get_rank = [&](long long val) { return lower_bound(coords.begin(), coords.end(), val) - coords.begin() + 1; };
sort(grp.begin(), grp.end(), [](const Segment* a, const Segment* b) { return a->h > b->h; });
bit_n = coords.size();
bit.assign(bit_n + 1, 0);
int p = 0;
while (p < group_size) {
int q = p;
while (q < group_size && grp[q]->h == grp[p]->h) {
q++;
}
for (int k = p; k < q; ++k) {
int r_rank = get_rank(grp[k]->r);
total_z += bit_query(r_rank - 1);
}
for (int k = p; k < q; ++k) {
int l_rank = get_rank(grp[k]->l);
bit_update(l_rank, 1);
}
p = q;
}
i = j;
}
cout << total_z << endl;
return 0;
}