Subversion Repositories distributed

Rev

Rev 37 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. package de.viathinksoft.immortal.genX;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.BufferedWriter;
  5. import java.io.File;
  6. import java.io.FileNotFoundException;
  7. import java.io.FileReader;
  8. import java.io.FileWriter;
  9. import java.io.IOException;
  10. import java.math.BigInteger;
  11.  
  12. /**
  13.  * Immortable Iterator
  14.  *
  15.  * @author Daniel Marschall, Big thanks to the users of MatheBoard.de
  16.  * @see http://www.matheboard.de/archive/435725/thread.html
  17.  */
  18. public class ImmortalNumberSearch {
  19.  
  20.         private static final String SIGNATURE = "Immortal Number Report File Version 2.02";
  21.         private static final String SIGNATURE_MINOR = "Iterator GenX Java (100k save-interval, load-integrity-check, int32-r, array-object) r38";
  22.         private static final String END_SIG = "END OF REPORT";
  23.         private static final int SOFTBREAK = 76;
  24.  
  25.         private ByteArray a;
  26.         private String filename;
  27.         private int saveInterval = 100000;
  28.         private int u = -1;
  29.         private int r = -1; // FUTURE: r als BigInteger
  30.         private String creation_time;
  31.         private String backupDir = "backup";
  32.  
  33.         private static final int INITIAL_SIZE = 1000000;
  34.         private static final int EXPANSION_SIZE = 1000000;
  35.  
  36.         private boolean do_integrity_test = true;
  37.         private boolean verbose = true;
  38.  
  39.         public boolean isDo_integrity_test() {
  40.                 return do_integrity_test;
  41.         }
  42.  
  43.         public void setDo_integrity_test(boolean doIntegrityTest) {
  44.                 do_integrity_test = doIntegrityTest;
  45.         }
  46.  
  47.         public boolean isVerbose() {
  48.                 return verbose;
  49.         }
  50.  
  51.         public void setVerbose(boolean verbose) {
  52.                 this.verbose = verbose;
  53.         }
  54.  
  55.         protected void log(String s) {
  56.                 if (verbose) {
  57.                         String timestamp = DateUtils.now("EEE, d MMM yyyy HH:mm:ss Z");
  58.                         // FUTURE: In eine Log-Datei schreiben
  59.                         System.out.println(timestamp + " - " + s);
  60.                 }
  61.         }
  62.  
  63.         public ImmortalNumberSearch(String filename) throws LoadException {
  64.                 this.filename = filename;
  65.                 load();
  66.         }
  67.  
  68.         public String getBackupDir() {
  69.                 return backupDir;
  70.         }
  71.  
  72.         public void setBackupDir(String backupDir) {
  73.                 this.backupDir = backupDir;
  74.         }
  75.  
  76.         public int getSaveInterval() {
  77.                 return this.saveInterval;
  78.         }
  79.  
  80.         public ByteArray getA() {
  81.                 return a;
  82.         }
  83.  
  84.         public int getU() {
  85.                 return u;
  86.         }
  87.  
  88.         public int getR() {
  89.                 return r;
  90.         }
  91.  
  92.         public String getFilename() {
  93.                 return filename;
  94.         }
  95.  
  96.         public void setSaveInterval(int saveInterval) {
  97.                 this.saveInterval = saveInterval;
  98.         }
  99.  
  100.         private boolean savePointExists() {
  101.                 return new File(filename).exists();
  102.         }
  103.  
  104.         private void load() throws LoadException {
  105.                 if (!savePointExists()) {
  106.                         this.a = new ByteArray(INITIAL_SIZE, EXPANSION_SIZE);
  107.                         return;
  108.                 }
  109.  
  110.                 BufferedReader f;
  111.                 try {
  112.                         f = new BufferedReader(new FileReader(filename));
  113.                 } catch (FileNotFoundException e) {
  114.                         // Will not happen because of savePointExists().
  115.                         return;
  116.                 }
  117.  
  118.                 u = -1;
  119.                 r = -1;
  120.  
  121.                 try {
  122.                         try {
  123.                                 String s = f.readLine();
  124.                                 if (!s.equals(SIGNATURE)) {
  125.                                         throw new LoadException("Corrupt: Wrong signature");
  126.                                 }
  127.  
  128.                                 f.readLine(); // Minor. signature
  129.                                 f.readLine(); // ""
  130.  
  131.                                 f.readLine(); // "(Starting time)"
  132.                                 creation_time = f.readLine();
  133.                                 f.readLine(); // ""
  134.  
  135.                                 f.readLine(); // "(Save timestamp)"
  136.                                 f.readLine(); // Timestamp
  137.                                 f.readLine(); // ""
  138.  
  139.                                 f.readLine(); // "(Digits)"
  140.                                 s = f.readLine();
  141.                                 u = Integer.parseInt(s) - 1;
  142.                                 f.readLine(); // ""
  143.  
  144.                                 f.readLine(); // "(r)"
  145.                                 s = f.readLine();
  146.                                 r = Integer.parseInt(s); // FUTURE: (1) Multi-Line-Support?
  147.                                 f.readLine(); // ""
  148.  
  149.                                 this.a = new ByteArray(u + 1 + EXPANSION_SIZE, EXPANSION_SIZE);
  150.  
  151.                                 f.readLine(); // "(M5 reversed)"
  152.                                 do {
  153.                                         s = f.readLine();
  154.                                         for (int i = 0; i < s.length(); i++) {
  155.                                                 a.add(Byte.parseByte(s.substring(i, i + 1)));
  156.                                         }
  157.                                 } while (!s.equals(""));
  158.  
  159.                                 if (u + 1 != a.count()) {
  160.                                         throw new LoadException(
  161.                                                         "Corrupt: Formal and actual length mismatch!");
  162.                                 }
  163.  
  164.                                 s = f.readLine();
  165.                                 if (!s.equals(END_SIG)) {
  166.                                         throw new LoadException("Corrupt: End-signature mismatch.");
  167.                                 }
  168.                         } finally {
  169.                                 f.close();
  170.                         }
  171.                 } catch (IOException e) {
  172.                         throw new LoadException(e);
  173.                 }
  174.         }
  175.  
  176.         private void save(boolean integrityTest) throws SaveException {
  177.                 if ((integrityTest) && (do_integrity_test)) {
  178.                         log("Beginne Selbsttest vor erster Speicherung...");
  179.  
  180.                         if (!integryTest()) {
  181.                                 throw new SaveException(
  182.                                                 "Integrity test failed. (Loaded file broken?) Will not save.");
  183.                         }
  184.                 }
  185.  
  186.                 log("Speichere bei " + (u + 1) + " digits");
  187.  
  188.                 String timestamp = DateUtils.now("EEE, d MMM yyyy HH:mm:ss Z");
  189.                 String timestamp_filename = DateUtils.now("dd-MM-yyyy_HH-mm-ss");
  190.  
  191.                 try {
  192.                         BufferedWriter f = new BufferedWriter(new FileWriter(filename));
  193.  
  194.                         try {
  195.                                 f.write(SIGNATURE + "\r\n");
  196.  
  197.                                 f.write(SIGNATURE_MINOR + "\r\n");
  198.                                 f.write("\r\n");
  199.  
  200.                                 f.write("(Starting time)\r\n");
  201.                                 f.write(creation_time + "\r\n");
  202.                                 f.write("\r\n");
  203.  
  204.                                 f.write("(Save timestamp)\r\n");
  205.                                 f.write(timestamp + "\r\n");
  206.                                 f.write("\r\n");
  207.  
  208.                                 f.write("(Digits)\r\n");
  209.                                 f.write((u + 1) + "\r\n");
  210.                                 f.write("\r\n");
  211.  
  212.                                 f.write("(r)\r\n");
  213.                                 f.write(r + "\r\n"); // FUTURE: (1) Multi-Line-Support?
  214.                                 f.write("\r\n");
  215.  
  216.                                 f.write("(M5 reversed)\r\n");
  217.                                 int i;
  218.                                 for (i = 0; i < a.count(); i++) {
  219.                                         byte xa = a.get(i);
  220.                                         f.write("" + xa);
  221.                                         if ((i + 1) % SOFTBREAK == 0) {
  222.                                                 f.write("\r\n");
  223.                                         }
  224.                                 }
  225.                                 if (i % SOFTBREAK != 0) { /* nicht +1, da i++ am Ende */
  226.                                         f.write("\r\n");
  227.                                 }
  228.                                 f.write("\r\n");
  229.  
  230.                                 f.write(END_SIG);
  231.                                 f.write("\r\n");
  232.                         } finally {
  233.                                 f.close();
  234.                         }
  235.                 } catch (IOException e) {
  236.                         throw new SaveException("Could not save master file.", e);
  237.                 }
  238.  
  239.                 // Make backup
  240.  
  241.                 new File(backupDir).mkdir();
  242.                 String bak_filename = backupDir + "/immortal_" + timestamp_filename
  243.                                 + "_" + (u + 1) + ".txt";
  244.                 try {
  245.                         FileUtils.copyFile(new File(filename), new File(bak_filename));
  246.                 } catch (IOException e) {
  247.                         throw new SaveException("Could not make backup", e);
  248.                 }
  249.  
  250.                 if ((integrityTest) && (do_integrity_test)) {
  251.                         log("Erste Speicherung absolviert. Stabile Phase hat begonnen.");
  252.                 }
  253.         }
  254.  
  255.         public void calcIterate(int amount) throws SaveException {
  256.                 log("Beginn der Rechenarbeit...");
  257.  
  258.                 int cnt = 0;
  259.  
  260.                 // Wir führen beim ersten Speichern einen weiteren
  261.                 // integrity-Test durch. Grund: Wäre bei einer Fortsetzung einer
  262.                 // Datei das "r" falsch (der Datenteil aber korrekt), dann würde
  263.                 // diese Datei beim Speichern ungültige Daten enthalten (Zahl
  264.                 // nicht immortabel).
  265.                 boolean firstSave = true;
  266.  
  267.                 if (a.isEmpty()) {
  268.                         creation_time = DateUtils.now("EEE, d MMM yyyy HH:mm:ss Z");
  269.                         a.add((byte) 5);
  270.                         a.add((byte) 2);
  271.                         u = 1;
  272.                         r = 2;
  273.                         cnt += 2;
  274.                 }
  275.  
  276.                 int s, m, k;
  277.  
  278.                 do {
  279.                         r = (r /* - a.get(u) */) / 10 + a.get(u);
  280.                         s = 0;
  281.                         for (m = 1, k = u; m < k; m++, k--) {
  282.                                 s += a.get(m) * a.get(k);
  283.                         }
  284.                         r += 2 * s;
  285.                         if (m == k) {
  286.                                 r += a.get(m) * a.get(m);
  287.                         }
  288.                         u++;
  289.                         a.add((byte) (r % 10));
  290.  
  291.                         cnt++;
  292.                         if (cnt % saveInterval == 0) {
  293.                                 save(firstSave);
  294.                                 firstSave = false;
  295.                         }
  296.                 } while (cnt != amount);
  297.  
  298.                 // Wir brauchen nicht zwei Mal zu speichern
  299.                 if (cnt - 1 % saveInterval != 0) {
  300.                         log("Letzte Speicherung vor Ende...");
  301.                         save(firstSave);
  302.                         // firstSave = false;
  303.                 }
  304.  
  305.                 log("Ende der Rechenarbeit...");
  306.         }
  307.  
  308.         public byte getDigitReverse(int u) {
  309.                 return a.get(u);
  310.         }
  311.  
  312.         public StringBuilder M5_StringBuilder(int u) {
  313.                 StringBuilder s = new StringBuilder();
  314.                 for (int i = 0; i < a.count(); i++) {
  315.                         byte xa = a.get(i);
  316.                         s.append("" + xa);
  317.                 }
  318.                 s.reverse();
  319.                 return s;
  320.         }
  321.  
  322.         public String M5_String(int u) {
  323.                 return M5_StringBuilder(u).toString();
  324.         }
  325.  
  326.         public BigInteger M5_BigInteger(int u) {
  327.                 return new BigInteger(M5_String(u));
  328.         }
  329.  
  330.         public StringBuilder M6_StringBuilder(int u) {
  331.                 StringBuilder s = new StringBuilder();
  332.                 for (int i = 0; i < a.count(); i++) {
  333.                         byte xa = a.get(i);
  334.                         if (i > 0) {
  335.                                 s.append("" + (9 - xa));
  336.                         } else {
  337.                                 s.append("" + (11 - xa));
  338.                         }
  339.                 }
  340.                 s.reverse();
  341.                 return s;
  342.         }
  343.  
  344.         public String M6_String(int u) {
  345.                 return M6_StringBuilder(u).toString();
  346.         }
  347.  
  348.         public BigInteger M6_BigInteger(int u) {
  349.                 return new BigInteger(M6_String(u));
  350.         }
  351.  
  352.         protected static boolean isImmortable(BigInteger num, int m) {
  353.                 // Alternativ
  354.                 // return num.pow(2).toString().endsWith(num.toString());
  355.  
  356.                 // n² === n (mod 10^m) <===> n²%10^m == n%10^m == n
  357.  
  358.                 BigInteger m_pow = BigInteger.TEN.pow(m);
  359.                 return num.pow(2).mod(m_pow).equals(num);
  360.         }
  361.  
  362.         protected static boolean isImmortable(BigInteger num) {
  363.                 int m = num.toString().length();
  364.                 return isImmortable(num, m);
  365.         }
  366.  
  367.         public boolean integryTest() {
  368.                 // return isImmortable(M5_BigInteger(u), a.count());
  369.                 return isImmortable(M6_BigInteger(u), a.count());
  370.         }
  371. }
  372.