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