summaryrefslogtreecommitdiff
path: root/macros/generic/knst/knd.c
diff options
context:
space:
mode:
Diffstat (limited to 'macros/generic/knst/knd.c')
-rw-r--r--macros/generic/knst/knd.c352
1 files changed, 352 insertions, 0 deletions
diff --git a/macros/generic/knst/knd.c b/macros/generic/knst/knd.c
new file mode 100644
index 0000000000..a099bb25ad
--- /dev/null
+++ b/macros/generic/knst/knd.c
@@ -0,0 +1,352 @@
+/*
+ AUTHOR: Jordi Saludes <saludes@grec.upc.es>
+ DATE: 12/9/1996
+ FILE: knd.c
+
+ kndecode - Decoder of knapsack tests answers.
+
+ SYNOPSIS
+
+ kndecode [-i] [-apSGI] filename1 [filename2]
+
+ DESCRIPTION
+
+ kndecode decodes page totals for knapsack tests. kndecode
+ reads first the exam description, then the students identifiers and
+ answers and returns grades, decoded answers and other information.
+ When filename2 is given, filename1 contains the exam
+ description (usually a .ans file) and filename2 contains
+ the students data and answers. When only one file name is given,
+ filename1 contains first the exam description and then, after a blank
+ line, the students data.
+ filename2 can be `-', namely the standard input.
+ Exam description
+ The exam description is usally written by the TeX run of
+ exam file into a .ans file. Lines of such desciption are
+ \opts q p r: Question number q has p options. Option number r
+ is the right one.
+ \pagebot q: Question q is the last one in current page.
+ \Key k d: k is the key for the test and d is the
+ detection factor.
+ Data lines
+ Each line contains identifier, page totals (and possibly version
+ number) of a student.
+ Fields are separated by spaces. First field must be the
+ identifier (long int) of student, next ones are
+ the consecutive page totals. However if one of these fields
+ f is negative, it is assumed that it represents a new
+ version number instead of a page total. Version number is -f
+ from now on.
+
+ If no options are given the student identifier is not expected.
+
+ OPTIONS
+
+ i Read incidences. Second field must be an octal
+ number standing for incidences ocurred in the
+ pre-processing of the exam data file.
+ a Return answers of exam, separated by tabs.
+ p Return plain (decoded) page totals,
+ for archiving purposes.
+ S Return the number of right, wrong and null answers.
+ G Return grade. Right answer means +1, null answer
+ means 0, value of wrong answers are adjusted
+ to have 0 expectation when questions are answered
+ picking answers randomly.
+ Modify routine "qualifica" to change grading.
+ I Output incidences. Input incidences from
+ pre-processing (when i option is given) are
+ OR-ed with incidences generated by kndecode and
+ returned.
+ None All information returned by the a, p, S or G
+ options is displayed in more human-readable form.
+ No student identifier is expected as first field.
+
+ EXAMPLES
+
+ kndecode foo.ans -
+ -38353 17942344
+(Version number) (Page 1 total)
+
+ kndecode -G foo.ans - (Only grade)
+ 1001 -38353 17942344 674532
+(IDentifier) (V) (P1) (P2)
+
+ kndecode -i -GI foo.ans - (Grade and output incidences)
+ 1001 2 -38353 17942344 674532
+ (ID) (Incid.) (V) (P1) (P2)
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define NSDF 1 /* Page total is not a multiple of df */
+#define BVER 2 /* Bad version number: Not inversible */
+#define BCNT 4 /* Bad control number. From pre-processing */
+#define DUPR 8 /* Duplicated record. From pre-processing */
+#define BDEC 16 /* Bad decoding */
+
+#define MQNS 100 /* Max number of questions */
+#define MPAGS 10 /* Max number of pages in a test */
+
+#define GRADE 1
+#define PLAIN 2
+#define STAT 4
+#define ANSW 8
+#define IIND 16
+#define OIND 32
+
+#define NSDF_str "%s: %ld is not a multiple of %hd\n"
+#define BVER_str "%s: %ld has no inverse\n"
+#define BDEC_str "%s: Bad decoding for %ld\n"
+#define BFMT_str "%s: Bad format file\n"
+#define UNKO_str "%s: Unknown option\n"
+
+
+typedef struct {short int nopt,marca,ref; long int w;} question;
+
+question qn[MQNS];
+short int npag,df,nq;
+short int pb[MPAGS];
+/* ref[MQNS],nopt[MQNS],marca[MQNS],pb[MPAGS]; */
+long int key;
+/* w[MQNS]; */
+
+int getOptions(int argc, char *argv[], FILE **dataF, short *opts);
+int aplana(long *pla, long a, long s, long modul);
+int inverteix(long *in, long b, long key);
+int deco(long v, long s, question r[],short n,long *pla);
+float qualifica(short *n,short *b, short *m);
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{ FILE *dataF;
+ int errs;
+ short int i,j,incid,opts;
+ short int bons,mals,nuls;
+ long int dni,suma,versio=1L,spla;
+ float nota;
+ int c;
+
+ if(getOptions(argc,argv,&dataF,&opts)==0)
+ return(1);
+ while(1) {
+ incid=0;
+ bons=mals=nuls=0;
+ nota=0.0;
+ if(opts) {
+ if(fscanf(dataF,"%ld",&dni)==EOF) break;
+ if (opts&IIND && fscanf(dataF,"%ho",&incid)==EOF) break;
+ printf("%ld",dni);
+ }
+ for(i=0;i<npag;) {
+ if (fscanf(dataF,"%ld",&suma)==EOF) goto sortir;
+ if (suma<0) {
+ inverteix(&versio,-suma,key);
+ continue;
+ }
+ if (suma%df!=0) {
+ fprintf(stderr,NSDF_str,argv[0],suma,df);
+ incid|=NSDF;
+ goto aborta;
+ }
+ i++;
+ if(deco(versio, suma/df,qn+pb[i-1],pb[i]-pb[i-1],&spla)) {
+ fprintf(stderr,BDEC_str,argv[0],suma/df);
+ incid|=BDEC;
+ goto aborta;
+ }
+ if(!opts) {
+#define LOOP for (j=pb[i-1];j<pb[i];j++)
+ printf("\n\nPg. %d\t",i); LOOP printf("%2d ",j+1);
+ printf("\n\t"); LOOP printf("---");
+ printf("\n\t"); LOOP printf("%2hd ",qn[j].ref);
+ printf("\n\t"); LOOP printf("%2hd ",qn[j].marca);
+ printf("\n\t"); LOOP
+ printf(" %c ", qn[j].marca==0? ' ':(qn[j].marca==qn[j].ref? 'B':'M'));
+ }
+ if(opts&PLAIN)
+ printf("\t%ld",spla);
+ if (opts&ANSW)
+ LOOP printf("\t%hd",qn[j].marca);
+ }
+ /* Now summaries */
+ nota=qualifica(&nuls,&bons,&mals);
+ if(!opts) {
+ printf("\n\nRight\tWrong\tNo answ\t\tGrade");
+ printf("\n%d\t%d\t%d\t\t%4.2f\n\n\n",bons,mals,nuls,nota);
+ }
+ if (opts&GRADE)
+ printf("\t%4.2f", nota);
+ if(opts&STAT)
+ printf("\t%hd\t%hd\t%hd", bons,mals,nuls);
+aborta: if (opts&OIND) printf("\t%ho",incid);
+ printf("\n");
+ }
+sortir:
+ fclose(dataF);
+ return 0;
+}
+
+
+/** Get the multiplicative inverse of b modulo key
+ ** Return in in.
+ ** Uses algorith Y in Knuth
+ **
+ **/
+int inverteix(long *in, long b, long key)
+{ long u1=1,u2=0,u3,v1,v2,v3,t1,t2,t3;
+ long u=b,v=key;
+ int k;
+ if (b<=0) return 1;
+ for (k=0; (u&1)==0 && (v&1)==0; k++) {u/=2; v/=2;}
+ u3=u; v1=v; v2=1-u; v3=v;
+ if ((u&1)==1) {
+ t1=0; t2= -1; t3= -v;
+ goto y4;
+ } else {
+ t1=1; t2=0; t3=u;
+ }
+y3: if( (t1&1)==0 && (t2&1)==0) {
+ t1/=2; t2/=2; t3/=2;
+ } else {
+ t1=(t1+v)/2; t2=(t2-u)/2; t3/=2;
+ }
+y4: if((t3&1)==0) goto y3;
+ if(t3>0) {
+ u1=t1; u2=t2; u3=t3;
+ } else {
+ v1= v-t1; v2= -u-t2; v3= -t3;
+ }
+ t1=u1-v1; t2=u2-v2; t3=u3-v3;
+ if (t1<0) { t1+=v; t2-=u;}
+ if (t3!=0) goto y3;
+ *in= u1;
+ return 0;
+}
+
+
+
+
+/** Return grade. Give also the nuwer of
+ ** right, wrong and null answers.
+ **/
+float qualifica(short *n,short *b,short *m)
+{ short int i;
+ float nota=0.0;
+ (*n)= (*b)= (*m)= 0;
+ for(i=0; i<nq; i++) {
+ if(qn[i].marca==0)
+ (*n)++;
+ else
+ if(qn[i].marca==qn[i].ref) {
+ (*b)++; nota++;
+ } else {
+ (*m)++; nota-=1.0/(qn[i].nopt-1);
+ }
+ }
+ return (nota*10.0)/nq;
+}
+
+
+
+
+/** Given the plain total sp, obtain n answers in res
+ **
+ **/
+int deco(long int v, long int s, question res[], short n,long *pla)
+{ long int sp;
+ short int i;
+
+ if(s<0) return(1);
+ s= s%key;
+ for (sp=0;v;s=(s+s)%key,v>>=1)
+ if(v&1) sp=(sp+s)%key;
+ *pla=sp;
+ for (i=n-1; i>=0; i--) {
+ if ((res[i].marca=sp/res[i].w)>res[i].nopt) return(1);
+ sp= sp%res[i].w;
+ }
+ return(0);
+}
+
+
+
+
+/** Get the options in the command line
+ **
+ **/
+int getOptions(int argc, char *argv[], FILE **dataF, short int *opts)
+{ short int k,i,j,na,cref;
+ long int m=1;
+ int errs, rcs;
+ char opt, rec[50];
+ *dataF=NULL;
+
+ if (argc<=1) return(0);
+ *opts=0;
+ for (k=1;k<argc;k++) {
+ if (argv[k][0]!='-') break; /* File names */
+ switch(argv[k][1]) {
+ case 'i': (*opts)|=IIND;
+ break; /* No incidences */
+ default:
+ for(i=1;(opt=argv[k][i])!=0;i++)
+ switch(opt) {
+ case 'G': /* Give grade */
+ (*opts)|=GRADE; break;
+ case 'p': /* Give plain total */
+ (*opts)|=PLAIN; break;
+ case 'S': /* statistics */
+ (*opts)|=STAT; break;
+ case 'a': /* Give answers */
+ (*opts)|=ANSW; break;
+ case 'I': /* Output incidences */
+ (*opts)|=OIND; break;
+ default:
+ fprintf(stderr, UNKO_str,argv[0]);
+ return(0);
+ }
+ }
+ }
+ *dataF= fopen(argv[k++],"r");
+ if(!(*dataF)) return(0);
+ /* Read the file header*/
+ nq=0; pb[0]=0; npag=0;
+ do {
+ if(!fgets(rec,50,*dataF)) break;
+ rcs=sscanf(rec, "\\Key %ld %hd\n",&key, &df);
+ if(sscanf(rec, "\\opts %hd %hd %hd",&i, &na,&cref)) {
+ rcs++;
+ qn[i-1].ref=cref;
+ qn[i-1].nopt=na;
+ }
+ if(sscanf(rec, "\\pagebot %hd\n",&i)) {
+ rcs++;
+ pb[++npag]=i;
+ nq= nq<i? i: nq;
+ }
+ } while (rcs>0);
+ for(j=0,i=1; i<=npag; i++)
+ for(m=1; j<pb[i]; j++) {
+ qn[j].w= m; /* printf("%ld ",m); */
+ m*=(qn[j].nopt+1);
+ }
+ fprintf(stderr,
+ "\nReading header\nkey= %ld, df= %hd, nq=%hd\n",key,df,nq);
+ if(argc<=k) return(1); /* Use the same file */
+ fclose(*dataF);
+ if(argv[k][0]=='-')
+ *dataF= stdin;
+ else {
+ *dataF= fopen(argv[k],"r");
+ if(!(*dataF)) return(0);
+ }
+ return(1);
+}
+
+
+