/* * arithprb.c: * * Prints a page of randomly generated arithmetic problems. Good for combatting * Alzheimer's. The page is followed by another containing the solutions. * * By Terry R. McConnell * * Compile: cc -o arithprb arithprb.c * */ #include #include #include #include #ifndef BUFFER_SIZE #define BUFFER_SIZE 16184 #endif /* This needs to be changed in strings below too */ #define NPROBS 12 #define VERSION "1.1" #define PROGNAME "arithprb" #define USAGE "arithprb [-n -s -c -t -vh]" #define HELP "-h: print this helpful message\n\ -v: print version number and exit\n\ -n: generate nn problems (default is 12) \n\ -s: use nn as RNG seed (default is current system time if available.)\n\ -c: print problems in nn columns (default is 3.)\n\ -t: write output as a TeX source file (latex)\n\n\ Prints page of arithmetic problems to stdout, followed by page of answers. \n\n" /* In its default setup, this program writes to stdout a description of a page of arithmetic problems. It can write either an ascii text "prettyprint" version of the arithmetic problems, or a LaTeX source file, which must subsequently be typeset to see the problems. In either version, the program views the page as a table of 3 columns and 4 rows (in the default setup). Each table entry is a conceptual box containing a problem. In the LaTeX version the table is created with a \table command sequence, and every entry (problem) in the table is in turn a \table containing 4+ lines typeset to represent the problem, with or without answer. The default configuration can be changed easily by changing defines below, but it is not so easy to change the default TeX setup. */ /* output types */ #define ASCII 0 #define TEX 1 /* Call these depending on type of output */ static int make_prob_ascii(int row, int col, int type, int ans); static int make_prob_tex(int row, int col, int type, int ans); static int(*make_prob)(int,int,int,int); /* Stuff for the ascii problem generator */ #define BANNER "\n\nDo the following arithmetic problems:\n\ (Answers on next page.)\n\n\n\n\n\n\n" static char page_buf[BUFFER_SIZE]; static int cspace = 8; /* blank cols to leave between problems */ static int rspace = 8; /* blank rows to leave between problems. Should be more than double the length of maxnum below */ static int cols = 3; /* number of problem columns */ /* These settings will produce 4 digit multiplication problems and division problems with 8 digit dividends and 4 digit divisors. Think carefully before you change them */ static int probcols = 13; /* this should be at least one more than thrice as long as the length of maxnum below */ static int maxnum = 9999; /* length of the longest generated addend. A dividend can be up to twice this long. */ static int underbarlen = 4; /* should match the number of digits of maxnum */ /* A macro for indexing into the page_buf */ #define BUF_POSITION(row,col,rowoffset) page_buf+((col)-1)*(probcols+cspace)+\ (((row)-1)*(3+rspace)+(rowoffset))*(cols*(probcols+cspace)+1) /* Stuff for the TeX problem generator */ /* This gets printed at the top of the output document. Edit it to implement font and size changes, etc. The default defines needed to create problems which have a particular look and feel are given below near the tex problem routine */ #define TEX_DOC_HEADER "\\documentclass{report}\n\ \\pagestyle{empty}\n\ \\setlength{\\oddsidemargin}{0in}\n\ \\setlength{\\textwidth}{18 cm}\n\ \\setlength{\\textheight}{22 cm}\n\ \\begin{document}\n\\large\n" #define TEX_DOC_FOOTER "\\end{document}\n\\bye\n" /* Make 3 columns of problems. There are 5 columns shown because two columns contain blank problems for filling purposes. */ #define TEX_PAGE_HEADER "\\begin{tabular}{rrrrr}\n" #define TEX_PAGE_FOOTER "\\end{tabular}\n" #ifdef _NO_RANDOM #define RANDOM rand #define SRANDOM srand #else #define RANDOM random #define SRANDOM srandom #endif /* Default values */ #define INITIAL_SEED 3445 #ifndef _MAX_RAND #define _MAX_RAND RAND_MAX #endif /* Problem types */ #define ADD 0 #define SUB 1 #define MULT 2 #define DIV 3 #define NO_ANSWERS 0 #define ANSWERS 1 /* return a number chosen at random from 0,1...,k-1 */ int random_on(int k){ double U; /* U(0,1) random variable */ int rval; U = ((double)RANDOM())/((double)_MAX_RAND); rval = (int)((double)k*U); return rval; } int main(int argc, char **argv){ int c,i=1; int output_type = ASCII; int nprobs = NPROBS; int row=1,col=1; int seed = 0; int type; time_t timenow; timenow = time(NULL); /* time stamp */ make_prob = make_prob_ascii; /* Process command line */ while((i= BUFFER_SIZE ){ fprintf(stderr,"Internal buffer too small for problem set size\n"); fprintf(stderr,"Tone down request, or compile with bigger buffer\n"); exit(1); } /* Seed the random number generator */ /* If no seed is supplied, then use current system time */ if(!seed) if((seed = (long)time(NULL)) == -1){ seed = INITIAL_SEED; /* if all else fails */ fprintf(stderr, "Warning: no seed available. Using %d\n",INITIAL_SEED); } SRANDOM((unsigned)seed); if(output_type == ASCII ){ /* print the date and version number of problem set (i.e, the seed ) */ printf("\n\nDate: %sVersion: %d\n",ctime(&timenow),seed); /* print the banner */ printf("%s", BANNER); } else { printf(TEX_DOC_HEADER); printf("\\today \\ \\ (Version %d.) See attached page for answers.\n \n", seed); printf("\\nopagebreak\n\n"); printf(TEX_PAGE_HEADER); } /* Generate the problems */ for(i=0;i= arg2 ? arg1 : arg2); arg2 = (arg2 < i ? arg2 : i); /* calculate the answer to the problem here, if needed */ if(ans == ANSWERS){ switch(type){ case ADD: answer = arg1 + arg2; break; case MULT: answer = arg1 * arg2; break; case SUB: answer = arg1 - arg2; break; case DIV: answer = arg1/arg2; break; default: fprintf(stderr,"make_prob: called with screwy problem type\n"); exit(1); } } switch(type){ case ADD: /* these have same format, except for operation sign*/ case MULT: case SUB: /* locate initial buffer position of upper left corner of problem output rectangle */ bptr = BUF_POSITION(row,col,0); /* create a right justified string for top operand */ sprintf(lbuf,"%d",arg1); l = strlen(lbuf); xtra = probcols - l; xtra_save = xtra; /* needed to line up operation sign later */ /* pad with blanks in order to right justify */ for(i=0;i= arg2 ? arg1 : arg2); arg2 = (arg2 < i ? arg2 : i); /* calculate the answer to the problem here, if needed */ if(ans == ANSWERS){ switch(type){ case ADD: answer = arg1 + arg2; break; case MULT: answer = arg1 * arg2; break; case SUB: answer = arg1 - arg2; break; case DIV: answer = arg1/arg2; break; default: fprintf(stderr,"make_prob: called with screwy problem type\n"); exit(1); } } /* Multiplication, subtraction and addition have a similar format. We handle division separately below. */ if((type == ADD)||(type==SUB)||(type==MULT)){ /* print a placeholder line of the right column width */ printf(PROBM_HEADER); printf(COLUMN_HEADER); printf("%d\\\\\n",arg1); switch(type){ case ADD: /* these have same format, except for operation sign*/ printf("+\\ "); break; case SUB: printf("-\\ "); break; case MULT: printf("$\\times$\\ "); break; } printf("%d\\\\ \n",arg2); if(ans == ANSWERS) printf("\\hline\n%d\n",answer); else printf("\\hline\n\\ \n"); printf(PROBM_FOOTER); printf("\n"); } else if(type == DIV){ printf(PROBD_HEADER); if(ans == ANSWERS){ /* The answer comes on top in a division problem. */ /* Leave 4 spaces room above the divisor. This crude and should be fixed. It assumes the divisor is 4 digits, but about 10% of the time it is less */ printf("\\ \\ \\ \\ &"); /* Determine how many digits in the answer, then put it enough hard spaces so it will be roughly right. justified. Again, this needs to be fixed */ sprintf(lbuf,"%d",answer); xtra = 9 - strlen(lbuf); for(i=0;i