#include<bits/stdc++.h> 
#include <ext/pb_ds/assoc_container.hpp> 
#include <ext/pb_ds/tree_policy.hpp> 
using namespace std;
using namespace __gnu_pbds;  
#define ordered_set tree<ll, null_type , less<ll> , rb_tree_tag , tree_order_statistics_node_update> 
#define ll long long
#define ull unsigned long long
#define pb push_back
#define inf 1e18
#define mk make_pair
#define ld long double
#define mod 1000000007
#define fi first
#define se second
#define fastIO ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define test ll t; cin>>t; while(t--)
#define setbits __builtin_popcount
#define endl '\n'
#define LOCAL
#define sim template < class c
#define ris return * this
#define dor > debug & operator <<
#define eni(x) sim > typename \
enable_if<sizeof dud<c>(0) x 1, debug&>::type operator<<(c i) {
sim > struct rge { c b, e; };
sim > rge<c> range(c i, c j) { return rge<c>{i, j}; }
sim > auto dud(c* x) -> decltype(cerr << *x, 0);
sim > char dud(...);
struct debug {
#ifdef LOCAL
~debug() { cerr << endl; }
eni(!=) cerr << boolalpha << i; ris; }
eni(==) ris << range(begin(i), end(i));}
sim, class b dor(pair < b, c > d) {
    ris << "(" << d.first << ", " << d.second << ")";
}
sim dor(rge<c> d) {
    *this << "[";
    for (auto it = d.b; it != d.e; ++it)
        *this << ", " + 2 * (it == d.b) << *it;
    ris << "]";
}
#else
sim dor(const c&) { ris; }
#endif
};
#define imie(...) " [" << #__VA_ARGS__ ": " << (__VA_ARGS__) << "] "
int main()
{
    fastIO;
    ll n;
    cin>>n;
    pair<ll,ll>p[n];
    for(int i=0;i<n;i++)
    {
        cin>>p[i].fi>>p[i].se;
    }
    ll val,val1,ans=0;
    for(int i=0;i<n;i++)
    {
        for(int j=i+1;j<n;j++)
        {
            val=p[j].se-p[i].se;
            val1=p[j].fi-p[i].fi;
            ll val2=__gcd(val,val1);
            val/=val2;
            val1/=val2;
            if(abs(val)<abs(val1))
            {
                ans++;
            }
            if(val1==1 && abs(val)==1)
            {
                ans++;
            }
            
        }
    }
    cout<<ans<<endl;

}
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
using ll = long long;
using P = pair<int,int>;

struct UnionFind {
	// それ自身の場合はsize の-1 掛けたもの
	// それ以外は自分の親のid
	vector<int> r;

	UnionFind(int N) {
		r = vector<int>(N, -1);
	}

	int root(int x) {
		if (r[x] < 0) return x;
		return r[x] = root(r[x]);
	}

	bool unite(int x, int y){
		x = root(x);
		y = root(y);
		if (x == y) return false;
		if (r[x] > r[y]) swap(x,y);
		r[x] += r[y];
		r[y] = x;
		return true;
	}

	int size(int x){
		return -r[root(x)];
	}
};

const int ncol= 400000;

int main() {
	int n;
	cin >> n;
	vector<int> a(n),b(n);
	UnionFind UF(ncol);
	map<int,int> deg;// 頂点i に接続する 辺の数
	set<int> v;// 頂点
	rep(i,n){
		cin >> a[i] >> b[i];
		UF.unite(a[i],b[i]);
		deg[a[i]]++;
		deg[b[i]]++;
		v.insert(a[i]);
		v.insert(b[i]);
	}
	// 頂点数、辺の数
	map<int,int> nhen;// 辺の数
	for (auto it : v) {
		int g=UF.root(it);
		nhen[g] += deg[it];
	}
	// for (auto it : v) {
	// 	int g=UF.root(it);
	// 	cout << "id: " << g << ", n edge: " << nhen[g] << endl;
	// }
	vector<bool> touch(ncol,false);
	int ans=0;
	for (auto it : v) {
		int g=UF.root(it);
		int nv = UF.size(it);
		int nedge = nhen[g]/2;
		// cout << "id: " << g << ", n vert: " << nv << ", n edge: " << nedge << endl;
		if (touch[g]==false) {
			if (nedge >= nv ) {
				ans += nv;
			} else {
				ans += nv-1;
			}
		}
		touch[g] = true;
	}
	cout << ans << endl;
	return 0;
}
