Skip to content

Commit 54606d4

Browse files
authored
Add Depth First Search in M4 (#5125)
1 parent 6e007d1 commit 54606d4

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed

archive/m/m4/depth-first-search.m4

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
divert(-1)
2+
define(`show_usage',
3+
`Usage: please provide a tree in an adjacency matrix form 'dnl
4+
`("0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0") 'dnl
5+
`together with a list of vertex values ("1, 3, 5, 2, 4") and the integer to find ("4")
6+
m4exit(`1')')
7+
8+
dnl Reference: https://www.gnu.org/software/m4/manual/m4.html#index-array
9+
dnl array_get(varname, idx)
10+
define(`array_get', `defn(format(``%s[%s]'', `$1', `$2'))')
11+
12+
dnl array_set(varname, idx, value)
13+
define(`array_set', `define(format(``%s[%s]'', `$1', `$2'), `$3')')
14+
15+
dnl 2D versions of "array_get" and "array_set"
16+
dnl array2_get(varname, idx1, idx2)
17+
define(`array2_get', `defn(format(``%s[%s][%s]'', `$1', `$2', `$3'))')
18+
19+
dnl array2_set(varname, idx1, idx2, value)
20+
define(`array2_set', `define(format(``%s[%s][%s]'', `$1', `$2', `$3'), `$4')')
21+
22+
dnl is_valid(n)
23+
define(`is_valid', `eval(regexp(`$1', `^\s*-?[0-9]+\s*$') >= 0)')
24+
25+
dnl parse_int_list(varname, args):
26+
dnl varname["length"] = 0
27+
dnl foreach arg in args:
28+
dnl if not is_valid(arg):
29+
dnl Return 0
30+
dnl varname[varname["length"]] = arg
31+
dnl varname["length"] = varname["length"] + 1
32+
dnl Return 1
33+
define(`parse_int_list',
34+
`array_set(`$1', `length', 0)dnl
35+
_parse_int_list(`$1', $2)'dnl
36+
)
37+
define(`_parse_int_list',
38+
`ifelse(is_valid(`$2'), 0, `0',
39+
`array_set(`$1', array_get(`$1', `length'), `$2')dnl
40+
array_set(`$1', `length', incr(array_get(`$1', `length')))dnl
41+
ifelse(eval($# > 2), 1, `_parse_int_list(`$1', shift(shift($@)))', `1')'dnl
42+
)'dnl
43+
)
44+
45+
dnl create_graph(conn_mtx_varname, vert_varname, graph_varname):
46+
dnl // Create nodes
47+
dnl graph_varname["length"] = vert_varname["length"]
48+
dnl for i = 0 to conn_vert["length"] - 1:
49+
dnl graph_varname[i]["value"] = vert_varname[i]
50+
dnl graph_varname[i]["length"] = 0
51+
dnl
52+
dnl // Connect children to nodes
53+
dnl idx = 0
54+
dnl for i = 0 to vert_varname["length"] - 1:
55+
dnl for j = 0 to vert_varname["length"] - 1:
56+
dnl if conn_mtx_varname[idx] > 0:
57+
dnl graph_varname[i][graph_varname[i]["length"]] = j
58+
dnl graph_varname[i]["length"] = graph_varname[i]["length"] + 1
59+
dnl
60+
dnl idx = idx + 1
61+
define(`create_graph',
62+
`array_set(`$3', `length', array_get(`$2', `length'))dnl
63+
_create_nodes(`$2', `$3', 0)dnl
64+
_connect_children(`$1', `$2', `$3', 0, 0)dnl
65+
'dnl
66+
)
67+
68+
dnl vert_varname=$1, graph_varname=$2, i=$3
69+
define(`_create_nodes',
70+
`ifelse(eval($3 < array_get(`$1', `length')), 1,
71+
`array2_set(`$2', `$3', `value', array_get(`$1', `$3'))dnl
72+
array2_set(`$2', `$3', `length', 0)dnl
73+
_create_nodes(`$1', `$2', incr($3))'dnl
74+
)'dnl
75+
)
76+
77+
dnl conn_mtx_varname=$1, vert_varname=$2, graph_varname=$3, i=$4, idx=$5
78+
define(`_connect_children',
79+
`ifelse(eval($4 < array_get(`$2', `length')), 1,
80+
`_connect_children_inner(`$1', `$2', `$3', `$4', `$5', 0)dnl
81+
_connect_children(`$1', `$2', `$3', incr($4), eval($5 + array_get(`$2', `length')))'dnl
82+
)'dnl
83+
)
84+
85+
dnl conn_mtx_varname=$1, vert_varname=$2, graph_varname=$3, i=$4, idx=$5, j=$6
86+
define(`_connect_children_inner',
87+
`ifelse(eval($6 < array_get(`$2', `length')), 1,
88+
`ifelse(eval(array_get(`$1', $5) + 0 > 0), 1,
89+
`array2_set(`$3', `$4', array2_get(`$3', `$4', `length'), `$6')dnl
90+
array2_set(`$3', `$4', `length', incr(array2_get(`$3', `$4', `length')))'dnl
91+
)dnl
92+
_connect_children_inner(`$1', `$2', `$3', `$4', incr($5), incr($6))'dnl
93+
)'dnl
94+
)
95+
96+
dnl depth_first_search(graph_varname, target):
97+
dnl // Indicate nothing visited
98+
dnl visited = ""
99+
dnl
100+
dnl // Indicate node not found
101+
dnl found_node = -1
102+
dnl
103+
dnl // Start depth first search at root (node 0)
104+
dnl depth_first_search_rec(graph_varname, target, 0)
105+
dnl
106+
dnl return found_node
107+
define(`depth_first_search',
108+
`pushdef(`visited', `')dnl
109+
pushdef(`found_node', `-1')dnl
110+
depth_first_search_rec(`$1', `$2', 0)dnl
111+
popdef(`visited')dnl
112+
found_node`'dnl
113+
popdef(`found_node')'dnl
114+
)
115+
116+
dnl depth_first_search_rec(graph_varname, target, node):
117+
dnl // Note: visited is a string that looks like this: "|<node1>||<node2>|..."
118+
dnl // Since M4 does not have anything like a set, this is the easiest way to
119+
dnl // represent it, and the "index" function can be used to see if a node has
120+
dnl // been visited (negative if not visited)
121+
dnl
122+
dnl // If target value found, indicate node found
123+
dnl if graph_varname[node]["value"] == target:
124+
dnl found_node = node
125+
dnl else:
126+
dnl // Indicate node is visited
127+
dnl visited = visited + "|" + node + "|"
128+
dnl
129+
dnl // For each child node
130+
dnl for i = 0 to graph_varname[node]["length"] - 1
131+
dnl // If child node not visited
132+
dnl if "|" + graph_varname[node][i] + "|" not in visited:
133+
dnl // Recursively check this child node
134+
dnl depth_first_search_rec(graph_varname, target, graph_varname[node][i])
135+
dnl
136+
dnl // Exit loop if node found
137+
dnl if found_node >= 0:
138+
dnl break
139+
define(`depth_first_search_rec',
140+
`ifelse(eval(array2_get(`$1', `$3', `value') == $2), 1,
141+
`define(`found_node', `$3')',
142+
`define(`visited', visited`|$3|')dnl
143+
_depth_first_search_rec(`$1', `$2', `$3', 0)'dnl
144+
)'dnl
145+
)
146+
147+
dnl graph_varname=$1, target=$2, node=$3, i=$4
148+
define(`_depth_first_search_rec',
149+
`ifelse(eval(found_node < 0 && $4 < array2_get(`$1', `$3', `length')), 1,
150+
`ifelse(eval(index(visited, `|'array2_get(`$1', `$3', `$4')`|') < 0), 1,
151+
`depth_first_search_rec(`$1', `$2', array2_get(`$1', `$3', `$4'))'dnl
152+
)dnl
153+
_depth_first_search_rec(`$1', `$2', `$3', incr($4))'dnl
154+
)'dnl
155+
)
156+
157+
divert(0)dnl
158+
ifelse(eval(ARGC < 3 ||
159+
!parse_int_list(`conn_mtx', ARGV1) ||
160+
!parse_int_list(`vert', ARGV2) ||
161+
!is_valid(ARGV3)
162+
), 1, `show_usage()')dnl
163+
create_graph(`conn_mtx', `vert', `graph')dnl
164+
define(`node', depth_first_search(`graph', ARGV3))dnl
165+
ifelse(eval(node >= 0), 1, `true', `false')

0 commit comments

Comments
 (0)