/* Program: PI.C Author : Kim Moser Date : 23 September, 1992 System : IBM PC / Borland C++ 3.0 Descrip: Computes Pi to a given number of decimal places. Based on a version (which didn't work correctly after the 3250th decimal place) which contained the following comment: Programmed by Bill Davidsen after the method of G. M. Roe, based on a version for "E" supplied with the "B" compiler, 1970 Also based on an almost identical version supplied by Leonard Broughton (which works correctly). Kim Moser added support for disk swapping, continuing a saved session, and variable memory usage. */ #include #include #include #include #include #include #include // kbhit() #include #ifndef __LARGE__ #error PI should compiled in the LARGE memory model for best results! #endif #ifdef __cplusplus long min(long value1, long value2); long min(long value1, long value2) { return ( (value1 < value2) ? value1 : value2); } long max(long value1, long value2); long max(long value1, long value2) { return ( (value1 > value2) ? value1 : value2); } #endif #define MAX_STOR 21 // Why 21? I have no idea. "It was like that when I got there." [K.M.] static struct { long count,n,i,col,col1,loc, swaps, stor[MAX_STOR]; } GLOBAL = { 0, 0, 0, 0, 0, 0, 0, {0} // We need to initialize only stor[0]. }; /****************************************************************************/ static void out(char *s, ...); static void out(char *s, ...) /* Prints args to stdout, and, if stdout is not a tty, to stderr, too. */ { va_list argptr; /* Pointer to arguments */ va_start(argptr, s); vfprintf(stdout, s, argptr); //if (!isatty(fileno(stdout))) vfprintf(stderr, s, argptr); va_end(argptr); } #if 0 void shift(long *l1,long *l2,long lp,long lmod) { long k; k =((*l2) > 0 ? (*l2) / lmod : -(-(*l2) / lmod) -1); *l2 -= k * lmod; *l1 += k * lp; } #endif #define shift(l1,l2,lp,lmod) { \ k =((l2) > 0 ? (l2) / (lmod) : -(-(l2) / (lmod)) -1); \ l2 -= k * (lmod); \ l1 += k * (lp); \ } static void yprint(long m); static void yprint(long m) { if (GLOBAL.count < GLOBAL.n) { if (++GLOBAL.col == 6) { GLOBAL.col = 1; if (++GLOBAL.col1 == 10) { GLOBAL.col1 = 0; out("\n%4ld", m%10); } else { out("%2ld", m%10); } } else { out("%ld", m); } GLOBAL.count++; } } static void xprint(long m); static void xprint(long m) { long ii,wk,wk1; if (m < 8) { for (ii = 1; ii <= GLOBAL.loc;) yprint(GLOBAL.stor[(int)(ii++)]); GLOBAL.loc = 0; } else if (m > 9) { wk = m / 10; m %= 10; for (wk1 = GLOBAL.loc; wk1 >= 1; wk1--) { wk += GLOBAL.stor[(int)wk1]; GLOBAL.stor[(int)wk1] = wk % 10; wk /= 10; } } GLOBAL.stor[(int)(++GLOBAL.loc)] = m; #if 0 if (GLOBAL.loc >= MAX_STOR) { fprintf(stderr, "GLOBAL.loc = %ld, MAX_STOR=%ld.\n", GLOBAL.loc, MAX_STOR); } #endif } //------------------------------------------------------------------------ static int CONT=0, // 1 when continuing a saved session SWAP_FILES_OPEN=0, ALL_IN_RAM=0; // Whether enough RAM was allocated to do computations w/o swap files static long SWAPS=0; // How many swaps done this session static long huge *mf, huge *ms; static FILE *MF_FP=NULL, *MS_FP=NULL; static long RANGE_SIZE = 0, // How many longs are allocated to mf/ms RANGE_LOW = 0, // VIRTUAL index of mf/ms[0] RANGE_HIGH = 0, // VIRTUAL index of mf/ms[RANGE_COUNT-1] RANGE_COUNT = 0; // How many valid longs in mf/ms static char* SWAPPATHNAME1 = ""; static char* SWAPPATHNAME2 = ""; static char* DATAPATHNAME = ""; static char SWAPBASENAME1[] = "PI_MF.SWP"; static char SWAPBASENAME2[] = "PI_MS.SWP"; static char DATABASENAME[] = "PI.DAT"; static char *SWAPFILENAME1, // Gets built out of SWAPPATHNAME1 + SWAPBASENAME1 *SWAPFILENAME2, // Ditto, respectively *DATAFILENAME; // Ditto, respectively static int NO_SWAP=0; // 1 = Don't use swap files static int REDIRECTED; // Whether output was redirected to a file static void wipeswapfiles(void); static void wipeswapfiles(void) { long count; fputs("Wiping swap files...", stderr); for (count = (GLOBAL.n + 3) / RANGE_SIZE; count > 0; count--) { if (fwrite(mf, sizeof(long), RANGE_SIZE, MF_FP) != RANGE_SIZE) { fprintf(stderr, "wipeswapfiles(): fwrite() failed for %ld longs to MF_FP (1).\n", RANGE_SIZE); exit(-1); } if (fwrite(ms, sizeof(long), RANGE_SIZE, MS_FP) != RANGE_SIZE) { fprintf(stderr, "wipeswapfiles(): fwrite() failed for %ld longs to MS_FP (1).\n", RANGE_SIZE); exit(-1); } } if ((count = ((GLOBAL.n + 3) % RANGE_SIZE)) != 0) { if (fwrite(mf, sizeof(long), count, MF_FP) != count) { fprintf(stderr, "wipeswapfiles(): fwrite() failed for %ld longs to MF_FP (2).\n", count); exit(-1); } if (fwrite(ms, sizeof(long), count, MS_FP) != count) { fprintf(stderr, "wipeswapfiles(): fwrite() failed for %ld longs to MS_FP (2).\n", count); exit(-1); } } fputs("done.\n", stderr); } static void openswapfiles(void); static void openswapfiles(void) { SWAP_FILES_OPEN = 1; fputs("Opening swap files...", stderr); if ((MF_FP = fopen(SWAPFILENAME1, CONT ? "r+b" : "w+b")) == NULL) { fprintf(stderr, "openswapfiles(): fopen() failed for '%s'.\n", SWAPFILENAME1); exit(-1); } //fprintf(stderr, "Swap file '%s' opened.\n", SWAPFILENAME1); if ((MS_FP = fopen(SWAPFILENAME2, CONT ? "r+b" : "w+b")) == NULL) { fprintf(stderr, "openswapfiles(): fopen() failed for '%s'.\n", SWAPFILENAME2); exit(-1); } //fprintf(stderr, "Swap file '%s' opened.\n", SWAPFILENAME2); fputs("done.\n", stderr); if (!CONT) { wipeswapfiles(); } } static void closeswapfiles(void); static void closeswapfiles(void) { fputs("Closing swap files...", stderr); if (fclose(MF_FP)) { fprintf(stderr, "closeswapfiles(): fclose() failed for MF_FP.\n"); } //fprintf(stderr, "Swap file '%s' closed.\n", SWAPFILENAME1); if (fclose(MS_FP)) { fprintf(stderr, "closeswapfiles(): fclose() failed for MS_FP.\n"); } //fprintf(stderr, "Swap file '%s' closed.\n", SWAPFILENAME2); fputs("done.\n", stderr); } static void loadrange(long i); static void loadrange(long i) { size_t r1, r2; if (fseek(MF_FP, i*sizeof(long), SEEK_SET)) { fprintf(stderr, "loadrange(): fseek() failed for %ld'th long in MF_FP.\n", i); exit(-1); } if (fseek(MS_FP, i*sizeof(long), SEEK_SET)) { fprintf(stderr, "loadrange(): fseek() failed for %ld'th long in MS_FP.\n", i); exit(-1); } r1 = fread(mf, sizeof(long), RANGE_SIZE, MF_FP); r2 = fread(ms, sizeof(long), RANGE_SIZE, MS_FP); if (r1 != r2) { fprintf(stderr, "%ld longs read from MF_FP, %ld read from MS_FP.\n", r1, r2); exit(-1); } RANGE_LOW = i; RANGE_HIGH = RANGE_LOW + (RANGE_COUNT = r1) - 1; //fprintf(stderr, "Virtual range mf[%ld..%ld] loaded.\n", RANGE_LOW, RANGE_HIGH); } static void saverange(void); static void saverange(void) { size_t r1, r2; if (fseek(MF_FP, RANGE_LOW*sizeof(long), SEEK_SET)) { fprintf(stderr, "loadrange(): fseek() failed for %ld'th long in MF_FP.\n", RANGE_LOW); exit(-1); } if (fseek(MS_FP, RANGE_LOW*sizeof(long), SEEK_SET)) { fprintf(stderr, "loadrange(): fseek() failed for %ld'th long in MS_FP.\n", RANGE_LOW); exit(-1); } r1 = fwrite(mf, sizeof(long), RANGE_COUNT, MF_FP); r2 = fwrite(ms, sizeof(long), RANGE_COUNT, MS_FP); if (r1 != r2) { fprintf(stderr, "%ld longs written to MF_FP, %ld to MS_FP.\n", r1, r2); exit(-1); } GLOBAL.swaps++; // Swaps for entire computation SWAPS++; // Swaps this session (will be same if all 1 session) //fprintf(stderr, "Virtual range mf[%ld..%ld] saved.\n", RANGE_LOW, RANGE_LOW+RANGE_COUNT-1); } static void swaprange(long i); static void swaprange(long i) { if (REDIRECTED) fputs(" (swapping)", stderr); saverange(); loadrange(i); if (REDIRECTED) fputs("\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b", stderr); } static void allocate(long max_bytes); static void allocate(long max_bytes) { long requested_n, requested_bytes, needed_bytes; long huge *lp = NULL; int reduced=0; // Whether we needed to reduce the requirements based on max_bytes requested_n = (GLOBAL.n + 3); needed_bytes = requested_bytes = 2 * requested_n * sizeof(long); if (max_bytes && (requested_bytes > max_bytes)) { requested_n = ((max_bytes / 2) / sizeof(long)); reduced = 1; } do { requested_bytes = 2 * requested_n * sizeof(long); if ((lp = (long *) farcalloc((unsigned long) requested_bytes, (unsigned long) 1)) != NULL) break; requested_n--; } while (requested_n > 0); if (lp == NULL) { fprintf(stderr, "allocate(): unable to allocate memory.\n"); exit(-1); } RANGE_HIGH = (RANGE_SIZE = RANGE_COUNT = requested_n) - 1; mf = lp; ms = lp + RANGE_SIZE; ALL_IN_RAM = (requested_n == GLOBAL.n + 3); if (!ALL_IN_RAM && NO_SWAP) { fprintf(stderr, "Unable to allocate %ld bytes (%ld.%ld KB).\nTry decreasing the number of decimal places, or don't use the -n option.\n", needed_bytes, needed_bytes/1024, (100*(needed_bytes%1024)) / 1024 ); if (reduced) { fprintf(stderr, "Or, increase the amount of memory to use (specified with the -m option).\n"); } farfree(mf); exit(-1); } fprintf(stderr, "%ld bytes (%ld.%ld KB) allocated", requested_bytes, requested_bytes/1024, (100*(requested_bytes%1024)) / 1024); if (!ALL_IN_RAM) { fputs(" (swap files needed!)", stderr); } fputc('\n', stderr); if (CONT || !ALL_IN_RAM) { openswapfiles(); } } static void readdat(void); static void readdat(void) { FILE *fp; if ((fp = fopen(DATAFILENAME, "rb")) == NULL) { fprintf(stderr, "readdat(): fopen() failed for data file.\n"); exit(-1); } if (fread(&GLOBAL, sizeof(GLOBAL), 1, fp) != 1) { fprintf(stderr, "readdat(): fread() failed for data file.\n"); exit(-1); } fclose(fp); } static void writedat(void); static void writedat(void) { FILE *fp; if ((fp = fopen(DATAFILENAME, "wb")) == NULL) { fprintf(stderr, "writedat(): fopen() failed for data file.\n"); exit(-1); } if (fwrite(&GLOBAL, sizeof(GLOBAL), 1, fp) != 1) { fprintf(stderr, "writedat(): fwrite() failed for data file.\n"); exit(-1); } fclose(fp); } //------------------------------------------------------------------------ static char TITLE_LINE[] = "PI v1.1 7/25/1993 Kim Moser"; static void usage(void); static void usage(void) { fprintf(stderr, "\ %s\n\ Computes Pi to a given number of decimal places\n\ \n\ usage: pi [-m] [-s=] [-d] | -c\n\ \n\ -m = Allocate only up to KB of RAM for workspace\n\ (default: use as much RAM as available)\n\ -s = Use for swap file (1='%s', 2='%s')\n\ -d = Use for data file '%s' (when saving data)\n\ -n = Don't use swap files (and don't save session when aborting)\n\ = how many decimal places to compute\n\ -c = continue where left off from previous session\n\ \n\ Based on a version which contained the following comment:\n\ \n\ Programmed by Bill Davidsen after the method of G. M. Roe,\n\ based on a version for \"E\" supplied with the \"B\" compiler, 1970\n\ \n\ Thanks to Leonard Broughton for supplying an almost identical (but bug-free)\n\ version, upon which this program was based. Kim Moser added support for\n\ disk swapping, continuing a saved session, and variable memory usage.\n\ ", TITLE_LINE, SWAPBASENAME1, SWAPBASENAME2, DATABASENAME ); exit(-1); } static int getkey(void); static int getkey(void) { int r; return (((r = getch()) != 0) ? r : getch()+256); } static void show_percent_done(long percent_done); static void show_percent_done(long percent_done) { fprintf(stderr, "%ld of %ld places (%ld%%) done", GLOBAL.count, GLOBAL.n, percent_done); } static void status(long percent_done); static void status(long percent_done) { fputs("\nStatus: ", stderr); if (!REDIRECTED) { show_percent_done(percent_done); fputs("; ", stderr); } if (ALL_IN_RAM) { fputs("no swap files in use.", stderr); } else { fprintf(stderr, "%ld swap%s total", GLOBAL.swaps, (GLOBAL.swaps==1 ? "" : "s")); if (GLOBAL.swaps != SWAPS) { fprintf(stderr, " (%ld this session).", SWAPS); } } fputc('\n', stderr); } static void showkeys(void); static void showkeys(void) { fputs("\ \n\ Press any of the following keys:\n\ \n\ = Save current state and quit\n\ ? = Show status\n\ \n\ ", stderr); } static char* buildfilename(char* path, char* base); static char* buildfilename(char* path, char* base) { char* s; char ch; s = (char*) malloc(strlen(path) + strlen(base) + 3); strcpy(s, path); ch = strlen(path) ? path[strlen(path) - 1] : '\0'; if (ch && ch != ':' && ch != '\\') { strcat(s, "\\"); } strcat(s, base); return (s); } #define kf 25 #define ks 57121L int main(int argc,char *argv[]) { long k; long i, i_minus_1, temp, temp_minus_2, temp_times_kf, temp_times_ks, i_minus_range_low, i_minus_1_minus_range_low; long nd; long outer_limit, inner_limit; int stopped=0; long max_kb=0; // Max KB to use (0=no limit) long prev_count=-1; long percent_done, prev_percent_done=-1; REDIRECTED = !isatty(fileno(stdout)); SWAPFILENAME1 = buildfilename(SWAPPATHNAME1, SWAPBASENAME1); SWAPFILENAME2 = buildfilename(SWAPPATHNAME2, SWAPBASENAME2); DATAFILENAME = buildfilename(DATAPATHNAME, DATABASENAME); if (argc < 2) usage(); for (i=1; i < argc; i++) { if (argv[i][0] == '-') { switch (toupper(argv[i][1])) { case 'C': CONT = 1; break; case 'M': if ((max_kb = atol(argv[i]+2)) <= 0) { usage(); } break; case 'N': NO_SWAP = 1; break; case 'S': switch (atoi(argv[i]+2)) { case 1: SWAPPATHNAME1 = argv[i]+4; break; case 2: SWAPPATHNAME2 = argv[i]+4; break; default: usage(); break; } case 'D': DATAPATHNAME = argv[i]+4; break; default: usage(); break; } } else { GLOBAL.n = atol(argv[i]); } } if (CONT) readdat(); if (GLOBAL.n <= 0) usage(); fprintf(stderr, "%s\n", TITLE_LINE); allocate(max_kb * 1024); if (CONT) { fprintf(stderr, "Continuing to compute Pi to %ld places...\n", GLOBAL.n); loadrange(0); } else { // At this point, if swap file(s) were opened, assume // current range loaded starts at virtual index 0. if (!ALL_IN_RAM) { fputs("Initializing swap files...", stderr); } mf[1] = 1; for (i = 2; i <= GLOBAL.n;) { if (i < RANGE_LOW || i+1 > RANGE_HIGH) { //fprintf(stderr, "* i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld\n", i, RANGE_LOW, RANGE_HIGH); swaprange(i); } for (outer_limit = min((RANGE_HIGH-1), GLOBAL.n); i <= outer_limit; i += 2) { //if (i - RANGE_LOW < 0 || i - RANGE_LOW > RANGE_HIGH) fprintf(stderr, "aaa Range violation: i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld.\n", i, RANGE_LOW, RANGE_HIGH); //if (i+1 - RANGE_LOW < 0 || i+1 - RANGE_LOW > RANGE_HIGH) fprintf(stderr, "bbb Range violation: i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld.\n", i, RANGE_LOW, RANGE_HIGH); mf[i - RANGE_LOW] = -16; mf[i + 1 - RANGE_LOW] = 16; } } for (i = 1; i <= GLOBAL.n;) { if (i < RANGE_LOW || i+1 > RANGE_HIGH) { //fprintf(stderr, "** i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld\n", i, RANGE_LOW, RANGE_HIGH); swaprange(i); } for (outer_limit = min((RANGE_HIGH-1), GLOBAL.n); i <= outer_limit; i += 2) { //if (i - RANGE_LOW < 0 || i - RANGE_LOW > RANGE_HIGH) fprintf(stderr, "ccc Range violation: i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld.\n", i, RANGE_LOW, RANGE_HIGH); //if (i+1 - RANGE_LOW < 0 || i+1 - RANGE_LOW > RANGE_HIGH) fprintf(stderr, "ddd Range violation: i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld.\n", i, RANGE_LOW, RANGE_HIGH); ms[i - RANGE_LOW] = -4; ms[i + 1 - RANGE_LOW] = 4; } } if (!ALL_IN_RAM) { fputs("done.\n", stderr); } fprintf(stderr, "Computing Pi to %ld places...\n", GLOBAL.n); } fputs("(Press to quit, '?' for help)\n", stderr); if (!CONT) out("\n 3."); for (;;) { percent_done = ((100*GLOBAL.count) / GLOBAL.n); if (REDIRECTED && (GLOBAL.count != prev_count || prev_percent_done != percent_done)) { fputc('\r', stderr); show_percent_done(percent_done); prev_count = GLOBAL.count; prev_percent_done = percent_done; } if (GLOBAL.count >= GLOBAL.n) break; for (i = 1, outer_limit = (GLOBAL.n - GLOBAL.count); i <= outer_limit;) { if (i < RANGE_LOW || i > RANGE_HIGH) { //fprintf(stderr, "Multiplying by 10.\n"); //fprintf(stderr, "*** i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld\n", i, RANGE_LOW, RANGE_HIGH); swaprange(i); } for (inner_limit = min(RANGE_HIGH, (outer_limit)); i <= inner_limit; i++) { //if (i - RANGE_LOW < 0 || i - RANGE_LOW > RANGE_HIGH) fprintf(stderr, "@ Range violation: i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld.\n", i, RANGE_LOW, RANGE_HIGH); mf[i - RANGE_LOW] *= 10; ms[i - RANGE_LOW] *= 10; } } for (i = GLOBAL.n - GLOBAL.count; i >= 2;) { if (i-1 < RANGE_LOW || i > RANGE_HIGH) { //fprintf(stderr, "**** i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld\n", i, RANGE_LOW, RANGE_HIGH); //fprintf(stderr, "(Loading range %ld.)\n", max(0, (i-RANGE_SIZE+1))); swaprange(max(0, (i - RANGE_SIZE + 1))); } for (outer_limit = max((RANGE_LOW+1), 2); i >= outer_limit; i--) { i_minus_1 = i - 1; temp = (2 * i) - 1; temp_minus_2 = temp - 2; temp_times_kf = temp * kf; temp_times_ks = temp * ks; i_minus_range_low = i - RANGE_LOW; i_minus_1_minus_range_low = i_minus_1 - RANGE_LOW; if (i_minus_1_minus_range_low < 0 || i_minus_1_minus_range_low > RANGE_HIGH) fprintf(stderr, "@@ Range violation: i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld.\n", i, RANGE_LOW, RANGE_HIGH); if (i_minus_range_low < 0 || i_minus_range_low > RANGE_HIGH) fprintf(stderr, "@@@ Range violation: i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld.\n", i, RANGE_LOW, RANGE_HIGH); shift(mf[i_minus_1_minus_range_low],mf[i_minus_range_low],temp_minus_2,temp_times_kf); shift(ms[i_minus_1_minus_range_low],ms[i_minus_range_low],temp_minus_2,temp_times_ks); } } // Prepare for next 2 shift()'s: if (RANGE_LOW) { //fprintf(stderr, "***** i=%ld, RANGE_LOW=%ld, RANGE_HIGH=%ld\n", i, RANGE_LOW, RANGE_HIGH); //fprintf(stderr, "(Loading range %ld.)\n", max(0, (i-RANGE_SIZE+1))); swaprange(0); } nd = 0; shift(nd,mf[1],1L,5L); shift(nd,ms[1],1L,239L); xprint(nd); if (kbhit()) { int ch = toupper(getkey()); if (ch == '?') { status(percent_done); } else if (ch == 27) { stopped = 1; break; } else { showkeys(); } } } fputs("\n\n", stderr); fputs(stopped ? "Aborted.\n" : "Done.\n", stderr); if (!NO_SWAP) { if (CONT || stopped) { fprintf(stderr, "Saving current state:\n"); writedat(); if (!SWAP_FILES_OPEN) openswapfiles(); saverange(); } else { if (SWAP_FILES_OPEN) { saverange(); } } if (SWAP_FILES_OPEN) closeswapfiles(); } farfree(mf); // Don't free ms[], since it's just the second 1/2 of mf return (0); }