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