Skip to content

GraphNodeId

Description

Stable identifier used to refer to a graph node.

Node ids are opaque 64-bit values. Internally they pack a per-slot generation in the high 32 bits and a slot index in the low 32 bits.

A node id is stable only while that node remains live. After GraphCommitChanges deletes a node, any old id or handle referring to that slot becomes stale and must not be used with live-node access APIs.

Usage example (Cross-references)

Usage examples (Cross-references)
    #define GRAPH_SLOT_MARKED   ((u32)1u << 1)
    
    static GraphNodeId graph_make_node_id(u32 index, u32 generation) {
        return (((u64)generation) << 32) | (u64)index;
    }
    }
    
    static void graph_validate_node_id(const GenericGraph *graph, GraphNodeId node_id) {
        u32                     index;
        u32                     generation;
    }
    
    static GenericGraphSlot *graph_require_live_slot(GenericGraph *graph, GraphNodeId node_id) {
        graph_validate_node_id(graph, node_id);
        return graph_slot_ptr_raw(graph, GraphNodeIdIndex(node_id));
    }
    
    static const GenericGraphSlot *graph_require_live_slot_const(const GenericGraph *graph, GraphNodeId node_id) {
        graph_validate_node_id(graph, node_id);
        return graph_slot_ptr_const_raw(graph, GraphNodeIdIndex(node_id));
        slot->data = NULL;
    
        deinit_vec(GENERIC_VEC(&slot->out_neighbors), sizeof(GraphNodeId));
        slot->out_neighbors = VecInitT(slot->out_neighbors, graph->allocator);
        deinit_vec(GENERIC_VEC(&slot->in_neighbors), sizeof(GraphNodeId));
        deinit_vec(GENERIC_VEC(&slot->out_neighbors), sizeof(GraphNodeId));
        slot->out_neighbors = VecInitT(slot->out_neighbors, graph->allocator);
        deinit_vec(GENERIC_VEC(&slot->in_neighbors), sizeof(GraphNodeId));
        slot->in_neighbors = VecInitT(slot->in_neighbors, graph->allocator);
    }
    
    static bool graph_neighbors_contains(const GraphNeighbors *neighbors, GraphNodeId node_id) {
        size idx;
    }
    
    static size graph_find_neighbor_index(const GraphNeighbors *neighbors, GraphNodeId node_id) {
        size idx;
    }
    
    static size graph_find_pending_edge_removal_index(const GenericGraph *graph, GraphNodeId from, GraphNodeId to) {
        size idx;
    }
    
    static bool graph_remove_edge_now(GenericGraph *graph, GraphNodeId from, GraphNodeId to) {
        GraphNeighbors *out_neighbors;
        GraphNeighbors *in_neighbors;
        }
    
        remove_range_vec(GENERIC_VEC(out_neighbors), NULL, sizeof(GraphNodeId), out_idx, 1);
        remove_range_vec(GENERIC_VEC(in_neighbors), NULL, sizeof(GraphNodeId), in_idx, 1);
        graph->edge_count -= 1;
    
        remove_range_vec(GENERIC_VEC(out_neighbors), NULL, sizeof(GraphNodeId), out_idx, 1);
        remove_range_vec(GENERIC_VEC(in_neighbors), NULL, sizeof(GraphNodeId), in_idx, 1);
        graph->edge_count -= 1;
        return true;
    }
    
    static size graph_remove_marked_outgoing_edges(GenericGraph *graph, GraphNodeId from) {
        size            removed   = 0;
        GraphNeighbors *neighbors = graph_out_neighbors_ptr(graph, from);
    
        while (idx < VecLen(neighbors)) {
            GraphNodeId             neighbor_id = VecAt(neighbors, idx);
            const GenericGraphSlot *slot;
    }
    
    static size graph_remove_all_outgoing_edges(GenericGraph *graph, GraphNodeId from) {
        size            removed   = 0;
        GraphNeighbors *neighbors = graph_out_neighbors_ptr(graph, from);
    
        while (VecLen(neighbors)) {
            GraphNodeId to = VecLast(neighbors);
            if (!graph_remove_edge_now(graph, from, to)) {
                LOG_FATAL("Graph failed to remove outgoing edge during node deletion");
        for (slot_index = 0; slot_index < VecLen(&graph->slots); slot_index++) {
            const GenericGraphSlot *slot    = VecPtrAt((GraphSlots *)&graph->slots, slot_index);
            GraphNodeId             self_id = graph_make_node_id((u32)slot_index, slot->generation);
            u64                     neighbor_i;
    
                for (neighbor_i = 0; neighbor_i < VecLen(&slot->out_neighbors); neighbor_i++) {
                    GraphNodeId             neighbor_id = VecAt(&slot->out_neighbors, neighbor_i);
                    const GenericGraphSlot *target_slot;
    
                for (neighbor_i = 0; neighbor_i < VecLen(&slot->in_neighbors); neighbor_i++) {
                    GraphNodeId             predecessor_id = VecAt(&slot->in_neighbors, neighbor_i);
                    const GenericGraphSlot *source_slot;
                graph_release_slot(graph, slot, item_size);
            } else {
                deinit_vec(GENERIC_VEC(&slot->out_neighbors), sizeof(GraphNodeId));
                slot->out_neighbors = VecInitT(slot->out_neighbors, graph->allocator);
                deinit_vec(GENERIC_VEC(&slot->in_neighbors), sizeof(GraphNodeId));
                deinit_vec(GENERIC_VEC(&slot->out_neighbors), sizeof(GraphNodeId));
                slot->out_neighbors = VecInitT(slot->out_neighbors, graph->allocator);
                deinit_vec(GENERIC_VEC(&slot->in_neighbors), sizeof(GraphNodeId));
                slot->in_neighbors = VecInitT(slot->in_neighbors, graph->allocator);
                slot->visit_count  = 0;
    }
    
    GraphNodeId graph_push_node(GenericGraph *graph, const void *item_data, size item_size) {
        GraphNodeId       node_id;
        GenericGraphSlot  slot;
    
    GraphNodeId graph_push_node(GenericGraph *graph, const void *item_data, size item_size) {
        GraphNodeId       node_id;
        GenericGraphSlot  slot;
        GenericGraphSlot *slot_ptr;
                graph_free_node_data(graph, slot_ptr->data, item_size);
                slot_ptr->data = NULL;
                deinit_vec(GENERIC_VEC(&slot_ptr->out_neighbors), sizeof(GraphNodeId));
                slot_ptr->out_neighbors = VecInitT(slot_ptr->out_neighbors, graph->allocator);
                deinit_vec(GENERIC_VEC(&slot_ptr->in_neighbors), sizeof(GraphNodeId));
                deinit_vec(GENERIC_VEC(&slot_ptr->out_neighbors), sizeof(GraphNodeId));
                slot_ptr->out_neighbors = VecInitT(slot_ptr->out_neighbors, graph->allocator);
                deinit_vec(GENERIC_VEC(&slot_ptr->in_neighbors), sizeof(GraphNodeId));
                slot_ptr->in_neighbors = VecInitT(slot_ptr->in_neighbors, graph->allocator);
                slot_ptr->visit_count  = 0;
            )) {
            graph_free_node_data(graph, slot.data, item_size);
            deinit_vec(GENERIC_VEC(&slot.out_neighbors), sizeof(GraphNodeId));
            deinit_vec(GENERIC_VEC(&slot.in_neighbors), sizeof(GraphNodeId));
            return 0;
            graph_free_node_data(graph, slot.data, item_size);
            deinit_vec(GENERIC_VEC(&slot.out_neighbors), sizeof(GraphNodeId));
            deinit_vec(GENERIC_VEC(&slot.in_neighbors), sizeof(GraphNodeId));
            return 0;
        }
    }
    
    GraphNodeId graph_push_node_owned(GenericGraph *graph, void *item_data, size item_size) {
        GraphNodeId node_id;
    
    GraphNodeId graph_push_node_owned(GenericGraph *graph, void *item_data, size item_size) {
        GraphNodeId node_id;
    
        if (!graph || !item_data || !item_size) {
    }
    
    bool graph_contains_node(GenericGraph *graph, GraphNodeId node_id) {
        u32               index;
        u32               generation;
    }
    
    GraphNode graph_get_node(GenericGraph *graph, GraphNodeId node_id) {
        GraphNode node;
    }
    
    void *graph_node_ptr_at(GenericGraph *graph, GraphNodeId node_id) {
        return graph_require_live_slot(graph, node_id)->data;
    }
    }
    
    GraphNeighbors *graph_out_neighbors_ptr(GenericGraph *graph, GraphNodeId node_id) {
        return &graph_require_live_slot(graph, node_id)->out_neighbors;
    }
    }
    
    GraphNeighbors *graph_in_neighbors_ptr(GenericGraph *graph, GraphNodeId node_id) {
        return &graph_require_live_slot(graph, node_id)->in_neighbors;
    }
    }
    
    size graph_out_degree(GenericGraph *graph, GraphNodeId node_id) {
        return VecLen(graph_out_neighbors_ptr(graph, node_id));
    }
    }
    
    size graph_in_degree(GenericGraph *graph, GraphNodeId node_id) {
        return VecLen(graph_in_neighbors_ptr(graph, node_id));
    }
    }
    
    GraphNodeId graph_neighbor_at(GenericGraph *graph, GraphNodeId from, size neighbor_idx) {
        GraphNeighbors *neighbors;
    }
    
    GraphNodeId graph_predecessor_at(GenericGraph *graph, GraphNodeId to, size predecessor_idx) {
        GraphNeighbors *neighbors;
    }
    
    bool graph_has_edge(GenericGraph *graph, GraphNodeId from, GraphNodeId to) {
        GraphNeighbors *neighbors;
    }
    
    bool graph_add_edge(GenericGraph *graph, GraphNodeId from, GraphNodeId to) {
        GraphNeighbors *out_neighbors;
        GraphNeighbors *in_neighbors;
                GENERIC_VEC(out_neighbors),
                (const u8 *)&to,
                sizeof(GraphNodeId),
                VecLen(out_neighbors),
                1
                GENERIC_VEC(in_neighbors),
                (const u8 *)&from,
                sizeof(GraphNodeId),
                VecLen(in_neighbors),
                1
                1
            )) {
            remove_range_vec(GENERIC_VEC(out_neighbors), NULL, sizeof(GraphNodeId), VecLen(out_neighbors) - 1, 1);
            return false;
        }
    }
    
    bool graph_mark_edge_for_removal(GenericGraph *graph, GraphNodeId from, GraphNodeId to) {
        GraphPendingEdgeRemoval pending;
    }
    
    bool graph_edge_marked_for_removal(GenericGraph *graph, GraphNodeId from, GraphNodeId to) {
        ValidateGraph(graph);
        graph_validate_node_id(graph, from);
    }
    
    bool graph_unmark_edge_for_removal(GenericGraph *graph, GraphNodeId from, GraphNodeId to) {
        size idx;
            GenericGraphSlot *slot = VecPtrAt(&graph->slots, slot_index);
            if (graph_slot_is_occupied(slot) && graph_slot_is_marked(slot)) {
                GraphNodeId marked_id = graph_make_node_id((u32)slot_index, slot->generation);
                (void)graph_remove_all_outgoing_edges(graph, marked_id);
            }
            GenericGraphSlot *slot = VecPtrAt(&graph->slots, slot_index);
            if (graph_slot_is_occupied(slot) && !graph_slot_is_marked(slot)) {
                GraphNodeId live_id = graph_make_node_id((u32)slot_index, slot->generation);
                (void)graph_remove_marked_outgoing_edges(graph, live_id);
            }
    
        while (iter->neighbor_index < VecLen(neighbors)) {
            GraphNodeId neighbor_id  = VecAt(neighbors, iter->neighbor_index);
            iter->neighbor_index    += 1;
    
        while (iter->predecessor_index < VecLen(neighbors)) {
            GraphNodeId predecessor_id  = VecAt(neighbors, iter->predecessor_index);
            iter->predecessor_index    += 1;
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNode   node_b;
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNode   node_b;
        GraphAddEdge(&graph, a, b);
        ZstrGraph graph = GraphInit(&alloc);
    
        GraphNodeId red   = GraphAddNodeR(&graph, "red");
        GraphNodeId green = GraphAddNodeR(&graph, "green");
        GraphNodeId blue  = GraphAddNodeR(&graph, "blue");
    
        GraphNodeId red   = GraphAddNodeR(&graph, "red");
        GraphNodeId green = GraphAddNodeR(&graph, "green");
        GraphNodeId blue  = GraphAddNodeR(&graph, "blue");
        GraphNodeId red   = GraphAddNodeR(&graph, "red");
        GraphNodeId green = GraphAddNodeR(&graph, "green");
        GraphNodeId blue  = GraphAddNodeR(&graph, "blue");
    
        GraphAddEdge(&graph, red, green);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId node_id = GraphAddNodeR(&graph, 11);
        GraphNode   node    = GraphGetNode(&graph, node_id);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a    = GraphAddNodeR(&graph, 10);
        GraphNode   node = GraphGetNode(&graph, a);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphAddEdge(&graph, a, b);
        result = result && (GraphPredecessorAt(&graph, a, 0) == c);
    
        GraphNodeId d = GraphAddNodeR(&graph, 40);
    
        result = result && (GraphNodeIdIndex(d) == GraphNodeIdIndex(b));
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a    = GraphAddNodeR(&graph, 10);
        GraphNode   node = GraphGetNode(&graph, a);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
    
        bool result = GraphAddEdge(&graph, a, a);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNodeId d = GraphAddNodeR(&graph, 40);
        GraphNodeId a = GraphAddNodeR(&graph, 10);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNodeId d = GraphAddNodeR(&graph, 40);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
        GraphNodeId d = GraphAddNodeR(&graph, 40);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a         = GraphAddNodeR(&graph, 10);
        GraphNodeId b         = GraphAddNodeR(&graph, 20);
        u64         counts[2] = {0};
    
        GraphNodeId a         = GraphAddNodeR(&graph, 10);
        GraphNodeId b         = GraphAddNodeR(&graph, 20);
        u64         counts[2] = {0};
        result      = result && !GraphContainsNode(&graph, b);
    
        GraphNodeId reused = GraphAddNodeR(&graph, 99);
    
        result = result && (GraphNodeIdIndex(reused) == GraphNodeIdIndex(b));
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a    = GraphAddNodeR(&graph, 10);
        GraphNode   node = GraphGetNode(&graph, a);
    
        bool        result            = VecCapacity(&graph.slots) >= 8;
        GraphNodeId first_id          = GraphAddNodeR(&graph, 10);
        GraphNodeId second_id         = GraphAddNodeR(&graph, 20);
        GraphNodeId third_id          = GraphAddNodeR(&graph, 30);
        bool        result            = VecCapacity(&graph.slots) >= 8;
        GraphNodeId first_id          = GraphAddNodeR(&graph, 10);
        GraphNodeId second_id         = GraphAddNodeR(&graph, 20);
        GraphNodeId third_id          = GraphAddNodeR(&graph, 30);
        u64         slot_count        = VecLen(&graph.slots);
        GraphNodeId first_id          = GraphAddNodeR(&graph, 10);
        GraphNodeId second_id         = GraphAddNodeR(&graph, 20);
        GraphNodeId third_id          = GraphAddNodeR(&graph, 30);
        u64         slot_count        = VecLen(&graph.slots);
        size        slot_capacity     = VecCapacity(&graph.slots);
        StrGraph    graph = GraphInitWithDeepCopy(str_init_copy, str_deinit, &alloc);
        Str         name  = StrInitFromZstr("alpha", &alloc);
        GraphNodeId node_id;
        GraphNode   node;
        Str        *stored_name;
        typedef Graph(Str) StrGraph;
        StrGraph    graph = GraphInitWithDeepCopy(NULL, str_deinit, &alloc);
        GraphNodeId node_id;
        GraphNode   node;
        Str        *stored_name;
        int      shared = 7;
    
        GraphNodeId id0 = GraphAddNodeL(&graph, owned);
        GraphNodeId id1 = GraphAddNodeR(&graph, shared);
    
        GraphNodeId id0 = GraphAddNodeL(&graph, owned);
        GraphNodeId id1 = GraphAddNodeR(&graph, shared);
    
        bool result = GraphNodeIdIndex(id0) == 0 && GraphNodeIdGeneration(id0) == 1 && GraphNodeIdIndex(id1) == 1;
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        bool result = GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        bool result = GraphAddEdge(&graph, a, a);
    
    static u64 node_id_hash(const void *data, u32 size) {
        u64 x = *(const GraphNodeId *)data;
        (void)size;
    
    static i32 node_id_compare(const void *lhs, const void *rhs) {
        GraphNodeId a = *(const GraphNodeId *)lhs;
        GraphNodeId b = *(const GraphNodeId *)rhs;
        return (a > b) - (a < b);
    static i32 node_id_compare(const void *lhs, const void *rhs) {
        GraphNodeId a = *(const GraphNodeId *)lhs;
        GraphNodeId b = *(const GraphNodeId *)rhs;
        return (a > b) - (a < b);
    }
    
    typedef Graph(Str) CityGraph;
    typedef Map(Str, GraphNodeId) CityIndex;
    
    static GraphNodeId city_add_intersection(CityGraph *graph, CityIndex *index, const Str *name, DefaultAllocator *alloc) {
    typedef Map(Str, GraphNodeId) CityIndex;
    
    static GraphNodeId city_add_intersection(CityGraph *graph, CityIndex *index, const Str *name, DefaultAllocator *alloc) {
        GraphNodeId id = GraphAddNodeR(graph, StrInitFromCstr(StrBegin(name), StrLen(name), alloc));
    
    static GraphNodeId city_add_intersection(CityGraph *graph, CityIndex *index, const Str *name, DefaultAllocator *alloc) {
        GraphNodeId id = GraphAddNodeR(graph, StrInitFromCstr(StrBegin(name), StrLen(name), alloc));
    
        Str key_copy = StrInitFromCstr(StrBegin(name), StrLen(name), alloc);
    }
    
    static bool city_reachable_from(GraphNode node, GraphNodeId goal_id) {
        if (GraphNodeVisitCount(node) > 0) {
            return false;
    
    static bool city_reachable(CityGraph *graph, CityIndex *index, const Str *from, const Str *to) {
        GraphNodeId *from_id = MapGetFirstPtr(index, *from);
        GraphNodeId *to_id   = MapGetFirstPtr(index, *to);
    static bool city_reachable(CityGraph *graph, CityIndex *index, const Str *from, const Str *to) {
        GraphNodeId *from_id = MapGetFirstPtr(index, *from);
        GraphNodeId *to_id   = MapGetFirstPtr(index, *to);
    
        if (!from_id || !to_id) {
        Str s_unknown = StrInitFromZstr("Unknown", &alloc);
    
        GraphNodeId alpha = city_add_intersection(&graph, &index, &s_alpha, &alloc);
        GraphNodeId beta  = city_add_intersection(&graph, &index, &s_beta, &alloc);
        GraphNodeId gamma = city_add_intersection(&graph, &index, &s_gamma, &alloc);
    
        GraphNodeId alpha = city_add_intersection(&graph, &index, &s_alpha, &alloc);
        GraphNodeId beta  = city_add_intersection(&graph, &index, &s_beta, &alloc);
        GraphNodeId gamma = city_add_intersection(&graph, &index, &s_gamma, &alloc);
        GraphNodeId delta = city_add_intersection(&graph, &index, &s_delta, &alloc);
        GraphNodeId alpha = city_add_intersection(&graph, &index, &s_alpha, &alloc);
        GraphNodeId beta  = city_add_intersection(&graph, &index, &s_beta, &alloc);
        GraphNodeId gamma = city_add_intersection(&graph, &index, &s_gamma, &alloc);
        GraphNodeId delta = city_add_intersection(&graph, &index, &s_delta, &alloc);
        GraphNodeId echo  = city_add_intersection(&graph, &index, &s_echo, &alloc);
        GraphNodeId beta  = city_add_intersection(&graph, &index, &s_beta, &alloc);
        GraphNodeId gamma = city_add_intersection(&graph, &index, &s_gamma, &alloc);
        GraphNodeId delta = city_add_intersection(&graph, &index, &s_delta, &alloc);
        GraphNodeId echo  = city_add_intersection(&graph, &index, &s_echo, &alloc);
        GraphNodeId gamma = city_add_intersection(&graph, &index, &s_gamma, &alloc);
        GraphNodeId delta = city_add_intersection(&graph, &index, &s_delta, &alloc);
        GraphNodeId echo  = city_add_intersection(&graph, &index, &s_echo, &alloc);
    
        GraphAddEdge(&graph, alpha, beta);
    
        typedef Graph(int) IntGraph;
        typedef Map(GraphNodeId, u64) CountMap;
    
        IntGraph graph  = GraphInit(&alloc);
        CountMap counts = MapInit(node_id_hash, node_id_compare, &alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
    
        GraphAddEdge(&graph, a, d);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        GraphAddEdge(&graph, a, b);
        IntGraph graph = GraphInit(&alloc);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
    
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
        GraphNodeId a = GraphAddNodeR(&graph, 1);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
        GraphNodeId b = GraphAddNodeR(&graph, 2);
        GraphNodeId c = GraphAddNodeR(&graph, 3);
        GraphNodeId d = GraphAddNodeR(&graph, 4);
    
        GraphAddEdge(&graph, a, c);
    /// TAGS: Graph, Node, Id, Index
    ///
    #define GraphNodeIdIndex(id) ((u32)((GraphNodeId)(id) & UINT32_MAX))
    
    ///
    /// TAGS: Graph, Node, Id, Generation
    ///
    #define GraphNodeIdGeneration(id) ((u32)(((GraphNodeId)(id) >> 32) & UINT32_MAX))
    
    ///
    typedef struct {
        void       *_graph_;
        GraphNodeId _id_;
    } GraphNode;
    /// TAGS: Graph, Edge, Neighbor, Vec
    ///
    typedef Vec(GraphNodeId) GraphNeighbors;
    
    ///
    
    typedef struct {
        GraphNodeId from;
        GraphNodeId to;
    } GraphPendingEdgeRemoval;
    typedef struct {
        GraphNodeId from;
        GraphNodeId to;
    } GraphPendingEdgeRemoval;
Last updated on