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