OSDN Git Service

016ec3338a4567d8cd69bbc2e282705aad3718a6
[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 "twister.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 #include <intrin.h>
35
36 //-----------------------------------------------------------------------------
37 // Const
38 //-----------------------------------------------------------------------------
39
40 #define HASH_LEN 384U
41
42 #define DISTANCE_MIN 182U
43 #define DISTANCE_MAX DISTANCE_MIN + 16U
44
45 #define THREAD_COUNT 8U
46
47 #undef ENABLE_STATS
48 #undef ENABLE_TRACE
49
50 #define ROW_NUM (UINT8_MAX+2)           /*total number of rows*/
51 #define ROW_LEN (HASH_LEN / CHAR_BIT)   /*number of bits per row*/
52
53 #define __DISTANCE_STR(X) #X
54 #define _DISTANCE_STR(X) __DISTANCE_STR(X)
55 #define DISTANCE_STR _DISTANCE_STR(DISTANCE_MIN)
56
57 #define MAGIC_NUMBER 0x3C6058A7C1132CB2ui64
58 #define THREAD_ID (pthread_getw32threadid_np(pthread_self()))
59
60 //-----------------------------------------------------------------------------
61 // Globals
62 //-----------------------------------------------------------------------------
63
64 static uint8_t g_table[ROW_NUM][ROW_LEN];
65 static uint8_t g_thread_buffer[THREAD_COUNT][ROW_LEN];
66
67 static size_t g_spinpos = 0;
68 static char SPINNER[4] = { '/', '-', '\\', '|' };
69
70 #ifdef ENABLE_STATS
71 static volatile long long g_stat_too_hi = 0i64;
72 static volatile long long g_stat_too_lo = 0i64;
73 #endif //ENABLE_STATS
74
75 //-----------------------------------------------------------------------------
76 // Utility Functions
77 //-----------------------------------------------------------------------------
78
79 #ifdef ENABLE_TRACE
80 #define TRACE(X, ...) printf("[%04X] " X "\n", THREAD_ID, __VA_ARGS__)
81 #else
82 #define TRACE(X, ...) __noop()
83 #endif
84
85 static inline void print_row(uint8_t *const row_buffer)
86 {
87         for (size_t w = 0; w < ROW_LEN; ++w)
88         {
89                 printf("%02X", row_buffer[w]);
90         }
91         puts("");
92 }
93
94 static inline void flip_bit_at(uint8_t *const row_buffer, const size_t pos)
95 {
96         row_buffer[pos >> 3] ^= ((uint8_t)(1U << (pos & 0x7)));
97 }
98
99 static inline void flip_rand_n(uint8_t *const row_buffer, twister_t *const rand, const uint32_t n)
100 {
101         bool taken[HASH_LEN];
102         memset(&taken, 0, sizeof(bool) * HASH_LEN);
103         for (uint32_t i = 0; i < n; ++i)
104         {
105                 size_t next;
106                 do
107                 {
108                         next = next_rand_range(rand, HASH_LEN);
109                 }
110                 while (taken[next]);
111                 flip_bit_at(row_buffer, next);
112                 taken[next] = true;
113         }
114 }
115
116 static inline bool check_distance_rows(const size_t index_1, const size_t index_2)
117 {
118         const uint32_t dist = hamming_distance(&g_table[index_1][0], &g_table[index_2][0], ROW_LEN);
119         return (dist <= DISTANCE_MAX) && (dist >= DISTANCE_MIN);
120 }
121
122 static inline uint32_t check_distance_buff(const size_t index, const uint8_t *const row_buffer, const uint32_t limit)
123 {
124         uint32_t error = 0U;
125 #ifdef ENABLE_STATS
126         bool reason;
127 #endif //ENABLE_STATS
128         for (size_t k = 0; k < index; k++)
129         {
130
131                 const uint32_t dist = hamming_distance(&g_table[k][0], row_buffer, ROW_LEN);
132                 if (dist > DISTANCE_MAX)
133                 {
134                         const uint32_t current = dist - DISTANCE_MAX;
135                         if (current > error)
136                         {
137 #ifdef ENABLE_STATS
138                                 reason = false;
139 #endif //ENABLE_STATS
140                                 if ((error = current) >= limit)
141                                 {
142                                         break; /*early termination*/
143                                 }
144                         }
145                 }
146                 else if (dist < DISTANCE_MIN)
147                 {
148                         const uint32_t current = DISTANCE_MIN - dist;
149                         if (current > error)
150                         {
151 #ifdef ENABLE_STATS
152                                 reason = true;
153 #endif //ENABLE_STATS
154                                 if ((error = current) >= limit)
155                                 {
156                                         break; /*early termination*/
157                                 }
158                         }
159                 }
160         }
161 #ifdef ENABLE_STATS
162         _InterlockedIncrement64(reason ? &g_stat_too_lo : &g_stat_too_hi);
163 #endif //ENABLE_STATS
164         return error;
165 }
166
167 static inline bool update_global_minimum(volatile long *const global_minimum, const long value)
168 {
169         long old_minimum = LONG_MAX;
170         for (;;)
171         {
172                 if (value > old_minimum)
173                 {
174                         return false;
175                 }
176                 const long new_minimum = _InterlockedCompareExchange(global_minimum, value, old_minimum);
177                 if (new_minimum == old_minimum)
178                 {
179                         return true;
180                 }
181                 old_minimum = new_minimum;
182         }
183 }
184
185 static dump_table(FILE *out)
186 {
187         fputs("uint8_t MHASH_384_TABLE_XOR[UINT8_MAX+2][MHASH_384_LEN] =\n{\n", out);
188         for (size_t i = 0; i < ROW_NUM; i++)
189         {
190                 fputs("\t{ ", out);
191                 for (size_t j = 0; j < ROW_LEN; j++)
192                 {
193                         if (j > 0)
194                         {
195                                 fputc(',', out);
196                         }
197                         fprintf(out, "0x%02X", g_table[i][j]);
198                 }
199                 fprintf(out, " }%s /*%02X*/\n", (i != (ROW_NUM - 1)) ? "," : " ", (uint32_t)(i % 0x100));
200         }
201         fputs("};\n", out);
202 }
203
204 //-----------------------------------------------------------------------------
205 // Thread function
206 //-----------------------------------------------------------------------------
207
208 typedef struct
209 {
210         size_t index;
211         uint8_t *row_buffer;
212         sem_t *stop;
213         pthread_mutex_t *mutex;
214         volatile long *minimum;
215 }
216 thread_data_t;
217
218 static void* thread_main(void *const param)
219 {
220         const thread_data_t *const data = ((const thread_data_t*)param);
221         twister_t rand;
222         bxmller_t bxmller;
223         uint8_t temp[ROW_LEN];
224         for(;;)
225         {
226                 rand_init(&rand, make_seed());
227                 gaussian_noise_init(&bxmller);
228                 rand_next_bytes(&rand, data->row_buffer, ROW_LEN);
229                 uint32_t error = check_distance_buff(data->index, data->row_buffer, HASH_LEN);
230                 if(error > 0U)
231                 {
232                         do
233                         {
234                                 for (int32_t round = 0; round < 4999; ++round)
235                                 {
236                                         if (!(round & 0x3FF))
237                                         {
238                                                 if (SEM_TRYWAIT(data->stop))
239                                                 {
240                                                         return NULL;
241                                                 }
242                                         }
243                                         for (size_t rand_loop = 0; rand_loop < 99991U; ++rand_loop)
244                                         {
245                                                 rand_next_bytes(&rand, temp, ROW_LEN);
246                                                 const uint32_t next_error = check_distance_buff(data->index, temp, error);
247                                                 if (next_error < error)
248                                                 {
249                                                         round = -1;
250                                                         memcpy(data->row_buffer, temp, sizeof(uint8_t) * ROW_LEN);
251                                                         if (!((error = next_error) > 0U))
252                                                         {
253                                                                 TRACE("Success by rand-init");
254                                                                 goto success;
255                                                         }
256                                                 }
257                                         }
258                                 }
259                                 for (int32_t round = 0; round < 743; ++round)
260                                 {
261                                         TRACE("Optimizer round %u of 743", round);
262                                         if (!round)
263                                         {
264                                                 for (size_t xchg_pos = 0U; xchg_pos < ROW_LEN; ++xchg_pos)
265                                                 {
266                                                         uint8_t original = data->row_buffer[xchg_pos];
267                                                         for (uint16_t xchg_val = 0U; xchg_val <= UINT8_MAX; ++xchg_val)
268                                                         {
269                                                                 data->row_buffer[xchg_pos] = (uint8_t)xchg_val;
270                                                                 const uint32_t next_error = check_distance_buff(data->index, data->row_buffer, error);
271                                                                 if (next_error < error)
272                                                                 {
273                                                                         TRACE("Improved by xchg-byte");
274                                                                         original = (uint8_t)xchg_val;
275                                                                         round = -1;
276                                                                         if (!((error = next_error) > 0U))
277                                                                         {
278                                                                                 TRACE("Success by xchg-byte");
279                                                                                 goto success;
280                                                                         }
281                                                                 }
282                                                         }
283                                                         data->row_buffer[xchg_pos] = original;
284                                                 }
285                                                 for (size_t flip_pos_w = 0U; flip_pos_w < HASH_LEN; ++flip_pos_w)
286                                                 {
287                                                         if (SEM_TRYWAIT(data->stop))
288                                                         {
289                                                                 return NULL;
290                                                         }
291                                                         flip_bit_at(data->row_buffer, flip_pos_w);
292                                                         bool revert_w = true;
293                                                         const uint32_t next_error = check_distance_buff(data->index, data->row_buffer, error);
294                                                         if (next_error < error)
295                                                         {
296                                                                 TRACE("Improved by flip-1");
297                                                                 revert_w = false;
298                                                                 round = -1;
299                                                                 if (!((error = next_error) > 0U))
300                                                                 {
301                                                                         TRACE("Sucess by flip-1");
302                                                                         goto success;
303                                                                 }
304                                                         }
305                                                         for (size_t flip_pos_x = flip_pos_w + 1U; flip_pos_x < HASH_LEN; ++flip_pos_x)
306                                                         {
307                                                                 flip_bit_at(data->row_buffer, flip_pos_x);
308                                                                 bool revert_x = true;
309                                                                 const uint32_t next_error = check_distance_buff(data->index, data->row_buffer, error);
310                                                                 if (next_error < error)
311                                                                 {
312                                                                         TRACE("Improved by flip-2");
313                                                                         revert_w = false;
314                                                                         revert_x = false;
315                                                                         round = -1;
316                                                                         if (!((error = next_error) > 0U))
317                                                                         {
318                                                                                 TRACE("Sucess by flip-2");
319                                                                                 goto success;
320                                                                         }
321                                                                 }
322                                                                 for (size_t flip_pos_y = flip_pos_x + 1U; flip_pos_y < HASH_LEN; ++flip_pos_y)
323                                                                 {
324                                                                         flip_bit_at(data->row_buffer, flip_pos_y);
325                                                                         bool revert_y = true;
326                                                                         const uint32_t next_error = check_distance_buff(data->index, data->row_buffer, error);
327                                                                         if (next_error < error)
328                                                                         {
329                                                                                 TRACE("Improved by flip-3");
330                                                                                 revert_w = false;
331                                                                                 revert_x = false;
332                                                                                 revert_y = false;
333                                                                                 round = -1;
334                                                                                 if (!((error = next_error) > 0U))
335                                                                                 {
336                                                                                         TRACE("Sucess by flip-3");
337                                                                                         goto success;
338                                                                                 }
339                                                                         }
340                                                                         for (size_t flip_pos_z = flip_pos_y + 1U; flip_pos_z < HASH_LEN; ++flip_pos_z)
341                                                                         {
342                                                                                 flip_bit_at(data->row_buffer, flip_pos_z);
343                                                                                 const uint32_t next_error = check_distance_buff(data->index, data->row_buffer, error);
344                                                                                 if (next_error < error)
345                                                                                 {
346                                                                                         TRACE("Improved by flip-4");
347                                                                                         revert_w = false;
348                                                                                         revert_x = false;
349                                                                                         revert_y = false;
350                                                                                         round = -1;
351                                                                                         if (!((error = next_error) > 0U))
352                                                                                         {
353                                                                                                 TRACE("Sucess by flip-4");
354                                                                                                 goto success;
355                                                                                         }
356                                                                                 }
357                                                                                 else
358                                                                                 {
359                                                                                         flip_bit_at(data->row_buffer, flip_pos_z);
360                                                                                 }
361                                                                         }
362                                                                         if (revert_y)
363                                                                         {
364                                                                                 flip_bit_at(data->row_buffer, flip_pos_y);
365                                                                         }
366                                                                 }
367                                                                 if (revert_x)
368                                                                 {
369                                                                         flip_bit_at(data->row_buffer, flip_pos_x);
370                                                                 }
371                                                         }
372                                                         if (revert_w)
373                                                         {
374                                                                 flip_bit_at(data->row_buffer, flip_pos_w);
375                                                         }
376                                                 }
377                                         }
378                                         for (size_t rand_mode = 0; rand_mode < 2U; ++rand_mode)
379                                         {
380                                                 memcpy(temp, &data->row_buffer, sizeof(uint8_t) * ROW_LEN);
381                                                 for (size_t rand_loop = 0; rand_loop < 99991U; ++rand_loop)
382                                                 {
383                                                         rand_next_bytes(&rand, &temp[rand_mode ? (ROW_LEN / 2U) : 0U], ROW_LEN / 2U);
384                                                         const uint32_t next_error = check_distance_buff(data->index, temp, error);
385                                                         if (next_error < error)
386                                                         {
387                                                                 TRACE("Improved by rand-replace #%zu", rand_mode);
388                                                                 round = -1;
389                                                                 memcpy(data->row_buffer, temp, sizeof(uint8_t) * ROW_LEN);
390                                                                 if (!((error = next_error) > 0U))
391                                                                 {
392                                                                         TRACE("Success by rand-replace");
393                                                                         goto success;
394                                                                 }
395                                                         }
396                                                 }
397                                         }
398                                         for (size_t refine_loop = 0; refine_loop < 9973U; ++refine_loop)
399                                         {
400                                                 if (!(refine_loop & 0x3FF))
401                                                 {
402                                                         if (SEM_TRYWAIT(data->stop))
403                                                         {
404                                                                 return NULL;
405                                                         }
406                                                 }
407                                                 const uint32_t flip_count = gaussian_noise_next(&rand, &bxmller, 3.14159, 5U, HASH_LEN);
408                                                 for (size_t refine_step = 0; refine_step < 997U; ++refine_step)
409                                                 {
410                                                         memcpy(temp, data->row_buffer, sizeof(uint8_t) * ROW_LEN);
411                                                         flip_rand_n(temp, &rand, flip_count);
412                                                         const uint32_t next_error = check_distance_buff(data->index, temp, error);
413                                                         if (next_error < error)
414                                                         {
415                                                                 TRACE("Improved by rand-flip (%u)", flip_count);
416                                                                 round = -1;
417                                                                 memcpy(data->row_buffer, temp, sizeof(uint8_t) * ROW_LEN);
418                                                                 if (!((error = next_error) > 0U))
419                                                                 {
420                                                                         TRACE("Success by rand-flip (%u)", flip_count);
421                                                                         goto success;
422                                                                 }
423                                                         }
424                                                 }
425                                         }
426                                 }
427                         }
428                         while (update_global_minimum(data->minimum, (long)error));
429                         TRACE("Restarting");
430                 }
431                 else
432                 {
433                         break; /*success*/
434                 }
435         }
436
437 success:
438         if (check_distance_buff(data->index, data->row_buffer, HASH_LEN))
439         {
440                 fprintf(stderr, "ERROR MISCOMPARE!\n");
441                 abort();
442         }
443
444         MUTEX_LOCK(data->mutex);
445         if (SEM_TRYWAIT(data->stop))
446         {
447                 MUTEX_UNLOCK(data->mutex);
448                 return NULL;
449         }
450
451         SEM_POST(data->stop, THREAD_COUNT);
452         MUTEX_UNLOCK(data->mutex);
453         return data->row_buffer; /*success*/
454 }
455
456 static void* thread_spin(void *const param)
457 {
458         unsigned long delay = 1U;
459         sem_t *const stop = ((sem_t*)param);
460         for (;;)
461         {
462                 if (SEM_TRYWAIT(stop))
463                 {
464                         printf("\b\b\b[!]");
465                         return NULL;
466                 }
467                 _sleep(delay);
468                 if (delay >= 500)
469                 {
470 #ifdef ENABLE_STATS
471                         const long long s_too_lo = g_stat_too_lo, s_too_hi = g_stat_too_hi;
472                         const long long s_too_ac = s_too_lo + s_too_hi;
473                         printf("Too low: %lld (%.2f), too high: %lld (%.2f)\n", s_too_lo, s_too_lo / ((double)s_too_ac), s_too_hi, s_too_hi / ((double)s_too_ac));
474 #endif //ENABLE_STATS
475                         printf("\b\b\b[%c]", SPINNER[g_spinpos]);
476                         g_spinpos = (g_spinpos + 1) % 4;
477                 }
478                 else
479                 {
480                         delay *= 2U;
481                 }
482         }
483 }
484
485 //-----------------------------------------------------------------------------
486 // Save / Load
487 //-----------------------------------------------------------------------------
488
489 static bool save_table_data(const wchar_t *const filename, const size_t rows_completed_in)
490 {
491         FILE *const file = _wfopen(filename, L"wb");
492         if (file)
493         {
494                 bool success = true;
495                 const uint64_t magic_number = MAGIC_NUMBER;
496                 const uint32_t hash_len = HASH_LEN, distance_min = DISTANCE_MIN, distance_max = DISTANCE_MAX, rows_completed = (uint32_t)rows_completed_in;
497                 fwrite(&magic_number, sizeof(uint64_t), 1, file);
498                 fwrite(&hash_len, sizeof(uint32_t), 1, file);
499                 fwrite(&distance_min, sizeof(uint32_t), 1, file);
500                 fwrite(&distance_max, sizeof(uint32_t), 1, file);
501                 fwrite(&rows_completed, sizeof(uint32_t), 1, file);
502                 for (uint32_t i = 0; i < rows_completed; ++i)
503                 {
504                         const uint32_t checksum = adler32(&g_table[i][0], ROW_LEN);
505                         fwrite(&checksum, sizeof(uint32_t), 1, file);
506                         fwrite(&g_table[i][0], sizeof(uint8_t), ROW_LEN, file);
507                 }
508                 if (ferror(file))
509                 {
510                         printf("ERROR: Failed to write table data!\n");
511                         success = false;
512                 }
513                 fclose(file);
514                 return success;
515         }
516         else
517         {
518                 printf("ERROR: Failed to open table file for writing!\n");
519                 return false;
520         }
521 }
522
523 static bool load_table_data(const wchar_t *const filename, size_t *const rows_completed_out)
524 {
525         FILE *const file = _wfopen(filename, L"rb");
526         if (file)
527         {
528                 bool success = true;
529                 uint64_t magic_number;
530                 uint32_t hash_len, distance_min, distance_max, rows_completed;
531                 fread(&magic_number, sizeof(uint64_t), 1, file);
532                 fread(&hash_len, sizeof(uint32_t), 1, file);
533                 fread(&distance_min, sizeof(uint32_t), 1, file);
534                 fread(&distance_max, sizeof(uint32_t), 1, file);
535                 fread(&rows_completed, sizeof(uint32_t), 1, file);
536                 if (ferror(file) || feof(file))
537                 {
538                         printf("ERROR: Failed to read the table header!\n");
539                         success = false;
540                         goto failed;
541                 }
542                 if (magic_number != MAGIC_NUMBER)
543                 {
544                         printf("ERROR: Table file format could not be recognized!\n");
545                         success = false;
546                         goto failed;
547                 }
548                 if ((hash_len != HASH_LEN) || (distance_min != DISTANCE_MIN) || (distance_max != DISTANCE_MAX) || (rows_completed > ROW_NUM))
549                 {
550                         printf("ERROR: Table properties are incompatibe with this instance!\n");
551                         success = false;
552                         goto failed;
553                 }
554                 for (size_t i = 0; i < rows_completed; ++i)
555                 {
556                         uint32_t checksum_expected;
557                         if ((fread(&checksum_expected, sizeof(uint32_t), 1, file) != 1) || (fread(&g_table[i][0], sizeof(uint8_t), ROW_LEN, file) != ROW_LEN))
558                         {
559                                 printf("ERROR: Failed to read table data from file!\n");
560                                 success = false;
561                                 goto failed;
562                         }
563                         if (adler32(&g_table[i][0], ROW_LEN) != checksum_expected)
564                         {
565                                 printf("ERROR: Table checksum does *not* match table contents!\n");
566                                 success = false;
567                                 goto failed;
568                         }
569                         for (size_t j = 0; j < i; j++)
570                         {
571                                 if (!check_distance_rows(i, j))
572                                 {
573                                         printf("ERROR: Table distance verification has failed!\n");
574                                         success = false;
575                                         goto failed;
576                                 }
577                         }
578                 }
579         failed:
580                 fclose(file);
581                 if (success && rows_completed_out)
582                 {
583                         *rows_completed_out = (size_t)rows_completed;
584                 }
585                 return success;
586         }
587         else
588         {
589                 printf("ERROR: Failed to open table file for reading!\n");
590                 return false;
591         }
592 }
593
594 //-----------------------------------------------------------------------------
595 // MAIN
596 //-----------------------------------------------------------------------------
597
598 int wmain(int argc, wchar_t *argv[])
599 {
600         pthread_t thread_id[THREAD_COUNT + 1];
601         thread_data_t thread_data[THREAD_COUNT];
602         sem_t stop_flag;
603         pthread_mutex_t stop_mutex;
604         FILE *file_out = NULL;
605         size_t initial_row_index = 0;
606         volatile long minimum;
607
608         printf("MHash GenTableXOR [%s]\n\n", __DATE__);
609         printf("HashLen: %d, Distance Min/Max: %d/%d, Threads: %d\n\n", HASH_LEN, DISTANCE_MIN, DISTANCE_MAX, THREAD_COUNT);
610
611         if ((HASH_LEN % (8 * sizeof(uint32_t))) != 0)
612         {
613                 crit_exit("FATAL: Hash length must be a multiple of 32 bits!");
614         }
615
616         if (argc < 2)
617         {
618                 printf("Table file not specified!\n\n");
619                 printf("Usage:\n");
620                 printf("   GenTables_XOR.exe <table_file>\n\n");
621                 return 1;
622         }
623
624         for (size_t i = 0; i < ROW_NUM; i++)
625         {
626                 memset(&g_table[i][0], 0, sizeof(uint8_t) * ROW_LEN);
627         }
628
629         memset(&thread_id, 0, sizeof(pthread_t) * (THREAD_COUNT + 1));
630         memset(&thread_data, 0, sizeof(thread_data_t) * THREAD_COUNT);
631         for (size_t t = 0; t < THREAD_COUNT; t++)
632         {
633                 memset(&g_thread_buffer[t][0], 0, sizeof(uint8_t) * ROW_LEN);
634         }
635
636         SEM_INIT(&stop_flag);
637         MUTEX_INIT(&stop_mutex);
638
639         if (_waccess(argv[1], 4) == 0)
640         {
641                 printf("Loading existing table data and proceeding...\n");
642                 if (!load_table_data(argv[1], &initial_row_index))
643                 {
644                         return 1;
645                 }
646         }
647
648         for (size_t i = initial_row_index; i < ROW_NUM; i++)
649         {
650                 char time_string[64];
651                 printf("\aRow %03u of %03u [%c]", (uint32_t)(i+1U), ROW_NUM, SPINNER[g_spinpos]);
652                 minimum = LONG_MAX;
653                 g_spinpos = (g_spinpos + 1) % 4;
654 #ifdef ENABLE_STATS
655                 g_stat_too_hi = g_stat_too_lo = 0i64;
656 #endif //INCREMENT_STAT
657
658                 PTHREAD_CREATE(&thread_id[THREAD_COUNT], NULL, thread_spin, &stop_flag);
659                 for (size_t t = 0; t < THREAD_COUNT; t++)
660                 {
661                         thread_data[t].index = i;
662                         thread_data[t].row_buffer = &g_thread_buffer[t][0];
663                         thread_data[t].stop = &stop_flag;
664                         thread_data[t].mutex = &stop_mutex;
665                         thread_data[t].minimum = &minimum;
666                         PTHREAD_CREATE(&thread_id[t], NULL, thread_main, &thread_data[t]);
667                         PTHREAD_SET_PRIORITY(thread_id[t], -15);
668                 }
669
670                 for (size_t t = 0; t < THREAD_COUNT; t++)
671                 {
672                         void *return_value = NULL;
673                         PTHREAD_JOIN(thread_id[t], &return_value);
674                         if (return_value)
675                         {
676                                 memcpy(&g_table[i][0], return_value, sizeof(uint8_t) * ROW_LEN);
677                         }
678                 }
679
680                 PTHREAD_JOIN(thread_id[THREAD_COUNT], NULL);
681                 get_time_str(time_string, 64);
682                 printf("\b\b\b[#] - %s\n", time_string);
683
684                 if (!save_table_data(argv[1], i + 1U))
685                 {
686                         return 1; /*failed to save current table data*/
687                 }
688         }
689
690         printf("\n-----\n\n");
691
692         dump_table(stdout);
693         if (fopen_s(&file_out, "table_XOR." DISTANCE_STR ".txt", "w") == 0)
694         {
695                 dump_table(file_out);
696                 fclose(file_out);
697         }
698
699         printf("\n-----\n\n");
700
701         for (size_t i = 0; i < ROW_NUM; i++)
702         {
703                 for (size_t j = 0; j < ROW_NUM; j++)
704                 {
705                         if (i == j)
706                         {
707                                 continue;
708                         }
709                         if (!check_distance_rows(i, j))
710                         {
711                                 crit_exit("FATAL: Table verification has failed!");
712                         }
713                 }
714         }
715
716         SEM_DESTROY(&stop_flag);
717         MUTEX_DESTROY(&stop_mutex);
718
719         printf("COMPLETED.\n\n");
720         system("shutdown /s /t 180");
721         return getchar();
722 }