1- # graph.omc — graph algorithms: BFS, DFS, Dijkstra, toposort, PageRank, SCC
1+ # graph.omc — graph algorithms: BFS, DFS, Dijkstra, toposort, PageRank, connected components
22
33# ── Graph construction ──────────────────────────────────────────────────────
44
@@ -13,29 +13,29 @@ fn graph_new_undirected() {
1313
1414# graph_add_node(g, id, data?)
1515fn graph_add_node(g, id, data) {
16- h key = to_str (id)
16+ h key = to_string (id)
1717 g["nodes"][key] = data
18- if not dict_has(g["edges"], key) {
18+ if ! dict_has(g["edges"], key) {
1919 g["edges"][key] = []
2020 }
2121}
2222
2323# graph_add_edge(g, from, to, weight?)
2424fn graph_add_edge(g, from_node, to_node, weight) {
25- h f = to_str (from_node)
26- h t = to_str (to_node)
25+ h f = to_string (from_node)
26+ h t = to_string (to_node)
2727 h w = weight
2828 if w == null { w = 1 }
29- if not dict_has(g["edges"], f) { g["edges"][f] = [] }
30- if not dict_has(g["edges"], t) { g["edges"][t] = [] }
29+ if ! dict_has(g["edges"], f) { g["edges"][f] = [] }
30+ if ! dict_has(g["edges"], t) { g["edges"][t] = [] }
3131 arr_push(g["edges"][f], {to: t, weight: w})
32- if not g["directed"] {
32+ if ! g["directed"] {
3333 arr_push(g["edges"][t], {to: f, weight: w})
3434 }
3535}
3636
3737fn graph_neighbors(g, node) {
38- h key = to_str (node)
38+ h key = to_string (node)
3939 if dict_has(g["edges"], key) { return g["edges"][key] }
4040 return []
4141}
@@ -45,9 +45,9 @@ fn graph_neighbors(g, node) {
4545# bfs(g, start) → [node_ids in visit order]
4646fn bfs(g, start) {
4747 h visited = {}
48- h queue = [to_str (start)]
48+ h queue = [to_string (start)]
4949 h order = []
50- visited[to_str (start)] = true
50+ visited[to_string (start)] = true
5151 while arr_len(queue) > 0 {
5252 h current = queue[0]
5353 queue = arr_slice(queue, 1, arr_len(queue))
@@ -56,7 +56,7 @@ fn bfs(g, start) {
5656 h i = 0
5757 while i < arr_len(neighbors) {
5858 h nb = neighbors[i]["to"]
59- if not dict_has(visited, nb) {
59+ if ! dict_has(visited, nb) {
6060 visited[nb] = true
6161 arr_push(queue, nb)
6262 }
@@ -66,13 +66,13 @@ fn bfs(g, start) {
6666 return order
6767}
6868
69- # bfs_path(g, start, end ) → [path] or null
69+ # bfs_path(g, start, goal ) → [path] or null
7070fn bfs_path(g, start, goal) {
7171 h visited = {}
7272 h parent = {}
73- h queue = [to_str (start)]
74- h s = to_str (start)
75- h e = to_str (goal)
73+ h queue = [to_string (start)]
74+ h s = to_string (start)
75+ h e = to_string (goal)
7676 visited[s] = true
7777 parent[s] = null
7878 while arr_len(queue) > 0 {
@@ -91,7 +91,7 @@ fn bfs_path(g, start, goal) {
9191 h i = 0
9292 while i < arr_len(neighbors) {
9393 h nb = neighbors[i]["to"]
94- if not dict_has(visited, nb) {
94+ if ! dict_has(visited, nb) {
9595 visited[nb] = true
9696 parent[nb] = current
9797 arr_push(queue, nb)
@@ -111,7 +111,7 @@ fn _dfs_visit(g, node, visited, order) {
111111 h i = 0
112112 while i < arr_len(neighbors) {
113113 h nb = neighbors[i]["to"]
114- if not dict_has(visited, nb) {
114+ if ! dict_has(visited, nb) {
115115 _dfs_visit(g, nb, visited, order)
116116 }
117117 i = i + 1
@@ -122,39 +122,39 @@ fn _dfs_visit(g, node, visited, order) {
122122fn dfs(g, start) {
123123 h visited = {}
124124 h order = []
125- _dfs_visit(g, to_str (start), visited, order)
125+ _dfs_visit(g, to_string (start), visited, order)
126126 return order
127127}
128128
129129# ── Topological sort ─────────────────────────────────────────────────────────
130130
131- fn _topo_visit(g, node, visited, stack, cyclic ) {
132- if dict_has(cyclic , node) { return }
133- if dict_has(visited, node) { return }
134- cyclic [node] = true
131+ fn _topo_visit(g, node, visited, stack, in_progress ) {
132+ if dict_has(in_progress , node) { return null; }
133+ if dict_has(visited, node) { return null; }
134+ in_progress [node] = true
135135 h neighbors = graph_neighbors(g, node)
136136 h i = 0
137137 while i < arr_len(neighbors) {
138138 h nb = neighbors[i]["to"]
139- _topo_visit(g, nb, visited, stack, cyclic )
139+ _topo_visit(g, nb, visited, stack, in_progress )
140140 i = i + 1
141141 }
142- dict_delete(cyclic , node)
142+ dict_delete(in_progress , node)
143143 visited[node] = true
144144 arr_push(stack, node)
145145}
146146
147- # toposort(g) → [node_ids] in topological order, or null if cycle
147+ # toposort(g) → [node_ids] in topological order
148148fn toposort(g) {
149149 h visited = {}
150150 h stack = []
151- h cyclic = {}
151+ h in_progress = {}
152152 h nodes = dict_keys(g["nodes"])
153153 h i = 0
154154 while i < arr_len(nodes) {
155155 h n = nodes[i]
156- if not dict_has(visited, n) {
157- _topo_visit(g, n, visited, stack, cyclic )
156+ if ! dict_has(visited, n) {
157+ _topo_visit(g, n, visited, stack, in_progress )
158158 }
159159 i = i + 1
160160 }
@@ -165,7 +165,7 @@ fn toposort(g) {
165165
166166# dijkstra(g, start) → {dist: {node: dist}, prev: {node: prev}}
167167fn dijkstra(g, start) {
168- h s = to_str (start)
168+ h s = to_string (start)
169169 h dist = {}
170170 h prev = {}
171171 h unvisited = []
@@ -180,7 +180,6 @@ fn dijkstra(g, start) {
180180 }
181181 dist[s] = 0
182182 while arr_len(unvisited) > 0 {
183- # find min dist unvisited node
184183 h u = null
185184 h min_d = 999999999
186185 h j = 0
@@ -193,7 +192,6 @@ fn dijkstra(g, start) {
193192 j = j + 1
194193 }
195194 if u == null { break }
196- # remove u from unvisited
197195 h new_unvisited = []
198196 h k = 0
199197 while k < arr_len(unvisited) {
@@ -202,27 +200,29 @@ fn dijkstra(g, start) {
202200 }
203201 unvisited = new_unvisited
204202 h neighbors = graph_neighbors(g, u)
205- h n = 0
206- while n < arr_len(neighbors) {
207- h edge = neighbors[n ]
203+ h n2 = 0
204+ while n2 < arr_len(neighbors) {
205+ h edge = neighbors[n2 ]
208206 h v = edge["to"]
209- h alt = dist[u] + edge["weight"]
210- if alt < dist[v] {
211- dist[v] = alt
212- prev[v] = u
207+ if dict_has(dist, v) {
208+ h alt = dist[u] + edge["weight"]
209+ if alt < dist[v] {
210+ dist[v] = alt
211+ prev[v] = u
212+ }
213213 }
214- n = n + 1
214+ n2 = n2 + 1
215215 }
216216 }
217217 return {dist: dist, prev: prev}
218218}
219219
220- # shortest_path(g, start, end ) → [path] or null
220+ # shortest_path(g, start, goal ) → [path] or null
221221fn shortest_path(g, start, goal) {
222222 h result = dijkstra(g, start)
223223 h prev = result["prev"]
224- h e = to_str (goal)
225- if prev[e] == null and e != to_str (start) { return null }
224+ h e = to_string (goal)
225+ if prev[e] == null and e != to_string (start) { return null; }
226226 h path = []
227227 h node = e
228228 while node != null {
@@ -290,7 +290,7 @@ fn connected_components(g) {
290290 h i = 0
291291 while i < arr_len(nodes) {
292292 h n = nodes[i]
293- if not dict_has(visited, n) {
293+ if ! dict_has(visited, n) {
294294 h order = []
295295 _dfs_visit(g, n, visited, order)
296296 arr_push(components, order)
@@ -300,33 +300,36 @@ fn connected_components(g) {
300300 return components
301301}
302302
303+ # ── Cycle detection ──────────────────────────────────────────────────────────
304+
305+ fn _cycle_check(g, node, visited, rec_stack) {
306+ visited[node] = true
307+ rec_stack[node] = true
308+ h neighbors = graph_neighbors(g, node)
309+ h i = 0
310+ while i < arr_len(neighbors) {
311+ h nb = neighbors[i]["to"]
312+ if !dict_has(visited, nb) {
313+ if _cycle_check(g, nb, visited, rec_stack) { return true }
314+ } elif dict_has(rec_stack, nb) {
315+ return true
316+ }
317+ i = i + 1
318+ }
319+ dict_delete(rec_stack, node)
320+ return false
321+ }
322+
303323# has_cycle(g) → bool
304324fn has_cycle(g) {
305325 h visited = {}
306326 h rec_stack = {}
307- fn _cycle_check(node) {
308- visited[node] = true
309- rec_stack[node] = true
310- h neighbors = graph_neighbors(g, node)
311- h i = 0
312- while i < arr_len(neighbors) {
313- h nb = neighbors[i]["to"]
314- if not dict_has(visited, nb) {
315- if _cycle_check(nb) { return true }
316- } else if dict_has(rec_stack, nb) {
317- return true
318- }
319- i = i + 1
320- }
321- dict_delete(rec_stack, node)
322- return false
323- }
324327 h nodes = dict_keys(g["nodes"])
325328 h i = 0
326329 while i < arr_len(nodes) {
327330 h n = nodes[i]
328- if not dict_has(visited, n) {
329- if _cycle_check(n ) { return true }
331+ if ! dict_has(visited, n) {
332+ if _cycle_check(g, n, visited, rec_stack ) { return true }
330333 }
331334 i = i + 1
332335 }
0 commit comments