Skip to content

Commit cd5c5a3

Browse files
authored
Add Job Sequencing in M4 (#5255)
1 parent f816f34 commit cd5c5a3

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed

archive/m/m4/job-sequencing.m4

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
divert(-1)
2+
define(`show_usage',
3+
`Usage: please provide a list of profits and a list of deadlines
4+
m4exit(`1')')
5+
6+
dnl Reference: https://www.gnu.org/software/m4/manual/m4.html#index-array
7+
dnl array_get(var_name, idx)
8+
define(`array_get', `defn(format(``%s[%s]'', `$1', `$2'))')
9+
10+
dnl array_set(var_name, idx, value)
11+
define(`array_set', `define(format(``%s[%s]'', `$1', `$2'), `$3')')
12+
13+
dnl 2D versions of "array_get" and "array_set"
14+
dnl array2_get(varname, idx1, idx2)
15+
define(`array2_get', `defn(format(``%s[%s][%s]'', `$1', `$2', `$3'))')
16+
17+
dnl array2_set(varname, idx1, idx2, value)
18+
define(`array2_set', `define(format(``%s[%s][%s]'', `$1', `$2', `$3'), `$4')')
19+
20+
dnl array2_swap(varname, idx1, idx2, common_idx):
21+
dnl t = varname[idx1][common_idx]
22+
dnl varname[idx1][common_idx] = varname[idx2][common_idx]
23+
dnl varname[idx2][common_idx] = t
24+
define(`array2_swap',
25+
`pushdef(`t', array2_get(`$1', `$2', `$4'))dnl
26+
array2_set(`$1', `$2', `$4', array2_get(`$1', `$3', `$4'))dnl
27+
array2_set(`$1', `$3', `$4', t)dnl
28+
popdef(`t')'dnl
29+
)
30+
31+
dnl is_valid(n)
32+
define(`is_valid', `eval(regexp(`$1', `^\s*-?[0-9]+\s*$') >= 0)')
33+
34+
dnl parse_int_list(varname, args):
35+
dnl varname[length] = 0
36+
dnl foreach arg in args:
37+
dnl if not is_valid(arg):
38+
dnl Return 0
39+
dnl varname[varname[length]] = arg
40+
dnl varname[length] = varname[length] + 1
41+
dnl Return 1
42+
define(`parse_int_list',
43+
`array_set(`$1', `length', 0)dnl
44+
_parse_int_list(`$1', $2)'dnl
45+
)
46+
define(`_parse_int_list',
47+
`ifelse(is_valid(`$2'), 0, `0',
48+
`array_set(`$1', array_get(`$1', `length'), `$2')dnl
49+
array_set(`$1', `length', incr(array_get(`$1', `length')))dnl
50+
ifelse(eval($# > 2), 1, `_parse_int_list(`$1', shift(shift($@)))', `1')'dnl
51+
)'dnl
52+
)
53+
54+
dnl Reference: https://www.techiedelight.com/job-sequencing-problem-deadlines/
55+
dnl job_sequencing(profits_varname, deadlines_varname, jobs_varname, slots_varname):
56+
dnl // Set up job details and longest deadline
57+
dnl longest_deadline = 0
58+
dnl jobs_varname["length"] = profits_varname["length"]
59+
dnl for i = 0 to profits_varname["length"] - 1:
60+
dnl jobs_varname[i]["profit"] = profits_varname[i]
61+
dnl jobs_varname[i]["deadline"] = deadlines_varname[i]
62+
dnl if deadlines_varname[i] > longest_deadline:
63+
dnl longest_deadline = deadlines_varname[i]
64+
dnl
65+
dnl // Initialize job slots
66+
dnl slots_varname["length"] = longest_deadline + 1
67+
dnl for i = 0 to longest_deadline:
68+
dnl slots_varname[i] = -1
69+
dnl
70+
dnl // Sort jobs by profit then deadline
71+
dnl bubble_sort(jobs_varname)
72+
dnl
73+
dnl // For each job, see if there is available slot at or before the deadline
74+
dnl // if so, store this job in that slot
75+
dnl for i = 0 to jobs_varname["length"] - 1:
76+
dnl for j = jobs_varname[i]["deadline"] down to 1:
77+
dnl if slots_varname[j] < 0:
78+
dnl slots_varname[j] = i
79+
dnl break
80+
define(`job_sequencing',
81+
`pushdef(`longest_deadline', 0)dnl
82+
_set_jobs(`$1', `$2', `$3')dnl
83+
_init_slots(`$4')dnl
84+
bubble_sort(`$3')dnl
85+
_set_slots(`$3', `$4', 0)dnl
86+
popdef(`longest_deadline')dnl
87+
'dnl
88+
)
89+
90+
dnl profits_varname=$1, deadlines_varname=$2, jobs_varname=$3
91+
define(`_set_jobs',
92+
`array_set(`$3', `length', array_get(`$1', `length'))dnl
93+
_set_jobs_inner(`$1', `$2', `$3', 0)dnl
94+
'dnl
95+
)
96+
97+
dnl profits_varname=$1, deadlines_varname=$2, jobs_varname=$3, i=$4
98+
define(`_set_jobs_inner',
99+
`ifelse(eval($4 < array_get(`$1', `length')), 1,
100+
`array2_set(`$3', `$4', `profit', array_get(`$1', `$4'))dnl
101+
array2_set(`$3', `$4', `deadline', array_get(`$2', `$4'))dnl
102+
ifelse(eval(array_get(`$2', `$4') > longest_deadline), 1,
103+
`define(`longest_deadline', array_get(`$2', `$4'))')dnl
104+
_set_jobs_inner(`$1', `$2', `$3', incr($4))'dnl
105+
)'dnl
106+
)
107+
108+
dnl slots_varname=$1
109+
define(`_init_slots',
110+
`array_set(`$1', `length', incr(longest_deadline))dnl
111+
_init_slots_inner(`$1', 0)dnl
112+
'dnl
113+
)
114+
115+
dnl slots_varname=$1, i=$2
116+
define(`_init_slots_inner',
117+
`ifelse(eval($2 <= longest_deadline), 1,
118+
`array_set(`$1', `$2', -1)dnl
119+
_init_slots_inner(`$1', incr($2))'dnl
120+
)'dnl
121+
)
122+
123+
dnl jobs_varname=$1, slots_varname=$2, i=$3
124+
define(`_set_slots',
125+
`ifelse(eval($3 < array_get(`$1', `length')), 1,
126+
`_set_slots_inner(`$2', `$3', array2_get(`$1', `$3', `deadline'))dnl
127+
_set_slots(`$1', `$2', incr($3))'dnl
128+
)'dnl
129+
)
130+
131+
dnl slots_varname=$1, i=$2, j=$3
132+
define(`_set_slots_inner',
133+
`ifelse(eval($3 >= 1), 1,
134+
`ifelse(eval(array_get(`$1', `$3') < 0), 1,
135+
`array_set(`$1', `$3', `$2')',
136+
`_set_slots_inner(`$1', `$2', decr($3))'dnl
137+
)'dnl
138+
)'dnl
139+
)
140+
141+
dnl Reference: https://en.wikipedia.org/wiki/Bubble_sort#Pseudocode_implementation
142+
dnl bubble_sort(varname):
143+
dnl n = varname[length]
144+
dnl do:
145+
dnl swapped = false
146+
dnl for i from 1 to n-1:
147+
dnl if compare_jobs(varname, i-1, i):
148+
dnl swap_jobs(varname, i-1, i)
149+
dnl swapped = true
150+
dnl while swapped
151+
define(`bubble_sort', `ifelse(_bubble_sort_outer(`$1', 1, 0), 1, `bubble_sort(`$1')')')
152+
define(`_bubble_sort_outer',
153+
`ifelse(eval($2 < array_get(`$1', `length')), 0,
154+
`$3',
155+
`ifelse(_bubble_sort_inner(`$1', $2), 1,
156+
`_bubble_sort_outer(`$1', incr($2), 1)', `_bubble_sort_outer(`$1', incr($2), $3)'dnl
157+
)'dnl
158+
)'dnl
159+
)
160+
define(`_bubble_sort_inner',
161+
`ifelse(eval(compare_jobs(`$1', decr($2), `$2')), 1,
162+
0, `swap_jobs(`$1', decr($2), $2)1'`'dnl
163+
)'dnl
164+
)
165+
166+
dnl compare_jobs(job_varname, idx1, idx2):
167+
dnl // Prioritize by profit, then deadline:
168+
dnl // 0 means jobs need to be swapped, 1 otherwise
169+
dnl return (job_varname[idx1]["profit"] > job_varname[idx2]["profit"]) or
170+
dnl (job_varname[idx1]["profit"] == job_varname[idx2]["profit"] and
171+
dnl job_varname[idx1]["deadline"] > job_varname[idx2]["deadline"])
172+
define(`compare_jobs',
173+
`eval(
174+
array2_get(`$1', `$2', `profit') > array2_get(`$1', `$3', `profit') ||
175+
(array2_get(`$1', `$2', `profit') == array2_get(`$1', `$3', `profit') &&
176+
array2_get(`$1', `$2', `deadline') > array2_get(`$1', `$3', `deadline'))
177+
)'dnl
178+
)
179+
180+
dnl swap_jobs(job_varname, idx1, idx2):
181+
dnl swap(job_varname[idx1]["profit"], job_varname[idx2]["profit"])
182+
dnl swap(job_varname[idx1]["deadline"], job_varname[idx2]["deadline"])
183+
define(`swap_jobs',
184+
`array2_swap(`$1', `$2', `$3', `profit')dnl
185+
array2_swap(`$1', `$2', `$3', `deadline')'dnl
186+
)
187+
188+
dnl get_total_profit(jobs_varname, slots_varname):
189+
dnl total = 0
190+
dnl for i = 1 to slots_varname["length"] - 1:
191+
dnl if slots_varname[i] >= 0:
192+
dnl total = total + jobs_varname[slots_varname[i]]["profit"]
193+
dnl return total
194+
define(`get_total_profit', `_get_total_profit(`$1', `$2', 0, 1)')
195+
196+
dnl jobs_varname=$1, slots_varname=$2, total=$3, i=$4
197+
define(`_get_total_profit',
198+
`ifelse(eval($4 >= array_get(`$2', `length')), 1, `$3',
199+
`ifelse(eval(array_get(`$2', `$4') >= 0), 1,
200+
`_get_total_profit(`$1', `$2', eval($3 + array2_get(`$1', array_get(`$2', `$4'), `profit')), incr($4))',
201+
`_get_total_profit(`$1', `$2', `$3', incr($4))'dnl
202+
)'dnl
203+
)'dnl
204+
)
205+
206+
divert(0)dnl
207+
ifelse(eval(
208+
ARGC < 2 ||
209+
!parse_int_list(`profits', ARGV1) ||
210+
!parse_int_list(`deadlines', ARGV2)
211+
), 1, `show_usage()')dnl
212+
ifelse(eval(
213+
array_get(`profits', `length') != array_get(`deadlines', `length')
214+
), 1, `show_usage()')dnl
215+
job_sequencing(`profits', `deadlines', `jobs', `slots')dnl
216+
get_total_profit(`jobs', `slots')

0 commit comments

Comments
 (0)