Now update the BacktrackState to use aliases for adjacency lookups and ring closure checks.


Edit

    struct BacktrackState {
        const std::vector<WayRef>& ways;
        const std::unordered_map<int64_t, std::vector<std::pair<size_t, bool>>>& adj;
        std::vector<bool>& used;

        // Try to close a ring from ring_last_nid back to ring_first_nid
        // path = ordered list of (way_index, reversed) already chosen
        bool try_close(int64_t ring_first_nid, int64_t ring_last_nid,
                       std::vector<std::pair<size_t, bool>>& path, int depth) {
            if (depth > 5000) return false;

            // Closed?
            if (ring_first_nid == ring_last_nid && !path.empty()) return true;

            // Find candidates adjacent to ring_last_nid
            auto it = adj.find(ring_last_nid);
            if (it == adj.end()) return false;

            for (const auto& [wi, is_last] : it->second) {
                if (used[wi]) continue;
                const auto* g = ways[wi].geom;

                // If is_last, this way's last_node matches ring_last, so traverse reversed
                bool reversed = is_last;
                int64_t new_last = reversed ? g->first_node_id : g->last_node_id;

                used[wi] = true;
                path.push_back({wi, reversed});

                if (try_close(ring_first_nid, new_last, path, depth + 1)) {
                    return true;
                }

                path.pop_back();
                used[wi] = false;
            }
            return false;
        }
    };

    struct BacktrackState {
        const std::vector<WayRef>& ways;
        const std::unordered_map<int64_t, std::vector<std::pair<size_t, bool>>>& adj;
        std::vector<bool>& used;
        const std::unordered_map<int64_t, std::vector<int64_t>>& aliases;

        // Check if two node IDs match (directly or via coordinate alias)
        bool nids_match(int64_t a, int64_t b) const {
            if (a == b) return true;
            auto it = aliases.find(a);
            if (it != aliases.end()) {
                for (auto alias : it->second) {
                    if (alias == b) return true;
                }
            }
            return false;
        }

        // Collect all adjacent way candidates for a node ID (including aliases)
        void get_candidates(int64_t nid, std::vector<std::pair<size_t, bool>>& out) const {
            out.clear();
            auto it = adj.find(nid);
            if (it != adj.end()) {
                out.insert(out.end(), it->second.begin(), it->second.end());
            }
            // Also check aliases
            auto ait = aliases.find(nid);
            if (ait != aliases.end()) {
                for (auto alias : ait->second) {
                    if (alias == nid) continue;
                    auto it2 = adj.find(alias);
                    if (it2 != adj.end()) {
                        out.insert(out.end(), it2->second.begin(), it2->second.end());
                    }
                }
            }
        }

        // Try to close a ring from ring_last_nid back to ring_first_nid
        bool try_close(int64_t ring_first_nid, int64_t ring_last_nid,
                       std::vector<std::pair<size_t, bool>>& path, int depth) {
            if (depth > 5000) return false;

            // Closed?
            if (!path.empty() && nids_match(ring_first_nid, ring_last_nid)) return true;

            // Find candidates adjacent to ring_last_nid (including aliases)
            std::vector<std::pair<size_t, bool>> candidates;
            get_candidates(ring_last_nid, candidates);

            for (const auto& [wi, is_last] : candidates) {
                if (used[wi]) continue;
                const auto* g = ways[wi].geom;

                bool reversed = is_last;
                int64_t new_last = reversed ? g->first_node_id : g->last_node_id;

                used[wi] = true;
                path.push_back({wi, reversed});

                if (try_close(ring_first_nid, new_last, path, depth + 1)) {
                    return true;
                }

                path.pop_back();
                used[wi] = false;
            }
            return false;
        }
    };