DOL.cpp


// Problem definition by Eli Tilevich:

// Well,  I guess I would like to code a simple registration system with a very
// limited functionality which
// would only allow students register for classes, cancel classes, and delete
// students from the system.
// Basically, it would be something very similar to test18e.c; however I would
// like to specifically concentrate on
// handling the many-to-many relation between students and classes. I would
// like to answer the question of which
// facilities in different class libraries help the programmer handle this
// non-trivial software paradigm.
// --------------------------------------------------------------------------

// Program coded by Jiri Soukup, Nov.17/99
// Modified by Eli Tilevich, Nov.28/99

// IMPORTANT:
// If all your objects are automatically allocated (as in this program),
// you can remove all the ZZ_INIT(...); statements.
// This will make the initialization of all objects slightly faster.

// Notes:
//    This program will not allow you to test performance or memory size,
//    unless you generate a larger volume of data automatically. I am sure you
//    can modify this program accordingly.
// 
//    Remember, that a big part of DOL is the data persistency.
//    I am not demonstrating it here, but some part of data initialization
//    etc. is required for this, which may show as a minor performance
//    penalty.

//    I used simple collections for Students and Classes.
//    For you this is the easiest code to read,
//    but the lookup requires a search
//    through the collection. If would be much faster to use a hash table.

//    If you want to see such a version let me know.
//    This would be also a test of how flexible
//    my implementation is (maintenance, flexibility, robustness).
//    For saving data to disk, I am using the 'binary' mode, which has
//    the simplest interface, but is not the fastest. Memory blasting
//    is about 10x faster.

// Running the program:
//   just type: tilevich
//   If you type STOP instead of your name, the program will stop and save
//   all the data to disk file eli.dat.
//   When you invoke the program next time, it will pick up the old data
//   automatically. In order to start fresh, just remove file eli.dat from
//   your directory.

#include <stdio.h>
#include <time.h>
#define ZZmain
#include "zzincl.h"


long  starttime;

#define TASK(s)    printf("%s", s);
#define CIN        starttime = clock();
#define COUT       printf("%d\t", clock()-starttime);
#define NL         printf("\n")


// School encapsulates your problem
class School {
    ZZ_EXT_School
public:
    School(){ZZ_INIT(School);}
    
	static char *getName(int i,char *buf);
	
	Student * find_student(const char * name);

	void list_student_classes(Student * sp);

	Takes* find_student_class(Student *sp, const char * name);

	Class* find_class(const char * name);

	void add_student_class(Student * sp, Takes * tp, Class * cp);

	void delete_student_class(Takes * tp);

	void delete_student(Student * sp);

protected:


};


class Student {
    ZZ_EXT_Student
public:
    Student(char *name);
};

class Class {
    ZZ_EXT_Class   
public:
    Class(char *name);
};

class Takes {
    int mark;
    int absentDays;
    ZZ_EXT_Takes  
public:
    Takes() {ZZ_INIT(Takes);}
    int addAbsent(int d){absentDays+=d; return absentDays;} // for d=0 reports
    int getMark(){return mark;}
    void setMark(int m){mark=m;}
};


// ----------- data organization --------------
ZZ_HYPER_UTILITIES(util);
ZZ_HYPER_M_TO_N(scRelation,Student,Takes,Class);
ZZ_HYPER_NAME(studentName,Student);
ZZ_HYPER_NAME(className,Class);
ZZ_HYPER_DOUBLE_COLLECT(students,School,Student);
ZZ_HYPER_DOUBLE_COLLECT(classes,School,Class);
// --------------------------------------------

static students_iterator sit;
static classes_iterator cit;
static scRelation_sIterator sourceIter;

Student::Student(char *name){
    ZZ_INIT(Student);
    char* p=util.strAlloc(name);
    studentName.add(this,p);
}

Class::Class(char *name){
    ZZ_INIT(Class);
    char* p=util.strAlloc(name);
    className.add(this,p);
}

// pick up names including spaces, if i>0 skip the first word
char* School::getName(int i,char *buf){
    char *p;
    for(p=buf; *p!='\n'; p++)continue;
    *p='\0'; // mark the end of the name
    if(i==0)return buf;
    for(p=buf; *p!='\0' && *p!=' '; p++)continue;
    if(*p=='\0')return p;
    for( ;*p==' ' && *p!='\0'; p++);
    return p;
}


Student * School::find_student(const char * name){
        
	Student *sp;
	
	sit.start(this); // search for 'your name'
    ITERATE(sit,sp){
       if(!strcmp(name, studentName.fwd(sp)))break;
	}

	return sp;

}

void School::list_student_classes(Student * sp){

    Takes *tp;
	Class *cp;

	printf("registered in:\n");
    sourceIter.start(sp);
    ITERATE(sourceIter, tp){ // walk through all Takes's of sp
	     cp=scRelation.target(tp);
         printf("%s\n", className.fwd(cp));
    }
    
	printf("\n");

}


Takes* School::find_student_class(Student *sp, const char * name){
    
	Class *cp;
	Takes *tp;
	
	sourceIter.start(sp);
    ITERATE(sourceIter, tp){ // walk through all Takes's of sp
       cp = scRelation.target(tp);
       if(!strcmp(name, className.fwd(cp)))break;
    }

	return tp;

}



Class* School::find_class(const char * name){

   
   Class *cp;
   
   cit.start(this);
   
   ITERATE(cit, cp){ // search for the class of the given name
     if(!strcmp(name, className.fwd(cp)))break;
   }

   return cp;

}


void School::add_student_class(Student * sp, Takes * tp, Class * cp){

	tp = new Takes; // many-to-many involves 3 object types!!
    scRelation.add(sp,tp,cp);

}


void School::delete_student_class(Takes * tp){
	scRelation.del(tp); // disconnect tp
    delete tp;


}

void School::delete_student(Student * sp){

TASK("Measuring School::delete_student DOL\n");
CIN;

	
	Takes * tp;

	sourceIter.start(sp); // delete all class links of this student
    ITERATE(sourceIter,tp){ // walk through all Takes's of sp
        scRelation.del(tp); // disconnect tp
        delete tp;          // yes, this is allowed within the loop!
    }
    
	students.del(this, sp);

COUT;
NL;

}



#define BSIZE 80
char buff[BSIZE];
char* fileName="eli.dat";

main(int argc,char **argv) {
    
	School *school; Student *sp; Takes *tp; Class *cp;
    
	void *root[1]; char *type[1],*name,cc; FILE *fp;

    fp=fopen(fileName,"r"); // just test whether the file exists
    if(!fp)school=new School;
    else {                   // pick up the old data from the disk
        fclose(fp);
        util.open(fileName,1,root,type);
        if(util.error()){printf("wrong file format: %s\n",fileName); return 1;}
        school=(School*)(root[0]);
    }

    for(;;){
        printf("type your name:\n");
        fgets(buff,BSIZE,stdin);
        name = School::getName(0,buff);
        if(!strcmp(name,"STOP"))break;
		
		sp = school->find_student(name);
        
        if(sp){
            if(strcmp(name, "eli") != 0)
				school->list_student_classes(sp);
		
		}
        else {
            printf("new student=%s\n",name);
            sp=new Student(name);
            students.add(school,sp);

			//Generate test data for timing
			if(strcmp(name, "eli") == 0){
				
				char class_name[1<<5];               
                for(int cl = 0; cl < 100000; cl ++){
					sprintf(class_name, "CS%d", cl);
					cp=new Class(class_name);
					classes.add(school,cp);
					school->add_student_class(sp, tp, cp);
				}
            			
			}
        }
        for(;;){
            printf("menu(A className,D className,R=remove student,E=exit):\n");
            fgets(buff,BSIZE,stdin);
            sscanf(buff,"%c",&cc);
            name = School::getName(1,buff);
            if(cc=='A'){
                // first check that this student isn't already registered there
                tp = school->find_student_class(sp, name);
				if(tp){ printf("... you are already registered\n"); continue;}

                // find the class
                cp = school->find_class(name);
				if(!cp){
                    printf("... new class\n");
                    cp=new Class(name);
                    classes.add(school,cp);
                }
                school->add_student_class(sp, tp, cp);
            }
            else if(cc=='D'){
                tp = school->find_student_class(sp, name);
                if(!tp){
                    printf(" you are not registered for this class\n");
                    continue;
                }
                school->delete_student_class(tp);
            }
            else if(cc=='R'){
				school->delete_student(sp);
                break;
            }
            else if(cc=='E')break;
            else printf("unknown command=%c\n",cc);
        }
    }

    root[0]=(void*)school;
    type[0]="School";
    util.save("eli.dat",1,root,type);
    return(0);
}
#include "zzfunc.c"