// $Id: module1.h,v 1.9 1995/12/02 16:09:49 kiran Exp kiran $ // $Author: kiran $ // $Date: 1995/12/02 16:09:49 $ // $Source: /1d2/kiran/src/mtp/PROB-4/src/RCS/module1.h,v $ // $Revision: 1.9 $ // $Log: module1.h,v $ #include #include #include #include #include const bool FALSE = false; const bool TRUE = true; const int MAX_BUF = 1024 ; const int MAX_ROOMS = 20 ; // As per SRS maximum number of rooms const int MAX_SLOTS = 15 ; // As per SRS maximum number of slots const int MAX_COURSES = 30 ; // As per SRS maximum number of courses const int NUM_OF_CATEGORIES = 4 ; // ERROR CODES const int PREF_NOT_HONORED = 0 ; const int NOT_SCHEDULED = 1 ; const int ROOM_NOT_FOUND = 2 ; const int UNSAFE_ALLOTMENT = 3 ; const int PREF_ROOM_NOT_FOUND = 4 ; // `This' particular Pref not honored -> ROOM const int EXCESS_PG = 5 ; // Too many PG's // end here // Forward declarations class TimeTable ; class TableOfCtoBeSched ; class ConflictTable ; class PGReserve ; // Abstraction for Course class Course { private : char *courseID ; public : Course(){} ; Course( char *id ) ; inline char *tellCourseID () { return courseID ; } ; bool isAValidID( ) ; // To test whether courseID is invalid inline bool isAPGCourse() { return ( courseID[2] == '6' ) ; } void setAttr( char * ) ; }; // Abstraction for Room class Room { private : int roomNo ; int capacity ; public : Room() {} ; Room( int num , int cap ) { roomNo = num ; capacity = cap ; } ; inline bool tellAttrib( int &num , int &cap ) { num = roomNo ; cap = capacity ; }; bool areAttribValid( ) ; // Atrribute validation }; // Abstraction for LectureSlot class LectureSlot { private : char *slotID ; public : LectureSlot() {} ; LectureSlot( char *id ) ; inline char *tellSlotID() { return slotID ; } ; bool isAValidSlot( ) ; void setAttr( char *id ) ; }; /* Include List Abstraction here */ const int MAX_ELEMENTS = 500 ; // Maximum Number of List Elements // Generic List Abstraction template class List { private : Element_t listElem[ MAX_ELEMENTS ] ; int currentPtr ; int stateMarker ; public : List() { currentPtr = -1 ; stateMarker = 0 ; } inline bool isListEmpty() { return ( currentPtr == -1 ) ; } bool insertElement ( Element_t element ) ; bool getNextElement ( Element_t &element ) ; inline void resetStateMarker (){ stateMarker = 0 ; } inline int getStateMarker() { // State marker starts from 0 not from -1 return stateMarker-1 ; } bool getIthElement( int , Element_t &) ; bool setIthElement( int , Element_t ) ; inline bool listSize(int &size) { if ( currentPtr == -1 ) return FALSE ; size = currentPtr ; return TRUE ; } } ; // Check for upperbound and then insert element into the list template bool List :: insertElement ( Element_t element ) { if ( currentPtr >= MAX_ELEMENTS ) return FALSE ; listElem[ ++currentPtr ] = element ; return TRUE ; } template bool List :: getNextElement ( Element_t &element ) { if ( isListEmpty() ) { // cout << "List empty "<< endl; return FALSE ; } // State marker beyond the end of list ?? if ( stateMarker > currentPtr ) return FALSE ; element = listElem[ stateMarker ] ; stateMarker++ ; // Advance state marker return TRUE ; } // Tell element at the specified index template bool List :: getIthElement( int i , Element_t &element ) { if ( i < 0 ) return FALSE ; if ( i > currentPtr ) return FALSE ; element = listElem[ i ] ; return TRUE ; } template bool List :: setIthElement( int i , Element_t element ) { if ( i < 0 ) return FALSE ; if ( i > currentPtr ) return FALSE ; listElem[ i ] = element ; return TRUE ; } // Abstraction for room-database class RoomDB { private : List roomDB ; public : bool insert( Room * ) ; // into roomDB void sortRooms() ; // On capacity bool getSuitableRoom( int , int & ) ;// param : enroll , marker // Start from the 'marker' index from then on search for a suitable // room whose capacity is more than 'enroll'. If none found // return false else return index of the suitable room ( not boy ). inline int tellSize() { // How many entries in DB ? int size ; if ( ! roomDB.listSize(size) ) { cout << " FATAL error : Rooms DataBase is Empty " << endl ; exit(1) ; } return size ; } inline Room *tellIthElem( int i ) { // Return 'i'th entry in the DB Room *tmp ; if (! roomDB.getIthElement( i , tmp ) ) return NULL ; return tmp ; } }; // Abstraction for course-database class CourseDB { private : List courseDB ; public : bool insert( Course *) ; // into courseDB bool lookUp( int & ,Course &) ; // Search the DB for 'the course' and return it's // index inline int tellSize() { int size ; if ( ! courseDB.listSize( size ) ) { cout << "FATAL Error : Course DataBase is Empty " << endl ; exit(1) ; } return size ; } inline Course *tellIthElem( int i ) { Course *tmp ; if ( ! courseDB.getIthElement( i , tmp ) ) return NULL ; return tmp ; } }; // Abstraction for slot database class SlotDB { private : List slotDB ; public : bool insert( LectureSlot *) ; // into slot DB bool lookUp( int & ,LectureSlot &); // Return index of the lecture slot inline int tellSize() { int size=1 ; if ( ! slotDB.listSize(size) ) { cout << "FATAL Error : Slot DataBase is Empty " << endl ; exit(1) ; } return size ; } inline LectureSlot *tellIthElem( int i ) { LectureSlot *tmp ; if ( ! slotDB.getIthElement( i ,tmp ) ) return NULL ; return tmp ; } }; class InputFile_1 { private : ifstream inFile ; char *fileName ; bool semicolonExists( char * ) ; // TRUE if semicolon present in the line bool buildRoomDB( RoomDB & ) ; // Parse the input file // until roomDB is built bool build_CS_DB( CourseDB &, SlotDB & ) ; // Parse rest of the file to build // course & slot db's char skipOverWhiteSpaces() ; // Return non-whitespace char starting from // the current position void getNextRoomEntry( int &, int & , int & ) ; // Local to buildRoomDB() // Get next room:capacity from the file void getNextToken( int & , char *& ) ; // char disting's 'c'or's' // Return one meaningful token from the file. public : bool build_CRS_DBs( char *fName,CourseDB &cDB , RoomDB &rDB , SlotDB &sDB ) ; // Just make use of the utility methods to parse and build // course , room and slot databases }; /*********************************************************************/ // Class specifications related to InputFile 2 // class CtoBeSched { // Base class protected : int enrollment ; int coursePtr ; public : virtual void getScheduled( TimeTable *& , RoomDB * , SlotDB * , ConflictTable *& , TableOfCtoBeSched *& , PGReserve *& ){} ; // Schedule this course to be scheduled entry virtual int setAttr( int , int ) ; // Set enrollment and course pointer inline bool tellAttr( int &cptr , int &enroll ) { cptr = coursePtr ; enroll = enrollment ; } } ; class PGwithPref : public CtoBeSched { private : List prefList ; // List of preferences void getScheduled( TimeTable *& , RoomDB * , SlotDB * , ConflictTable *& , TableOfCtoBeSched *& , PGReserve *& ) ; public : bool insertPref ( int ) ; // into prefList }; class UGwithPref : public CtoBeSched { private : List prefList ; // List of preferences public : void getScheduled( TimeTable *& , RoomDB * , SlotDB * , ConflictTable *& , TableOfCtoBeSched *& , PGReserve *&) ; bool insertPref ( int ) ; // into prefList }; class PGwithoutPref : public CtoBeSched { public : void getScheduled( TimeTable *& , RoomDB * , SlotDB * , ConflictTable *& , TableOfCtoBeSched *& , PGReserve *&) ; }; class UGwithoutPref : public CtoBeSched { public : void getScheduled( TimeTable *& , RoomDB * , SlotDB * , ConflictTable *& , TableOfCtoBeSched *& , PGReserve *&) ; }; class PGReserve { private : int reserve[ MAX_COURSES ] [MAX_SLOTS] [MAX_ROOMS] ; bool solely_depends(int , int ) ; // param( course ,slot ) Is this course can be scheduled only // in this slot and no other slot return true. Otherwise false. bool cross_check_succeeds(List *,int , int ) ; // param ( pglist,course,slot) // Check for each slot other than 'slot' , if there is some slot // on which any other course other than 'course' is solely depending // return true. public : PGReserve() ; void initialize(List *,SlotDB *,RoomDB *,TimeTable * ) ; // Reserve suitable slots for PG without prefs so that they do not starve // by the greedyness of UGwithoutPrefs bool isAllotmentSafe( List *,int , int ) ; // Prudent test to foresee whether this allotment making some PG without // pref strave. bool thereIsSomeOtherRoom( int , int , int, bool & ) ; // Is there any other suitable room for this course ? bool thereIsSomeRoom( int , int ) ; // Is there any room suitable at all for this particular (course,slot) combi. void updatePGReserve( int , int ) ; // This particular ( room , slot ) assigned. So update the reservation // chart by marking this as exhausted. void nullifyCourse( int , int , int ) ; // param ( course,room,,slot ). This course is done. So , remove this course // from the reservation chart. And updatePGreserve with the knowledge thar // ( room ,slot ) is assigned bool getSuitableSchedule(TimeTable *&,List *,int cPtr , int rNum , int &slot , int &room ,bool & ) ; // Given a course ( cPtr ) , return the suitable room ( room ) and slot (slot) // where it can be scheduled }; class TableOfCtoBeSched { // Table of courses to be scheduled private : List table [NUM_OF_CATEGORIES] ; PGReserve *pgReserve ; // Reservation chart for PGsansP public : bool scheduleAll( TimeTable *& , SlotDB * ,RoomDB * , ConflictTable *&) ; // Send 'getScheduled' message to all objects in the table in the order bool insert( int , CtoBeSched * ) ; // into table inline bool isAllotmentSafe(int room,int pref) { pgReserve->isAllotmentSafe(&(table[2]),room,pref) ; } inline bool updatePGReserve( int room , int pref ) { pgReserve->updatePGReserve( room , pref ) ; } inline bool getSuitableSchedule( TimeTable *&ttbl,int cPtr , int rNum ,int &slot , int &room , bool &no_room) { return pgReserve->getSuitableSchedule(ttbl,&(table[2]),cPtr,rNum,slot,room, no_room ) ; } inline bool nullifyCourse( int cp , int rp , int sp ) { pgReserve->nullifyCourse(cp,rp,sp); } }; class TimeTableEntry { // One entry in the time table private : int coursePtr ; // Index into Course DB bool booked ; // Some other course booked it ? public : TimeTableEntry() { coursePtr = -1 ; booked = FALSE ; } inline void setAttr( int cptr , bool mark ) { coursePtr = cptr ; booked = mark ; } inline bool isThisBooked() { return booked ; } inline int tellWho() { return coursePtr ; } inline void printEntry( CourseDB *cDB ) { if ( coursePtr != -1 ) cout << cDB->tellIthElem( coursePtr )->tellCourseID() ; else cout << "*****" ; } }; class TimeTable { private : int PGOccupied[ MAX_SLOTS ] ; TimeTableEntry table[ MAX_ROOMS ][ MAX_SLOTS ] ; public : TimeTable() ; bool bookASlot( int , int , int ) ; // Set table[i][j] = ptr ; inline bool isColumnEmpty( int colIndex ) { return (PGOccupied[ colIndex ] == -1 ); } inline bool setPGOccupied( int slotIndex ,int coursePtr ) { if ( (slotIndex < 0 ) || (slotIndex >= MAX_SLOTS ) ) return FALSE; return (bool)( PGOccupied[ slotIndex ] = coursePtr ); } inline int PGwho( int coursePtr ) { return PGOccupied[ coursePtr ] ; } inline int who( int roomPtr , int slotPtr ) { return table[roomPtr][slotPtr].tellWho() ; } void printTimeTable( CourseDB * , RoomDB * , SlotDB *) ; inline bool isAlreadyOccupied( int roomPtr , int slotPtr ) { return table[roomPtr][slotPtr].isThisBooked() ; } bool huntForSuitableRoom( int , int , RoomDB *, int & ) ; // Search in table }; class ConflictTable_Entry { private : int errorCode ; int coursePtr ; int conflictCourse ; int preference ; int roomPtr ; public : inline void setAttr( int E, int c,int cc,int p,int r) { errorCode = E ; coursePtr = c ; conflictCourse = cc ; preference = p ; roomPtr = r ; } void printAttr(CourseDB *,SlotDB *,RoomDB *) ; }; class ConflictTable { private : List< ConflictTable_Entry *> table ; public : bool insertEntry( int , int , int , int , int ) ; void printTable(CourseDB *,SlotDB *,RoomDB *) ; }; class InputFile_2 { private : ifstream inFile ; char *fileName ; char skipOverWhiteSpaces() ; char *getNextToken() ; char *getNextPref() ; // Return one preference at a time starting from // the second. bool prefGiven() ; // Preferences Given ??? public : bool buildCtoBeSched(char *fName , TableOfCtoBeSched &, CourseDB *,SlotDB *) ; }; ////// COURSE ////////// Course :: Course ( char *id ) { setAttr( id ) ; } void Course :: setAttr ( char *id ) { if ( id == NULL ) return ; courseID = new char[ strlen(id) + 1 ] ; if ( strcpy( courseID , id ) == NULL ) cout << " Error : Course::Course() -> Course ID not set "<< endl; courseID[ strlen(id) + 1 ] = '\0' ; } bool Course :: isAValidID ( ) { if ( courseID == NULL ) return FALSE ; // Must be in the form 'csDDD' if ( strlen(courseID) != 5 ) return FALSE ; // First two characters must be 'cs' if ( (courseID[0] != 'c') || (courseID[1] != 's') ) return FALSE ; // NEXT three must be digits if ( ! ( (isdigit(courseID[2]) && (isdigit(courseID[3])) && (isdigit(courseID[4])) ) ) ) return FALSE ; return TRUE ; } /******** ROOM ********/ bool Room :: areAttribValid ( ) { if ( (roomNo < 100) || (roomNo > 999) ) return FALSE ; if ( (capacity < 10 ) || (capacity > 300) ) return FALSE ; return TRUE ; } /******* Lecture Slot *************/ LectureSlot :: LectureSlot( char *id ) { setAttr( id ) ; } void LectureSlot :: setAttr( char *id ) { if ( id == NULL ) return ; slotID = new char[ strlen(id) + 1 ] ; if ( strcpy( slotID , id ) == NULL ) cout << " Error :LectureSlot::LectureSlot() -> LectureSlot ID not set "<< endl; slotID[ strlen(id) + 1 ] = '\0' ; } bool LectureSlot :: isAValidSlot ( ) { char *id = slotID ; if ( id == NULL ) return FALSE ; // Valid slots are MWFd , MWFdd , TTD , TTd:dd or TTdd:dd form int len = strlen( id ) ; switch ( len ) { case 3 : // It must be of the form 'TTd' if ( ! ( (id[0] == 'T') && (id[1] == 'T') && (isdigit(id[2])) ) ) return FALSE ; break ; case 4 : // It must be of the form 'MWFd' if ( ! ( (id[0] == 'M') && (id[1] == 'W') && (id[2] == 'F') && isdigit(id[3]) ) ) return FALSE ; break ; case 5 : // It must be of the form 'MWFdd' if ( ! ( (id[0] == 'M') && (id[1] == 'W') && (id[2] == 'F') && isdigit(id[3]) && isdigit( id[4] ) && id[3] != '0') ) return FALSE ; break ; case 6 : // It must be of the form 'TTd:dd' if ( ! ( (id[0] == 'T') && (id[1] == 'T') && (isdigit(id[2])) && ( id[3] == ':') && isdigit(id[4]) && isdigit(id[5]) ) ) return FALSE ; break ; case 7 : // It must be of the form 'TTdd:dd' if ( ! ( (id[0] == 'T') && (id[1] == 'T') && isdigit(id[2]) && id[2] != '0' && isdigit(id[3]) && ( id[4] == ':') && isdigit(id[5]) && isdigit(id[6]) ) ) return FALSE ; break ; default : return FALSE ; } /* switch */ return TRUE ; } /* End of LectureSlot :: isAValid /******* COURSE - DB ********/ bool CourseDB :: insert ( Course *course ) { if ( course == NULL ) return FALSE ; return courseDB.insertElement( course ) ; } bool CourseDB :: lookUp ( int &index , Course &cs ) { courseDB.resetStateMarker() ; // Start from the begining Course *tmp ; while ( courseDB.getNextElement( tmp ) ) { if ( strcmp( cs.tellCourseID() ,tmp->tellCourseID() ) == 0 ) { index = courseDB.getStateMarker() ; return TRUE ; } } return FALSE ; } /******** SLOT - DB *******/ bool SlotDB :: insert ( LectureSlot *slot ) { if ( slot == NULL ) return FALSE ; return slotDB.insertElement( slot ) ; } bool SlotDB :: lookUp ( int &index ,LectureSlot &lSlot ) { slotDB.resetStateMarker() ; // Start from the begining LectureSlot *tmp ; while ( slotDB.getNextElement( tmp ) ) { if ( strcmp( lSlot.tellSlotID() ,tmp->tellSlotID() ) == 0 ) { index = slotDB.getStateMarker() ; return TRUE ; } } return FALSE ; } /****** ROOM - DB ***********/ bool RoomDB :: insert( Room *room ) { if ( room == NULL ) return FALSE ; return roomDB.insertElement( room ) ; } void RoomDB :: sortRooms() { Room *roomPtr1,*roomPtr2 ; int i,j,enroll1,enroll2,room,maxElems ; if ( ! roomDB.listSize( maxElems ) ) return ; // List empty for( i = 0 ; i < maxElems ; i++ ) { for( j = i+1 ; j <= maxElems ; j++ ) { roomDB.getIthElement(i,roomPtr1 ) ; roomPtr1->tellAttrib( room , enroll1 ) ; roomDB.getIthElement(j,roomPtr2 ) ; roomPtr2->tellAttrib( room , enroll2 ) ; if( enroll1 > enroll2 ) { // Swap ptr places roomDB.setIthElement( i , roomPtr2 ) ; roomDB.setIthElement( j , roomPtr1 ) ; } } } } bool RoomDB :: getSuitableRoom( int enroll , int &marker ) { // Start from the 'marker' index from then on search for a suitable room // whose capacity is more than 'enroll' . If none is found return FALSE // else treturn the index of the suitable room. int dbSize , room , capacity ; Room *roomPtr ; if ( ! roomDB.listSize( dbSize ) ) return FALSE ; if ( marker < -1 ) return FALSE ; if ( marker >= dbSize ) return FALSE ; // Start frm the next of state marker for( int i = marker+1 ; i <= dbSize ; i++ ) { roomDB.getIthElement( i , roomPtr ) ; roomPtr->tellAttrib( room , capacity ) ; if ( capacity >= enroll ) { marker = i ; return TRUE ; } } return FALSE ; } //////////////////////////////////////////////////// // // // CourseToBeScheduled related classes // // // //////////////////////////////////////////////////// int CtoBeSched :: setAttr ( int enroll , int crs ) { enrollment = enroll ; coursePtr = crs ; } void PGwithPref :: getScheduled( TimeTable *&timeTable , RoomDB *rDB , SlotDB *sDB , ConflictTable *&cTab, TableOfCtoBeSched *&tbl, PGReserve *&pgR) { bool scheduled = FALSE ; int coursePtr , prefPtr , enrollment ,roomPtr = -1 ; tellAttr( coursePtr , enrollment ) ; prefList.resetStateMarker() ; while( prefList.getNextElement( prefPtr ) ) { if ( timeTable->isColumnEmpty( prefPtr ) ) { if ( rDB->getSuitableRoom( enrollment , roomPtr ) ) { timeTable->bookASlot( roomPtr , prefPtr , coursePtr ) ; timeTable->setPGOccupied( prefPtr , coursePtr ) ; scheduled = TRUE ; break ; } else { // Room having sufficient capacity not found cTab->insertEntry( PREF_ROOM_NOT_FOUND , coursePtr , coursePtr , prefPtr, roomPtr ) ; } }else { cTab->insertEntry( PREF_NOT_HONORED, coursePtr , timeTable->PGwho( prefPtr ), prefPtr , roomPtr ) ; } } if ( !scheduled ) cTab->insertEntry( NOT_SCHEDULED, coursePtr , coursePtr , prefPtr , roomPtr ) ; } bool TimeTable :: huntForSuitableRoom( int pref,int enroll , RoomDB *rDB , int &rPtr ) { // Look at the column of TimeTable[][pref] to find any rooms empty while( rDB->getSuitableRoom( enroll , rPtr) ) { if ( isAlreadyOccupied( rPtr , pref ) ) continue ; else return TRUE ; } return FALSE ; } void UGwithPref :: getScheduled (TimeTable *&ttbl , RoomDB *rDB , SlotDB *sDB , ConflictTable *&cTab, TableOfCtoBeSched *&tcts , PGReserve *&pgReserve) { int coursePtr, enrollment , prefPtr , roomPtr = -1 ; tellAttr( coursePtr , enrollment ) ; prefList.resetStateMarker() ; while( prefList.getNextElement( prefPtr ) ) { // There are more prefs .. roomPtr = -1 ; if ( ! ttbl->isColumnEmpty( prefPtr ) ) { // Some PG already occupied this slot // So , go ahead with out any probs if ( ttbl->huntForSuitableRoom( prefPtr ,enrollment , rDB , roomPtr ) ) { // There is suitable room too. ttbl->bookASlot( roomPtr , prefPtr , coursePtr ) ; tcts->updatePGReserve( roomPtr , prefPtr ) ; return ; } else { // But , no suitable room . roomPtr = -1 ; cTab->insertEntry( PREF_ROOM_NOT_FOUND , coursePtr , coursePtr , prefPtr, roomPtr ) ; continue ; // Get next pref } } // NO PG occupies this . Hmm , here we have problems. else { bool roomAvailable = FALSE ; bool unsafeAllotment = FALSE ; while ( ttbl->huntForSuitableRoom( prefPtr , enrollment , rDB , roomPtr) ) { roomAvailable = TRUE ; if ( tcts->isAllotmentSafe(roomPtr , prefPtr ) ) { ttbl->bookASlot( roomPtr , prefPtr , coursePtr ) ; tcts->updatePGReserve( roomPtr , prefPtr ) ; return ; }else unsafeAllotment = TRUE ; } // While loop if ( unsafeAllotment ) cTab->insertEntry( UNSAFE_ALLOTMENT ,coursePtr,coursePtr, prefPtr , roomPtr ) ; if ( ! roomAvailable ) { cTab->insertEntry( PREF_ROOM_NOT_FOUND , coursePtr , coursePtr ,prefPtr, roomPtr ) ; } } } cTab->insertEntry( NOT_SCHEDULED , coursePtr , coursePtr , prefPtr , roomPtr); } void PGwithoutPref :: getScheduled( TimeTable *&ttbl ,RoomDB *rDB, SlotDB *sDB , ConflictTable *&ct, TableOfCtoBeSched *&tcts , PGReserve *&pgR ) { int sz ; int sptr , rptr ; bool no_room = FALSE ; sz = rDB->tellSize( ) ; if ( tcts->getSuitableSchedule( ttbl , coursePtr ,sz,sptr,rptr,no_room ) ) { ttbl->bookASlot( rptr , sptr , coursePtr ) ; ttbl->setPGOccupied( sptr , coursePtr ) ; tcts->nullifyCourse( coursePtr , rptr , sptr ) ; } else if ( no_room ) ct->insertEntry( ROOM_NOT_FOUND,coursePtr,coursePtr,sptr,rptr) ; else // Excess PG ct->insertEntry( EXCESS_PG,coursePtr,coursePtr,sptr,rptr) ; } void UGwithoutPref :: getScheduled( TimeTable *&ttbl ,RoomDB *rDB, SlotDB *sDB , ConflictTable *&ct, TableOfCtoBeSched *&tcts , PGReserve *&pgR ) { // Get suitable room , with slot as no barrier see if // there are any empty slots . Fill them otherwise add to conflict list int size ; size = sDB->tellSize() ; for( int i = 0 ; i <= size ; i++ ) { // For each slot int roomPtr = -1 ; while( ttbl->huntForSuitableRoom( i ,enrollment , rDB , roomPtr ) ) { ttbl->bookASlot( roomPtr,i,coursePtr ) ; return ; } } ct->insertEntry( ROOM_NOT_FOUND , coursePtr , coursePtr ,size,size) ; // Neglect last two args } bool PGwithPref :: insertPref( int lSlot ) { return prefList.insertElement( lSlot ) ; } bool UGwithPref :: insertPref( int lSlot ) { return prefList.insertElement( lSlot ) ; } bool TableOfCtoBeSched :: insert ( int index , CtoBeSched *cPtr ) { if ( index >= NUM_OF_CATEGORIES ) return FALSE ; if ( index < 0 ) return FALSE ; return table[index].insertElement( cPtr ) ; } bool TableOfCtoBeSched :: scheduleAll( TimeTable *&ttbl , SlotDB *sDB , RoomDB *rDB , ConflictTable *&ctbl ) { // MPDIFY THIS FUNCTION int size ; CtoBeSched *tmp ; int cptr , enrollment ; pgReserve = new PGReserve ; // Initialize PGReserve for( int i = 0 ; i < NUM_OF_CATEGORIES ; i++ ) { if ( i == 1 ) pgReserve->initialize( &(table[2]),sDB,rDB,ttbl) ; table[i].resetStateMarker() ; while ( table[i].getNextElement( tmp ) ) { //Debug tmp->tellAttr( cptr , enrollment ) ; TableOfCtoBeSched *tmp1 = this ; tmp->getScheduled( ttbl , rDB , sDB ,ctbl, tmp1 , pgReserve) ; } } } void ConflictTable_Entry :: printAttr(CourseDB *cDB , SlotDB *sDB , RoomDB *rDB) { Room *room ; Course *course ; LectureSlot *lslot ; char *cStr ; char *sStr ; int rNum , enroll ; course = cDB->tellIthElem( coursePtr ) ; cStr = course->tellCourseID(); cout << endl << endl << "Course : " << cStr << endl ; switch ( errorCode ) { case ROOM_NOT_FOUND : cout << "Could not find suitable and unoccupied room" <tellIthElem( preference ) ; sStr = lslot->tellSlotID( ) ; cout << "Preference : " << sStr << " not honored " << endl ; cout << "Reason : " ; cout << "Could not find suitable and unoccupied room" <tellIthElem( preference ) ; sStr = lslot->tellSlotID( ) ; cout << "Preference : " << sStr << " not honored " << endl ; course = cDB->tellIthElem( conflictCourse ) ; cStr = course->tellCourseID(); cout << "Reason : " ; cout << "Conflicts with : " << cStr << endl ; break ; case UNSAFE_ALLOTMENT : lslot = sDB->tellIthElem( preference ) ; sStr = lslot->tellSlotID( ) ; cout << "Preference : " << sStr << " not honored " << endl ; cout << "Reason : " ; cout << " Scheduling with this Pref , jeopardizes some PG course" << endl ; break ; case NOT_SCHEDULED : cout << "None of the preferences could be satisfied" <printAttr( cDB , sDB , rDB ) ; } bool ConflictTable :: insertEntry( int ecode, int c , int cc, int p, int r) { ConflictTable_Entry *cEnt = new ConflictTable_Entry ; cEnt->setAttr( ecode , c, cc,p,r ); return table.insertElement( cEnt ) ; } //////////////////////////////////////////////////// // INPUTFILE_1 // /////////////////////////////////////////////////// char InputFile_1 :: skipOverWhiteSpaces() { char ch ; inFile.get( ch ) ; while ( ( ch != EOF ) && (( ch == ' ' ) || ( ch == '\t' ) || ( ch == '\n' )) ) inFile.get( ch ) ; return ch ; } bool InputFile_1 :: semicolonExists( char *token ) { int i = 0 ; while ( token[i] != '\0' ) if ( token[i++] == ';' ) return TRUE ; return FALSE ; } void InputFile_1 :: getNextRoomEntry( int &fileState , int &roomNo , int &cap ) { int index = 0 ; char ch , token[MAX_BUF] , hold[MAX_BUF] ; // fileState = ( RoomEntry = 0 , RoomToken = 1 , EOF =2 , Semicolon = 3 ,Error = 4 ) memset( token , '\0' , MAX_BUF ) ; if ( fileState == -1 ){ // We are starting afresh lookin for 'rooms' ch = skipOverWhiteSpaces() ; inFile.unget() ; inFile.getline( token , MAX_BUF ) ; fileState = (strcmp(token,"rooms") == 0)?1:4 ; return ; } if ( ! inFile.getline( token , MAX_BUF ) ) { fileState = 2 ; return ; } // skip over leading junk while ( (token[index] == ' ' || token[index] == '\t') && token[index] != '\0' ) index++ ; if ( token[index] == '\0' ) { fileState = 4 ; return ; } if ( token[index] == ';' ) { fileState = 3 ; return ; } int i = 0 ; while( isdigit( token[index] ) ) hold[i++] = token[index++] ; if ( i == 0 || token[index] != ':' ) { cerr << "Invalid Room:Capacity pair -> " << token << endl ; fileState = ( semicolonExists( token ) ) ? 3 : 4 ; return ; } hold[i] = '\0' ; roomNo = atoi( hold ) ; memset( hold , '\0' , MAX_BUF ) ; i = 0 ; index++ ; // Move off ':' while ( isdigit( token[index] ) ) hold[i++] = token[index++] ; if ( i==0 ) { cerr << "Invalid Room:Capacity pair -> " << token << endl ; fileState = ( semicolonExists( token ) ) ? 3 : 4 ; return ; } hold[i] = '\0' ; cap = atoi( hold ) ; fileState = 1 ; } // **********--> Build Room DB () bool InputFile_1 :: buildRoomDB( RoomDB &rDB ) { int state = -1 , room , cap ; // First check for the token 'rooms' getNextRoomEntry( state , room , cap ) ; if ( state != 1 ) { cerr << " FATAL : Expecting token 'rooms' : but could not find " << endl ; exit( 1 ); } getNextRoomEntry( state , room , cap ) ; int numOfRooms=0; while ( (state != 2 ) && ( state != 3 ) ) { // Not EOF and Semicolon do .. if ( state != 4 ) { // Create a room object check for validity and insert Room *roomPtr = new Room( room , cap ) ; if ( ! roomPtr->areAttribValid( ) ) { // Just Skip over Invalid Room Entry cerr << "Invalid Room : Capacity Pair => " << room << ":" < get next token **********/ void InputFile_1 :: getNextToken( int &fileState , char *&token ) { char ch ; int index = 0 ; token = new char[MAX_BUF] ; // Same definition of fileState ( defined in getNextToken ) will also // hold here if ( fileState == -1 ) { // Parsing for magic token ch = skipOverWhiteSpaces() ; if ( ch == EOF ) { fileState = 2 ; return ; } // EOF state if ( ch == ';' ) { fileState = 3 ; return ; } // Semicolon state inFile.unget() ; fileState = 1 ; inFile.getline( token , MAX_BUF ) ; return ; } ch = skipOverWhiteSpaces() ; if ( ch == EOF ) { fileState = 2 ; return ; } // EOF state while( ! inFile.eof() && ch != ',' && ch != ';' && ch != ' ' && ch != '\t' && ch != '\n') { token[index++] = ch ; ch = inFile.get() ; } token[index] = '\0' ; if ( inFile.eof() ) { fileState = 2 ; return ; } if ( ch == ';' ) { fileState = 3 ; return ; } if ( index == 0 ) { fileState = 4 ; return ; } fileState = 1 ; } //*******--> build Course DB ********/ bool InputFile_1 :: build_CS_DB( CourseDB &cDB , SlotDB &sDB ) { int state = -1 ; char *token ; char ch ; // First check for the token 'courses' getNextToken( state , token ) ; // Parsing for courses is going on.. if ( ( state == 2 ) || ( state == 4 ) || (strcmp( token , "courses") != 0 ) ) { // Magic string 'courses' not found .. cerr << "FATAL : Expecting token 'courses' : but could not find " << endl ; exit( 1 ) ; } int numOfCourses = 0 ; while ( (state != 2 ) && (state != 3 ) ) { // Not EOF ans Semicolon do .. getNextToken( state , token ) ; if ( (state != 4) && token[0] != '\0' ) { Course *course = new Course( token ) ; if ( course->isAValidID() ) { if ( numOfCourses < MAX_COURSES ) { // cout <<" INSERT Course = " << token << endl ; cDB.insert( course ) ; numOfCourses++ ; } else { cout << "Number of courses in the Input file exceed : " << MAX_COURSES << endl ; cout << "So ,Skipping over .. CourseName= " << token << endl ; } } else { cerr << "Invalid Course : " << token << endl ; delete course ; } } else if ( token[0] != '\0' ) cerr << "Invalid Course : " << token << endl ; } if ( state != 3 ) return FALSE ; // No semicolon after course list state = -1 ; getNextToken( state , token ) ; // Parsing for slots is going on .. if ( ( state == 2 ) || ( state == 4 ) || (strcmp( token , "times") != 0 ) ) { cout << "Expecting token 'times' : but could not find " << endl ; exit( 1 ) ; } int numOfSlots = 0 ; while ( (state != 2 ) && (state != 3) ) { // Not EOF and Semicolon do . getNextToken( state , token ) ; if ( state != 4 && token[0] != '\0' ) { LectureSlot *sl = new LectureSlot( token ) ; if ( sl->isAValidSlot() ) { if ( numOfSlots < MAX_SLOTS ) { // cout << " INSERT Slot = " << token << endl ; sDB.insert( sl ) ; numOfSlots++ ; } else { cerr << " Number of slots in inputfile exceed : "< Could not open input file" << endl ; exit( 1 ); } buildRoomDB( rDB ) ; // Build Room Database build_CS_DB( cDB , sDB ) ; // Build both Course and Slot DB's inFile.close() ; } ///////////////////////////////////////////////////////////////////// // InputFile2 related objects and method specification // ////////////////////////////////////////////////////////////////////// char InputFile_2 :: skipOverWhiteSpaces() { char ch ; if ( inFile.eof() ) return EOF ; inFile.get( ch ) ; while ( ( !inFile.eof() ) && ( (ch == ' ') || (ch == '\t') || (ch == '\n')) ) inFile.get(ch) ; return ch ; } char *InputFile_2 :: getNextToken( ) { // Assumed that file is open char ch = skipOverWhiteSpaces() ; char Token[ MAX_BUF ] ; char *toBeReturned ; int index = 0 ; if ( ch == EOF ) return NULL ; while ( ((isalnum( ch )) || ( ch == ':' )) && ( !inFile.eof() ) ) { Token[index++] = ch ; // Checking 'colon' : since we use this function to get 1st pref inFile.get( ch ) ; } if ( index == 0 ) return NULL ; // Token not found probably EOF inFile.unget( ) ; // Put back the character to it's position Token[index] = '\0' ; toBeReturned = (char *)new char[ index ] ; strcpy( toBeReturned , Token ) ; return toBeReturned ; } char *InputFile_2 :: getNextPref( ) { char ch = skipOverWhiteSpaces() ; char pref[ MAX_BUF ] ; char *toBeReturned ; int index = 0 ; if ( ch == EOF ) return NULL ; if ( ch != ',' ) { inFile.unget();return NULL ;} // Pref has to be preceded by a ',' // except for the first time. ch = skipOverWhiteSpaces() ; if ( ch == EOF ) return NULL ; while ( isalnum(ch) || ( ch == ':' ) ) { pref[index++] = ch ; inFile.get( ch ) ; } if ( index == 0 ) return NULL ; // Probably EOF inFile.unget( ) ; // pref[index] = '\0' ; toBeReturned = (char *)new char[ index ] ; strcpy( toBeReturned , pref ) ; return toBeReturned ; } bool InputFile_2 :: prefGiven() { char ch ; inFile.get( ch ) ; while ( ( ch == ' ') || (ch == '\t' ) ) inFile.get( ch ) ; if ( isalpha( ch ) ) { inFile.unget( ) ; // Put the chracter back in file return TRUE ; } else return FALSE ; } bool InputFile_2 :: buildCtoBeSched( char *fName , TableOfCtoBeSched &tbl , CourseDB *cDB , SlotDB *sDB ) { if ( fName == NULL ) return FALSE ; inFile.open( fName , ios::in ) ; if ( !inFile ) { cout << "FATAL : InputFile_2 :: Could not open file " << fName << endl ; exit(1) ; } LectureSlot slot ; // Temporary Var int slotPtr ; // Pointer the Object in the Database Course course ; // Temporary Var int coursePtr ; // Pointer the Object in the Database char *token ; int arrayIndex ; bool prefExist ; // First we need to skip over Magic Line // 'Course Number blah blah ...' char ch = skipOverWhiteSpaces() ; // We are positioned at the start of this line token = new char[MAX_BUF] ; inFile.getline( token , MAX_BUF ) ; // Slurped into token memset(token,'\0',MAX_BUF) ; // Dumped into thin air while ( (token = getNextToken()) != NULL ) { // 'token' now holds a course identifier char *cName = token ; prefExist = FALSE ; if ( (token = getNextToken()) == NULL ) { cerr << "Incorrect format in file : " << fName << " : at line : " << cName << " ..." << endl ; cerr << "Skipping over it .."<lookUp( coursePtr , course ) ) { // Check for error cout << " Course : " << cName << " though valid not found in the database " <setAttr( enroll , coursePtr ) ; if ( prefExist ) { /* Do this only when prefs exist */ // Parse all Preferences ... token = getNextToken() ; // Contains the first preference .. while ( token ) { // Set up a pointer to the existing LectureSlot DB element slot.setAttr( token ) ; if ( ! slot.isAValidSlot() ) { cout << " Inputfile2 : Invalid preference : "<< token << " in line " << cName << " ...." << endl ; cout << "Skipping Over it .. " << endl ; } else { // OK ! , evrything sounds good // One more hitch here.. Slot may be valid but not // present in our database. if ( ! sDB->lookUp( slotPtr , slot ) ) { // Valid Lecture Slot , but not found in DB cout << " Preference " << token << " , though valid not found in DB " << endl ; } else { // Valid and found DB if ( arrayIndex == 0 ) ((PGwithPref *)cts)->insertPref( slotPtr ) ; else ((UGwithPref *)cts)->insertPref( slotPtr ) ; } } // Get next preference token = getNextPref() ; } } // If PrefExist condition block tbl.insert(arrayIndex , cts ) ; } // ELSE - Label : Though valid } else cout << " BuildCtoBeSched()=> Invalid course " << cName << endl ; } // While loop inFile.close() ; } /////////////////////////////////////// // PGReserve // //////////////////////////////////////// PGReserve :: PGReserve() { for(int i = 0 ; i < MAX_COURSES ; i++ ) for( int j = 0 ; j < MAX_SLOTS ; j++ ) for( int k = 0 ; k < MAX_ROOMS ; k++ ) reserve[i][j][k] = 0 ; // Set to Error State } void PGReserve :: initialize(List *pgList, SlotDB *sDB , RoomDB *rDB , TimeTable *ttbl) { CtoBeSched *pg ; int size , cPtr , enroll ,room_index = -1 ; int numSlots ; if ( ! pgList->listSize( size ) ) return ; numSlots = sDB->tellSize( ) ; // How many slots ? for( int i = 0 ; i <= size ; i++ ) { // For each PGwithoutPref course.. pgList->getIthElement( i , pg ) ; // Get the pg entry pg->tellAttr( cPtr , enroll ) ; // Get the attributes // Find a suitable room for this // course for( int j =0 ; j<= numSlots ; j++ ) { // For each of thses slots if ( ttbl->isColumnEmpty( j ) ) { // if no other PG occupies int roomPtr = -1 ; while( rDB->getSuitableRoom( enroll , roomPtr ) ) { // For each room that can // accomadate if ( ! ttbl->isAlreadyOccupied( roomPtr , j ) ) reserve[cPtr][j][roomPtr] = 1 ; } } } } } bool PGReserve :: thereIsSomeOtherRoom( int i , int j , int rPtr ,bool &atleastOne ) { for( int k = 0 ; k < MAX_ROOMS ; k++ ) { if ( reserve[i][j][k] != 0 ) { atleastOne = TRUE ; // There is atleast one single room if ( k != rPtr ) // There is some room diff from rPtr return TRUE ; } } return FALSE ; } bool PGReserve :: thereIsSomeRoom( int i , int j ) { for( int k = 0 ; k < MAX_ROOMS ; k++ ) if ( reserve[i][j][k] != 0 ) return TRUE ; return FALSE ; } bool PGReserve :: solely_depends( int cptr , int sptr ) { for(int i = 0 ; i < MAX_SLOTS ; i ++ ) { if ( i != sptr ) { // any other slot if ( thereIsSomeRoom(cptr , i ) ) return FALSE ; } } if ( ! thereIsSomeRoom(cptr , sptr ) ) return FALSE ; return TRUE ; } bool PGReserve :: cross_check_succeeds( List *pgList ,int cptr , int sptr ) { // Check for each slot other than sptr , if there is something , solely // on which any other course is depending to be scheduled. CtoBeSched *pg ; int cp , enr ; int sz ; bool flag ; pgList->listSize( sz ) ; for ( int i = 0 ; i < MAX_SLOTS ; i++ ) { // For each slot if ( i != sptr ) // other than sptr for(int k = 0 ; k < MAX_ROOMS ; k++ ) // Foreach room see.. // if there is any other crs // that just has [cptr][i][k] == 1 if ( reserve[cptr][i][k] == 1 ) { for( int crs = 0 ; crs <= sz ; crs ++ ) { pgList->getIthElement( crs , pg) ; pg->tellAttr( cp , enr ) ; bool dummy ; if ( solely_depends( cp , i ) ) { if ( ! thereIsSomeOtherRoom( cp , i, k , dummy ) ) return TRUE ; } } } } return FALSE ; } bool PGReserve :: isAllotmentSafe( List *pgList , int roomPtr , int slotPtr ) { int size ; bool emptySlots[ MAX_SLOTS ] ; // Just to check whether slot list is empty bool allEmpty ; if ( ! pgList->listSize( size ) ) return TRUE ; for( int i = 0 ; i <= size; i++ ) { // For each course do.. CtoBeSched *pg ; int j ,cPtr , enroll ; pgList->getIthElement( i , pg ) ; pg->tellAttr( cPtr , enroll ) ; bool flagCourseSafe = FALSE ; bool flagSlotSafe = FALSE ; for (j = 0 ; j < MAX_SLOTS ; j++ ) emptySlots[j] = FALSE ; allEmpty = TRUE ; for( j= 0 ; j < MAX_SLOTS ; j++ ) {// For each slot do .. if ( slotPtr == j ) { if ( thereIsSomeOtherRoom( cPtr , j , roomPtr , emptySlots[j] ) ) { flagSlotSafe = TRUE ; continue ; } else emptySlots[j] = !emptySlots[j] ; } else // In room list of some other slot if ( thereIsSomeRoom( cPtr , j ) ) { // Just check whether there is some room in this slot flagCourseSafe = TRUE ; continue; } else emptySlots[j] = TRUE ; } // EACH SLOT loop for( j = 0 ; j < MAX_SLOTS ; j++ ) allEmpty = emptySlots[j] && allEmpty ; if ( allEmpty ) flagCourseSafe = TRUE ; // All Empty slots you can not schedule // this , No harm in neglecting this // ( flagCourseSafe ) ? cout << cPtr << "is safe" : cout << cPtr << "is unsafe" // << endl; if ( flagCourseSafe && ( !flagSlotSafe ) && (! allEmpty) ) { // Course is safe but slot is jeopardized // We can give away slot since course is // safe . But do a cross check to see if // there are other courses dependent // solely on the left over. if ( cross_check_succeeds( pgList ,cPtr , slotPtr ) ) { flagCourseSafe = FALSE ; flagSlotSafe = FALSE ; } } flagCourseSafe = flagCourseSafe || flagSlotSafe ; // ( flagCourseSafe ) ? cout << cPtr << "is safe" : cout << cPtr << "is unsafe" // << endl; if ( ! flagCourseSafe ) return FALSE ; } return TRUE ; } void PGReserve :: updatePGReserve( int rPtr , int sPtr ) { for( int i = 0 ; i < MAX_COURSES ; i++ ) reserve[i][sPtr][rPtr] = 0 ; } void PGReserve :: nullifyCourse( int cp , int rp , int sp ) { for(int i=0 ;i < MAX_SLOTS ; i++ ) for(int j=0 ; j < MAX_ROOMS ; j++ ) reserve[cp][i][j] = 0 ; updatePGReserve( rp , sp ) ; } bool PGReserve :: getSuitableSchedule( TimeTable *&ttbl , List *pgList , int cPtr, int rNum , int &slot , int &room , bool &no_room ) { int sz ,crs; CtoBeSched *pg ; bool no_solely_dependent_course ; no_room = TRUE ; pgList->listSize( sz ) ; for(int i = 0 ; i < MAX_SLOTS ; i++ ) // For each slot for(int j = 0 ; j <= rNum ; j++ ) // For each room if ( reserve[cPtr][i][j] == 1 ) { // If there is something // Check somebody direly needs this, it might be cPtr itself. // in this case this as the schedule. Otherwise skip over // other room or slot . no_room = FALSE ; if ( ttbl->isColumnEmpty( i ) ) { no_solely_dependent_course = TRUE ; crs = 0 ; while( (crs <= sz) && (no_solely_dependent_course) ) { // For each course pgList->getIthElement( crs , pg ) ; int coursePtr,enrollment ; pg->tellAttr( coursePtr , enrollment ) ; if ( solely_depends( coursePtr , i ) ) { // Solely dependent on this slot bool dummy ; if ( ! thereIsSomeOtherRoom( coursePtr , i , j , dummy ) ) if ( coursePtr != cPtr ) // If this is not same as cPtr // coursePtr Clashes. no_solely_dependent_course = FALSE ; } crs++ ; } if ( no_solely_dependent_course ) { slot = i ; room = j ; return TRUE ; } } } return FALSE ; } /////////////////////////////////////// // TimeTable // /////////////////////////////////////// TimeTable :: TimeTable() { for( int j = 0 ; j < MAX_SLOTS ; j++ ) PGOccupied[ j ] = -1 ; // Error status } bool TimeTable :: bookASlot( int i ,int j , int coursePtr ) { if ( (i < 0) || (i >= MAX_ROOMS) || (j < 0) || (j > MAX_SLOTS) ) return FALSE ; table[i][j].setAttr( coursePtr , TRUE ) ; // Mark this as booked return TRUE ; } void TimeTable :: printTimeTable( CourseDB *cDB , RoomDB *rDB , SlotDB *sDB ) { // Preliminary . Modify afterwards int roomNo, cap ; for( int i = 0 ; i <= rDB->tellSize() ; i++ ) { rDB->tellIthElem( i )->tellAttrib( roomNo , cap ); cout << " Room = " << roomNo ; for( int j = 0 ; j <= sDB->tellSize() ; j++ ) { cout << "[" ; cout << sDB->tellIthElem( j )->tellSlotID() ; cout << "->" ; table[i][j].printEntry( cDB ) ; cout << "]-- " ; } cout << endl ; } } main(int argc, char *argv[] ) { char fName[MAX_BUF] ; InputFile_1 in1 ; InputFile_2 in2 ; TableOfCtoBeSched tbl ; CourseDB cDB ; RoomDB rDB ; SlotDB sDB ; TimeTable *timeTable = new TimeTable ; ConflictTable *conflictTable = new ConflictTable ; if ( argc < 3 ) { cout << "Usage : schedule InputFile_1 InputFile_2 " << endl ; exit(1) ; } /* * Build Course , Room and Slot Databases */ in1.build_CRS_DBs( argv[1] , cDB , rDB , sDB ) ; /* * Build Table of Courses to bes scheduled */ in2.buildCtoBeSched( argv[2] , tbl ,&cDB , &sDB ) ; /* * Realized that Scheduler Object is not needed . * main program itself can drive the application * by sorting Rooms DB and then asking the TableOfCoursesToBeSched object to * schedule all.Otherwise this work would have to be done by an object named 'Scheduler' */ rDB.sortRooms() ; tbl.scheduleAll( timeTable , &sDB ,&rDB , conflictTable ) ; /* * Now Just Print Out TimeTable ... */ cout << " ************ TIME TABLE ***************** " << endl ; cout << " ------------------------------------------------ " << endl << endl << endl ; timeTable->printTimeTable( &cDB , &rDB , &sDB ) ; /* * Now Print Out Conflict Table ... */ cout << endl << endl << endl << " ************ CONFLICT TABLE ************* " << endl ; cout << " ----------------------------------------------- " << endl << endl << endl ; conflictTable->printTable( &cDB,&sDB,&rDB) ; }