|
| 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