// Immortal Number Search // by Daniel Marschall // Needed linux packages: libgmpxx4ldbl (TODO: c99 equivalent), libgomp1 // ------------------------------------------------------------------------------------------------------ /** * Immortal Number Search for C99 1.60.6 (10.02.2020) * Base 10, Root 5 * Multithread x64/BigInt version * Immortal Number Report File Version 3.00 * * @author Daniel Marschall - Big credit goes to the users of MatheBoard.de * @see http://www.matheboard.de/archive/435725/thread.html * @see http://www.viathinksoft.de/~daniel-marschall/computing/immortal/ * * More source codes, documents and analyse/stat-utilities coming soon! */ // ------------------------------------------------------------------------------------------------------ #define verbose //#define r_sum_debug // #define do_integrity_test // 35sec/12step mit libdiv <-- faster // 37sec/12step ohne libdiv #define use_libdivide // Some performance-stuff... try it yourself // LOOKUP CAST 20sec/5step 49sec/12step // LOOKUP NO CAST 19sec/5step 47sec/12step // MUL CAST 17sec/5step 38sec/12step <-- FASTEST! // MUL NO CAST 19sec/5step 47sec/12step //#define use_multiply_lookup #define use_multiply_cast // #define prefer_for_loop_skipping #define stats_general // #define stats_distribution // Seit 1.7 // Für "sum" BigInt anstelle Int64 verwenden. // Aber die Performance leidet extrem darunter // 32-Bit Überlauf findet sehr früh statt. 64-Bit Überlauf unwahrscheinlich. // #define sum_bigint // Seit 1.7 // Für "r" BigInt anstelle Int64 verwenden. // Aber die Performance leidet extrem darunter // 32-Bit Überlauf findet sehr früh statt. 64-Bit Überlauf unwahrscheinlich. // #define r_bigint // Note: "sum" and "r" are unsigned 64-bit (unsigned long long). With unsigned 32 bit (unsigned long), // "r" will overflow sooner than "sum". // The maximal possible immortal number using 32-bit "r" is exactly 190,875,592 (step 1,908). // With the unsigned 64-bit-implementation you can compute approx 819,855,292,164,868,960 digits without overflow. // Requires "aptitude install libgomp1" and "g++ -fopenmp" #define multithread #ifdef multithread #define TN_STATIC 2 #ifndef TN_STATIC #define TN_RECHECK_EVERY_X_DIGIT 1000 #define TN_DAY 2 #define TN_NIGHT 4 #define DAYTIME_WEEKEND_START 10 #define DAYTIME_WEEKEND_END 24 #define DAYTIME_WORKDAY_START 8 #define DAYTIME_WORKDAY_END 22 #endif #endif // Recommended! #define simple_selftest_step1 // ------------------------------------------------------------------------------------------------------ #include #include #include #include #include #ifdef use_libdivide // http://libdivide.com/documentation.html #include "libdivide.h" #endif #ifdef r_bigint #define include_gmp #endif #ifdef sum_bigint #define include_gmp #endif #ifdef do_integrity_test #define include_gmp #endif #ifdef include_gmp #include #endif #ifdef multithread #include #endif #define DATA_FILENAME "data.txt" #define DATA_NEW "data.tmp" #ifdef stats_general #define STATS_GENERAL "stats_general.txt" #endif #ifdef stats_distribution #define STATS_DISTRIBUTION "stats_distribution.txt" #endif #define BASE 10 // DO NOT CHANGE #define ROOT 5 // DO NOT CHANGE #define SAVE_INTERVAL 100000 // "1 Step" #define EXPANSION_SIZE 10000000 // 100 steps #define INITIAL_SIZE 10000000 // 100 steps #define VERSION "1.60.6" // ------------------------------------------------------------------------------------------------------ static inline char* getTimeStamp(char *timestamp_buf, const int len) { time_t now = time(0); strftime(timestamp_buf, len, "%a, %d %b %Y %H:%M:%S %z", localtime(&now)); return timestamp_buf; } // ACHTUNG: BEI GROSSEN ZAHLEN GIBTS KERNEL PANIC (Speicher erschöpft) #ifdef do_integrity_test static bool isImmortal(const mpz_t n, const unsigned int m) { // unsigned int m = strlen(n); mpz_t rop; mpz_init(rop); mpz_t m_pow; mpz_init(m_pow); mpz_ui_pow_ui(m_pow, 10, m); mpz_powm_ui(rop, n, 2, m_pow); // rop = n^2 % (mod 10^m) bool res = mpz_cmp(rop, n) == 0; mpz_clear(m_pow); mpz_clear(rop); return res; } #endif #ifdef multithread static inline int tn() { #ifdef TN_STATIC return TN_STATIC; #else time_t Zeitstempel; struct tm *t; Zeitstempel = time(0); t = localtime(&Zeitstempel); bool weekend = (t->tm_wday == 0) || (t->tm_wday == 6); bool night; if (weekend) { night = (t->tm_hour < DAYTIME_WEEKEND_START) || (t->tm_hour >= DAYTIME_WEEKEND_END); } else { night = (t->tm_hour < DAYTIME_WORKDAY_START) || (t->tm_hour >= DAYTIME_WORKDAY_END); } if (night) { return TN_NIGHT; } else { return TN_DAY; } #endif } #endif static inline void mygetline(char* line, size_t size, FILE* myfile) { if (fgets(line, size, myfile) == NULL) { line = ""; return; } size_t ln = strlen(line) - 1; if (line[ln] == '\n') line[ln] = '\0'; ln = strlen(line) - 1; if (line[ln] == '\r') line[ln] = '\0'; return; } static inline time_t get_mtime(const char *path) { // http://stackoverflow.com/questions/4021479/getting-file-modification-time-on-unix-using-utime-in-c struct stat statbuf; if (stat(path, &statbuf) == -1) { perror(path); exit(EXIT_FAILURE); } return statbuf.st_mtime; } // TODO: mergen mit stats_distribution? die werte lieber direkt in der main() aus den variablen rausschreiben? wäre doch einfacherer! // TODO: csv wäre besser #ifdef stats_general static int main_stats_general(time_t unixtime, const char* maj_sig, const char* min_sig, char* creation_time, char* save_timestamp, unsigned int base, unsigned int root, unsigned /* long long */ int digits, #ifdef r_bigint mpz_t r #else unsigned long long int r #endif ) { FILE *myoutfile = fopen(STATS_GENERAL, "a"); // __try { fprintf(myoutfile, "Unix time:\t%d\n", (int)unixtime); fprintf(myoutfile, "Maj. Sig:\t%s\n", maj_sig); fprintf(myoutfile, "Min. Sig:\t%s\n", min_sig); fprintf(myoutfile, "Testbegin:\t%s\n", creation_time); fprintf(myoutfile, "Savetime:\t%s\n", save_timestamp); fprintf(myoutfile, "Base:\t%d\n", base); fprintf(myoutfile, "Root:\t%d\n", root); fprintf(myoutfile, "Digit count:\t%d\n", digits); #ifdef r_bigint fprintf(myoutfile, "Param r:\t%s\n", mpz_get_str(NULL,10,r)); #else fprintf(myoutfile, "Param r:\t%lld\n", r); #endif fprintf(myoutfile, "\n"); // } __finally { fclose(myoutfile); // } return EXIT_SUCCESS; } #endif #ifdef stats_distribution static int main_stats_distribution(/*unsigned*/ char* a, unsigned /* long long */ int digits) { int count[10]; for (unsigned int i = 0; i < 10; ++i) count[i] = 0; // TODO: omp? for (unsigned int j = 0; j < digits; ++j) { ++count[a[j]]; } // Now save the stat FILE *myfile = fopen(STATS_DISTRIBUTION, "w"); // __try { fprintf(myfile, "Immortal Number Search\n"); fprintf(myfile, "Digit Stat 1.0\n"); fprintf(myfile, "\n"); unsigned int sigma = 0; for (unsigned int i = 0; i < 10; ++i) { fprintf(myfile, "Digit %d\t%d\n", i, count[i]); sigma += count[i]; } fprintf(myfile, "\n"); fprintf(myfile, "Sigma\t%d\n", sigma); fprintf(myfile, "Digits\t%d\n", digits); fprintf(myfile, "Sigma Match\t"; if (sigma == digits) { fprintf(myfile, "OK"); } else { fprintf(myfile, "FAILED!"); } fprintf(myfile, "\n"); // } __finally { fclose(myfile); // } return EXIT_SUCCESS; } #endif static void minsig(char *SIGNATURE_MINOR) { sprintf(SIGNATURE_MINOR, "Immortal Number Search for C99 %s", VERSION); #ifdef multithread #ifdef TN_STATIC sprintf(SIGNATURE_MINOR, "%s(Static MT %d)", SIGNATURE_MINOR, TN_STATIC); #else sprintf(SIGNATURE_MINOR, "%s(Dynamic MT)", SIGNATURE_MINOR); #endif #endif #ifdef sum_bigint sprintf(SIGNATURE_MINOR, "%s sumGMP", SIGNATURE_MINOR); #else sprintf(SIGNATURE_MINOR, "%s sum%d", SIGNATURE_MINOR, (int)sizeof(unsigned long long int)*8 /* 8 bits = 1 byte */); #endif #ifdef r_bigint sprintf(SIGNATURE_MINOR, "%s rGMP", SIGNATURE_MINOR); #else sprintf(SIGNATURE_MINOR, "%s r%d", SIGNATURE_MINOR, (int)sizeof(unsigned long long int)*8 /* 8 bits = 1 byte */); #endif } int main(void) { #define SIGNATURE "Immortal Number Report File Version 3.00" #define END_SIG "END OF REPORT" #define SOFTBREAK 76 char SIGNATURE_MINOR[2048]; minsig(SIGNATURE_MINOR); #ifdef verbose printf("%s\n", SIGNATURE_MINOR); #endif char creation_time[255]; /*unsigned*/ char* a; unsigned /* long long */ int a_size = INITIAL_SIZE; unsigned /* long long */ int top = -1; // u unsigned /* long long */ int digits = -1; // u+1 #ifdef r_bigint mpz_t r; mpz_init(r); #else unsigned long long int r; // UINT64! #endif char debug_timestamp[255]; bool a_empty; #ifdef verbose getTimeStamp(debug_timestamp, 255); printf("%s - Program started\n", debug_timestamp); #endif #ifdef use_multiply_lookup unsigned long int multiply_lookup[10][10]; for (int i = 0; i<10; ++i) { for (int j = 0; j<10; ++j) { multiply_lookup[i][j] = (unsigned long int)i*j; } } #endif #ifdef multithread #ifdef TN_STATIC int this_tn = tn(); omp_set_num_threads(this_tn); #else int tn_check_cnt = 1; int this_tn = tn(); int prev_tn = this_tn; omp_set_num_threads(this_tn); getTimeStamp(debug_timestamp, 255); #endif fprintf(stdout, "%s - Threads: %d\n", debug_timestamp, this_tn); #endif char line[255]; FILE* myfile = fopen(DATA_FILENAME, "r"); if (myfile) { #ifdef verbose getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Loading from %s ...\n", debug_timestamp, DATA_FILENAME); #endif // __try { top = -1; digits = -1; #ifdef r_bigint mpz_set_si(r, -1); #else r = -1; #endif mygetline(line, sizeof line, myfile); if (strcmp(line, SIGNATURE) != 0 /* not equal */) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Load error: Wrong format signature. Expecting '%s' but is '%s'.\n", debug_timestamp, SIGNATURE, line); fclose(myfile); return EXIT_FAILURE; } mygetline(line, sizeof line, myfile); // Minor. signature mygetline(line, sizeof line, myfile); // "" mygetline(line, sizeof line, myfile); // "(Starting time)" mygetline(creation_time, sizeof creation_time, myfile); mygetline(line, sizeof line, myfile); // "" mygetline(line, sizeof line, myfile); // "(Save timestamp)" mygetline(line, sizeof line, myfile); // Timestamp mygetline(line, sizeof line, myfile); // "" mygetline(line, sizeof line, myfile); // "(Base)" mygetline(line, sizeof line, myfile); // 10 unsigned int base = atoi(line); if (base != BASE) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - This edition can only work with base = %d.\n", debug_timestamp, BASE); fclose(myfile); return EXIT_FAILURE; } mygetline(line, sizeof line, myfile); // "" mygetline(line, sizeof line, myfile); // "(Root)" mygetline(line, sizeof line, myfile); // 5 unsigned int root = atoi(line); if (root != ROOT) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - This edition can only work with root = %d.\n", debug_timestamp, ROOT); fclose(myfile); return EXIT_FAILURE; } mygetline(line, sizeof line, myfile); // "" mygetline(line, sizeof line, myfile); // "(Digits)" mygetline(line, sizeof line, myfile); digits = atoi(line); mygetline(line, sizeof line, myfile); // "" mygetline(line, sizeof line, myfile); // "(r)" mygetline(line, sizeof line, myfile); #ifdef r_bigint mpz_set_str(r, line, 10); #else // atoi is for signed 32 bit // r = atoi(line); r = strtoull(line, NULL, 10); // strtoull is for uint64 #endif mygetline(line, sizeof line, myfile); // "" a_size = digits + EXPANSION_SIZE; a = (/*unsigned*/ char*) calloc(a_size, sizeof(/*unsigned*/ char)); if (a == NULL) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Memory allocation failed!\n", debug_timestamp); fclose(myfile); return EXIT_FAILURE; } mygetline(line, sizeof line, myfile); // "(Reversed notation)" do { mygetline(line, sizeof line, myfile); for (unsigned int i = 0; i < strlen(line); ++i) { /*unsigned*/ char toadd = line[i] - '0'; if (digits == top + 1) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Corrupt: Formal and actual length mismatch!\n", debug_timestamp); fclose(myfile); return EXIT_FAILURE; } // Cannot overflow if input file is OK. a[++top] = toadd; a_empty = false; } } while (strcmp(line, "") != 0 /* not equal */); if (digits != top + 1) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Corrupt: Formal and actual length mismatch (%d != %d)!\n", debug_timestamp, digits, top + 1); fclose(myfile); return EXIT_FAILURE; } mygetline(line, sizeof line, myfile); if (strcmp(line, END_SIG) != 0 /* not equal */) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Corrupt: End-signature mismatch.\n", debug_timestamp); fclose(myfile); return EXIT_FAILURE; } // } __finally { fclose(myfile); // } #ifdef verbose getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Done loading %d digits\n", debug_timestamp, digits); #endif } else { a_size = INITIAL_SIZE; a = (/*unsigned*/ char*) calloc(a_size, sizeof(/*unsigned*/ char)); if (a == NULL) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Memory allocation failed!\n", debug_timestamp); return EXIT_FAILURE; } a_empty = true; } #ifdef do_integrity_test bool firstSave = true; #endif unsigned /* long long */ int cnt = SAVE_INTERVAL; if (a_empty) { getTimeStamp(debug_timestamp, 255); // creation_time = debug_timestamp; strcpy(creation_time, debug_timestamp); cnt -= 2; top += 2; if (top >= a_size) { a_size += EXPANSION_SIZE; a = (/*unsigned*/ char*) realloc(a, a_size * sizeof(/*unsigned*/ char)); if (a == NULL) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Memory reallocation failed!\n", debug_timestamp); return EXIT_FAILURE; } } a[top-1] = 5 /* ROOT */; a[top] = 2; a_empty = false; digits = 2; #ifdef r_bigint mpz_set_ui(r, a[top]); #else r = a[top]; #endif } #ifdef r_bigint mpz_t tmp_digit; mpz_init(tmp_digit); #endif #ifndef r_bigint #ifdef use_libdivide struct libdivide_u64_t fast_d = libdivide_u64_gen(10); unsigned long long int divt = libdivide_u64_do(r, &fast_d); #else ldiv_t divt; divt = ldiv(r, 10); #endif #endif #ifdef sum_bigint mpz_t sum; #else unsigned long long int sum; // UINT64! #endif const __m128i mzero __attribute__((aligned(16))) = _mm_setzero_si128(); const __m128i m0to15 __attribute__((aligned(16))) = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); #ifdef multithread #ifdef use_libdivide const struct libdivide_u64_t fast_12928 = libdivide_u64_gen(12928); const struct libdivide_u64_t fast_32 = libdivide_u64_gen(32); #endif #endif // TODO: Abbruchkriterium? z.B. signal? while (true) { #ifdef multithread #ifndef TN_STATIC if (tn_check_cnt > TN_RECHECK_EVERY_X_DIGIT) { tn_check_cnt = 0; this_tn = tn(); if (this_tn != prev_tn) { getTimeStamp(debug_timestamp, 255); fprintf(stdout, "%s - Threads: %d -> %d\n", debug_timestamp, prev_tn, this_tn); omp_set_num_threads(this_tn); prev_tn = this_tn; } } else { tn_check_cnt++; } #endif #endif #ifdef sum_bigint mpz_init(sum); #else sum = 0; #endif // Step 1 // http://stackoverflow.com/questions/34254375/can-the-multiplication-of-chars-digits-be-made-more-performant #ifdef multithread char* ini_m = a+1; char* ini_k = a+top-15; if (ini_m + 15 < ini_k) { // Z = ((a+top-15)-(a+1)-16) / (404*16*2); #ifdef use_libdivide unsigned long long int Z = libdivide_u64_do(top-32, &fast_12928); #else unsigned long long int Z = (top-32) / 12928; #endif #pragma omp parallel for schedule(static) for (int j=0; j<=Z; ++j) { unsigned long long int z = j*6464; // j*404*16; char* m = ini_m + z; char* k = ini_k - z; __m128i msum = mzero; for (int i = 0; i < 404 && m + 15 < k; m += 16, k -= 16, ++i) { __m128i am = _mm_loadu_si128((__m128i*)(m)); __m128i ak = _mm_loadu_si128((__m128i*)(k)); ak = _mm_shuffle_epi8(ak, m0to15); msum = _mm_add_epi16(msum, _mm_maddubs_epi16(am, ak)); } msum = _mm_add_epi32(_mm_unpacklo_epi16(msum, mzero), _mm_unpackhi_epi16(msum, mzero)); msum = _mm_hadd_epi32(msum, msum); msum = _mm_hadd_epi32(msum, msum); #pragma omp critical(sum) #ifdef sum_bigint mpz_add(sum, sum, _mm_cvtsi128_si32(msum)); #else sum += _mm_cvtsi128_si32(msum); #endif } } #ifdef prefer_for_loop_skipping for (m=a+1, k=a+top-15; m + 15 < k; m += 16, k -= 16); k += 15; #else #ifdef use_libdivide unsigned long long int Z = libdivide_u64_do(top, &fast_32); #else unsigned long long int Z = top/32; #endif unsigned long long int z = Z*16; char* m = a+1+z; char* k = a+top-z; #endif for (; m < k; ++m, --k) { #ifdef sum_bigint mpz_add(sum, sum, *m * *k); #else sum += *m * *k; #endif } #else char *m, *k; for (m = a + 1, k = a + top - 15; m + 15 < k;) { __m128i msum = mzero; for (int i = 0; i < 404 && m + 15 < k; m += 16, k -= 16, ++i) { __m128i am = _mm_loadu_si128((__m128i*)(m)); __m128i ak = _mm_loadu_si128((__m128i*)(k)); ak = _mm_shuffle_epi8(ak, m0to15); msum = _mm_add_epi16(msum, _mm_maddubs_epi16(am, ak)); } msum = _mm_add_epi32(_mm_unpacklo_epi16(msum, mzero), _mm_unpackhi_epi16(msum, mzero)); msum = _mm_hadd_epi32(msum, msum); msum = _mm_hadd_epi32(msum, msum); #ifdef sum_bigint mpz_add(sum, sum, _mm_cvtsi128_si32(msum)); #else sum += _mm_cvtsi128_si32(msum); #endif } k += 15; for (; m < k; ++m, --k) { #ifdef sum_bigint mpz_add(sum, sum, *m * *k); #else sum += *m * *k; #endif } #endif // Step 2 #ifdef r_bigint mpz_fdiv_q_ui(r, r, 10); // "floor" div mpz_add_ui(r, r, a[top]); #ifdef sum_bigint mpz_add(r, r, sum); mpz_add(r, r, sum); #else mpz_add_ui(r, r, sum+sum); #endif #else // r = (r - a[top]) / 10 + a[top] + 2*sum; #ifdef use_libdivide #ifdef sum_bigint r = divt/*.quot*/ + a[top] + 2*mpz_get_ui(sum); #else r = divt/*.quot*/ + a[top] + sum + sum; #endif #else #ifdef sum_bigint r = divt.quot + a[top] + 2*mpz_get_ui(sum); #else r = divt.quot + a[top] + sum + sum; #endif #endif #endif // Step 3 if ((top&1 /*%2*/) != 0) { char* m = a + (top>>1)+1; // m = top/2+1; #ifdef use_multiply_lookup #ifdef r_bigint mpz_add_ui(r, r, (unsigned long int)multiply_lookup[*m][*m]); #else #ifdef use_multiply_cast r += (unsigned long long int)multiply_lookup[*m][*m]; #else r += multiply_lookup[*m][*m]; #endif #endif #else #ifdef r_bigint mpz_add_ui(r, r, (unsigned long int)*m * *m); #else #ifdef use_multiply_cast r += (unsigned long long int)*m * *m; #else r += *m * *m; #endif #endif #endif } // Increase digit count ++digits; // Do we need to expand our memory allocation? if (++top >= a_size) { a_size += EXPANSION_SIZE; a = (/*unsigned*/ char*) realloc(a, a_size * sizeof(/*unsigned*/ char)); if (a == NULL) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Memory reallocation failed!\n", debug_timestamp); return EXIT_FAILURE; } } #ifdef r_sum_debug // TODO: r_bigint und sum_bigint printf("R/Sum-Debug:\t%d\t%d\t%llu\n", digits, r, sum); sleep(1); #endif // Write digit #ifdef r_bigint mpz_mod_ui(tmp_digit, r, 10); a[top] = mpz_get_ui(tmp_digit); #else // a[top] = r % 10; #ifdef use_libdivide divt = libdivide_u64_do(r, &fast_d); a[top] = r - divt*10; #else divt = ldiv(r, 10); a[top] = divt.rem; #endif #endif if (--cnt == 0) { cnt = SAVE_INTERVAL; // Manual test #ifdef simple_selftest_step1 if (digits == 100000) { #ifdef r_bigint if (mpz_cmp_ui(r, 2243589) != 0) { #else if (r != 2243589) { #endif getTimeStamp(debug_timestamp, 255); printf("%s - Simple selftest for step 1 failed: r mismatch...\n", debug_timestamp); return EXIT_FAILURE; #ifdef verbose } else { getTimeStamp(debug_timestamp, 255); printf("%s - Simple selftest for step 1: OK!\n", debug_timestamp); #endif } } #endif #ifdef do_integrity_test if (firstSave) { // Wir führen beim ersten Speichern einen weiteren // integrity-Test durch. Grund: Wäre bei einer Fortsetzung einer // Datei das "r" falsch (der Datenteil aber korrekt), dann würde // diese Datei beim Speichern ungültige Daten enthalten (Zahl // nicht immortal). #ifdef verbose getTimeStamp(debug_timestamp, 255); printf("%s - Beginning self test before first save...\n", debug_timestamp); #endif mpz_t num; mpz_init(num); mpz_t m_pow; mpz_init(m_pow); mpz_t ra; mpz_init(ra); for (unsigned int i = 0; i < digits; ++i) { /*unsigned*/ char xa = a[i]; // Convert from Root-5 to Root-6 if (i == 0) { xa = 11 - xa; } else { xa = 9 - xa; } // Vorne Anhängen mpz_ui_pow_ui(m_pow, 10, i); mpz_mul_ui(ra, m_pow, xa); mpz_add(num, num, ra); } mpz_clear(ra); mpz_clear(m_pow); if (!isImmortal(num, digits)) { getTimeStamp(debug_timestamp, 255); printf(stderr, "%s - Integrity test failed. (Loaded file broken?) Will not save.\n", debug_timestamp); return EXIT_FAILURE; } mpz_clear(num); } #endif getTimeStamp(debug_timestamp, 255); char* save_timestamp = debug_timestamp; // Write data file #ifdef verbose getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Save %d digits to %s ...\n", debug_timestamp, digits, DATA_FILENAME); #endif FILE *myfile = fopen(DATA_NEW, "w"); // __try { fprintf(myfile, "%s\n", SIGNATURE); fprintf(myfile, "%s\n\n", SIGNATURE_MINOR); fprintf(myfile, "(Starting time)\n%s\n\n", creation_time); fprintf(myfile, "(Save timestamp)\n%s\n\n", save_timestamp); fprintf(myfile, "(Base)\n%d\n\n", BASE); fprintf(myfile, "(Root)\n%d\n\n", ROOT); fprintf(myfile, "(Digits)\n%d\n\n", digits); #ifdef r_bigint fprintf(myfile, "(r)\n%s\n\n", mpz_get_str(NULL,10,r)); #else fprintf(myfile, "(r)\n%lld\n\n", r); #endif fprintf(myfile, "(Reversed notation)\n"); char outputline[SOFTBREAK + 2]; outputline[SOFTBREAK] = '\n'; outputline[SOFTBREAK + 1] = 0; int outputline_pos = 0; for (unsigned /* long long */ int i = 0; i < digits;) { /*unsigned*/ char xa = a[i] + '0'; outputline[outputline_pos++] = xa; if ((++i) % SOFTBREAK == 0) { fprintf(myfile, outputline); outputline_pos = 0; } } if (digits % SOFTBREAK != 0) { outputline[outputline_pos++] = '\n'; outputline[outputline_pos++] = 0; fprintf(myfile, outputline); //outputline_pos = 0; } fprintf(myfile, "\n%s\n", END_SIG); // } __finally { fclose(myfile); // } // Log to console #ifdef verbose getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Saved %d digits to %s\n", debug_timestamp, digits, DATA_FILENAME); #endif // Integrity Test #ifdef do_integrity_test if (firstSave) { #ifdef verbose getTimeStamp(debug_timestamp, 255); printf("%s - First save absolved. Stable phase has begun.\n", debug_timestamp); #endif firstSave = false; } #endif // Create statistics #ifdef stats_distribution { int res = main_stats_distribution(a, digits); if (res != EXIT_SUCCESS) return res; } #endif #ifdef stats_general { int res = main_stats_general(get_mtime(DATA_NEW), SIGNATURE, SIGNATURE_MINOR, creation_time, save_timestamp, BASE, ROOT, digits, r); if (res != EXIT_SUCCESS) return res; } #endif // Replace the old data file remove(DATA_FILENAME); // on Windows, rename() will fail otherwise int res = rename(DATA_NEW, DATA_FILENAME); if (res != 0) { getTimeStamp(debug_timestamp, 255); fprintf(stderr, "%s - Could not move '%s' to '%s' (Error code %d).\n", debug_timestamp, DATA_NEW, DATA_FILENAME, res); return EXIT_FAILURE; } } } #ifdef r_bigint mpz_clear(tmp_digit); mpz_clear(r); #endif #ifdef sum_bigint mpz_clear(sum); #endif free(a); }