/*
 * Copyright (c) 2014-2020 Cisco Systems, Inc.  All rights reserved
 * Copyright (c) 2017      Amazon.com, Inc. or its affiliates.  All Rights
 *                         reserved.
 * Copyright (c) 2019      Intel, Inc.  All rights reserved.
 * Copyright (c) 2021-2022 Nanook Consulting.  All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

/*
 * This file defines a number of internal structures to the BP graph
 * code which need to be exposed only for unit testing.  This file
 * should not be included in code that uses the BP graph interface.
 */

#ifndef BIPARTITE_GRAPH_INTERNAL
#define BIPARTITE_GRAPH_INTERNAL 1

struct prte_bp_graph_edge_t {
    pmix_object_t super;

    pmix_list_item_t outbound_li;
    pmix_list_item_t inbound_li;

    /** source of this edge */
    int source;

    /** v_index of target of this edge */
    int target;

    /** cost (weight) of this edge */
    int64_t cost;

    /**
     * (flow-network) capacity of this edge.  Zero-capacity edges essentially do
     * not exist and will be ignored by most of the algorithms implemented here.
     */
    int capacity;

    /** any other information associated with this edge */
    void *e_data;
};

struct prte_bp_graph_vertex_t {
    /** index in the graph's array of vertices */
    int v_index;

    /** any other information associated with the vertex */
    void *v_data;

    /** linked list of edges for which this vertex is a source */
    pmix_list_t out_edges;

    /** linked list of edges for which this vertex is a target */
    pmix_list_t in_edges;
};

struct prte_bp_graph_t {
    /** number of vertices currently in this graph */
    int num_vertices;

    /** vertices in this graph (with number of set elements == num_vertices) */
    pmix_pointer_array_t vertices;

    /** index of the source vertex, or -1 if not present */
    int source_idx;

    /** index of the sink vertex, or -1 if not present */
    int sink_idx;

    /** user callback to clean up the v_data */
    prte_bp_graph_cleanup_fn_t v_data_cleanup_fn;

    /** user callback to clean up the e_data */
    prte_bp_graph_cleanup_fn_t e_data_cleanup_fn;
};

#define LIST_FOREACH_CONTAINED(item, list, type, member)                                   \
    for (item = container_of((list)->pmix_list_sentinel.pmix_list_next, type, member);     \
         &item->member != &(list)->pmix_list_sentinel;                                     \
         item = container_of(((pmix_list_item_t *) (&item->member))->pmix_list_next, type, \
                             member))

#define LIST_FOREACH_SAFE_CONTAINED(item, next, list, type, member)                                \
    for (item = container_of((list)->pmix_list_sentinel.pmix_list_next, type, member),             \
        next = container_of(((pmix_list_item_t *) (&item->member))->pmix_list_next, type, member); \
         &item->member != &(list)->pmix_list_sentinel; item = next,                                \
        next = container_of(((pmix_list_item_t *) (&item->member))->pmix_list_next, type, member))

#define NUM_VERTICES(g) (g->num_vertices)

#define CHECK_VERTEX_RANGE(g, v)                 \
    do {                                         \
        if ((v) < 0 || (v) >= NUM_VERTICES(g)) { \
            return PRTE_ERR_BAD_PARAM;           \
        }                                        \
    } while (0)

/* cast away any constness of &g->vertices b/c the pmix_pointer_array API is
 * not const-correct */
#define V_ID_TO_PTR(g, v_id)                                                                       \
    ((prte_bp_graph_vertex_t *) pmix_pointer_array_get_item((pmix_pointer_array_t *) &g->vertices, \
                                                            v_id))

#define FOREACH_OUT_EDGE(g, v_id, e_ptr, _err)                                              \
    prte_bp_graph_vertex_t *_v;                                                             \
    _v = V_ID_TO_PTR((g), (v_id));                                                          \
    if (NULL == _v) {                                                                       \
        return (_err);                                                                      \
    }                                                                                       \
    LIST_FOREACH_CONTAINED(e_ptr, &(_v->out_edges), prte_bp_graph_edge_t, outbound_li)

#define FOREACH_IN_EDGE(g, v_id, e_ptr, _err)                                               \
    prte_bp_graph_vertex_t *_v;                                                             \
    _v = V_ID_TO_PTR((g), (v_id));                                                          \
    if (NULL == _v) {                                                                       \
        return (_err);                                                                      \
    }                                                                                       \
    LIST_FOREACH_CONTAINED(e_ptr, &(_v->in_edges), prte_bp_graph_edge_t, inbound_li)

/* Iterate over (u,v) edge pairs along the given path, where path is defined
 * by the predecessor array "pred".  Stops when a -1 predecessor is
 * encountered.  Note: because it is a *predecessor* array, the traversal
 * starts at the sink and progresses towards the source. */
#define FOREACH_UV_ON_PATH(pred, source, sink, u, v) \
    for (u = pred[sink], v = sink; u != -1; v = u, u = pred[u])

bool prte_bp_graph_bellman_ford(prte_bp_graph_t *gx, int source, int target, int *pred);

int prte_bp_graph_bipartite_to_flow(prte_bp_graph_t *g);

#endif
