Problem Statement

Mr. Hagiwara, a witch, has a very negative personality. When she feels depressed, she uses the magic of digging to make holes and cry and fill them. The magic of digging is as follows.

She first draws N line segments on the ground. And when she casts the spell, a hole is created in the area surrounded by the line. Two holes that are in a crossing / inclusion relationship are counted as one hole. However, two holes that are in contact only at the vertices are counted as separate holes.

Figures 1 and 2 show Sample Inputs 1 and 2. First, in FIG. 1, since the triangle A and the triangle B are in contact with each other only at the vertices, they are counted as separate holes. Since the B triangle and the C triangle overlap each other, they are counted as one hole. Therefore, FIG. 1 shows two holes. In Figure 2, the D rectangle is contained within the E rectangle, so these are counted as one hole. Since E also intersects the hole in the upper right, Fig. 2 shows one hole.

<image>

You are the caretaker of Mr. Hagiwara and you have to fill the myriad holes she made. Create a program to find out how many holes she made to find out how many holes she had to fill.

Constraints

* 1 <= N <= 50
* -1000 <= coordinates <= 1000
* The positions of (xai, yai) and (xbi, ybi) are different
* When two line segments intersect, the four endpoints of the two line segments will not line up in a straight line.

Input

Each data set is input in the following format.


N
xa1 ya1 xb1 yb1
xa2 ya2 xb2 yb2
...
xaN yaN xbN ybN


All inputs are represented by integers. The first line is the number of line segments. Then, line segment information is given over N lines. One line segment is represented by the endpoints (xai, yai) and (xbi, ybi) of the line segment.

Output

Output the number of holes in one line.

Examples

Input

8
5 0 5 10
5 10 0 5
0 5 5 0
10 0 10 10
10 10 5 5
5 5 10 0
10 2 15 5
15 5 10 8


Output

2


Input

11
0 0 10 0
10 0 10 10
10 10 0 10
0 10 0 0
1 2 9 2
8 1 8 9
9 8 1 8
2 9 2 1
8 11 11 8
8 11 14 10
11 8 10 14


Output

1
#include <cassert>// c
#include <ctime>
#include <iostream>// io
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>// container
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <stack>
#include <algorithm>// other
#include <complex>
#include <numeric>
#include <functional>
#include <random>
#include <regex>

using namespace std;
typedef long long ll;

#define ALL(c) (c).begin(),(c).end()
#define FOR(i,l,r) for(int i=(int)l;i<(int)r;++i)
#define REP(i,n) FOR(i,0,n)
#define FORr(i,l,r) for(int i=(int)r-1;i>=(int)l;--i)
#define REPr(i,n) FORr(i,0,n)
#define EACH(i,c) for(__typeof((c).begin()) i=(c).begin(); i!=(c).end(); ++i)
#define IN(l,v,r) ((l)<=(v) && (v)<(r))
#define UNIQUE(v) v.erase(unique(ALL(v)),v.end())
//debug
#define DUMP(x)  cerr << #x << " = " << (x)
#define LINE()    cerr<< " (L" << __LINE__ << ")"

template<typename T,typename U> T pmod(T x,U M){return (x%M+M)%M;}

stringstream ss;

// must template
typedef long double D;
const D INF = 1e12,EPS = 1e-8;

typedef complex<D> P;
#define X real()
#define Y imag()
istream& operator >> (istream& is,complex<D>& p){
    D x,y;is >> x >> y;p=P(x,y);return is;
}

int sig(D a,D b=0){return a<b-EPS?-1:a>b+EPS?1:0;}
template<typename T> bool eq(const T& a,const T& b){return sig(abs(a-b))==0;}
bool compX (const P& a,const P& b){return !eq(a.X,b.X)?sig(a.X,b.X)<0:sig(a.Y,b.Y)<0;}
namespace std{
    bool operator <  (const P& a,const P& b){return compX(a,b);}
    bool operator == (const P& a,const P& b){return eq(a,b);}
};
// a×b
D cross(const P& a,const P& b){return imag(conj(a)*b);}
// a・b
D dot(const P& a,const P& b) {return real(conj(a)*b);}
int ccw(const P& a,P b,P c){
    b -= a; c -= a;
    if (cross(b,c) > EPS)   return +1;       // counter clockwise
    if (cross(b,c) < -EPS)   return -1;       // clockwise
    if (dot(b,c) < 0)     return +2;       // c--a--b on line
    if (norm(b) < norm(c)) return -2;       // a--b--c on line
    return 0; //a--c--b on line (c==b,c==a)
}
// //must template
//直線・線分
struct L : public vector<P> {
    P vec() const {return this->at(1)-this->at(0);}
    L(const P &a, const P &b){push_back(a); push_back(b);}
    L(){push_back(P());push_back(P());}
};
istream& operator >> (istream& is,L& l){P s,t;is >> s >> t;l=L(s,t);return is;}

bool isIntersectLL(const L &l, const L &m) {
    return sig(cross(l.vec(), m.vec()))!=0 || // non-parallel
    sig(cross(l.vec(), m[0]-l[0])) ==0;   // same line
}
bool isIntersectLS(const L &l, const L &s) {
    return sig(cross(l.vec(), s[0]-l[0])*       // s[0] is left of l
        cross(l.vec(), s[1]-l[0]))<=0; // s[1] is right of l
}
bool isIintersectLP(const L &l, const P &p) {return sig(cross(l[1]-p, l[0]-p))==0;}

// verified by ACAC003 B
// http://judge.u-aizu.ac.jp/onlinejudge/creview.jsp?rid=899178&cid=ACAC003
bool isIntersectSS(const L &s, const L &t) {
    return ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1]) <= 0 &&
        ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1]) <= 0;
}
bool isIntersectSP(const L &s, const P &p) {
    return sig(abs(s[0]-p)+abs(s[1]-p),abs(s[1]-s[0])) <=0; // triangle inequality
}

// 交点計算
// verified by AOJLIB
// http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=1092231
P crosspoint(const L &l, const L &m) {
    D A = cross(l.vec(), m.vec());
    D B = cross(l.vec(), l[1] - m[0]);
    if (sig(A)==0 && sig(B)==0) return m[0]; // same line
    if (sig(A)==0) assert(false); //交点を持たない．
    return m[0] + B / A * (m[1] - m[0]);
}

typedef D Cost; Cost CINF=1e20;
struct Edge{
    int f,t;Cost c;D th;
    Edge(int f,int t,Cost c,D th) : f(f),t(t),c(c),th(th){
    };
    bool operator<(const Edge& r) const{return th<r.th;}
};
typedef vector<vector<Edge> >  Graph;

Graph segmentArrangement(vector<L> ss, vector<P> &ps) {
    vector<vector<D>> vs(ss.size());
    REP(i,ss.size()){vs[i].push_back(0);vs[i].push_back(abs(ss[i][1]-ss[i][0]));}
    REP(i,ss.size())REP(j,ps.size())if(isIntersectSP(ss[i], ps[j]))
        vs[i].push_back(abs(ps[j]-ss[i][0]));
    
    REP(i,ss.size())FOR(j,i+1,ss.size()){
        L& a=ss[i],b=ss[j];
        if(isIntersectSS(a,b)){
            P p = crosspoint(a,b);
            vs[i].push_back(abs(p-a[0]));vs[j].push_back(abs(p-b[0]));              
        }
    }
    REP(i,vs.size()){sort(ALL(vs[i]));vs[i].erase(unique(ALL(vs[i]),eq<D>),vs[i].end());}
    REP(i,vs.size())REP(j,vs[i].size())ps.push_back(ss[i][0]+vs[i][j]*ss[i].vec()/abs(ss[i].vec()));
    sort(ALL(ps));UNIQUE(ps);

    Graph g(ps.size());
    REP(i,vs.size()){
        vector<D>& cut=vs[i];
        REP(j,cut.size()-1){
            P a = ss[i][0] + vs[i][j]*ss[i].vec()/abs(ss[i].vec()),b = ss[i][0] + vs[i][j+1]*ss[i].vec()/abs(ss[i].vec());
            int f=distance(ps.begin(),lower_bound(ALL(ps),a)), t=distance(ps.begin(),lower_bound(ALL(ps),b));
            g[f].emplace_back(f,t,abs(b-a),arg(b-a));
            g[t].emplace_back(t,f,abs(a-b),arg(a-b));
        }
    }
    return g;
}

typedef vector<P> Poly,ConvexPoly;

enum { OUT, ON, IN };
int contains(const Poly& ps, const P& p) {
    bool in = false;int n=ps.size();
    REP(i,n){
        P a = ps[i] - p, b = ps[pmod(i+1,n)] - p;
        if (a.Y > b.Y) swap(a, b);
        if (a.Y <= 0 && 0 < b.Y)
        if (cross(a, b) < 0) in = !in;
            if (cross(a, b) == 0 && dot(a, b) <= 0) return ON;
    }
    return in ? IN : OUT;
}

vector<Poly> find_polys(const vector<P>& ps,Graph& g){
    const int n=g.size();
    vector<vector<bool>> used(n);
    REP(i,n)used[i]=vector<bool>(g[i].size());
    
    REP(i,n)sort(ALL(g[i]));

    vector<Poly> res;
    REP(i,n)REP(j,g[i].size()){
        int v=i,e_idx=j;
        Poly qs;
        while(true){
            if(used[v][e_idx])break;
            used[v][e_idx] =true;
            Edge& e=g[v][e_idx];
            qs.push_back(ps[v]);
            REP(k,g[e.t].size())if(g[e.t][k].t==v){
                e_idx =(k+1)%g[e.t].size();
                v=e.t;
                break;
            }
        }
        if(qs.size()>=3)res.push_back(move(qs));
    }

    // cerr << res.size() <<endl;

    //remove ccw poly
    int idx = 0;
    while(idx < res.size()) {
        const P diff = res[idx][1] - res[idx][0];
        const P p = res[idx][0] + diff * 0.5L + diff * P(0,-1) * (2.0L * EPS /abs(diff));
        if(contains(res[idx], p))++idx;
        else res.erase(res.begin() + idx);
    }
    return res;
}

struct UnionFind{
    vector<int> par,rank,ss;int size;
    UnionFind(int n){
        REP(i,n) par.push_back(i);
        rank = vector<int>(n);ss=vector<int>(n,1);size=n;
    }
    int root(int x){
        if(par[x] == x)return x;
        return par[x] = root(par[x]);
    }
    bool same(int x,int y){
        return root(x) == root(y);
    }
    void unite(int x,int y){
        x = root(x);y = root(y);
        if(x==y)return;
        if(rank[x] < rank[y]){
            par[x] = y;ss[y]+=ss[x];
        }else{
            par[y] = x;ss[x]+=ss[y];
        }
        if(rank[x] == rank[y]) rank[x]++;
        size--;
    }
    int getS(int x){
        return ss[root(x)];
    }
};

int main(){
    cin.tie(0); ios::sync_with_stdio(false);
    ss   <<fixed <<setprecision(20);
    cout <<fixed <<setprecision(20);
    cerr <<fixed <<setprecision(20);

    int N;cin >>N;
    vector<L> ls(N);REP(i,N) cin >> ls[i];
    vector<P> ps;
    Graph g=segmentArrangement(ls,ps);
    vector<Poly>  face=find_polys(ps,g);
    // cerr << ps.size() <<endl;
    // cerr << face.size() <<endl;
    UnionFind uf(face.size());
    REP(i,face.size()){
        // cerr << i <<endl;
        // REP(l,face[i].size())cerr << face[i][l] << endl;
       
        REP(j,face.size())if(i!=j){
            bool isin=true;
            REP(k,face[j].size())
                if(contains(face[i],face[j][k])!=IN)isin=false;
            
            bool iscross=false;
            REP(k,face[j].size())REP(l,face[i].size()){
                L l1(face[j][k],face[j][pmod(k+1,face[j].size())]);
                L l2(face[i][l],face[i][pmod(l+1,face[i].size())]);

                if(isIntersectSS(l1,l2) && sig(cross(l1.vec(),l2.vec()))==0
                    && max(l1[0],l1[1])!= min(l2[0],l2[1])
                    && max(l2[0],l2[1])!= min(l1[0],l1[1])){
                    // cerr <<"!!"<<endl;
                    // cerr << l1[0]<<" " << l1[1] <<endl;
                    // cerr << l2[0]<<" " << l2[1] <<endl;
                    iscross=true;
                }
            }
            // cerr << i <<" " << j <<endl;
            // cerr << isin <<" " << iscross <<endl;
            if(isin || iscross)uf.unite(i,j);
        }
    }
    cout << uf.size <<endl;

    return 0;
 }