Coder Social home page Coder Social logo

Comments (1)

milahu avatar milahu commented on June 2, 2024

abandoned draft for topo_sort in cmake

  • strongly_connected_components
  • simple_cycles (90% done)
  • topo_sort
cmake_minimum_required(VERSION 3.22)
project(function_topo_sort)



function(quote_string output_name str)
    string(REPLACE "\\" "\\\\" str "${str}") # escape \ -> \\
    string(REPLACE "\"" "\\\"" str "${str}") # escape " -> \"
    set(str "\"${str}\"") # wrap string in quotes
    set(${output_name} "${str}" PARENT_SCOPE)
endfunction()



# list of lists
# Nested list support is thoroughly broken
# https://gitlab.kitware.com/cmake/cmake/-/issues/17565

function(deque_create name)
    # TODO error if exists
    set(__deque__${name}__size 0 PARENT_SCOPE)
    set(__deque__${name}__start 0 PARENT_SCOPE)
    set(__deque__${name}__end -1 PARENT_SCOPE)
endfunction()

# TODO function(deque_delete name)

function(deque_push_back name value)
    # size + 1
    MATH(EXPR new_size "${__deque__${name}__size}+1")
    set(__deque__${name}__size ${new_size} PARENT_SCOPE)
    # end + 1
    MATH(EXPR new_end "${__deque__${name}__end}+1")
    set(__deque__${name}__end ${new_end} PARENT_SCOPE)
    # set value
    set(__deque__${name}__value__${new_end} ${value} PARENT_SCOPE)
endfunction()

function(deque_push_front name value)
    # size + 1
    MATH(EXPR new_size "${__deque__${name}__size}+1")
    set(__deque__${name}__size ${new_size} PARENT_SCOPE)
    # start - 1
    MATH(EXPR new_start "${__deque__${name}__start}-1")
    set(__deque__${name}__start ${new_start} PARENT_SCOPE)
    # set value
    set(__deque__${name}__value__${new_start} ${value} PARENT_SCOPE)
endfunction()

function(deque_get name key result_name)
    set(${result_name})
    if(${__deque__${name}__size} EQUAL 0)
        return() # deque is empty
    endif()
    # validate key
    if(${key} LESS ${__deque__${name}__start} OR ${key} GREATER ${__deque__${name}__end})
        message(FATAL_ERROR "out of bound. key=${key} min=${__deque__${name}__start} max=${__deque__${name}__end}")
        return()
    endif()
    set(${result_name} ${__deque__${name}__value__${key}} PARENT_SCOPE)
endfunction()

# get + remove last value
function(deque_pop_back name result_name)
    set(${result_name})
    if(${__deque__${name}__size} EQUAL 0)
        return() # deque is empty
    endif()
    set(key ${__deque__${name}__end})
    set(${result_name} ${__deque__${name}__value__${key}} PARENT_SCOPE)
    set(__deque__${name}__value__${key} PARENT_SCOPE) # unset value
    # size - 1
    MATH(EXPR new_size "${__deque__${name}__size}-1")
    set(__deque__${name}__size ${new_size} PARENT_SCOPE)
    # end - 1
    MATH(EXPR new_end "${__deque__${name}__end}-1")
    set(__deque__${name}__end ${new_end} PARENT_SCOPE)
endfunction()

# get + remove first value
function(deque_pop_front name result_name)
    set(${result_name})
    if(${__deque__${name}__size} EQUAL 0)
        return() # deque is empty
    endif()
    set(key ${__deque__${name}__start})
    set(${result_name} ${__deque__${name}__value__${key}} PARENT_SCOPE)
    set(__deque__${name}__value__${key} PARENT_SCOPE) # unset value
    # size - 1
    MATH(EXPR new_size "${__deque__${name}__size}-1")
    set(__deque__${name}__size ${new_size} PARENT_SCOPE)
    # start + 1
    MATH(EXPR new_start "${__deque__${name}__start}+1")
    set(__deque__${name}__start ${new_start} PARENT_SCOPE)
endfunction()

function(deque_size name result_name)
    set(${result_name} ${__deque__${name}__size} PARENT_SCOPE)
endfunction()

function(deque_front name result_name)
    deque_get(${name} ${__deque__${name}__start} value)
    set(${result_name} ${value} PARENT_SCOPE)
endfunction()

function(deque_back name result_name)
    deque_get(${name} ${__deque__${name}__end} value)
    set(${result_name} ${value} PARENT_SCOPE)
endfunction()

function(deque_keys name result_name)
    set(result)
    if(${__deque__${name}__size} EQUAL 0)
        return()
    endif()
    foreach(key RANGE ${__deque__${name}__start} ${__deque__${name}__end})
        list(APPEND result ${key})
    endforeach()
    set(${result_name} ${result} PARENT_SCOPE)
endfunction()

function(deque_print name)
    deque_keys(${name} keys)
    foreach(key ${keys})
        deque_get(${name} ${key} value)
        #message("deque_print: name=${name}, key=${key}, value=${value}")
        message("${name}.${key} = ${value}")
    endforeach()
endfunction()

function(deque_print_value name key)
    deque_get(${name} ${key} value)
    message("${name}.${key} = ${value}")
endfunction()

function(deque_to_json name result_json_name)
    deque_keys(${name} keys)
    set(result_json "[]")
    foreach(key ${keys})
        # FIXME deque keys can be negative
        # json keys should be 0 1 2 3 ...
        deque_get(${name} ${key} value)
        # workaround for missing string(JSON ... SET_STRING ...)
        # https://gitlab.kitware.com/cmake/cmake/-/issues/23750
        quote_string(value "${value}")
        #message("deque_to_json: name=${name}, key=${key}, value=${value}")
        string(JSON result_json
            ERROR_VARIABLE json_error
            SET "${result_json}" "${key}" "${value}"
        )
        if(NOT json_error STREQUAL "NOTFOUND")
            message(FATAL_ERROR "deque_to_json: failed to set json idx ${key}: ${json_error}")
        endif()
        #message("deque_to_json: result_json = ${result_json}")
    endforeach()
    set(${result_json_name} ${result_json} PARENT_SCOPE)
endfunction()

if(FALSE)
    # test deque

    deque_create(test)

    message("empty deque:")
    deque_print(test)

    deque_push_back(test "a;a;a")
    message("deque 1 value:")
    deque_print(test)

    deque_push_back(test "b;b;b")
    message("deque 2 values:")
    deque_print(test)

    deque_push_front(test "z;z;z")
    message("deque 3 values:")
    deque_print(test)

    deque_front(test value)
    message("front = ${value}")

    deque_back(test value)
    message("back = ${value}")

    deque_print_value(test -1)
    deque_print_value(test 0)
    deque_print_value(test 1)
    #deque_print_value(test 2) # error: out of bound

endif()



function(map_create name)
    # TODO error if exists
    set(__map__${name}__keys PARENT_SCOPE)
endfunction()

function(map_set name key value)
    # set value
    set(__map__${name}__value__${key} ${value} PARENT_SCOPE)
    list(FIND __map__${name}__keys ${key} old_key_idx)
    if(${old_key_idx} GREATER -1) # key exists
        return()
    endif()
    # add key
    list(APPEND __map__${name}__keys ${key})
    set(__map__${name}__keys ${__map__${name}__keys} PARENT_SCOPE)
endfunction()

function(map_get name key result_name)
    # TODO check if key exists
    set(${result_name} ${__map__${name}__value__${key}} PARENT_SCOPE)
endfunction()

function(map_keys name result_name)
    set(${result_name} ${__map__${name}__keys} PARENT_SCOPE)
endfunction()

function(map_size name result_name)
    list(LENGTH __map__${name}__keys size)
    set(${result_name} ${size} PARENT_SCOPE)
endfunction()

function(map_remove name key)
    # remove key
    list(REMOVE_ITEM __map__${name}__keys ${key})
    set(__map__${name}__keys ${__map__${name}__keys} PARENT_SCOPE)
    # remove value
    set(${__map__${name}__value__${key}} PARENT_SCOPE)
endfunction()

function(map_print name)
    map_keys(${name} keys)
    foreach(key ${keys})
        map_get(${name} ${key} value)
        message("${name}.${key} = ${value}")
    endforeach()
endfunction()

if(FALSE)

    # test map

    map_create(test)
    message("empty map:")
    map_print(test)

    map_set(test a "a;a;a")
    map_size(test size)
    message("map with ${size} values:")
    map_print(test)

    map_set(test b "b;b;b")
    map_size(test size)
    message("map with ${size} values:")
    map_print(test)
endif()



#[==[
    # test list fns
    set(somelist "a;b;c")
    list(LENGTH somelist somelist_length)
    message("somelist_length = ${somelist_length}")
    list(FIND somelist b value_idx)
    message("b: value_idx = ${value_idx}")
    list(FIND somelist z value_idx)
    message("z: value_idx = ${value_idx}")
    #message(FATAL_ERROR todo)
]==]

#[==[
    # workaround for "bug" in foreach
    # foreach(idx RANGE -1) should not enter loop
    message("loop 1")
    foreach(idx RANGE -1)
        message("idx = ${idx}")
    endforeach()

    message("")

    message("loop 2")
    foreach(idx RANGE 0 -1 1)
        message("idx = ${idx}")
    endforeach()

    message(FATAL_ERROR todo)
]==]



message("topo_sort ...")

function(strongly_connected_components graph_json result_json_name)
    # Generate nodes in strongly connected components of graph
    # https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.components.strongly_connected_components.html

    # TODO filter graph to remove extra edges

    #preorder = dict()
    map_create(preorder)

    #lowlink = dict()
    map_create(lowlink)

    #scc_found = set()
    map_create(scc_found)

    #scc_queue = list()
    deque_create(scc_queue)

    # return by value instead of "yield scc"
    deque_create(result_deque)

    #i = 0
    set(i 0)  # Preorder counter

    #neighbors = {v: iter(G[v]) for v in G}
    #map_create(neighbors) # dict()

    #for source in G:
    message("strongly_connected_components: graph_json = ${graph_json}")
    string(JSON last_source_idx LENGTH "${graph_json}")
    math(EXPR last_source_idx "${last_source_idx}-1")
    foreach(source_idx RANGE 0 ${last_source_idx} 1)
        string(JSON source MEMBER "${graph_json}" ${source_idx})
        message("strongly_connected_components: source=${source}")
        #if source not in scc_found:
        map_get(scc_found ${source} source_in_scc_found)
        message("strongly_connected_components: source_in_scc_found=${source_in_scc_found}")
        if(NOT source_in_scc_found)
            message("strongly_connected_components: NOT source_in_scc_found")
            #queue = [source]
            set(queue ${source})
            #while queue:
            list(LENGTH queue queue_length)
            while(${queue_length} GREATER 0)
                message("strongly_connected_components: queue=${queue}")
                message("strongly_connected_components: queue_length=${queue_length}")
                #v = queue[-1]
                list(GET queue -1 v)
                message("strongly_connected_components: v=${v}")
                #if v not in preorder:
                map_get(preorder ${v} v_in_preorder)
                message("strongly_connected_components: v_in_preorder=${v_in_preorder}")
                if(NOT v_in_preorder)
                    message("strongly_connected_components: NOT v_in_preorder")
                    #i = i + 1
                    math(EXPR i "${i} + 1")
                    #preorder[v] = i
                    map_set(preorder ${v} ${i})
                endif()
                #done = True
                set(done TRUE)
                #for w in neighbors[v]:
                message("strongly_connected_components: graph_json = ${graph_json}")
                message("strongly_connected_components: queue = ${queue}")
                message("strongly_connected_components: v = ${v}")
                string(JSON last_w_idx ERROR_VARIABLE json_error LENGTH "${graph_json}" "${v}")
                if(NOT json_error STREQUAL "NOTFOUND")
                    # value was not found
                    # extra edge: target-node of edge is not a source-node in graph
                    #queue.pop()
                    list(POP_BACK queue)
                    # update loop counter
                    list(LENGTH queue queue_length)
                    continue()
                endif()
                math(EXPR last_w_idx "${last_w_idx}-1")
                message("strongly_connected_components: last_w_idx = ${last_w_idx}")
                if(${last_w_idx} GREATER -1)
                    foreach(w_idx RANGE 0 ${last_w_idx} 1)
                        message("strongly_connected_components: w_idx = ${w_idx}")
                        # object
                        #string(JSON w_key MEMBER "${graph_json}" "${v}" ${w_idx})
                        #string(JSON w GET "${graph_json}" "${v}" ${w_key})
                        # array
                        string(JSON w GET "${graph_json}" "${v}" ${w_idx})
                        message("strongly_connected_components: w=${w}")
                        #if w not in preorder:
                        map_get(preorder ${w} w_in_preorder)
                        if(NOT w_in_preorder)
                            #queue.append(w)
                            list(APPEND queue ${w})
                            #done = False
                            set(done FALSE)
                            #break
                            break()
                        endif()
                    endforeach()
                endif()
                #if done:
                if(${done})
                    #lowlink[v] = preorder[v]
                    map_get(preorder ${v} preorder_of_v)
                    map_set(lowlink ${v} ${preorder_of_v})
                    message("strongly_connected_components: preorder_of_v = ${preorder_of_v}")
                    #for w in G[v]:
                    string(JSON len LENGTH "${graph_json}" "${v}")
                    if(${len} GREATER 0)
                        math(EXPR last_w_idx "${len}-1")
                        foreach(w_idx RANGE 0 ${last_w_idx} 1)
                            string(JSON w GET "${graph_json}" "${v}" ${w_idx})
                            message("strongly_connected_components: v = ${v}")
                            message("strongly_connected_components: w = ${w}")
                            #if w not in scc_found:
                            map_get(scc_found "${w}" w_in_scc_found)
                            if(NOT scc_found)
                                #if preorder[w] > preorder[v]:
                                map_get(lowlink ${w} lowlink_w)
                                message("strongly_connected_components: lowlink_w = ${lowlink_w}")
                                if(lowlink_w) # wrong???
                                    message("strongly_connected_components: lowlink_w is set. lowlink_w=${lowlink_w}, w=${w}, lowlink_v=${lowlink_v}, v=${v}")
                                else()
                                    message("strongly_connected_components: lowlink_w is unset. lowlink_w=${lowlink_w}, w=${w}, lowlink_v=${lowlink_v}, v=${v}")
                                endif()

                                #if(lowlink_w) # FIXME wrong???
                                # extra edge
                                # lowlink_w is empty when the target-node of the edge
                                # is not a source-node in the graph

                                message("strongly_connected_components: lowlink_w is set. lowlink_w=${lowlink_w}, w=${w}, lowlink_v=${lowlink_v}, v=${v}")
                                map_get(preorder ${v} preorder_v)
                                map_get(preorder ${w} preorder_w)
                                map_get(lowlink ${v} lowlink_v)
                                #[[
                                message("strongly_connected_components: preorder_v = ${preorder_v}")
                                message("strongly_connected_components: preorder_w = ${preorder_w}")
                                message("strongly_connected_components: lowlink_v = ${lowlink_v}")
                                message("strongly_connected_components: lowlink_w = ${lowlink_w}")
                                ]]
                                if(${preorder_w} GREATER ${preorder_v})
                                    #lowlink[v] = min([lowlink[v], lowlink[w]])
                                    if(${lowlink_w} LESS ${lowlink_v})
                                        set(lowlink_v ${lowlink_w})
                                    endif()
                                    map_set(lowlink ${v} ${lowlink_v})
                                #else:
                                else()
                                    #lowlink[v] = min([lowlink[v], preorder[w]])
                                    if(${preorder_w} LESS ${lowlink_v})
                                        set(lowlink_v ${preorder_w})
                                    endif()
                                    map_set(lowlink ${v} ${lowlink_v})
                                endif()
                            endif()
                        endforeach()
                    endif()
                    #queue.pop()
                    list(POP_BACK queue)
                    #if lowlink[v] == preorder[v]:
                    message("strongly_connected_components: v = ${v}")
                    map_get(preorder ${v} preorder_v)
                    map_get(lowlink ${v} lowlink_v)
                    if(${lowlink_v} EQUAL ${preorder_v})
                        #scc = {v} # == set(v)
                        set(scc ${v})
                        message("strongly_connected_components: scc = ${scc}")
                        #while scc_queue and preorder[scc_queue[-1]] > preorder[v]:
                        deque_size(scc_queue scc_queue_size)
                        deque_back(scc_queue scc_queue_last) # scc_queue[-1]
                        map_get(preorder ${scc_queue_last} preorder_scc_queue_last) # preorder[scc_queue[-1]]
                        while(${scc_queue_size} GREATER 0 AND ${preorder_scc_queue_last} GREATER ${preorder_v})
                            #k = scc_queue.pop()
                            message("strongly_connected_components: scc_queue_size = ${scc_queue_size}")
                            message("strongly_connected_components: scc_queue_last = ${scc_queue_last}")
                            message("strongly_connected_components: scc_queue:")
                            deque_print(scc_queue)
                            deque_pop_back(scc_queue k)
                            #scc.add(k)
                            message("strongly_connected_components: scc append: k = ${k}")
                            list(APPEND scc ${k})
                            deque_size(scc_queue scc_queue_size)
                            deque_back(scc_queue scc_queue_last) # scc_queue[-1]
                        endwhile()
                        #scc_found.update(scc)
                        foreach(k ${scc})
                            map_set(scc_found ${k} TRUE)
                        endforeach()
                        #yield scc
                        #message("strongly_connected_components: result_deque_name = ${result_deque_name}")
                        #message("strongly_connected_components: yield: scc = ${scc}")
                        #deque_push_back(${result_deque_name} ${scc})
                        deque_push_back(result_deque "${scc}")
                    #else:
                    else()
                        #scc_queue.append(v)
                        deque_push_back(scc_queue ${v})
                    endif()
                endif()
                list(LENGTH queue queue_length)
            endwhile()
        endif()
    endforeach()
    # debug
    message("strongly_connected_components: result_deque:")
    deque_print(result_deque)
    # return
    # TODO return json
    deque_to_json(result_deque result_json)
    set(${result_json_name} ${result_json} PARENT_SCOPE)

    if(FALSE)
        set(__deque__${result_deque_name}__size ${__deque__result_deque__size} PARENT_SCOPE)
        set(__deque__${result_deque_name}__start ${__deque__result_deque__start} PARENT_SCOPE)
        set(__deque__${result_deque_name}__end ${__deque__result_deque__end} PARENT_SCOPE)
        deque_keys(result_deque keys)
        foreach(key ${keys})
            deque_get(result_deque ${key} value)
            #message("strongly_connected_components: result[${key}] = ${value}")
            set(__deque__${result_deque_name}__value__${key} "${value}" PARENT_SCOPE)
        endforeach()
    endif()
endfunction()


#[====[ loop json object
    set(json_str [[ {"a": 1, "b": 2} ]])

    string(JSON len LENGTH ${json_str})
    math(EXPR last_idx "${len}-1")
    foreach(idx RANGE ${last_idx})
        string(JSON key MEMBER ${json_str} ${idx})
        string(JSON val GET ${json_str} ${key})
        message("json.${key} = ${val}")
    endforeach()
]====]



function(simple_cycles graph_json result_json_name)
    # Find simple cycles (elementary circuits) of a directed graph.
    # https://networkx.org/documentation/stable/_modules/networkx/algorithms/cycles.html#simple_cycles
    set(${result_json_name} "[]")

    # TODO
    #def _unblock(thisnode, blocked, B):
    #    stack = {thisnode}
    #    while stack:
    #        node = stack.pop()
    #        if node in blocked:
    #            blocked.remove(node)
    #            stack.update(B[node])
    #            B[node].clear()

    # Johnson's algorithm requires some ordering of the nodes.
    # We assign the arbitrary ordering given by the strongly connected comps
    # There is no need to track the ordering as each node removed as processed.
    # Also we save the actual graph so we can mutate it. We only take the
    # edges because we do not want to copy edge and node attributes here.

    #subG = type(G)(G.edges())
    # this is a noop?

    #sccs = [scc for scc in nx.strongly_connected_components(subG) if len(scc) > 1]
    # all_sccs_json: all sccs
    # sccs_json: sccs with length > 1
    message("simple_cycles: graph_json = ${graph_json}")
    strongly_connected_components("${graph_json}" all_sccs_json)
    message("simple_cycles: all_sccs_json = ${all_sccs_json}")
    set(sccs_json "[]")
    string(JSON length LENGTH "${all_sccs_json}")
    math(EXPR last_idx "${length}-1")
    foreach(idx RANGE 0 ${last_idx} 1)
        string(JSON scc GET "${all_sccs_json}" ${idx})
        list(LENGTH scc scc_length)
        if(scc_length GREATER 1)
            string(JSON sccs_json_length LENGTH "${sccs_json}")
            quote_string(scc "${scc}")
            string(JSON sccs_json SET "${sccs_json}" ${sccs_json_length} "${scc}")
        endif()
    endforeach()
    message("simple_cycles: sccs_json = ${sccs_json}")

    #message(FATAL_ERROR todo)

    if("${sccs_json}" STREQUAL "[]")
        return()
    endif()

    # TODO
    #[[
    # Johnson's algorithm exclude self cycle edges like (v, v)
    # To be backward compatible, we record those cycles in advance
    # and then remove from subG
    for v in subG:
        if subG.has_edge(v, v):
            yield [v]
            subG.remove_edge(v, v)
    ]]

    #while sccs:
    # TODO while -> foreach
    string(JSON sccs_length LENGTH "${sccs_json}")
    while(sccs_length GREATER 0)
        #scc = sccs.pop()
        math(EXPR last_idx "${sccs_length}-1")
        string(JSON scc GET "${sccs_json}" ${last_idx})
        string(JSON sccs_json REMOVE "${sccs_json}" ${last_idx})
        message("scc = ${scc}")

        #sccG = subG.subgraph(scc)
        # TODO
        set(scc_graph_json "{}")
        # loop keys of scc
        #string(JSON scc_graph_json SET "${scc_graph_json}" "${key}" "${value}")

        foreach(scc_node ${scc})
            message("scc_node = ${scc_node}")
            string(JSON scc_graph_json SET "${scc_graph_json}" "${scc_node}" "[]")
            set(edge_idx 0)
            # get node from graph
            # filter edges of node, add to scc_graph_json
            # get intersection of lists: scc n all_edges
            string(JSON all_edges_json GET "${graph_json}" "${scc_node}")
            message("all_edges_json = ${all_edges_json}")
            foreach(scc_edge ${scc})
                message("scc_edge = ${scc_edge}")
                #[[ all_edges is semicolon list
                list(FIND all_edges "${scc_edge}" found_idx)
                # TODO find scc_edge in all_edges_json
                message("found_idx = ${found_idx}")
                if(NOT found_idx EQUAL -1)
                    # found in both
                    quote_string(value "${scc_edge}")
                    string(JSON scc_graph_json SET "${scc_graph_json}" "${scc_node}" ${edge_idx} "${value}")
                    math(EXPR edge_idx "${edge_idx}+1")
                endif()
                ]]
                # all_edges is json list
                # loop all edges of scc_node
                string(JSON len LENGTH "${graph_json}" "${scc_node}")
                if(len GREATER 0)
                    math(EXPR last_idx "${len}-1")
                    foreach(idx RANGE 0 ${last_idx} 1)
                        # get value
                        string(JSON graph_edge GET "${graph_json}" "${scc_node}" ${idx})
                        if(graph_edge STREQUAL scc_edge)
                            # found in both
                            quote_string(value "${scc_edge}")
                            string(JSON scc_graph_json SET "${scc_graph_json}" "${scc_node}" ${edge_idx} "${value}")
                            math(EXPR edge_idx "${edge_idx}+1")
                        endif()
                    endforeach()
                endif()

            endforeach()
        endforeach()
        message("scc_graph_json = ${scc_graph_json}")

        #message(FATAL_ERROR todo)

        # order of scc determines ordering of nodes
        #startnode = scc.pop()
        list(POP_BACK scc startnode)

        # Processing node runs "circuit" routine from recursive version
        #path = [startnode]
        #set(path ${startnode}) # TODO list -> json list
        #set(path ${startnode}) # TODO list -> json list
        set(path_json "[]")
        quote_string(value "${startnode}")
        string(JSON path_json SET "${path_json}" 0 "${value}")

        set(result_json "[]")

        #blocked = set()  # vertex: blocked from search?
        #blocked.add(startnode)
        #set(blocked "${startnode}")
        map_create(blocked_map)
        map_set(blocked_map "${startnode}" TRUE)

        #closed = set()  # nodes involved in a cycle
        #set(closed "")
        map_create(closed_map)

        #B = defaultdict(set)  # graph portions that yield no elementary circuit
        # TODO ???
        map_create(Blocked_map)

        #stack = [(startnode, list(sccG[startnode]))]  # sccG gives comp nbrs
        # tuples: (node, neighbors)
        set(stack_json "[]")
        string(JSON stack_json SET "${stack_json}" 0 "[]")
        quote_string(startnode_json "${startnode}")
        string(JSON stack_json SET "${stack_json}" 0 0 "${startnode_json}")
        string(JSON nbrs GET "${scc_graph_json}" "${startnode}")
        string(JSON stack_json SET "${stack_json}" 0 1 "${nbrs}")

        #while stack:
        string(JSON stack_length LENGTH "${stack_json}")
        while(stack_length GREATER 0)

            #thisnode, nbrs = stack[-1]
            math(EXPR stack_last_idx "${stack_length}-1")
            string(JSON thisnode GET "${stack_json}" ${stack_last_idx} 0)
            string(JSON nbrs_json GET "${stack_json}" ${stack_last_idx} 1)
            message("thisnode = ${thisnode}")
            message("nbrs = ${nbrs_json}")
            string(JSON nbrs_len LENGTH "${nbrs_json}")
            #if nbrs:
            if(nbrs_len GREATER 0)

                # TODO generalize: transpile python to cmake
                # cmake is similar to bash: everything is a string
                # goal: less code to maintain
                # python code is MUCH cleaner than cmake code
                # https://github.com/Luavis/sherlock.py
                # https://github.com/py2many/py2many

                #nextnode = nbrs.pop()
                math(EXPR nbrs_last_idx "${nbrs_len}-1")
                string(JSON nextnode GET "${nbrs_json}" ${nbrs_last_idx})
                string(JSON nbrs_json REMOVE "${nbrs_json}" ${nbrs_last_idx})

                if(nextnode STREQUAL startnode)
                    #yield path[:]
                    string(JSON result_length LENGTH "${result_json}")
                    string(JSON result_json SET "${result_json}" ${result_length} "${path_json}")
                    #closed.update(path)
                    # loop path, add nodes to closed
                    string(JSON path_length LENGTH "${path_json}")
                    if(path_length GREATER 0)
                        math(EXPR last_path_idx "${path_length}-1")
                        foreach(path_idx RANGE ${last_path_idx})
                            string(JSON path_node GET "${path_json}" ${path_idx})
                            map_set(closed_map "${path_node}" TRUE)
                        endforeach()
                    endif()
                    #message("Found a cycle: path=${path} closed=${closed}")
                else()
                    map_get(blocked_map "${nextnode}" nextnode_in_blocked_map)
                    #if nextnode not in blocked:
                    if(NOT nextnode_in_blocked_map)
                        #path.append(nextnode)
                        string(JSON path_length LENGTH "${path_json}")
                        string(JSON path_json SET "${path_json}" ${path_length} "${nextnode}")
                        #stack.append((nextnode, list(sccG[nextnode])))
                        string(JSON stack_length LENGTH "${stack_json}")
                        string(JSON stack_json SET "${stack_json}" ${stack_length} "[]")
                        # TODO what is nextnode
                        quote_string(nextnode_json "${nextnode}")
                        string(JSON stack_json SET "${stack_json}" ${stack_length} 0 "${nextnode_json}")
                        string(JSON nbrs GET "${scc_graph_json}" "${nextnode}")
                        string(JSON stack_json SET "${stack_json}" ${stack_length} 1 "${nbrs}")

                        #closed.discard(nextnode)
                        map_remove(closed_map "${nextnode}")

                        #blocked.add(nextnode)
                        map_set(blocked_map "${nextnode}" TRUE)

                        #continue
                        continue()
                    endif()
                endif()
            endif()

            # done with nextnode... look for more neighbors
            #if not nbrs:  # no more nbrs
            string(JSON nbrs_len LENGTH "${nbrs_json}")
            if(nbrs_len GREATER 0)

                #if thisnode in closed:
                map_get(closed_map "${thisnode}" thisnode_in_closed)
                if(thisnode_in_closed)
                    # TODO python 2 cmake
                    #def _unblock(thisnode, blocked, B):
                    #    stack = {thisnode}
                    #    while stack:
                    #        node = stack.pop()
                    #        if node in blocked:
                    #            blocked.remove(node)
                    #            stack.update(B[node])
                    #            B[node].clear()
                    #_unblock(thisnode, blocked, B)
                    set(unblock_stack_json "[]")
                    #stack = {thisnode}
                    string(JSON unblock_stack_json SET "${unblock_stack_json}" 0 "${thisnode}")
                    #while stack:
                    string(JSON unblock_stack_length LENGTH "${unblock_stack_json}")
                    while(unblock_stack_length GREATER 0)

                        #node = stack.pop()
                        math(EXPR unblock_stack_last_idx "${unblock_stack_length}-1")
                        string(JSON unblock_node GET "${unblock_stack_json}" ${unblock_stack_last_idx})
                        string(JSON unblock_stack_json REMOVE "${unblock_stack_json}" ${unblock_stack_last_idx})

                        #if node in blocked:
                        map_get(blocked_map "${unblock_node}" unblock_node_in_blocked)
                        if(unblock_node_in_blocked)
                            #blocked.remove(node)
                            map_remove(blocked_map "${unblock_node}")

                            #B = defaultdict(set)  # graph portions that yield no elementary circuit
                            # defaultdict:
                            # class collections.defaultdict(default_factory=None)
                            # same as dict, but returns default value for missing keys
                            #stack.update(B[node])
                            # TODO what is B

                            #B[node].clear()
                        endif()

                        # update loop counter
                        string(JSON unblock_stack_length LENGTH "${unblock_stack_json}")
                    endwhile()
                else()
                    # TODO python 2 cmake
                    for nbr in sccG[thisnode]:
                        if thisnode not in B[nbr]:
                            B[nbr].add(thisnode)
                endif()

                #stack.pop()
                string(JSON stack_length LENGTH "${stack_json}")
                math(EXPR stack_last_idx "${stack_length}-1")
                string(JSON stack_json REMOVE "${stack_json}" ${stack_last_idx})

                ##assert path[-1] == thisnode # debug

                #path.pop()
                string(JSON path_length LENGTH "${path_json}")
                math(EXPR path_last_idx "${path_length}-1")
                string(JSON path_json REMOVE "${path_json}" ${path_last_idx})
            endif()

            # update the loop counter
            string(JSON stack_length LENGTH "${stack_json}")
        endwhile()


        # TODO python 2 cmake

        # done processing this node
        H = subG.subgraph(scc)  # make smaller to avoid work in SCC routine
        sccs.extend(scc for scc in nx.strongly_connected_components(H) if len(scc) > 1)



        # update the loop counter
        math(EXPR sccs_length "${sccs_length}-1")
    endwhile()
endfunction()

function(topo_sort graph_json result_name)
    map_size(${graph_json} size)
    message("graph with ${size} nodes:")
    map_print(${graph_json})
    set(${result_name} todo;todo;todo PARENT_SCOPE)
endfunction()

map_create(graph_map)
map_set(graph_map 0 "1") # 1;2;3
map_set(graph_map 1 "2") # 2;3
map_set(graph_map 2 "0") # loop
#map_set(graph_map 2 "") # no loop
message("graph_map:")
map_print(graph_map)

#deque_create(scc_deque)
set(graph_json [[
    {
        "0": ["1"],
        "1": ["2"],
        "2": ["0"],
    }
]])
#        "2": [], # no cycle
#        "2": ["0"], # cycle

#        "0": ["1", "10", "11", "12"], # extra edges -> TODO implement

strongly_connected_components("${graph_json}" sccs_json)
message("sccs_json: ${sccs_json}")
#deque_print(scc_deque)


simple_cycles("${graph_json}" simple_cycles_json)
message("simple_cycles_json: ${simple_cycles_json}")


message(FATAL_ERROR todo)

topo_sort(graph_map sorted_nodes)
message("sorted_nodes = ${sorted_nodes}")

from modules.

Related Issues (1)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.