#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <numeric>
#include <utility>
#include <vector>

using namespace std;

template <class T> int sgn(T x) { return (x > 0) - (x < 0); }
template<class T>
struct Point {
	typedef Point P;
	T x, y;
	explicit Point(T x=0, T y=0) : x(x), y(y) {}
	bool operator<(P p) const { return tie(x,y) < tie(p.x,p.y); }
	bool operator==(P p) const { return tie(x,y)==tie(p.x,p.y); }
	P operator+(P p) const { return P(x+p.x, y+p.y); }
	P operator-(P p) const { return P(x-p.x, y-p.y); }
	P operator*(T d) const { return P(x*d, y*d); }
	P operator/(T d) const { return P(x/d, y/d); }
	T dot(P p) const { return x*p.x + y*p.y; }
	T cross(P p) const { return x*p.y - y*p.x; }
	T cross(P a, P b) const { return (a-*this).cross(b-*this); }
	T dist2() const { return x*x + y*y; }
	double dist() const { return sqrt((double)dist2()); }
	// angle to x-axis in interval [-pi, pi]
	double angle() const { return atan2(y, x); }
	P unit() const { return *this/dist(); } // makes dist()=1
	P perp() const { return P(-y, x); } // rotates +90 degrees
	P normal() const { return perp().unit(); }
	// returns point rotated 'a' radians ccw around the origin
	P rotate(double a) const {
		return P(x*cos(a)-y*sin(a),x*sin(a)+y*cos(a)); }
	friend ostream& operator<<(ostream& os, P p) {
		return os << "(" << p.x << "," << p.y << ")"; }
};

using P = Point<double>;
using Line = pair<P, P>;

template<class P>
pair<int, P> lineInter(P s1, P e1, P s2, P e2) {
	auto d = (e1 - s1).cross(e2 - s2);
	if (d == 0) // if parallel
		return {0, P(0, 0)};

	auto p = s2.cross(e1, e2), q = s2.cross(e2, s1);
	return {1, (s1 * p + e1 * q) / d};
}

constexpr int MAXN = 12;
constexpr double EPS = 1e-2;
int n;
Line lines[MAXN];
bool is_valid_isect[MAXN][MAXN];
P isects[MAXN][MAXN];

bool on_segment(P s, P e, P p) {
    return abs(p.cross(s, e)) <= EPS && (s - p).dot(e - p) < 0;
}

// only checks interior intersections
bool does_seg_isect(P a, P b, P c, P d) {
    auto side_a = a.cross(c, d);
    auto side_b = b.cross(c, d);
    auto side_c = c.cross(a, b);
    auto side_d = d.cross(a, b);

    return side_a * side_b < 0 && side_c * side_d < 0;
}

bool is_simple(const vector<P>& points) {
    /*
    cerr << "Trying polygon\n";
    for (const auto& p : points) {
        cerr << p << endl;
    }
    cerr << endl;
    */

    int m = points.size();
    bool is_valid = true;
    for (int i = 0; i < m; ++i) {
        int ni = (i + 1) % m;
        for (int j = 0; j < m; ++j) {
            if (i == j) continue;
            int nj = (j + 1) % m;

            // cerr << "Considering edge " << points[i] << points[ni] << " - " << points[j] << points[nj] << " " << does_seg_isect(points[i], points[ni], points[j], points[nj]) << endl;
            is_valid &= !does_seg_isect(points[i], points[ni], points[j], points[nj]);

            if (abs(i - j) > 1 && abs(i - j + m) > 1 && abs(j - i + m) > 1) {
                // cerr << "disjoint test " << i << " " << j << endl;
                is_valid &= !on_segment(points[i], points[ni], points[j]);
            }
        }
    }

    if (is_valid) {
        for (const auto& p : points) {
            // cerr << p << endl;
        }
        // cerr << endl;
    }

    return is_valid;
}

bool is_valid(const vector<int>& line_order) {
    /*
    for (auto& x : line_order) {
        cerr << " " << x;
    }
    cerr << endl;
    */
    vector<P> points;

    int m = line_order.size();
    for (int i = 0; i < m; ++i) {
        int j = (i + 1) % m;
        int x = line_order[i];
        int y = line_order[j];

        if (!is_valid_isect[x][y]) {
            // cerr << "Invalid edge: " << x << " " << y << endl;
            return false;
        }

        if (!points.empty() && (points.back() - isects[x][y]).dist2() <= EPS) {
            // Check for 0 length segments
            return false;
        }
        points.push_back(isects[x][y]);
    }

    if ((points[0] - points.back()).dist2() <= EPS) {
        return false;
    }

    return is_simple(points);
}

int main() {
    cin >> n;

    for (int i = 0; i < n; ++i) {
        double x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;

        lines[i] = make_pair(P(x1, y1), P(x2, y2));
    }

    for (int i = 0; i < n; ++i) {
        for (int j = i + 1; j < n; ++j) {
            auto res = lineInter(lines[i].first, lines[i].second, lines[j].first, lines[j].second);
            if (res.first == 1) {
                // cerr << "valid isect " << i << " " << j << " " << res.second << endl;
                is_valid_isect[i][j] = is_valid_isect[j][i] = true;
                isects[i][j] = isects[j][i] = res.second;
            }
        }
    }

    vector<int> line_order(n);
    iota(line_order.begin(), line_order.end(), 0);

    int32_t ans = 0;
    do {
        ans += is_valid(line_order);
    } while (next_permutation(line_order.begin() + 1, line_order.end()));

    ans /= 2;
    cout << ans << '\n';

    return 0;
}
