1. Concise Problem Statement
Given N (≤5000) non-degenerate line segments in the plane, count how many unordered pairs of segments form an L-shape—that is, they share exactly one endpoint and meet at a right angle.

2. Detailed Editorial

Overview
We need to count all pairs of segments (i, j) such that they share an endpoint P and their directions at P are perpendicular. Since N is up to 5000, an O(N²) check of all pairs is too slow (≈25 million checks). Instead, we process locally at each point.

Step-by-step

1. **Representation of a segment direction**
   For a segment with endpoints (x₁,y₁)→(x₂,y₂), define its direction vector Δ=(dx,dy)=(x₂−x₁,y₂−y₁). Normalize Δ by dividing by g = gcd(|dx|,|dy|) to make it primitive. Then canonically orient it so that:
   - dx < 0 ⇒ flip both signs
   - if dx==0, set dy=1
   - if dy==0, set dx=1
   This ensures two collinear segments have the same direction key (dx,dy).

2. **Bucket by endpoints**
   Build a map `slopes_per_point` whose keys are points P, and whose values are maps from direction Δ to the count of segments at P having that direction. For each segment, insert its normalized Δ into both endpoint buckets.

3. **Counting perpendicular pairs at each point**
   At a point P, suppose we have counts c(Δ) for each direction Δ. A perpendicular direction to Δ=(dx,dy) is computed as: swap dx and dy, then negate dx (i.e. (dx,dy)→(−dy,dx)), then re-canonicalize. The number of L-shapes at P using directions Δ and Δ′ is c(Δ)·c(Δ′). Summing over Δ gives twice the true count, because when you visit Δ′ you count the same pairs again. So we sum all c(Δ)·c(Δ′) and then divide by 2.

4. **Aggregate over all endpoints**
   Sum the half-counts from every point to get the final answer.

Complexities
- Building the structure: O(N · log N) for gcd and map insertions.
- Counting: total unique (point,direction) pairs ≤2N, so iterating and lookups are O(N · log N).
Fits within time/memory.

3. C++ Solution

```cpp
#include <bits/stdc++.h>

using namespace std;

template<typename T1, typename T2>
ostream& operator<<(ostream& out, const pair<T1, T2>& x) {
    return out << x.first << ' ' << x.second;
}

template<typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& x) {
    return in >> x.first >> x.second;
}

template<typename T>
istream& operator>>(istream& in, vector<T>& a) {
    for(auto& x: a) {
        in >> x;
    }
    return in;
};

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& a) {
    for(auto x: a) {
        out << x << ' ';
    }
    return out;
};

int n;
vector<array<int, 4>> a;

void read() {
    cin >> n;
    a.resize(n);
    for(auto& x: a) {
        cin >> x[0] >> x[1] >> x[2] >> x[3];
    }
}

void solve() {
    // An L-shape is two segments meeting at a shared endpoint at a right angle.
    // For each segment compute its normalized direction (dx, dy) reduced by gcd
    // and canonically oriented (dx >= 0, axis directions fixed), then for each
    // of its two endpoints record that direction. slopes_per_point maps a point
    // to a multiset of directions of segments incident there. At a point, two
    // incident segments are perpendicular when their directions are negatives
    // of a 90-degree rotation; for each direction we look up its perpendicular
    // direction (rotate by 90: (dx, dy) -> (-dy, dx), then re-canonicalize) and
    // add the product of the two counts. Summing over all points double-counts
    // each pair (once per endpoint considered), so we divide by 2.

    map<pair<int, int>, map<pair<int, int>, int>> slopes_per_point;

    for(auto i = 0; i < n; i++) {
        int dx = a[i][2] - a[i][0];
        int dy = a[i][3] - a[i][1];
        int g = gcd(dx, dy);
        dx /= g;
        dy /= g;

        if(dx < 0) {
            dx = -dx;
            dy = -dy;
        }

        if(dx == 0) {
            dy = 1;
        }

        if(dy == 0) {
            dx = 1;
        }

        slopes_per_point[{a[i][0], a[i][1]}][{dx, dy}]++;
        slopes_per_point[{a[i][2], a[i][3]}][{dx, dy}]++;
    }

    int ans = 0;
    for(auto& [_, slopes]: slopes_per_point) {
        int cur = 0;
        for(auto& [slope, cnt]: slopes) {
            auto [dx, dy] = slope;
            swap(dx, dy);
            dx *= -1;
            if(dx < 0) {
                dx = -dx;
                dy = -dy;
            }

            if(slopes.count({dx, dy})) {
                cur += cnt * slopes[{dx, dy}];
            }
        }

        ans += cur;
    }

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

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int T = 1;
    // cin >> T;
    for(int test = 1; test <= T; test++) {
        read();
        // cout << "Case #" << test << ": ";
        solve();
    }

    return 0;
}
```

4. Python Solution
```python
import sys
import threading

def main():
    import math
    input = sys.stdin.readline

    n = int(input())
    segments = [tuple(map(int, input().split())) for _ in range(n)]

    # Map: point (x,y) -> dict of direction (dx,dy) -> count
    mp = {}

    def normalize(dx, dy):
        """Return a canonical primitive direction for vector (dx,dy)."""
        g = math.gcd(dx, dy)
        dx //= g
        dy //= g
        # Ensure dx >= 0, or if dx==0 then dy>0
        if dx < 0:
            dx, dy = -dx, -dy
        if dx == 0:
            # vertical line: force dy = +1
            dy = 1
        if dy == 0:
            # horizontal line: force dx = +1
            dx = 1
        return dx, dy

    # Build the mapping
    for x1, y1, x2, y2 in segments:
        dx, dy = x2 - x1, y2 - y1
        dirn = normalize(dx, dy)
        for pt in ((x1,y1), (x2,y2)):
            if pt not in mp:
                mp[pt] = {}
            mp[pt][dirn] = mp[pt].get(dirn, 0) + 1

    ans = 0
    # For each shared endpoint, count perpendicular pairs
    for slopes in mp.values():
        for (dx, dy), cnt in slopes.items():
            # perpendicular vector
            pdx, pdy = -dy, dx
            pdx, pdy = normalize(pdx, pdy)
            # add cross count if exists
            cnt2 = slopes.get((pdx, pdy), 0)
            ans += cnt * cnt2

    # Every pair was counted twice (A with B and B with A)
    print(ans // 2)

if __name__ == "__main__":
    threading.Thread(target=main).start()
```

5. Compressed Editorial
Group segments by their endpoints, track primitive normalized directions. At each point, count how many segments go in each direction. For each direction Δ, its perpendicular is obtained by rotating 90°: (dx,dy)→(−dy,dx), then re-canonicalize. The number of L-shapes at that point is the sum over Δ of cnt(Δ)·cnt(perp(Δ)), divided by 2 to correct double counting. Summing across all points yields the answer in O(N log N).
