# ARC 082 E: ConvexScore

Keywords
convex hull DP
Time Complexity
$O(N^3)$

#include <iostream>
#include <algorithm>
#include <vector>
#include <complex>
#include <cmath>
#include <cassert>
#include <tuple>

using namespace std;

constexpr long long mod = 998244353;

using P = complex<long long>;

long long dot(P a, P b) {
return real(conj(a) * b);
}

long long cross(P a, P b) {
return imag(conj(a) * b);
}

long long gcd(long long a, long long b) {
a = abs(a);
b = abs(b);
if (b == 0) return a;
return gcd(b, a % b);
}

// Get minimum element pointing in the same direction.
P tomato(P a) {
long long g = gcd(a.real(), a.imag());
return P(a.real() / g, a.imag() / g);
}

// Lexical order (argument, norm)
bool argcomp(P p1, P p2) {
bool a = p1.imag() > 0 || (p1.imag() == 0 && p1.real() >= 0);
bool b = p2.imag() > 0 || (p2.imag() == 0 && p2.real() >= 0);
if (a != b) return a;
long long c = p2.real() * p1.imag();
long long d = p1.real() * p2.imag();
if (c != d) return c < d;
return norm(p1) < norm(p2);
}

namespace std {
bool operator<(const P &a, const P &b) {
return argcomp(a, b);
}
}

int main() {
int n;
cin >> n;

vector<P> ps(n);
for (int i = 0; i < n; i++) {
int x, y;
cin >> x >> y;
ps[i] = P(x, y);
}

vector<pair<int, int>> es;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j) continue;
es.emplace_back(i, j);
}
}
// If two vectors are pointing same direction,
// these are arranged such that both aren't taken.
sort(es.begin(), es.end(), [&](pair<int, int> a, pair<int, int> b) {
P d = ps[a.second] - ps[a.first];
P e = ps[b.second] - ps[b.first];
if (cross(d, e) == 0 && dot(d, e) >= 0) {
return dot(ps[a.second] - ps[a.first], ps[b.first] - ps[a.first]) < 0;
} else {
return d < e;
}
});

// Count the points inside (NOT on) the triangle(i,j,k)
static int in[200][200][200];
for (int j = 0; j < n; j++) {
vector<pair<P, int>> qs;
for (int i = 0; i < n; i++) {
if (i == j) continue;
qs.emplace_back(ps[i] - ps[j], i);
}
sort(qs.begin(), qs.end());
for (int i = 0; i < n; i++) {
if (i == j) continue;
P s = tomato(ps[j] - ps[i]);
int f = lower_bound(qs.begin(), qs.end(), make_pair(s, 0)) - qs.begin();
for (int l = 0; l < n - 1; l++) {
int k = qs[(f + l) % (n - 1)].second;
in[i][j][k] -= l + 1;
in[j][k][i] -= l + 1;
in[k][i][j] -= l + 1;
}
}
}
static int on[200][200];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j) continue;
for (int k = 0; k < n; k++) {
if (i == k || j == k) continue;
on[i][j] += cross(ps[k] - ps[i], ps[j] - ps[i]) == 0 && dot(ps[k] - ps[i], ps[j] - ps[i]) > 0 && dot(ps[k] - ps[j], ps[i] - ps[j]) > 0;
in[i][j][k] = max(0, in[i][j][k] + n);
}
}
}

vector<long long> two(n + 1);
two[0] = 1;
for (int i = 1; i <= n; i++) {
two[i] = two[i - 1] * 2 % mod;
}

// Easy
long long ans = 0;
for (int i = 0; i < n; i++) {
vector<long long> dp1(n);
vector<long long> dp2(n);
for (auto e : es) {
int u = e.first;
int v = e.second;

if (u == i) {
assert(dp1[v] == 0);
dp1[v] = two[on[u][v]];
} else if (v == i) {
dp2[v] += dp2[u];
} else {
int c = on[i][v] + on[u][v] + in[i][u][v];
dp2[v] += dp1[u] * two[c];
dp2[v] += dp2[u] * two[c];
}
dp2[v] %= mod;
}
ans += dp2[i];
ans %= mod;
}

cout << ans << endl;
}