#include using namespace std; #include /** keep-include */ using namespace __gnu_pbds; #define rep(i, a, b) for(int i = a; i < (b); ++i) #define all(x) begin(x), end(x) #define sz(x) (int)(x).size() #define pb push_back typedef long long ll; typedef long double ld; typedef vector vi; template using Tree = tree, rb_tree_tag, tree_order_statistics_node_update>; constexpr int INF = 1e9; struct ST { int n; vector> tree; ST(int _n) : n(_n), tree(2*n) {} void update(int p, pair v) { p += n; ll val = (ll)v.first*INF+v.second; while(p > 0) { tree[p].insert(val); p/=2; } } int query(int l, int r, int cap) { l+=n; r+=n; int res = 0; ll search = (ll)cap*INF-1; while(l <= r) { if(l%2==1) { res += sz(tree[l]); if(cap != 0) res -= tree[l].order_of_key(search); l++; } if(r%2==0) { res += sz(tree[r]); if(cap != 0) res -= tree[r].order_of_key(search); r--; } l/=2; r/=2; } return res; } }; struct P { ll x, y; P(ll _x=0, ll _y=0) : x(_x), y(_y) {} bool operator<(P p) { return pair{x, y} < pair{p.x, p.y}; }; friend ostream& operator<<(ostream& os, P p) { return os << "(" << p.x << "," << p.y << ")"; } }; typedef pair info; info get(info a) { auto [l, r] = a; ll dy = r.y-l.y; ll dx = r.x-l.x; assert(dx >= 0); assert(dx >= abs(dy)); assert(dx%2==0); assert(dy%2==0); ll m = l.x+dx/2; return {P(m-dy/2, r.y), P(m+dy/2, l.y)}; } pair slope(info a) { auto [u, _] = a; return {u.y+u.x, u.y}; } ll go1(vector& a, vector& b) { vector> events; auto add = [&events](vector& ps, int id) { for(auto p : ps) { auto [u, v] = get(p); if(u.x > v.x) swap(u, v); events.pb({u.x, -id}); events.pb({v.x, id}); } }; add(a, 1); add(b, 2); sort(all(events)); ll res = 0; vector seen(2); for(auto [_, e] : events) { int id = abs(e)-1; if(e < 0) { seen[id]++; res += seen[id^1]; } else { seen[id]--; } } return res; } ll go2(vector& v) { int n = sz(v); vector coords; for(int i = 0; i < n; i++) { v[i] = get(v[i]); coords.pb(v[i].first.x); coords.pb(v[i].second.x); } sort(all(coords)); coords.erase(unique(all(coords)), coords.end()); auto comp = [&](ll val) { return (int)(lower_bound(all(coords), val)-coords.begin()); }; sort(all(v), [](info a, info b) { return slope(a) < slope(b); }); ll res = 0; ST tree(sz(coords)); for(int i = 0; i < n; i++) { int l = comp(v[i].first.x); int r = comp(v[i].second.x); res += tree.query(0, l-1, r); res += tree.query(l, r, 0); tree.update(l, {r, i}); } return res; } int main() { cin.tie(0)->sync_with_stdio(0); int n; cin >> n; ll boxes = 0, horz = 0, vert = 0; vector hUp, hDown, vUp, vDown; for(int i = 0; i < n; i++) { P u, v; cin >> u.x >> u.y >> v.x >> v.y; u.x *= 2; u.y *= 2; v.x *= 2; v.y *= 2; if(v < u) swap(u, v); ll dx = v.x-u.x; ll dy = v.y-u.y; //square if(dx == abs(dy)) { boxes++; } //vert else if(dx > abs(dy)) { vert++; if(dy > 0) vUp.pb({u, v}); else vDown.pb({u, v}); } //horz else { horz++; swap(u.x, u.y); swap(v.x, v.y); if(v < u) swap(u, v); dx = v.x-u.x; dy = v.y-u.y; if(dy > 0) hUp.pb({u, v}); else hDown.pb({u, v}); } } ll ans = 0; ans += boxes*(boxes-1)/2; ans += boxes*(horz+vert); ans += horz*vert; auto rev = [](vector& v) { for(int i = 0; i < sz(v); i++) { v[i].first.x *= -1; v[i].second.x *= -1; swap(v[i].first, v[i].second); } }; ans += go1(vUp, vDown); ans += go1(hUp, hDown); ans += go2(vUp); rev(vDown); ans += go2(vDown); ans += go2(hUp); rev(hDown); ans += go2(hDown); cout << ans << "\n"; }