chiark / gitweb /
57a979654f705bd01912fe773cd01668a92ddb07
[matchsticks-search.git] / main.c
1 /*
2  * Searches for "good" ways to divide n matchsticks up and reassemble them
3  * into m matchsticks.  "Good" means the smallest fragment is as big
4  * as possible.
5  *
6  * Invoke as   ./main n m
7  *
8  * The algorithm is faster if the arguments are ordered so that n > m.
9  */
10
11 /*
12  * matchsticks/main.c  Copyright 2014 Ian Jackson
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  */
24
25 #define _GNU_SOURCE
26
27 #include <publib.h>
28
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <unistd.h>
35 #include <stdbool.h>
36 #include <inttypes.h>
37 #include <math.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <sys/uio.h>
41 #include <sys/fcntl.h>
42
43 #include <glpk.h>
44
45 #ifndef VERSION
46 #define VERSION "(unknown-version)"
47 #endif
48
49 /*
50  * Algorithm.
51  *
52  * Each input match contributes, or does not contribute, to each
53  * output match; we do not need to consider multiple fragments
54  * relating to the same input/output pair this gives an n*m adjacency
55  * matrix (bitmap).  Given such an adjacency matrix, the problem of
56  * finding the best sizes for the fragments can be expressed as a
57  * linear programming problem.
58  *
59  * We search all possible adjacency matrices, and for each one we run
60  * GLPK's simplex solver.  We represent the adjacency matrix as an
61  * array of bitmaps: one word per input stick, with one bit per output
62  * stick.
63  *
64  * However, there are a couple of wrinkles:
65  *
66  * To best represent the problem as a standard LP problem, we separate
67  * out the size of each fragment into a common minimum size variable,
68  * plus a fragment-specific extra size variable.  This reduces the LP
69  * problem size at the cost of making the problem construction, and
70  * interpretation of the results, a bit fiddly.
71  *
72  * Many of the adjacency matrices are equivalent.  In particular,
73  * permutations of the columns, or of the rows, do not change the
74  * meaning.  It is only necessasry to consider any one permutation.
75  * We make use of this by considering only adjacency matrices whose
76  * bitmap array contains bitmap words whose numerical values are
77  * nondecreasing in array order.
78  *
79  * Once we have a solution, we also avoid considering any candidate
80  * which involves dividing one of the input sticks into so many
81  * fragment that the smallest fragment would necessarily be no bigger
82  * than our best solution.  That is, we reject candidates where any of
83  * the hamming weights of the adjacency bitmap words are too large.
84  *
85  * We further winnow the set of possible adjacency matrices, by
86  * ensuring the same bit is not set in too many entries of adjmatrix
87  * (ie, as above, only considering output sticks).
88  *
89  * And, we want to do the search in order of increasing maximum
90  * hamming weight.  This is because in practice optimal solutions tend
91  * to have low hamming weight, and having found a reasonable solution
92  * early allows us to eliminate a lot of candidates without doing the
93  * full LP.
94  */
95
96 typedef uint32_t AdjWord;
97 #define PRADJ "08"PRIx32
98
99 #define FOR_BITS(j,m) for (j=0, j##bit=1; j < (m); j++, j##bit<<=1)
100
101 static int n, m, maxhamweight;
102 static AdjWord *adjmatrix;
103 static AdjWord adjall;
104
105 static double best;
106 static glp_prob *best_prob;
107 static AdjWord *best_adjmatrix;
108
109 static int n_max_frags, m_max_frags;
110 static int *weight;
111
112 static unsigned printcounter;
113
114 static void iterate(void);
115 static void iterate_recurse(int i, AdjWord min);
116 static bool preconsider_ok(int nwords, bool doprint);
117 static bool maxhamweight_ok(void);
118 static void optimise(bool doprint);
119
120 static void progress_eol(void) {
121   fprintf(stderr,"        \r");
122   fflush(stderr);
123 }
124
125 static void set_best(double new_best) {
126   best = new_best;
127   /*
128    * When computing n_max_frags, we want to set a value that will skip
129    * anything that won't provide strictly better solutions.  So we
130    * want
131    *             frags <      n / best
132    *                        _          _
133    *   <=>       frags <   |  n / best  |
134    *                        _          _
135    *   <=>       frags <=  |  n / best  | - 1
136    */
137   n_max_frags = ceil(n / best) - 1;
138   m_max_frags = ceil(m / best) - 1;
139 }
140
141 /*----- multicore support -----*/
142
143 /*
144  * Multicore protocol
145  *
146  * We fork into:
147  *   - master (parent)
148  *   - generator
149  *   - ncpu workers
150  *
151  * ipc facilities:
152  *   - one pipe ("work") from generator to workers
153  *   - ever-extending file ("bus") containing new "best" values
154  *   - one file for each worker giving maxhamweight and adjmatrix for best
155  *
156  * generator runs iterate_recurse to a certain depth and writes the
157  * candidates to a pipe
158  *
159  * workers read candidates from the pipe and resume iterate_recurse
160  * halfway through the recursion
161  *
162  * whenever a worker does a doprint, it checks the bus for new best
163  * value; actual best values are appended
164  *
165  * master waits for generator and all workers to finish and then
166  * runs optimise() for each worker's best, then prints
167  */ 
168
169 static int ncpus = 0, multicore_iteration_boundary = INT_MAX;
170
171 static int mc_bus, mc_work[2];
172 static off_t mc_bus_read;
173
174 typedef struct {
175   int w;
176   FILE *results;
177   pid_t pid;
178 } Worker;
179 static Worker *mc_us;
180 static bool mc_am_generator;
181
182 static void multicore_check_for_new_best(void);
183
184 #define MAX_NIOVS 4
185 static AdjWord mc_iter_min;
186 static int mc_niovs;
187 static size_t mc_iovlen;
188 static struct iovec mc_iov[MAX_NIOVS];
189
190 #define IOV0 (mc_niovs = mc_iovlen = 0)
191
192 #define IOV(obj, count) ({                              \
193     assert(mc_niovs < MAX_NIOVS);                       \
194     mc_iov[mc_niovs].iov_base = &(obj);                 \
195     mc_iov[mc_niovs].iov_len = sizeof(obj) * (count);   \
196     mc_iovlen += mc_iov[mc_niovs].iov_len;              \
197     mc_niovs++;                                         \
198   })
199
200 static void mc_rwvsetup_outer(void) {
201   IOV0;
202   IOV(maxhamweight, 1);
203   IOV(mc_iter_min, 1);
204   IOV(*adjmatrix, multicore_iteration_boundary);
205   IOV(*weight, m);
206 }
207
208 static void mc_rwvsetup_full(void) {
209   IOV0;
210   IOV(*adjmatrix, n);
211 }
212
213 static void vlprintf(const char *fmt, va_list al) {
214   vfprintf(stderr,fmt,al);
215   progress_eol();
216 }
217
218 static void LPRINTF(const char *fmt, ...) {
219   va_list al;
220   va_start(al,fmt);
221   vlprintf(fmt,al);
222   va_end(al);
223 }
224
225 static void mc_awaitpid(int wnum, pid_t pid) {
226   LPRINTF("master awaiting %2d [%ld]",wnum,(long)pid);
227   int status;
228   pid_t got = waitpid(pid, &status, 0);
229   assert(got == pid);
230   if (status) {
231     fprintf(stderr,"\nFAILED SUBPROC %2d [%ld] %d\n",
232             wnum, (long)pid, status);
233     exit(-1);
234   }
235 }
236
237 static void multicore_outer_iteration(int i, AdjWord min) {
238   static unsigned check_counter;
239
240   assert(i == multicore_iteration_boundary);
241   mc_iter_min = min;
242   mc_rwvsetup_outer();
243   ssize_t r = writev(mc_work[1], mc_iov, mc_niovs);
244   assert(r == mc_iovlen);
245   /* effectively, this writev arranges to transfers control
246    * to some worker's instance of iterate_recurse via mc_iterate_worker */
247
248   if (!(check_counter++ & 0xff))
249     multicore_check_for_new_best();
250 }
251
252 static void mc_iterate_worker(void) {
253   for (;;) {
254     mc_rwvsetup_outer();
255     ssize_t r = readv(mc_work[0], mc_iov, mc_niovs);
256     if (r == 0) break;
257     assert(r == mc_iovlen);
258     
259     bool ok = maxhamweight_ok();
260     if (!ok) continue;
261
262     ok = preconsider_ok(multicore_iteration_boundary, 1);
263     progress_eol();
264     if (!ok) continue;
265
266     /* stop iterate_recurse from trying to run multicore_outer_iteration */
267     int mc_org_it_bound = multicore_iteration_boundary;
268     multicore_iteration_boundary = INT_MAX;
269     iterate_recurse(mc_org_it_bound, mc_iter_min);
270     multicore_iteration_boundary = mc_org_it_bound;
271   }
272   if (best_adjmatrix) {
273     LPRINTF("worker %2d reporting",mc_us->w);
274     adjmatrix = best_adjmatrix;
275     mc_rwvsetup_full();
276     ssize_t r = writev(fileno(mc_us->results), mc_iov, mc_niovs);
277     assert(r == mc_iovlen);
278   }
279   LPRINTF("worker %2d ending",mc_us->w);
280   exit(0);
281 }
282
283 static void multicore(void) {
284   Worker *mc_workers;
285   int w;
286   pid_t genpid;
287
288   multicore_iteration_boundary = n / 2;
289
290   FILE *busf = tmpfile();  assert(busf);
291   mc_bus = fileno(busf);
292   int r = fcntl(mc_bus, F_GETFL);  assert(r >= 0);
293   r |= O_APPEND;
294   r = fcntl(mc_bus, F_SETFL, r);  assert(r >= 0);
295
296   r = pipe(mc_work);  assert(!r);
297
298   mc_workers = xmalloc(sizeof(*mc_workers) * ncpus);
299   for (w=0; w<ncpus; w++) {
300     mc_workers[w].w = w;
301     mc_workers[w].results = tmpfile();  assert(mc_workers[w].results);
302     mc_workers[w].pid = fork();  assert(mc_workers[w].pid >= 0);
303     if (!mc_workers[w].pid) {
304       mc_us = &mc_workers[w];
305       close(mc_work[1]);
306       LPRINTF("worker %2d running", w);
307       mc_iterate_worker();
308       exit(0);
309     }
310   }
311
312   close(mc_work[0]);
313
314   genpid = fork();  assert(genpid >= 0);
315   if (!genpid) {
316     mc_am_generator = 1;
317     LPRINTF("generator running");
318     iterate();
319     exit(0);
320   }
321
322   close(mc_work[1]);
323   mc_awaitpid(-1, genpid);
324   for (w=0; w<ncpus; w++)
325     mc_awaitpid(w, mc_workers[w].pid);
326
327   for (w=0; w<ncpus; w++) {
328     mc_rwvsetup_full();
329     LPRINTF("reading report from %2d",w);
330     ssize_t sr = preadv(fileno(mc_workers[w].results), mc_iov, mc_niovs, 0);
331     if (!sr) continue;
332     LPRINTF("got report from %2d",w);
333     maxhamweight = 0;
334     optimise(1);
335   }
336 }
337
338 static void multicore_check_for_new_best(void) {
339   if (!(mc_us || mc_am_generator))
340     return;
341
342   for (;;) {
343     double msg;
344     ssize_t got = pread(mc_bus, &msg, sizeof(msg), mc_bus_read);
345     if (!got) break;
346     assert(got == sizeof(msg));
347     if (msg > best)
348       set_best(msg);
349     mc_bus_read += sizeof(msg);
350   }
351 }
352
353 static void multicore_found_new_best(void) {
354   if (!mc_us)
355     return;
356
357   if (mc_us /* might be master */) fprintf(stderr,"    w%-2d ",mc_us->w);
358   ssize_t wrote = write(mc_bus, &best, sizeof(best));
359   assert(wrote == sizeof(best));
360 }
361
362 /*----- end of multicore support -----*/
363
364 static AdjWord *xalloc_adjmatrix(void) {
365   return xmalloc(sizeof(*adjmatrix)*n);
366 }
367
368 static void prep(void) {
369   adjall = ~((~(AdjWord)0) << m);
370   adjmatrix = xalloc_adjmatrix();
371   glp_term_out(GLP_OFF);
372   setlinebuf(stderr);
373   weight = calloc(sizeof(*weight), m);  assert(weight);
374   n_max_frags = INT_MAX;
375   m_max_frags = INT_MAX;
376 }
377
378 #if 0
379 static AdjWord one_adj_bit(int bitnum) {
380   return (AdjWord)1 << bitnum;
381 }
382 #endif
383
384 static int count_set_adj_bits(AdjWord w) {
385   int j, total = 0;
386   AdjWord jbit;
387   FOR_BITS(j,m)
388     total += !!(w & jbit);
389   return total;
390 }
391
392 #define PRINTF(...) if (!doprint) ; else fprintf(stderr, __VA_ARGS__)
393
394 static int totalfrags;
395
396 static bool maxhamweight_ok(void) {
397   return maxhamweight <= m_max_frags;
398 }
399
400 static bool preconsider_ok(int nwords, bool doprint) {
401   int i;
402
403   PRINTF("%2d ", maxhamweight);
404
405   bool had_max = 0;
406   for (i=0, totalfrags=0; i<nwords; i++) {
407     int frags = count_set_adj_bits(adjmatrix[i]);
408     PRINTF("%"PRADJ" ", adjmatrix[i]);
409     if (frags > m_max_frags) {
410       PRINTF(" too fine");
411       goto out;
412     }
413     had_max += (frags >= maxhamweight);
414     totalfrags += frags;
415   }
416   if (!had_max) {
417     /* Skip this candidate as its max hamming weight is lower than
418      * we're currently looking for (which means we must have done it
419      * already).  (The recursive iteration ensures that none of the
420      * words have more than the max hamming weight.) */
421     PRINTF(" nomaxham");
422     goto out;
423   }
424   return 1;
425
426  out:
427   return 0;
428 }
429
430 static void optimise(bool doprint) {
431   /* Consider the best answer (if any) for a given adjacency matrix */
432   glp_prob *prob = 0;
433   int i, j;
434   AdjWord jbit;
435
436   /*
437    * Up to a certain point, optimise() can be restarted.  We use this
438    * to go back and print the debugging output if it turns out that we
439    * have an interesting case.  The HAVE_PRINTED macro does this: its
440    * semantics are to go back in time and make sure that we have
441    * printed the description of the search case.
442    */
443 #define HAVE_PRINTED ({                                         \
444       if (!doprint) { doprint = 1; goto retry_with_print; }     \
445     })
446  retry_with_print:
447   if (prob) {
448     glp_delete_prob(prob);
449     prob = 0;
450   }
451
452   bool ok = preconsider_ok(n, doprint);
453   if (!ok)
454     goto out;
455
456   /*
457    * We formulate our problem as an LP problem as follows.
458    * In this file "n" and "m" are the matchstick numbers.
459    *
460    * Each set bit in the adjacency matrix corresponds to taking a
461    * fragment from old match i and making it part of new match j.
462    *
463    * The structural variables (columns) are:
464    *   x_minimum        minimum size of any fragment (bounded below by 0)
465    *   x_morefrag_i_j   the amount by which the size of the fragment
466    *                     i,j exceeds the minimum size (bounded below by 0)
467    *
468    * The auxiliary variables (rows) are:
469    *   x_total_i       total length for each input match (fixed variable)
470    *   x_total_j       total length for each output match (fixed variable)
471    *
472    * The objective function is simply
473    *   maximise x_minimum
474    *
475    * We use X_ and Y_ to refer to GLPK's (1-based) column and row indices.
476    * ME_ refers to entries in the list of constraint matrix elements
477    * which we build up as we go.
478    */
479
480   prob = glp_create_prob();
481
482   int Y_totals_i = glp_add_rows(prob, n);
483   int Y_totals_j = glp_add_rows(prob, m);
484   int X_minimum = glp_add_cols(prob, 1);
485
486   {
487   int next_matrix_entry = 1; /* wtf GLPK! */
488   int matrix_entries_size = next_matrix_entry + n + m + totalfrags*2;
489   double matrix_entries[matrix_entries_size];
490   int matrix_entries_XY[2][matrix_entries_size];
491
492 #define ADD_MATRIX_ENTRY(Y,X) ({                        \
493       assert(next_matrix_entry < matrix_entries_size);  \
494       matrix_entries_XY[0][next_matrix_entry] = (X);    \
495       matrix_entries_XY[1][next_matrix_entry] = (Y);    \
496       matrix_entries[next_matrix_entry] = 0;            \
497       next_matrix_entry++;                              \
498     })
499
500   int ME_totals_i__minimum = next_matrix_entry;
501   for (i=0; i<n; i++) ADD_MATRIX_ENTRY(Y_totals_i+i, X_minimum);
502
503   int ME_totals_j__minimum = next_matrix_entry;
504   for (j=0; j<m; j++) ADD_MATRIX_ENTRY(Y_totals_j+j, X_minimum);
505
506   /* \forall_i x_total_i = m */
507   /* \forall_i x_total_j = n */
508   for (i=0; i<n; i++) glp_set_row_bnds(prob, Y_totals_i+i, GLP_FX, m,m);
509   for (j=0; j<m; j++) glp_set_row_bnds(prob, Y_totals_j+j, GLP_FX, n,n);
510
511   /* x_minimum >= 0 */
512   glp_set_col_bnds(prob, X_minimum, GLP_LO, 0, 0);
513   glp_set_col_name(prob, X_minimum, "minimum");
514
515   /* objective is maximising x_minimum */
516   glp_set_obj_dir(prob, GLP_MAX);
517   glp_set_obj_coef(prob, X_minimum, 1);
518
519   for (i=0; i<n; i++) {
520     FOR_BITS(j,m) {
521       if (!(adjmatrix[i] & jbit))
522         continue;
523       /* x_total_i += x_minimum */
524       /* x_total_j += x_minimum */
525       matrix_entries[ ME_totals_i__minimum + i ] ++;
526       matrix_entries[ ME_totals_j__minimum + j ] ++;
527
528       /* x_morefrag_i_j >= 0 */
529       int X_morefrag_i_j = glp_add_cols(prob, 1);
530       glp_set_col_bnds(prob, X_morefrag_i_j, GLP_LO, 0, 0);
531       if (doprint) {
532         char buf[255];
533         snprintf(buf,sizeof(buf),"mf %d,%d",i,j);
534         glp_set_col_name(prob, X_morefrag_i_j, buf);
535       }
536
537       /* x_total_i += x_morefrag_i_j */
538       /* x_total_j += x_morefrag_i_j */
539       int ME_totals_i__mf_i_j = ADD_MATRIX_ENTRY(Y_totals_i+i, X_morefrag_i_j);
540       int ME_totals_j__mf_i_j = ADD_MATRIX_ENTRY(Y_totals_j+j, X_morefrag_i_j);
541       matrix_entries[ME_totals_i__mf_i_j] = 1;
542       matrix_entries[ME_totals_j__mf_i_j] = 1;
543     }
544   }
545
546   assert(next_matrix_entry == matrix_entries_size);
547
548   glp_load_matrix(prob, matrix_entries_size-1,
549                   matrix_entries_XY[1], matrix_entries_XY[0],
550                   matrix_entries);
551
552   int r = glp_simplex(prob, NULL);
553   PRINTF(" glp=%d", r);
554
555 #define OKERR(e) \
556   case e: PRINTF(" " #e ); goto out;
557 #define BADERR(e) \
558   case e: HAVE_PRINTED; printf(" " #e " CRASHING\n"); exit(-1);
559 #define DEFAULT \
560   default: HAVE_PRINTED; printf(" ! CRASHING\n"); exit(-1);
561
562   switch (r) {
563   OKERR(GLP_ESING);
564   OKERR(GLP_ECOND);
565   OKERR(GLP_EBOUND);
566   OKERR(GLP_EFAIL);
567   OKERR(GLP_ENOPFS);
568   OKERR(GLP_ENODFS);
569   BADERR(GLP_EBADB);
570   BADERR(GLP_EOBJLL);
571   BADERR(GLP_EOBJUL);
572   BADERR(GLP_EITLIM);
573   BADERR(GLP_ETMLIM);
574   BADERR(GLP_EINSTAB);
575   BADERR(GLP_ENOCVG);
576   case 0: break;
577   DEFAULT;
578   }
579
580   r = glp_get_status(prob);
581   PRINTF(" status=%d", r);
582
583   switch (r) {
584   OKERR(GLP_NOFEAS);
585   OKERR(GLP_UNDEF);
586   BADERR(GLP_FEAS);
587   BADERR(GLP_INFEAS);
588   BADERR(GLP_UNBND);
589   case GLP_OPT: break;
590   DEFAULT;
591   }
592
593   double got = glp_get_obj_val(prob);
594   PRINTF("  %g", got);
595   if (got <= best)
596     goto out;
597
598   HAVE_PRINTED;
599
600   set_best(got);
601   multicore_found_new_best();
602
603   if (best_prob) glp_delete_prob(best_prob);
604   best_prob = prob;
605
606   free(best_adjmatrix);
607   best_adjmatrix = xalloc_adjmatrix();
608   memcpy(best_adjmatrix, adjmatrix, sizeof(*adjmatrix)*n);
609
610   PRINTF(" BEST        \n");
611   return;
612
613   }
614  out:
615   if (prob)
616     glp_delete_prob(prob);
617   if (doprint) progress_eol();
618   if (doprint) multicore_check_for_new_best();
619 }
620
621 static void iterate_recurse(int i, AdjWord min) {
622   int j;
623   AdjWord jbit;
624
625   if (i >= n) {
626     printcounter++;
627     optimise(!(printcounter & 0xfff));
628     return;
629   }
630   if (i >= multicore_iteration_boundary) {
631     multicore_outer_iteration(i, min);
632     return;
633   }
634   for (adjmatrix[i] = min;
635        ;
636        adjmatrix[i]++) {
637     if (count_set_adj_bits(adjmatrix[i]) > maxhamweight)
638       goto again;
639     if (i == 0 && (adjmatrix[i] & (1+adjmatrix[i])))
640       goto again;
641
642     FOR_BITS(j,m)
643       if (adjmatrix[i] & jbit)
644         weight[j]++;
645     for (int j = 0; j < m; j++)
646       if (weight[j] >= n_max_frags)
647         goto takeout;
648
649     iterate_recurse(i+1, adjmatrix[i]);
650
651   takeout:
652     FOR_BITS(j,m)
653       if (adjmatrix[i] & jbit)
654         weight[j]--;
655
656   again:
657     if (adjmatrix[i] == adjall)
658       return;
659   }
660 }
661
662 static void iterate(void) {
663   for (maxhamweight=1; maxhamweight<=m; maxhamweight++) {
664     if (!maxhamweight_ok())
665       continue;
666
667     iterate_recurse(0, 1);
668   }
669 }
670
671 static void report(void) {
672   fprintf(stderr, "\n");
673   if (best_prob) {
674     double min = glp_get_obj_val(best_prob);
675     double a[n][m];
676     int i, j, cols;
677     for (i = 0; i < n; i++)
678       for (j = 0; j < m; j++)
679         a[i][j] = 0;
680     cols = glp_get_num_cols(best_prob);
681     for (i = 1; i <= cols; i++) {
682       int x, y;
683       if (2 != sscanf(glp_get_col_name(best_prob, i), "mf %d,%d", &x, &y))
684         continue;
685       a[x][y] = min + glp_get_col_prim(best_prob, i);
686     }
687     printf("%d into %d: min fragment %g   [%s]\n", n, m, min, VERSION);
688     for (i = 0; i < n; i++) {
689       for (j = 0; j < m; j++) {
690         if (a[i][j])
691           printf(" %9.3f", a[i][j]);
692         else
693           printf("          ");
694       }
695       printf("\n");
696     }
697   }
698   if (ferror(stdout) || fclose(stdout)) { perror("stdout"); exit(-1); }
699 }
700  
701 int main(int argc, char **argv) {
702   int opt;
703   while ((opt = getopt(argc,argv,"j:")) >= 0) {
704     switch (opt) {
705     case 'j': ncpus = atoi(optarg); break;
706     case '+': assert(!"bad option");
707     default: abort();
708     }
709   }
710   argc -= optind-1;
711   argv += optind-1;
712   assert(argc==3);
713   n = atoi(argv[1]);
714   m = atoi(argv[2]);
715
716   prep();
717
718   if (ncpus) multicore();
719   else iterate();
720
721   report();
722   return 0;
723 }