OSDN Git Service

04cf95628061f7018ee72794e32cdce6824b4a8b
[mhash384/mhash384.git] / tools / GenTables / src / gen_table_xor.c
1 /* ----------------------------------------------------------------------------------------------- */
2 /* MHash-384 - Generate tables utility program                                                     */
3 /* Copyright(c) 2016-2017 LoRd_MuldeR <mulder2@gmx.de>                                             */
4 /*                                                                                                 */
5 /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software   */
6 /* and associated documentation files(the "Software"), to deal in the Software without             */
7 /* restriction, including without limitation the rights to use, copy, modify, merge, publish,      */
8 /* distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the */
9 /* Software is furnished to do so, subject to the following conditions:                            */
10 /*                                                                                                 */
11 /* The above copyright notice and this permission notice shall be included in all copies or        */
12 /* substantial portions of the Software.                                                           */
13 /*                                                                                                 */
14 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING   */
15 /* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND      */
16 /* NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,     */
17 /* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  */
18 /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.         */
19 /* ----------------------------------------------------------------------------------------------- */
20
21 #include "common.h"
22 #include "thread_utils.h"
23 #include "msws.h"
24 #include "boxmuller.h"
25
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <time.h>
30 #include <stdbool.h>
31 #include <io.h>
32 #include <float.h>
33 #include <math.h>
34
35 //-----------------------------------------------------------------------------
36 // Const
37 //-----------------------------------------------------------------------------
38
39 #define HASH_LEN 384U
40 #define DISTANCE_MIN 190U
41
42 #define THREAD_COUNT 8U
43
44 #undef ENABLE_TRACE
45
46 #define ROW_NUM (UINT8_MAX+2)           /*total number of rows*/
47 #define ROW_LEN (HASH_LEN / CHAR_BIT)   /*number of bits per row*/
48
49 #define __DISTANCE_STR(X) #X
50 #define _DISTANCE_STR(X) __DISTANCE_STR(X)
51 #define DISTANCE_STR _DISTANCE_STR(DISTANCE_MIN)
52
53 #define MAGIC_NUMBER 0x3C6058A7C1132CB2ui64
54 #define THREAD_ID (pthread_getw32threadid_np(pthread_self()))
55
56 //-----------------------------------------------------------------------------
57 // Globals
58 //-----------------------------------------------------------------------------
59
60 static uint8_t g_table[ROW_NUM][ROW_LEN];
61 static uint8_t g_thread_buffer[THREAD_COUNT][ROW_LEN];
62
63 static const char SPINNER[4] = { '/', '-', '\\', '|' };
64 static const double SQRT2 = 1.41421356237309504880168872420969807856967187537694;
65
66 static size_t g_spinpos = 0;
67
68 //-----------------------------------------------------------------------------
69 // Utility Functions
70 //-----------------------------------------------------------------------------
71
72 #ifdef ENABLE_TRACE
73 #define TRACE(X, ...) printf("[%04X] " X "\n", THREAD_ID, __VA_ARGS__)
74 #else
75 #define TRACE(X, ...) __noop()
76 #endif
77
78 static inline void print_row(uint8_t *const row_buffer)
79 {
80         for (size_t w = 0; w < ROW_LEN; ++w)
81         {
82                 printf("%02X", row_buffer[w]);
83         }
84         puts("");
85 }
86
87 static inline void flip_bit_at(uint8_t *const row_buffer, const size_t pos)
88 {
89         row_buffer[pos >> 3] ^= ((uint8_t)(1U << (pos & 0x7)));
90 }
91
92 static inline void flip_rand_n(uint8_t *const row_buffer, msws_t rand, const uint32_t n)
93 {
94         bool taken[HASH_LEN];
95         memset(&taken, 0, sizeof(bool) * HASH_LEN);
96         for (uint_fast32_t i = 0; i < n; ++i)
97         {
98                 size_t next;
99                 do
100                 {
101                         next = msws_uint32_max(rand, HASH_LEN);
102                 }
103                 while (taken[next]);
104                 flip_bit_at(row_buffer, next);
105                 taken[next] = true;
106         }
107 }
108
109 static inline bool check_distance_rows(const uint_fast32_t distance_max, const size_t index_1, const size_t index_2)
110 {
111         const uint_fast32_t dist = hamming_distance(&g_table[index_1][0], &g_table[index_2][0], ROW_LEN);
112         return (dist <= distance_max) && (dist >= DISTANCE_MIN);
113 }
114
115 #define ERROR_ACC(MAX,ACC) (((MAX) << 20U) | (ACC))
116 static inline uint_fast32_t check_distance_buff(const uint_fast32_t distance_max, const size_t index, const uint8_t *const row_buffer, const uint32_t limit)
117 {
118         uint_fast32_t error_max = 0U, error_acc = 0U;
119         for (size_t k = 0; k < index; k++)
120         {
121                 const uint_fast32_t dist = hamming_distance(&g_table[k][0], row_buffer, ROW_LEN);
122                 if (dist > distance_max)
123                 {
124                         const uint_fast32_t current = dist - distance_max;
125                         error_acc += current;
126                         if (current > error_max)
127                         {
128                                 error_max = current;
129                         }
130                         if (ERROR_ACC(error_max, error_acc) >= limit)
131                         {
132                                 break; /*early termination*/
133                         }
134                 }
135                 else if (dist < DISTANCE_MIN)
136                 {
137                         const uint_fast32_t current = DISTANCE_MIN - dist;
138                         error_acc += current;
139                         if (current > error_max)
140                         {
141                                 error_max = current;
142                         }
143                         if (ERROR_ACC(error_max, error_acc) >= limit)
144                         {
145                                 break; /*early termination*/
146                         }
147                 }
148         }
149         return ERROR_ACC(error_max, error_acc);
150 }
151
152 static void dump_table(FILE *out)
153 {
154         fputs("uint8_t MHASH_384_TABLE_XOR[UINT8_MAX+2][MHASH_384_LEN] =\n{\n", out);
155         for (size_t i = 0; i < ROW_NUM; i++)
156         {
157                 fputs("\t{ ", out);
158                 for (size_t j = 0; j < ROW_LEN; j++)
159                 {
160                         if (j > 0)
161                         {
162                                 fputc(',', out);
163                         }
164                         fprintf(out, "0x%02X", g_table[i][j]);
165                 }
166                 fprintf(out, " }%s /*%02X*/\n", (i != (ROW_NUM - 1)) ? "," : " ", (uint32_t)(i % 0x100));
167         }
168         fputs("};\n", out);
169 }
170
171 //-----------------------------------------------------------------------------
172 // Thread function
173 //-----------------------------------------------------------------------------
174
175 typedef struct
176 {
177         size_t index;
178         uint8_t *row_buffer;
179         sem_t *stop;
180         pthread_mutex_t *mutex;
181         uint_fast32_t distance_max;
182 }
183 thread_data_t;
184
185 static void* thread_main(void *const param)
186 {
187         thread_data_t *const data = (thread_data_t*)param;
188         msws_t rand;
189         bxmller_t bxmller;
190         uint8_t temp[ROW_LEN];
191         for(;;)
192         {
193                 TRACE("Maximum distance: %u", data->distance_max);
194                 msws_init(rand, make_seed());
195                 gaussian_noise_init(&bxmller);
196                 msws_bytes(rand, data->row_buffer, ROW_LEN);
197                 uint_fast32_t error = check_distance_buff(data->distance_max, data->index, data->row_buffer, HASH_LEN);
198                 if(error > 0U)
199                 {
200                         for (int_fast16_t round = 0; round < 29989; ++round)
201                         {
202                                 if (!(round & 0x3FF))
203                                 {
204                                         if (SEM_TRYWAIT(data->stop))
205                                         {
206                                                 return NULL;
207                                         }
208                                 }
209                                 for (uint_fast16_t rand_loop = 0; rand_loop < 99991U; ++rand_loop)
210                                 {
211                                         msws_bytes(rand, temp, ROW_LEN);
212                                         const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, temp, error);
213                                         if (next_error < error)
214                                         {
215                                                 TRACE("Improved by rand-init (%08X -> %08X)", error, next_error);
216                                                 round = -1;
217                                                 memcpy(data->row_buffer, temp, sizeof(uint8_t) * ROW_LEN);
218                                                 if (!((error = next_error) > 0U))
219                                                 {
220                                                         TRACE("Success by rand-init <<<---");
221                                                         goto success;
222                                                 }
223                                         }
224                                 }
225                         }
226                         for (int_fast16_t round = 0; round < 997; ++round)
227                         {
228                                 TRACE("Optimizer round %u of 997", round + 1);
229                                 if (!round)
230                                 {
231                                         for (uint_fast16_t xchg_pos = 0U; xchg_pos < ROW_LEN; ++xchg_pos)
232                                         {
233                                                 uint8_t value = (uint8_t)msws_uint32(rand);
234                                                 uint8_t original = data->row_buffer[xchg_pos];
235                                                 for (uint_fast16_t xchg_cnt = 0U; xchg_cnt <= UINT8_MAX; ++xchg_cnt, ++value)
236                                                 {
237                                                         data->row_buffer[xchg_pos] = value;
238                                                         const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, data->row_buffer, error);
239                                                         if (next_error < error)
240                                                         {
241                                                                 TRACE("Improved by xchg-byte (%08X -> %08X)", error, next_error);
242                                                                 original = value;
243                                                                 round = -1;
244                                                                 if (!((error = next_error) > 0U))
245                                                                 {
246                                                                         TRACE("Success by xchg-byte <<<---");
247                                                                         goto success;
248                                                                 }
249                                                         }
250                                                 }
251                                                 data->row_buffer[xchg_pos] = original;
252                                         }
253                                         for (uint_fast16_t flip_pos_w = 0U; flip_pos_w < HASH_LEN; ++flip_pos_w)
254                                         {
255                                                 if (SEM_TRYWAIT(data->stop))
256                                                 {
257                                                         return NULL;
258                                                 }
259                                                 flip_bit_at(data->row_buffer, flip_pos_w);
260                                                 bool revert_w = true;
261                                                 const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, data->row_buffer, error);
262                                                 if (next_error < error)
263                                                 {
264                                                         TRACE("Improved by flip-1 (%08X -> %08X)", error, next_error);
265                                                         revert_w = false;
266                                                         round = -1;
267                                                         if (!((error = next_error) > 0U))
268                                                         {
269                                                                 TRACE("success by flip-1 <<<---");
270                                                                 goto success;
271                                                         }
272                                                 }
273                                                 for (uint_fast16_t flip_pos_x = flip_pos_w + 1U; flip_pos_x < HASH_LEN; ++flip_pos_x)
274                                                 {
275                                                         flip_bit_at(data->row_buffer, flip_pos_x);
276                                                         bool revert_x = true;
277                                                         const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, data->row_buffer, error);
278                                                         if (next_error < error)
279                                                         {
280                                                                 TRACE("Improved by flip-2 (%08X -> %08X)", error, next_error);
281                                                                 revert_w = false;
282                                                                 revert_x = false;
283                                                                 round = -1;
284                                                                 if (!((error = next_error) > 0U))
285                                                                 {
286                                                                         TRACE("success by flip-2 <<<---");
287                                                                         goto success;
288                                                                 }
289                                                         }
290                                                         for (uint_fast16_t flip_pos_y = flip_pos_x + 1U; flip_pos_y < HASH_LEN; ++flip_pos_y)
291                                                         {
292                                                                 flip_bit_at(data->row_buffer, flip_pos_y);
293                                                                 bool revert_y = true;
294                                                                 const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, data->row_buffer, error);
295                                                                 if (next_error < error)
296                                                                 {
297                                                                         TRACE("Improved by flip-3 (%08X -> %08X)", error, next_error);
298                                                                         revert_w = false;
299                                                                         revert_x = false;
300                                                                         revert_y = false;
301                                                                         round = -1;
302                                                                         if (!((error = next_error) > 0U))
303                                                                         {
304                                                                                 TRACE("success by flip-3 <<<---");
305                                                                                 goto success;
306                                                                         }
307                                                                 }
308                                                                 for (uint_fast16_t flip_pos_z = flip_pos_y + 1U; flip_pos_z < HASH_LEN; ++flip_pos_z)
309                                                                 {
310                                                                         flip_bit_at(data->row_buffer, flip_pos_z);
311                                                                         const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, data->row_buffer, error);
312                                                                         if (next_error < error)
313                                                                         {
314                                                                                 TRACE("Improved by flip-4 (%08X -> %08X)", error, next_error);
315                                                                                 revert_w = false;
316                                                                                 revert_x = false;
317                                                                                 revert_y = false;
318                                                                                 round = -1;
319                                                                                 if (!((error = next_error) > 0U))
320                                                                                 {
321                                                                                         TRACE("success by flip-4 <<<---");
322                                                                                         goto success;
323                                                                                 }
324                                                                         }
325                                                                         else
326                                                                         {
327                                                                                 flip_bit_at(data->row_buffer, flip_pos_z);
328                                                                         }
329                                                                 }
330                                                                 if (revert_y)
331                                                                 {
332                                                                         flip_bit_at(data->row_buffer, flip_pos_y);
333                                                                 }
334                                                         }
335                                                         if (revert_x)
336                                                         {
337                                                                 flip_bit_at(data->row_buffer, flip_pos_x);
338                                                         }
339                                                 }
340                                                 if (revert_w)
341                                                 {
342                                                         flip_bit_at(data->row_buffer, flip_pos_w);
343                                                 }
344                                         }
345                                 }
346                                 for (uint_fast16_t rand_mode = 0; rand_mode < 15U; ++rand_mode)
347                                 {
348                                         memcpy(temp, &data->row_buffer, sizeof(uint8_t) * ROW_LEN);
349                                         for (uint_fast16_t rand_loop = 0; rand_loop < 29927U; ++rand_loop)
350                                         {
351                                                 switch (rand_mode)
352                                                 {
353                                                         case 0x0: msws_bytes(rand, &temp[0U * (ROW_LEN / 1U)], ROW_LEN / 1U); break;
354                                                         case 0x1: msws_bytes(rand, &temp[0U * (ROW_LEN / 2U)], ROW_LEN / 2U); break;
355                                                         case 0x2: msws_bytes(rand, &temp[1U * (ROW_LEN / 2U)], ROW_LEN / 2U); break;
356                                                         case 0x3: msws_bytes(rand, &temp[0U * (ROW_LEN / 4U)], ROW_LEN / 4U); break;
357                                                         case 0x4: msws_bytes(rand, &temp[1U * (ROW_LEN / 4U)], ROW_LEN / 4U); break;
358                                                         case 0x5: msws_bytes(rand, &temp[2U * (ROW_LEN / 4U)], ROW_LEN / 4U); break;
359                                                         case 0x6: msws_bytes(rand, &temp[3U * (ROW_LEN / 4U)], ROW_LEN / 4U); break;
360                                                         case 0x7: msws_bytes(rand, &temp[0U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
361                                                         case 0x8: msws_bytes(rand, &temp[1U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
362                                                         case 0x9: msws_bytes(rand, &temp[2U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
363                                                         case 0xA: msws_bytes(rand, &temp[3U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
364                                                         case 0xB: msws_bytes(rand, &temp[4U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
365                                                         case 0xC: msws_bytes(rand, &temp[5U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
366                                                         case 0xD: msws_bytes(rand, &temp[6U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
367                                                         case 0xE: msws_bytes(rand, &temp[7U * (ROW_LEN / 8U)], ROW_LEN / 8U); break;
368                                                         default: abort();
369                                                 }
370                                                 const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, temp, error);
371                                                 if (next_error < error)
372                                                 {
373                                                         TRACE("Improved by rand-replace [%u] (%08X -> %08X)", rand_mode, error, next_error);
374                                                         round = -1;
375                                                         memcpy(data->row_buffer, temp, sizeof(uint8_t) * ROW_LEN);
376                                                         if (!((error = next_error) > 0U))
377                                                         {
378                                                                 TRACE("Success by rand-replace <<<---");
379                                                                 goto success;
380                                                         }
381                                                 }
382                                         }
383                                 }
384                                 const double sigma = SQRT2 * (1.0 + (((double)round) / 498.5));
385                                 for (uint_fast16_t refine_loop = 0; refine_loop < 9973U; ++refine_loop)
386                                 {
387                                         if (!(refine_loop & 0x3FF))
388                                         {
389                                                 if (SEM_TRYWAIT(data->stop))
390                                                 {
391                                                         return NULL;
392                                                 }
393                                         }
394                                         const uint32_t flip_count = gaussian_noise_next(rand, &bxmller, sigma, 5U, HASH_LEN);
395                                         for (size_t refine_step = 0; refine_step < 997U; ++refine_step)
396                                         {
397                                                 memcpy(temp, data->row_buffer, sizeof(uint8_t) * ROW_LEN);
398                                                 flip_rand_n(temp, rand, flip_count);
399                                                 const uint_fast32_t next_error = check_distance_buff(data->distance_max, data->index, temp, error);
400                                                 if (next_error < error)
401                                                 {
402                                                         TRACE("Improved by rand-flip [%u] (%08X -> %08X)", flip_count, error, next_error);
403                                                         round = -1;
404                                                         memcpy(data->row_buffer, temp, sizeof(uint8_t) * ROW_LEN);
405                                                         if (!((error = next_error) > 0U))
406                                                         {
407                                                                 TRACE("Success by rand-flip (%u) <<<---", flip_count);
408                                                                 goto success;
409                                                         }
410                                                 }
411                                         }
412                                 }
413                         }
414                         TRACE("Restarting");
415                         data->distance_max = min(data->distance_max + 3U, HASH_LEN); /*bump max. distance*/
416                 }
417                 else
418                 {
419                         break; /*success*/
420                 }
421         }
422
423 success:
424         if (check_distance_buff(data->distance_max, data->index, data->row_buffer, HASH_LEN))
425         {
426                 fprintf(stderr, "ERROR MISCOMPARE!\n");
427                 abort();
428         }
429
430         MUTEX_LOCK(data->mutex);
431         if (SEM_TRYWAIT(data->stop))
432         {
433                 MUTEX_UNLOCK(data->mutex);
434                 return NULL;
435         }
436
437         SEM_POST(data->stop, THREAD_COUNT);
438         MUTEX_UNLOCK(data->mutex);
439         return data->row_buffer; /*success*/
440 }
441
442 static void* thread_spin(void *const param)
443 {
444         unsigned long delay = 1U;
445         sem_t *const stop = ((sem_t*)param);
446         for (;;)
447         {
448                 if (SEM_TRYWAIT(stop))
449                 {
450                         printf("\b\b\b[!]");
451                         return NULL;
452                 }
453                 _sleep(delay);
454                 if (delay >= 500)
455                 {
456                         printf("\b\b\b[%c]", SPINNER[g_spinpos]);
457                         g_spinpos = (g_spinpos + 1) % 4;
458                 }
459                 else
460                 {
461                         delay *= 2U;
462                 }
463         }
464 }
465
466 //-----------------------------------------------------------------------------
467 // Save / Load
468 //-----------------------------------------------------------------------------
469
470 static bool save_table_data(const wchar_t *const filename, const size_t rows_completed_in, const uint_fast32_t current_dist_max)
471 {
472         wchar_t filename_temp[_MAX_PATH];
473         swprintf_s(filename_temp, _MAX_PATH, L"%s~%X", filename, make_seed());
474         FILE *const file = _wfopen(filename_temp, L"wb");
475         if (file)
476         {
477                 bool success = true;
478                 const uint64_t magic_number = MAGIC_NUMBER;
479                 const uint32_t hash_len = HASH_LEN, distance_min = DISTANCE_MIN, distance_max = current_dist_max, rows_completed = (uint32_t)rows_completed_in;
480                 fwrite(&magic_number, sizeof(uint64_t), 1, file);
481                 fwrite(&hash_len, sizeof(uint32_t), 1, file);
482                 fwrite(&distance_min, sizeof(uint32_t), 1, file);
483                 fwrite(&distance_max, sizeof(uint32_t), 1, file);
484                 fwrite(&rows_completed, sizeof(uint32_t), 1, file);
485                 for (uint32_t i = 0; i < rows_completed; ++i)
486                 {
487                         const uint32_t checksum = adler32(&g_table[i][0], ROW_LEN);
488                         fwrite(&checksum, sizeof(uint32_t), 1, file);
489                         fwrite(&g_table[i][0], sizeof(uint8_t), ROW_LEN, file);
490                 }
491                 if (ferror(file))
492                 {
493                         printf("ERROR: Failed to write table data!\n");
494                         success = false;
495                 }
496                 fclose(file);
497                 if (success)
498                 {
499                         for (size_t i = 0; i < 42; ++i)
500                         {
501                                 if (_wremove(filename))
502                                 {
503                                         if (errno != ENOENT)
504                                         {
505                                                 printf("ERROR: Failed to delete existing file!\n");
506                                                 sched_yield();
507                                                 continue;
508                                         }
509                                 }
510                                 break;
511                         }
512                         if (_wrename(filename_temp, filename))
513                         {
514                                 printf("ERROR: Failed to rename temp file!\n");
515                                 success = false;
516                         }
517                 }
518                 else
519                 {
520                         _wremove(filename_temp);
521                 }
522                 return success;
523         }
524         else
525         {
526                 printf("ERROR: Failed to open table file for writing!\n");
527                 return false;
528         }
529 }
530
531 static bool load_table_data(const wchar_t *const filename, size_t *const rows_completed_out, uint_fast32_t *const dist_max_out)
532 {
533         FILE *const file = _wfopen(filename, L"rb");
534         if (file)
535         {
536                 bool success = true;
537                 uint64_t magic_number;
538                 uint32_t hash_len, distance_min, distance_max, rows_completed;
539                 fread(&magic_number, sizeof(uint64_t), 1, file);
540                 fread(&hash_len, sizeof(uint32_t), 1, file);
541                 fread(&distance_min, sizeof(uint32_t), 1, file);
542                 fread(&distance_max, sizeof(uint32_t), 1, file);
543                 fread(&rows_completed, sizeof(uint32_t), 1, file);
544                 if (ferror(file) || feof(file))
545                 {
546                         printf("ERROR: Failed to read the table header!\n");
547                         success = false;
548                         goto failed;
549                 }
550                 if (magic_number != MAGIC_NUMBER)
551                 {
552                         printf("ERROR: Table file format could not be recognized!\n");
553                         success = false;
554                         goto failed;
555                 }
556                 if ((hash_len != HASH_LEN) || (distance_min != DISTANCE_MIN) || (distance_max > HASH_LEN) || (rows_completed > ROW_NUM))
557                 {
558                         printf("ERROR: Table properties are incompatibe with this instance!\n");
559                         success = false;
560                         goto failed;
561                 }
562                 for (size_t i = 0; i < rows_completed; ++i)
563                 {
564                         uint32_t checksum_expected;
565                         if ((fread(&checksum_expected, sizeof(uint32_t), 1, file) != 1) || (fread(&g_table[i][0], sizeof(uint8_t), ROW_LEN, file) != ROW_LEN))
566                         {
567                                 printf("ERROR: Failed to read table data from file!\n");
568                                 success = false;
569                                 goto failed;
570                         }
571                         if (adler32(&g_table[i][0], ROW_LEN) != checksum_expected)
572                         {
573                                 printf("ERROR: Table checksum does *not* match table contents!\n");
574                                 success = false;
575                                 goto failed;
576                         }
577                         for (size_t j = 0; j < i; j++)
578                         {
579                                 if (!check_distance_rows(distance_max, i, j))
580                                 {
581                                         printf("ERROR: Table distance verification has failed!\n");
582                                         success = false;
583                                         goto failed;
584                                 }
585                         }
586                 }
587         failed:
588                 fclose(file);
589                 if (success && rows_completed_out)
590                 {
591                         *rows_completed_out = (size_t)rows_completed;
592                         *dist_max_out = distance_max;
593                 }
594                 return success;
595         }
596         else
597         {
598                 printf("ERROR: Failed to open table file for reading!\n");
599                 return false;
600         }
601 }
602
603 //-----------------------------------------------------------------------------
604 // MAIN
605 //-----------------------------------------------------------------------------
606
607 int wmain(int argc, wchar_t *argv[])
608 {
609         pthread_t thread_id[THREAD_COUNT + 1];
610         thread_data_t thread_data[THREAD_COUNT];
611         sem_t stop_flag;
612         pthread_mutex_t stop_mutex;
613         FILE *file_out = NULL;
614         size_t initial_row_index = 0;
615         uint_fast32_t distance_max = DISTANCE_MIN;
616
617         printf("MHash GenTableXOR [%s]\n\n", __DATE__);
618         printf("HashLen: %d, Distance Min: %d, Threads: %d, MSVC: %u\n\n", HASH_LEN, DISTANCE_MIN, THREAD_COUNT, _MSC_FULL_VER);
619
620         if ((HASH_LEN % (8 * sizeof(uint32_t))) != 0)
621         {
622                 crit_exit("FATAL: Hash length must be a multiple of 32 bits!");
623         }
624
625         if (argc < 2)
626         {
627                 printf("Table file not specified!\n\n");
628                 printf("Usage:\n");
629                 printf("   GenTables_XOR.exe <table_file>\n\n");
630                 return 1;
631         }
632
633         for (size_t i = 0; i < ROW_NUM; i++)
634         {
635                 memset(&g_table[i][0], 0, sizeof(uint8_t) * ROW_LEN);
636         }
637
638         memset(&thread_id, 0, sizeof(pthread_t) * (THREAD_COUNT + 1));
639         memset(&thread_data, 0, sizeof(thread_data_t) * THREAD_COUNT);
640         for (size_t t = 0; t < THREAD_COUNT; t++)
641         {
642                 memset(&g_thread_buffer[t][0], 0, sizeof(uint8_t) * ROW_LEN);
643         }
644
645         SEM_INIT(&stop_flag);
646         MUTEX_INIT(&stop_mutex);
647
648         if (_waccess(argv[1], 4) == 0)
649         {
650                 printf("Loading existing table data and proceeding...\n");
651                 if (!load_table_data(argv[1], &initial_row_index, &distance_max))
652                 {
653                         return 1;
654                 }
655         }
656
657         for (size_t i = initial_row_index; i < ROW_NUM; i++)
658         {
659                 char time_string[64];
660                 printf("\aRow %03u of %03u [%c]", (uint32_t)(i+1U), ROW_NUM, SPINNER[g_spinpos]);
661                 g_spinpos = (g_spinpos + 1) % 4;
662
663                 PTHREAD_CREATE(&thread_id[THREAD_COUNT], NULL, thread_spin, &stop_flag);
664                 for (size_t t = 0; t < THREAD_COUNT; t++)
665                 {
666                         thread_data[t].index = i;
667                         thread_data[t].row_buffer = &g_thread_buffer[t][0];
668                         thread_data[t].stop = &stop_flag;
669                         thread_data[t].mutex = &stop_mutex;
670                         thread_data[t].distance_max = distance_max;
671                         PTHREAD_CREATE(&thread_id[t], NULL, thread_main, &thread_data[t]);
672                         PTHREAD_SET_PRIORITY(thread_id[t], -15);
673                 }
674
675                 for (size_t t = 0; t < THREAD_COUNT; t++)
676                 {
677                         void *return_value = NULL;
678                         PTHREAD_JOIN(thread_id[t], &return_value);
679                         if (return_value)
680                         {
681                                 memcpy(&g_table[i][0], thread_data[t].row_buffer, sizeof(uint8_t) * ROW_LEN);
682                                 distance_max = max(distance_max, thread_data[t].distance_max);
683                         }
684                 }
685
686                 PTHREAD_JOIN(thread_id[THREAD_COUNT], NULL);
687                 get_time_str(time_string, 64);
688                 printf("\b\b\b[#] - %s\n", time_string);
689
690                 if (!save_table_data(argv[1], i + 1U, distance_max))
691                 {
692                         return 1; /*failed to save current table data*/
693                 }
694         }
695
696         printf("\n-----\n\n");
697
698         dump_table(stdout);
699         if (fopen_s(&file_out, "table_XOR." DISTANCE_STR ".txt", "w") == 0)
700         {
701                 dump_table(file_out);
702                 fclose(file_out);
703         }
704
705         printf("\n-----\n\n");
706
707         for (size_t i = 0; i < ROW_NUM; i++)
708         {
709                 for (size_t j = 0; j < ROW_NUM; j++)
710                 {
711                         if (i == j)
712                         {
713                                 continue; //i==j
714                         }
715                         if (!check_distance_rows(distance_max, i, j))
716                         {
717                                 crit_exit("FATAL: Table verification has failed!");
718                         }
719                 }
720         }
721
722         SEM_DESTROY(&stop_flag);
723         MUTEX_DESTROY(&stop_mutex);
724
725         printf("COMPLETED.\n\n");
726         system("shutdown /s /t 180");
727         return getchar();
728 }