1: package de.luschny.math.ruler;
   2:   
3: import java.io.*;
4: import java.util.*;
   5:   
6: // Driver class for the generation of perfect rulers.
   7:   
8: public class Ruler {
   9:   
10: static String[] help =
  11:     {
12: " PURPOSE OF THE PROGRAM",
13: " Generating all perfect or optimal rulers of given length.",
14: "",
15: " A ruler with length L is 'perfect' if",
16: " (a) the ruler is complete, i.e. all distances d with",
17: " 1 <= d <= L can be measured with the ruler and",
18: " (b) there is no complete ruler with the same length which",
19: " possesses fewer marks. If, in addition,",
20: " (c) there is no complete ruler with the same number of marks",
21: " which has a greater length, then the ruler is 'optimal'.",
22: "",
23: " SYNTAX: \"function(from, to, options)\" ",
24: " (Like a function call with three arguments.)",
25: "",
26: " function is one out of { perfect, optimal, single }",
27: "",
28: " perfect -> generate all perfect rulers with length L",
29: " constrained by 1 <= from <= L <= to ",
30: " single -> generate only one perfect ruler with length L",
31: " constrained by 1 <= from <= L <= to ",
32: " optimal -> generate all optimal rulers with S segments",
33: " constrained by 1 <= from <= S <= to ",
34: "",
35: " 'options' are any substrings of \"cmdbtf\".",
36: "",
37: " c : count rulers only, don't print them",
38: " for example: Rulers(8,23), Number of rulers: 4",
39: " m : generate and print the marker-representation",
40: " for example: [0,1,4,10,16,18,21,23]",
41: " d : generate and print the difference representation",
42: " for example: <1,3,6,6,2,3,2>",
43: " b : generate and print the binary representation",
44: " for example ||--|-----|-----|-|--|-|",
45: " t : generate and print the type of the ruler",
46: " for example |1^3|2^3|3^3|7^3|14^3|21^3|",
47: " f : generate and print the difference-triangle",
48: "",
49: " All the above options can be combined in a single string.",
50: " The option 'c' overrides all other options.",
51: "",
52: " EXAMPLE USAGE: perfect(23,44,mdb), single(1,80,b)",
53: " optimal(1,20,c), optimal(12,12,mdbtf)",
54: "",
55: " **********************************************************",
56: " Note: If two different rulers are mirror-symmetric both",
57: " will be counted, but only one will be represented.",
58: " For example: [0,1,3] is mirror-symmetric to [0,2,3].",
59: " ***********************************************************",
60: "",
61: " File: Rulers will be saved to the file 'rulers.txt'.",
62: " Info: http://www.luschny.de/math/rulers/index.html"
  63:     };
  64:   
65: public static void main(String[] args)
  66:     {
67: String fileName = "ruler.txt";
  68:   
69: if(args.length == 1) run(args[0], fileName);
70: else run("noparam", fileName);
  71:   
72: // ----------------------------------
73: // Some test cases.
74: // Uncomment one line in the next paragraph
75: // ----------------------------------
76: // run("perfect(0,50,d)", fileName);
77: // run("perfect(1,23,mb)", fileName);
78: // run("single(1,80,b)", fileName);
79: // run("optimal(1,16,bd)", fileName);
80: // run("optimal(12,12,mdbtf)", fileName);
81: // ------------------------------------
  82:     }
  83:   
84: static void run(String arg, String fileName)
  85:     {
  86:        Rulez ruler;
87: boolean validParams = ! arg.equalsIgnoreCase("noparam");
  88:   
89: if(validParams)
  90:        {
91: System.out.println("FunctionCall: " + arg);
92: ruler = new Rulez(fileName);
  93:           validParams = ruler.compute(arg);
  94:        }
  95:   
96: if(! validParams)
  97:        {
98: System.out.println(" *** Missing or invalid arguments ***");
  99:            System.out.println();
100: for (int i = 0; i < help.length; i++)
 101:                System.out.println(help[i]);
102: return;
 103:        }
 104:   
105: System.out.println("Saved to file: " + fileName);
 106:     }
 107:  }
 108:   
109: class Rulez
 110:  {
111: static int[] optIndex = new int[]
 112:     {
 113:        1, 1, 3, 6, 9, 13, 17, 23, 29, 36, 43, 50,
 114:        58, 68, 79, 90, 101, 112, 123
 115:     };
 116:   
117: static int[] lenToSegment = new int[]
 118:     {
 119:        1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6,
 120:        6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9,
 121:        9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10,
 122:        11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12,
 123:        12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13,
 124:        13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
 125:        14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
 126:        16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17,
 127:        17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18,
 128:        18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19,
 129:        19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19
 130:     };
 131:   
132: // ENUM OPTION
133: static final int countOnly = 0, singleOnly = 1, markerRep = 2,
 134:           diffRep = 3, symbolicRep = 4, tableRep = 5, typeRep = 6;
 135:   
136: static boolean[] option =
 137:     {
138: false, false, false, false, false, false, false
 139:     };
 140:   
141: // ENUM FUNCTION
142: static int function, From, To;
143: static final int perfect = 0;
144: static final int optimal = 1;
145: static final int single = 2;
146: static String[] functionName =
 147:     {
148: "perfect", "optimal", "single"
 149:     };
 150:   
 151:     PerfectRuler generator;
 152:     StopWatch watch, mwatch, mainWatch;
 153:     PrintWriter rulerReport;
 154:   
155: static boolean validArgs(String args)
 156:     {
157: args = args.replace('(', ',');
158: args = args.replace(')', ',');
 159:   
160: Scanner sc = new Scanner(args).useDelimiter(",");
 161:   
162: try
 163:        {
 164:           String fun = sc.next();
165: int inFrom = sc.nextInt();
166: int inTo = sc.nextInt();
 167:           String opt = sc.next();
 168:           sc.close();
 169:   
170: if (fun.equalsIgnoreCase(functionName[perfect]))
 171:           {
 172:              function = perfect;
 173:           }
174: else if (fun.equalsIgnoreCase(functionName[optimal]))
 175:           {
 176:              function = optimal;
 177:           }
178: else if (fun.equalsIgnoreCase(functionName[single]))
 179:           {
 180:              function = single;
 181:           }
182: else
 183:           {
184: throw new IllegalArgumentException("Function name invalid!");
 185:           }
 186:   
187: option[countOnly] = opt.contains("c");
188: if (!option[countOnly])
 189:           {
190: boolean optset = false;
191: optset |= option[markerRep] = opt.contains("m");
192: optset |= option[diffRep] = opt.contains("d");
193: optset |= option[symbolicRep] = opt.contains("b");
194: optset |= option[typeRep] = opt.contains("t");
195: optset |= option[tableRep] = opt.contains("f");
 196:   
197: if (!optset)
 198:              {
199: throw new IllegalArgumentException("Options missing!");
 200:              }
 201:           }
 202:           option[singleOnly] = function == single;
 203:   
204: int lmin = 1; // 999 is a pseudo max
205: int lmax = function == optimal ? 999
 206:                   : (function == perfect ? 999 : 999);
 207:   
 208:           From = Math.min(Math.max(lmin, inFrom), lmax);
 209:           To = Math.max(lmin, Math.min(inTo, lmax));
 210:        }
211: catch (Exception e)
 212:        {
 213:           System.out.println(e.toString());
214: return false;
 215:        }
 216:   
217: return true;
 218:     }
 219:   
220: public Rulez(String fileName)
 221:     {
222: mainWatch = new StopWatch();
223: watch = new StopWatch();
224: mwatch = new StopWatch();
 225:   
226: try
 227:        {
 228:           rulerReport =
229: new PrintWriter(
230: new BufferedWriter(
231: new FileWriter(fileName, true)));
 232:        }
233: catch (IOException e)
 234:        {
 235:           System.err.println(e.toString());
236: return;
 237:        }
 238:   
239: generator = new PerfectRuler(rulerReport, option);
 240:     }
 241:   
242: public boolean compute(String args)
 243:     {
244: if(! validArgs(args)) return false;
 245:   
246: System.out.println("Computing " + functionName[function] +
247: " rulers from " + From + " to " + To +".");
 248:   
 249:        mainWatch.start();
250: int count = handler(function, From, To, option);
 251:        mainWatch.stop();
 252:   
253: System.out.println(count + " rulers");
 254:   
255: if (!option[countOnly])
 256:        {
257: System.out.println("Generated in " + mainWatch.toString());
 258:        }
 259:   
 260:        rulerReport.flush();
261: return true;
 262:     }
 263:   
264: int handler(int fun, int from, int to, boolean[] options)
 265:     {
266: int allcount = 0,
 267:            markcount = 0,
 268:            segment = lenToSegment[from];
 269:   
270: for (int i = from; i <= to; i++)
 271:        {
272: int S = fun == optimal ? i : lenToSegment[i];
273: int L = fun == optimal ? optIndex[i] : i;
 274:   
275: if (segment != S && fun != optimal)
 276:           {
 277:              mwatch.stop();
278: System.out.print("* S=" + segment + " -> " +markcount);
279: System.out.println(" in " + mwatch.toString());
 280:              mwatch.restart();
 281:   
 282:              markcount = 0;
 283:              segment = S;
 284:           }
 285:   
 286:           watch.restart();
287: int count = generator.ruler(S + 1, L);
 288:           watch.stop();
 289:   
 290:           allcount += count;
 291:           markcount += count;
 292:   
293: System.out.print("(" + S + "," + L + ") -> " + count);
294: System.out.println(" in " + watch.toString());
 295:        }
 296:   
297: if (fun != optimal)
 298:        {
 299:           mwatch.stop();
300: System.out.print("* S=" + segment + " -> " + markcount);
301: System.out.println(" in " + mwatch.toString());
 302:        }
 303:   
304: return allcount;
 305:     }
 306:  }
 307:   
308: //////////////////////////////////////////////////////
 309:   
310: class PerfectRuler {
 311:   
312: // The number of perfect rulers with lenght 1<=L<=123
313: private static int[] perfCount = {
 314:       1,    1,   1,   2,   3,   4,  2,  12,  8,    4,
 315:      38,   30,  14,   6, 130,  80, 32,  12,500,  326,
 316:     150,   66,  18,   4, 944, 460,166,  56, 12,    6,
 317:    2036,  890, 304, 120,  20,  10,  2,2678,974,  362,
 318:     100,   36,   4,   2,4892,2114,684, 238, 68,   22,
 319:       4,16318,6350,2286, 836, 330,108,  24, 12,31980,
 320:   12252, 4960,1806, 668, 238,  86,  6,  12,  4,15558,
 321:    5906, 2558, 850, 388, 120,  38,  4,   6,  4,    2,
 322:    4972, 2234, 798, 332, 106,  48,  4,   6,  2,    2,
 323:       2, 3392,1262, 626, 212,  76, 40,  16,  2,    2,
 324:       2,    2,3426,1506, 682, 360,138,  70,  28,   8,
 325:       2,    2,   2,6578,2984,1458,586, 374, 192,  98,
 326:      38,   14,   4,   4
 327:    };
 328:   
329: private PrintWriter file;
330: private int[][] rulers;
331: private int[] r;
332: private int L1, count, maxCount, perCount;
333: private boolean[] option;
 334:   
335: public PerfectRuler(PrintWriter report, boolean[] opt)
 336:     {
 337:        file = report;
 338:        option = opt;
339: rulers = new int [16000][];
 340:     }
 341:   
342: public int ruler (int M, int L)
 343:     {
 344:        perCount = L < perfCount.length ? perfCount[L] : 32000;
 345:        maxCount = option[Rulez.singleOnly] ? 1 : perCount;
 346:   
347: if (option[Rulez.countOnly])
 348:        {
349: file.println("Number of rulers(" + (M-1) + "," + L
350: + ") = " + maxCount);
351: return maxCount;
 352:        }
 353:   
 354:        maxCount = (maxCount + (maxCount | 1)) >> 1;
 355:   
356: int S = (M * (M - 1)) / 2;
 357:        L1 = L + 1;
 358:   
359: r = new int [M + 1];
 360:   
361: int[] R = new int [S + 1];
362: int[] K = new int [M + 2];
 363:   
 364:        K [2] = L; K [3] = 1;
 365:        R [1] = 1; R [L] = 1; R [L - 1] = 1;
 366:   
 367:        file.println();
368: file.println("Rulers(" + (M-1) + "," + L + ") List of rulers:");
 369:        file.println();
 370:        count = 0;
 371:   
372: try
 373:        {
374: generator(M, L, K, R, 3, S, 1, false);
 375:        }
376: catch (java.util.NoSuchElementException e)
377: { // --- do not handle: this is an early-out
 378:        }
 379:   
380: if (option[Rulez.singleOnly])
 381:        {
 382:           count = 1;
 383:        }
384: else
 385:        {
 386:           count = L < perfCount.length ? perCount : 2 * count;
387: file.print("Rulers(" + (M-1) + "," + L + ") ");
388: file.println("Number of rulers: " + count);
 389:        }
 390:   
 391:        file.flush();
 392:   
393: return count;
 394:     }
 395:   
396: private void generator(int M, int L, int[] K, int[] R,
397: int i, int S, int w, boolean b)
 398:     {
399: if (i < M)
 400:        {
401: int k = w;
 402:   
403: while (R [L - k] > 0)
 404:           {
 405:              k++;
 406:           }
 407:   
408: int t = L - M + i + 1;
 409:   
410: if ((t & 1) == 1)
 411:           {
 412:              t++;
 413:           }
 414:   
 415:           t >>= 1;
416: if (t < k) k = t;
 417:   
418: while (k > w)
 419:           {
420: expander(M, L, K, R, i + 1, S, k, true);
421: expander(M, L, K, R, i + 1, S, k, false);
 422:              k--;
 423:           }
 424:   
425: if (! b)
 426:           {
427: expander(M, L, K, R, i + 1, S, w, true);
 428:           }
 429:        }
430: else
 431:        {
 432:           checker(M, K);
 433:        }
 434:     }
 435:   
436: private void expander(int M, int L, int[] K, int[] R,
437: int i, int S, int w, boolean b)
 438:     {
439: int ka = b ? L - w : w;
 440:   
 441:        K [i] = ka;
 442:   
443: for (int j = 1; j < i; j++)
 444:        {
445: int len = ka - K [j];
 446:   
447: if (len < 0)
 448:           {
 449:              len = -len;
 450:           }
 451:   
452: if (R [len] > 0)
 453:           {
 454:              S--;
 455:           }
 456:   
 457:           R [len]++;
 458:        }
 459:   
460: if (S >= L)
 461:        {
 462:           generator(M, L, K, R, i, S, w, b);
 463:        }
 464:   
465: for (int j = 1; j < i; j++)
 466:        {
467: int len = ka - K [j];
 468:   
469: if (len < 0)
 470:           {
 471:              len = -len;
 472:           }
 473:   
 474:           R [len]--;
 475:        }
 476:     }
 477:   
478: private void checker (int M, int[] K)
 479:     {
480: int[] a = r;
 481:   
 482:        a [1] = K [1];
 483:        a [M] = K [2];
 484:   
485: int i, k;
 486:   
487: for (i = 3; i <= M; i++)
 488:        {
489: int j = i - 1;
 490:   
491: while (K [i] < a [j - 1])
 492:           {
 493:              a [j] = a [j - 1];
 494:              j--;
 495:           }
 496:   
 497:           a [j] = K [i];
 498:        }
 499:   
500: int[] rul = new int [M - 1];
 501:   
502: for (k = 1; k < M; k++)
 503:        {
504: int d = a [k + 1] - a [k];
505: if (d <= 0) return;
 506:   
 507:           rul [k - 1] = d;
 508:        }
 509:   
510: // equivalent by reflection: choose the smaller ruler
511: int M2 = M - 2;
 512:   
513: for (k = 0; k < (M - 1); k++)
 514:        {
515: if (rul [k] < rul [M2 - k])
 516:           {
517: break;
 518:           }
519: else
 520:           {
521: if (rul [k] != rul [M2 - k])
 522:              {
523: for (int j = (M2 - 1) >> 1; j >= 0; --j)
 524:                 {
525: int temp = rul [j];
 526:                    rul [j]  = rul [M2 - j];
 527:                    rul [M2 - j] = temp;
 528:                 }
529: break;
 530:              }
 531:           }
 532:        }
 533:   
534: for (i = count - 1; i >= 0; i--)
 535:        {
536: int[] rulI = rulers [i];
 537:           k = M2;
 538:   
539: while ((k >= 0) && (rul [k] == rulI [k]))
 540:           {
 541:              k--;
 542:           }
 543:   
544: if (k < 0) // ruler already found
 545:           {
546: return;
 547:           }
 548:        }
 549:   
550: // If we reach this point, we have found a new ruler!
551: // -- begin 'visit'
 552:   
 553:        rulers [count++] = rul;
 554:        printer(a);
 555:   
556: // -- end 'visit'
 557:   
558: if (maxCount <= count)
 559:        {
560: // done, early exit
561: throw new java.util.NoSuchElementException();
 562:        }
 563:     }
 564:   
565: //////////////// print - functions //////////////////////
 566:   
567: private void printer(final int[] a)
 568:     {
569: int nop = 0;
570: if (option[Rulez.countOnly])
 571:        {
572: return;
 573:        }
574: if (option[Rulez.symbolicRep])
 575:        {
 576:           nop++;
 577:           printSymbolic(a);
 578:        }
579: if (option[Rulez.markerRep])
 580:        {
 581:           nop++;
 582:           printRuler(a);
 583:        }
584: if (option[Rulez.diffRep])
 585:        {
 586:           nop++;
 587:           printDiffRepresentation(a);
 588:        }
589: if (option[Rulez.typeRep])
 590:        {
 591:           nop++;
 592:           printTypeOf(a);
 593:        }
594: if (option[Rulez.tableRep])
 595:        {
 596:           nop++;
 597:           printDiffTable(a);
 598:        }
599: if (nop > 1)
 600:        {
 601:           file.println();
 602:        }
 603:     }
 604:   
605: private void printRuler(final int[] a)
 606:     {
607: file.print("[0");
 608:   
609: for (int i = 2; i < a.length; i++)
 610:        {
611: file.print(",");
 612:           file.print(a[i]);
 613:        }
 614:   
615: file.println("]");
 616:     }
 617:   
618: private void printSymbolic(final int[] a)
 619:     {
620: StringBuilder sr = new StringBuilder(L1);
621: int l = 0;
 622:   
623: for (int i = 1; i < a.length; i++)
 624:        {
625: while (l++ < a[i])
 626:           {
627: sr.append('-');
 628:           }
629: sr.append('|');
 630:        }
 631:        file.println(sr.toString());
 632:     }
 633:   
634: private void printDiffRepresentation(final int[] a)
 635:     {
636: file.print("<" + (a[2] - a[1]));
 637:   
638: for (int i = 2; i < a.length - 1; i++)
 639:        {
640: int w = a[i + 1] - a[i];
641: file.print(",");
 642:           file.print(w);
 643:        }
 644:   
645: file.println(">");
 646:     }
 647:   
648: private void printDiffTable(final int[] a)
 649:     {
650: for (int i = a.length - 1; i > 0; i--)
 651:        {
652: for (int j = 1; j < i; j++)
 653:           {
654: int w = a[i] - a[i - j];
 655:              file.print(w);
656: file.print(",");
 657:           }
 658:           
 659:           file.println();
 660:        }
 661:     }
 662:   
663: private int[] typeOf(final int[] a)
 664:     {
665: int[] t = new int[L1];
 666:   
667: for (int i = a.length - 1; i > 0; i--)
 668:        {
669: for (int j = 1; j < i; j++)
 670:           {
671: int d = a[i] - a[i - j];
 672:              t[d] = t[d] + 1;
 673:           }
 674:        }
675: return t;
 676:     }
 677:   
678: private void printTypeOf(final int[] a)
 679:     {
680: int[] t = typeOf(a);
 681:   
682: file.print("|");
 683:   
684: if (t.length < 8)
 685:        {
686: for (int i = 1; i < t.length; i++)
 687:           {
688: file.print(i + "^" + t[i] + "|");
 689:           }
 690:        }
691: else
 692:        {
693: for (int i = 1; i < t.length; i++)
 694:           {
695: if (t[i] > 1)
 696:              {
697: file.print(i + "^" + t[i] + "|");
 698:              }
 699:           }
 700:        }
 701:        file.println();
 702:     }
 703:  }
 704:   
705: /**
706: * StopWatch
707: *
708: * @author Peter Luschny
709: * @version 2001-05-12
710: */
711: class StopWatch
 712:  {
713: private long elapsedCount = 0;
714: private long startCount = 0;
 715:   
716: /**
717: * Start the time-counter.
718: *
719: */
720: public void start()
 721:     {
 722:        startCount = System.currentTimeMillis();
 723:     }
 724:   
725: public void restart()
 726:     {
 727:        elapsedCount = 0;
 728:        startCount = System.currentTimeMillis();
 729:     }
 730:   
731: /**
732: * Stop the time-counter
733: *
734: */
735: public void stop()
 736:     {
737: long stopCount = System.currentTimeMillis();
 738:        elapsedCount += (stopCount - startCount);
 739:     }
 740:   
741: /**
742: * Clear the elapsed time-counter.
743: *
744: */
745: public void clear()
 746:     {
 747:        elapsedCount = 0;
 748:     }
 749:   
750: /**
751: * Get the elapsed time converted to seconds.
752: *
753: */
754: public double getSeconds()
 755:     {
756: return elapsedCount / 1000.0;
 757:     }
 758:   
759: /**
760: * Get the elapsed time as a formatted string.
761: *
762: */
 763:     @Override
764: public String toString()
 765:     {
766: return getSeconds() + " sec.";
 767:     }
 768:  }
 Copyright: Rulez by Peter Luschny
is licensed under a
  Copyright: Rulez by Peter Luschny
is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 License.
Please report bugs and comments to: peter (at) luschny (point) de.