Rev 20 | Rev 23 | 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"; |
20 | daniel-mar | 21 | private static final String SIGNATURE_MINOR = "Iterator GenX Java (100k save-interval, load-integrity-check, int32-r, array-object) r20"; |
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 | |
15 | daniel-mar | 33 | public ImmortableNumberSearch(String filename) throws LoadException { |
20 | daniel-mar | 34 | this.a = new ByteArray(1000000, 1000000); |
14 | daniel-mar | 35 | this.filename = filename; |
36 | load(); |
||
37 | } |
||
38 | |||
15 | daniel-mar | 39 | public String getBackupDir() { |
40 | return backupDir; |
||
41 | } |
||
42 | |||
43 | public void setBackupDir(String backupDir) { |
||
44 | this.backupDir = backupDir; |
||
45 | } |
||
46 | |||
14 | daniel-mar | 47 | public int getSaveInterval() { |
48 | return this.saveInterval; |
||
49 | } |
||
50 | |||
20 | daniel-mar | 51 | public ByteArray getA() { |
15 | daniel-mar | 52 | return a; |
53 | } |
||
54 | |||
55 | public int getU() { |
||
56 | return u; |
||
57 | } |
||
58 | |||
59 | public int getR() { |
||
60 | return r; |
||
61 | } |
||
62 | |||
63 | public String getFilename() { |
||
64 | return filename; |
||
65 | } |
||
66 | |||
14 | daniel-mar | 67 | public void setSaveInterval(int saveInterval) { |
68 | this.saveInterval = saveInterval; |
||
69 | } |
||
70 | |||
71 | private boolean savePointExists() { |
||
72 | return new File(filename).exists(); |
||
73 | } |
||
74 | |||
15 | daniel-mar | 75 | private void load() throws LoadException { |
76 | if (!savePointExists()) { |
||
14 | daniel-mar | 77 | return; |
15 | daniel-mar | 78 | } |
14 | daniel-mar | 79 | |
15 | daniel-mar | 80 | BufferedReader f; |
81 | try { |
||
82 | f = new BufferedReader(new FileReader(filename)); |
||
83 | } catch (FileNotFoundException e) { |
||
84 | // Will not happen because of savePointExists(). |
||
85 | return; |
||
86 | } |
||
14 | daniel-mar | 87 | |
20 | daniel-mar | 88 | a.clear(); |
15 | daniel-mar | 89 | u = -1; |
90 | r = -1; |
||
91 | |||
92 | try { |
||
93 | try { |
||
94 | String s = f.readLine(); |
||
95 | if (!s.equals(SIGNATURE)) { |
||
96 | throw new LoadException("Wrong signature"); |
||
97 | } |
||
98 | |||
99 | f.readLine(); // Minor. signature |
||
100 | f.readLine(); // "" |
||
17 | daniel-mar | 101 | |
15 | daniel-mar | 102 | f.readLine(); // "(Starting time)" |
103 | creation_time = f.readLine(); |
||
104 | f.readLine(); // "" |
||
105 | |||
106 | f.readLine(); // "(Save timestamp)" |
||
107 | f.readLine(); // Timestamp |
||
108 | f.readLine(); // "" |
||
109 | |||
19 | daniel-mar | 110 | f.readLine(); // "(Digits)" |
15 | daniel-mar | 111 | s = f.readLine(); |
20 | daniel-mar | 112 | u = Integer.parseInt(s) - 1; |
15 | daniel-mar | 113 | f.readLine(); // "" |
17 | daniel-mar | 114 | |
15 | daniel-mar | 115 | f.readLine(); // "(r)" |
116 | s = f.readLine(); |
||
117 | r = Integer.parseInt(s); // FUTURE: (1) Multi-Line-Support? |
||
118 | f.readLine(); // "" |
||
119 | |||
120 | f.readLine(); // "(M5rev)" |
||
121 | do { |
||
122 | s = f.readLine(); |
||
123 | for (int i = 0; i < s.length(); i++) { |
||
20 | daniel-mar | 124 | a.add(Byte.parseByte(s.substring(i, i + 1))); |
15 | daniel-mar | 125 | } |
126 | } while (!s.equals("")); |
||
127 | |||
128 | if (!integryTest()) { |
||
129 | throw new LoadException("Corrupt: Not immortable!"); |
||
130 | } |
||
131 | |||
20 | daniel-mar | 132 | if (u + 1 != a.count()) { |
15 | daniel-mar | 133 | throw new LoadException( |
134 | "Corrupt: Formal and actual length mismatch!"); |
||
135 | } |
||
136 | |||
137 | s = f.readLine(); |
||
138 | if (!s.equals(END_SIG)) { |
||
139 | throw new LoadException("Corrupt: End-signature mismatch."); |
||
140 | } |
||
141 | } finally { |
||
142 | f.close(); |
||
143 | } |
||
144 | } catch (IOException e) { |
||
145 | throw new LoadException(e); |
||
14 | daniel-mar | 146 | } |
15 | daniel-mar | 147 | } |
14 | daniel-mar | 148 | |
15 | daniel-mar | 149 | private void save(boolean integrityTest) throws SaveException { |
150 | if (integrityTest) { |
||
151 | if (!integryTest()) { |
||
17 | daniel-mar | 152 | throw new SaveException("Integrity test failed. Will not save."); |
14 | daniel-mar | 153 | } |
15 | daniel-mar | 154 | } |
155 | |||
156 | String timestamp = DateUtils.now("EEE, d MMM yyyy HH:mm:ss Z"); |
||
157 | String timestamp_filename = DateUtils.now("dd-MM-yyyy_HH-mm-ss"); |
||
17 | daniel-mar | 158 | |
15 | daniel-mar | 159 | try { |
160 | BufferedWriter f = new BufferedWriter(new FileWriter(filename)); |
||
14 | daniel-mar | 161 | |
15 | daniel-mar | 162 | try { |
163 | f.write(SIGNATURE + "\r\n"); |
||
14 | daniel-mar | 164 | |
15 | daniel-mar | 165 | f.write(SIGNATURE_MINOR + "\r\n"); |
166 | f.write("\r\n"); |
||
14 | daniel-mar | 167 | |
15 | daniel-mar | 168 | f.write("(Starting time)\r\n"); |
169 | f.write(creation_time + "\r\n"); |
||
170 | f.write("\r\n"); |
||
14 | daniel-mar | 171 | |
15 | daniel-mar | 172 | f.write("(Save timestamp)\r\n"); |
173 | f.write(timestamp + "\r\n"); |
||
174 | f.write("\r\n"); |
||
14 | daniel-mar | 175 | |
19 | daniel-mar | 176 | f.write("(Digits)\r\n"); |
20 | daniel-mar | 177 | f.write((u + 1) + "\r\n"); |
15 | daniel-mar | 178 | f.write("\r\n"); |
14 | daniel-mar | 179 | |
15 | daniel-mar | 180 | f.write("(r)\r\n"); |
181 | f.write(r + "\r\n"); // FUTURE: (1) Multi-Line-Support? |
||
14 | daniel-mar | 182 | f.write("\r\n"); |
15 | daniel-mar | 183 | |
184 | f.write("(M5rev)\r\n"); |
||
20 | daniel-mar | 185 | int i; |
186 | for (i = 0; i < a.count(); i++) { |
||
187 | byte xa = a.get(i); |
||
188 | f.write("" + xa); |
||
189 | if (i + 1 % SOFTBREAK == 0) { |
||
15 | daniel-mar | 190 | f.write("\r\n"); |
191 | } |
||
192 | } |
||
20 | daniel-mar | 193 | if (i + 1 % SOFTBREAK != 0) { |
15 | daniel-mar | 194 | f.write("\r\n"); |
195 | } |
||
196 | f.write("\r\n"); |
||
197 | |||
198 | f.write(END_SIG); |
||
199 | } finally { |
||
200 | f.close(); |
||
14 | daniel-mar | 201 | } |
15 | daniel-mar | 202 | } catch (IOException e) { |
203 | throw new SaveException("Could not save master file.", e); |
||
14 | daniel-mar | 204 | } |
15 | daniel-mar | 205 | |
206 | // Make backup |
||
207 | |||
208 | new File(backupDir).mkdir(); |
||
17 | daniel-mar | 209 | String bak_filename = backupDir + "/immortal_" + timestamp_filename |
210 | + "_" + (u + 1) + ".txt"; |
||
15 | daniel-mar | 211 | try { |
212 | FileUtils.copyFile(new File(filename), new File(bak_filename)); |
||
213 | } catch (IOException e) { |
||
214 | throw new SaveException("Could not make backup", e); |
||
14 | daniel-mar | 215 | } |
216 | } |
||
217 | |||
15 | daniel-mar | 218 | public void calcIterate(int amount) throws SaveException { |
14 | daniel-mar | 219 | int s, m, k; |
220 | int cnt = 0; |
||
221 | |||
15 | daniel-mar | 222 | // Wir führen beim ersten Speichern einen weiteren |
223 | // integrity-Test durch. Grund: Wäre bei einer Fortsetzung einer |
||
224 | // Datei das "r" falsch (der Datenteil aber korrekt), dann würde |
||
225 | // diese Datei beim Speichern ungültige Daten enthalten (Zahl |
||
226 | // nicht immortabel). |
||
227 | boolean firstSave = true; |
||
228 | |||
20 | daniel-mar | 229 | if (a.isEmpty()) { |
15 | daniel-mar | 230 | creation_time = DateUtils.now("EEE, d MMM yyyy HH:mm:ss Z"); |
20 | daniel-mar | 231 | a.add((byte) 5); |
232 | a.add((byte) 2); |
||
14 | daniel-mar | 233 | u = 1; |
234 | r = 2; |
||
235 | cnt += 2; |
||
236 | } |
||
237 | |||
15 | daniel-mar | 238 | do { |
20 | daniel-mar | 239 | r = (r - a.get(u)) / 10 + a.get(u); |
14 | daniel-mar | 240 | s = 0; |
241 | for (m = 1, k = u; m < k; m++, k--) { |
||
20 | daniel-mar | 242 | s += a.get(m) * a.get(k); |
14 | daniel-mar | 243 | } |
244 | r += 2 * s; |
||
245 | if (m == k) { |
||
20 | daniel-mar | 246 | r += a.get(m) * a.get(m); |
14 | daniel-mar | 247 | } |
248 | u++; |
||
20 | daniel-mar | 249 | a.add((byte) (r % 10)); |
14 | daniel-mar | 250 | |
17 | daniel-mar | 251 | cnt++; |
14 | daniel-mar | 252 | if (cnt % saveInterval == 0) { |
15 | daniel-mar | 253 | save(firstSave); |
254 | firstSave = false; |
||
14 | daniel-mar | 255 | } |
17 | daniel-mar | 256 | } while (cnt != amount); |
14 | daniel-mar | 257 | |
15 | daniel-mar | 258 | // Wir brauchen nicht zwei Mal zu speichern |
17 | daniel-mar | 259 | if (cnt - 1 % saveInterval != 0) { |
15 | daniel-mar | 260 | save(firstSave); |
261 | firstSave = false; |
||
262 | } |
||
263 | } |
||
264 | |||
20 | daniel-mar | 265 | public byte getDigitReverse(int u) { |
266 | return a.get(u); |
||
15 | daniel-mar | 267 | } |
268 | |||
269 | public StringBuilder M5_StringBuilder(int u) { |
||
270 | StringBuilder s = new StringBuilder(); |
||
20 | daniel-mar | 271 | for (int i = 0; i < a.count(); i++) { |
272 | byte xa = a.get(i); |
||
273 | s.append("" + xa); |
||
15 | daniel-mar | 274 | } |
275 | s.reverse(); |
||
276 | return s; |
||
277 | } |
||
278 | |||
279 | public String M5_String(int u) { |
||
280 | return M5_StringBuilder(u).toString(); |
||
281 | } |
||
282 | |||
283 | public BigInteger M5_BigInteger(int u) { |
||
284 | return new BigInteger(M5_String(u)); |
||
285 | } |
||
286 | |||
287 | public StringBuilder M6_StringBuilder(int u) { |
||
288 | StringBuilder s = new StringBuilder(); |
||
289 | boolean first = true; |
||
20 | daniel-mar | 290 | for (int i = 0; i < a.count(); i++) { |
291 | byte xa = a.get(i); |
||
15 | daniel-mar | 292 | if (first) { |
293 | s.append(6); // xa = 5 |
||
294 | first = false; |
||
295 | } else { |
||
20 | daniel-mar | 296 | s.append("" + (9 - xa)); |
14 | daniel-mar | 297 | } |
298 | } |
||
15 | daniel-mar | 299 | s.reverse(); |
300 | return s; |
||
14 | daniel-mar | 301 | } |
302 | |||
15 | daniel-mar | 303 | public String M6_String(int u) { |
304 | return M6_StringBuilder(u).toString(); |
||
305 | } |
||
306 | |||
307 | public BigInteger M6_BigInteger(int u) { |
||
308 | return new BigInteger(M6_String(u)); |
||
309 | } |
||
310 | |||
311 | private static boolean isImmortable(BigInteger num) { |
||
312 | // Ist das auch OK? |
||
313 | // return num.pow(2).toString().endsWith(num.toString()); |
||
314 | |||
315 | // n² === n (mod 10^m) <===> n²%10^m == n%10^m |
||
316 | int m = num.toString().length(); |
||
317 | BigInteger m_pow = BigInteger.TEN.pow(m); |
||
318 | return num.pow(2).mod(m_pow).equals(num.mod(m_pow)); |
||
319 | |||
320 | } |
||
321 | |||
322 | public boolean integryTest() { |
||
323 | // return isImmortable(M5_BigInteger(u)); |
||
324 | return isImmortable(M6_BigInteger(u)); |
||
325 | } |
||
14 | daniel-mar | 326 | } |