#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <complex>
#include <cmath>
#include <cstdlib>
#include <cfloat>
#include <ctime>
#include <queue>
using namespace std;
 
#define ALL(v) (v).begin(),(v).end()
 
typedef long double D;
typedef complex<D> P;
typedef const P &rP;
typedef pair<P,P> seg;
typedef pair<D,int> pdi;
typedef vector<P> polygon;
 
const D EPS = 1e-10L;
const D INF = LDBL_MAX;
 
inline bool eq(D x, D y){
	return abs(x - y) < EPS;
}
 
namespace std{
	inline bool operator< (const P &a, const P &b){
		if(!eq(real(a), real(b))){
			return real(a) < real(b);
		}
		if(!eq(imag(a), imag(b))){
			return imag(a) < imag(b);
		}
		return false;
	}
}
 
bool eqcomp(rP a, rP b){
	return eq(real(a), real(b)) && eq(imag(a), imag(b));
}
 
inline P difvec(const seg &s){
	return s.second - s.first;
}
 
inline D dot(rP a, rP b){
	return real(a) * real(b) + imag(a) * imag(b);
}
inline D cross(rP a, rP b){
	return real(a) * imag(b) - imag(a) * real(b);
}
 
int ccw(rP a, P b, P c){
	b -= a;
	c -= a;
	D cr = cross(b, c);
	if(!eq(cr, 0.0L)){
		return cr > 0.0L ? 1 : -1;
	}
	if(dot(b, c) < -EPS){ return 2; }
	if(abs(b) + EPS < abs(c)){ return -2; }
	return 0;
}
 
int ccw(const seg &s, rP pt){
	return ccw(s.first, s.second, pt);
}
 
D intrll(const seg &sa, const seg &sb){
	P da12 = difvec(sa);
	P db21 = -difvec(sb);
	P dab1 = sb.first - sa.first;
	D cr = cross(da12, db21);
	if(abs(cr) > EPS){
		return cross(dab1, db21) / cr;
	}
	return INF;
}
 
bool segcontains(const seg &s, const seg &t){
	return ccw(s, t.first) == 0 &&
		   ccw(s, t.second) == 0;
}
 
struct UF{
	vector<int> par;
	UF(int n) : par(n){
		for(int i = 1; i < n; ++i){
			par[i] = i;
		}
	}
	int find(int x){
		return x != par[x] ? par[x] = find(par[x]) : x;
	}
	bool unite(int x, int y){
		x = find(x);
		y = find(y);
		if(x == y){ return false; }
		if(rand() >> 7 & 1){
			swap(x, y);
		}
		par[x] = y;
		return true;
	}
};
 
 
 
vector<seg> lines;
vector<P> ps;
vector<vector<pdi> > adj;
vector<vector<int> > plgidx;
int plgnum;
int outidx = -1;
 
 
void mergeline(){
	for(int i = 0; i < lines.size(); ++i){
		for(int j = i + 1; j < lines.size(); ++j){
			if(abs(ccw(lines[i], lines[j].first)) != 1 &&
			   abs(ccw(lines[i], lines[j].second)) != 1
			){
				lines[j--] = lines.back();
				lines.pop_back();
			}
		}
	}
}
 
void arrange(){
	for(int i = 0; i < lines.size(); ++i){
		ps.push_back(lines[i].first);
		ps.push_back(lines[i].second);
		for(int j = i + 1; j < lines.size(); ++j){
			D t = intrll(lines[i], lines[j]);
			if(t != INF){
				P intr = lines[i].first + t * difvec(lines[i]);
				if(max(abs(real(intr)), abs(imag(intr))) <= 1001.0L + EPS){
					ps.push_back(intr);
				}
			}
		}
	}
	sort(ALL(ps));
	ps.erase(unique(ALL(ps), eqcomp), ps.end());
	 
	adj.resize(ps.size());
	 
	for(int i = 0; i < lines.size(); ++i){
		vector<pdi> ls;
		const P dif = difvec(lines[i]);
		for(int j = 0; j < ps.size(); ++j){
			if(abs(ccw(lines[i], ps[j])) != 1){
				ls.push_back(pdi(dot(dif, ps[j] - lines[i].first), j));
			}
		}
		sort(ALL(ls));
		for(int j = 0; j + 1 < ls.size(); ++j){
			int a = ls[j].second, b = ls[j + 1].second;
			rP pa = ps[a], pb = ps[b];
			adj[a].push_back(pdi(arg(pb - pa), b));
			adj[b].push_back(pdi(arg(pa - pb), a));
		}
	}
	 
}
 
void assignnum(int p, int k, int idx){
	P prvpt = ps[p];
	D area = 0.0;
 
	while(plgidx[p][k] < 0){
		plgidx[p][k] = idx;
		 
		int q = p;
		p = adj[p][k].second;
		for(k = 0; adj[p][k].second != q; ++k);
		if(!k--){ k += adj[p].size(); }
		 
		area += cross(prvpt, ps[p]);
		prvpt = ps[p];
	}
	 
	if(area < -EPS){
		outidx = idx;
	}
}
 
int main(){
	srand(time(0));
	 
	int n;
	cin >> n;
	D x1, x2, y1, y2;
 
	vector<seg> segs;
	segs.reserve(n);
	for(int i = 0; i < n; ++i){
		cin >> x1 >> y1 >> x2 >> y2;
		segs.push_back(seg(P(x1, y1), P(x2, y2)));
		ps.push_back(P(x1, y1));
		ps.push_back(P(x2, y2));
	}
 
	lines = segs;
	lines.push_back(seg(P(-1001, -1001), P(-1001, 1001)));
	lines.push_back(seg(P(-1001, 1001), P(1001, 1001)));
	lines.push_back(seg(P(1001, 1001), P(1001, -1001)));
	lines.push_back(seg(P(1001, -1001), P(-1001, -1001)));
	mergeline();
	arrange();
 
	plgidx.resize(ps.size());
	for(int i = 0; i < adj.size(); ++i){
		sort(ALL(adj[i]));
		plgidx[i].assign(adj[i].size(), -1);
	}
 
	for(int i = 0; i < plgidx.size(); ++i){
		for(int j = 0; j < plgidx[i].size(); ++j){
			if(plgidx[i][j] < 0){
				assignnum(i, j, plgnum++);
			}
		}
	}
 
	UF uf(plgnum);
	vector<vector<int> > G(plgnum);
	for(int i = 0; i < adj.size(); ++i){
		int prv = adj[i].size() - 1;
		for(int j = 0; j < adj[i].size(); ++j){
			int plg1 = plgidx[i][prv];
			int plg2 = plgidx[i][j];
			prv = j;
 
			G[plg1].push_back(plg2);
			G[plg2].push_back(plg1);
 
			const seg s(ps[i], ps[adj[i][j].second]);
			bool cntn = false;
			for(int k = 0; k < segs.size(); ++k){
				if(segcontains(segs[k], s)){
					cntn = true;
					break;
				}
			}
			if(!cntn){
				uf.unite(plg1, plg2);
			}
		}
	}
 
	vector<char> vis(plgnum);
	int ans = 0;
	for(int i = 0; i < plgnum; ++i){
		if(uf.find(i) == uf.find(outidx)){
			vis[i] = 1;
		}
	}
 
	queue<int> q;
	for(int i = 0; i < vis.size(); ++i){
		if(!vis[i]){
			++ans;
			vis[i] = 1;
			q.push(i);
			while(!q.empty()){
				int u = q.front();
				q.pop();
				for(int j = 0; j < G[u].size(); ++j){
					int v = G[u][j];
					if(!vis[v]){
						vis[v] = 1;
						q.push(v);
					}
				}
			}
		}
	}
	 
	cout << ans << endl;
}

Measured memory_bytes: 1572