Back to index

fet  5.18.0
timeconstraint.cpp
Go to the documentation of this file.
00001 /*
00002 File timeconstraint.cpp
00003 */
00004 
00005 /*
00006 Copyright 2002, 2003 Lalescu Liviu.
00007 
00008 This file is part of FET.
00009 
00010 FET is free software; you can redistribute it and/or modify
00011 it under the terms of the GNU General Public License as published by
00012 the Free Software Foundation; either version 2 of the License, or
00013 (at your option) any later version.
00014 
00015 FET is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with FET; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023 */
00024 
00025 #include "timetable_defs.h"
00026 #include "timeconstraint.h"
00027 #include "rules.h"
00028 #include "solution.h"
00029 #include "activity.h"
00030 #include "teacher.h"
00031 #include "subject.h"
00032 #include "activitytag.h"
00033 #include "studentsset.h"
00034 
00035 #include "matrix.h"
00036 
00037 #include <QString>
00038 
00039 #include <QMessageBox>
00040 
00041 #include <QSet>
00042 
00043 //for min max functions
00044 #include <algorithm>
00045 using namespace std;
00046 
00047 static QString trueFalse(bool x){
00048        if(!x)
00049               return QString("false");
00050        else
00051               return QString("true");
00052 }
00053 
00054 static QString yesNoTranslated(bool x){
00055        if(!x)
00056               return QCoreApplication::translate("TimeConstraint", "no", "no - meaning negation");
00057        else
00058               return QCoreApplication::translate("TimeConstraint", "yes", "yes - meaning affirmative");
00059 }
00060 
00061 //The following 2 matrices are kept to make the computation faster
00062 //They are calculated only at the beginning of the computation of the fitness
00063 //of the solution.
00064 /*static qint8 subgroupsMatrix[MAX_TOTAL_SUBGROUPS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
00065 static qint8 teachersMatrix[MAX_TEACHERS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];*/
00066 static Matrix3D<qint8> subgroupsMatrix;
00067 static Matrix3D<qint8> teachersMatrix;
00068 
00069 static int teachers_conflicts=-1;
00070 static int subgroups_conflicts=-1;
00071 
00072 //extern bool breakDayHour[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
00073 extern Matrix2D<bool> breakDayHour;
00074 
00075 /*extern bool teacherNotAvailableDayHour[MAX_TEACHERS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
00076 
00077 extern bool subgroupNotAvailableDayHour[MAX_TOTAL_SUBGROUPS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];*/
00078 extern Matrix3D<bool> teacherNotAvailableDayHour;
00079 
00080 extern Matrix3D<bool> subgroupNotAvailableDayHour;
00081 
00084 
00085 QString getActivityDetailedDescription(Rules& r, int id){
00086        QString s;
00087 
00088        int ai;
00089        for(ai=0; ai<r.activitiesList.size(); ai++)
00090               if(r.activitiesList[ai]->id==id)
00091                      break;
00092        
00093        if(ai==r.activitiesList.size()){
00094               s+=QCoreApplication::translate("Activity", "Invalid (inexistent) id for activity");
00095               return s;
00096        }
00097        assert(ai<r.activitiesList.size());
00098        
00099        Activity* act=r.activitiesList.at(ai);
00100        
00101        if(act->activityTagsNames.count()>0){
00102               s+=QCoreApplication::translate("Activity", "T:%1, S:%2, AT:%3, St:%4", "This is an important translation for an activity's detailed description, please take care (it appears in many places in constraints)."
00103                "The abbreviations are: Teachers, Subject, Activity tags, Students. This variant includes activity tags").arg(act->teachersNames.join(",")).arg(act->subjectName).arg(act->activityTagsNames.join(",")).arg(act->studentsNames.join(","));
00104        }
00105        else{
00106               s+=QCoreApplication::translate("Activity", "T:%1, S:%2, St:%3", "This is an important translation for an activity's detailed description, please take care (it appears in many places in constraints)."
00107                "The abbreviations are: Teachers, Subject, Students. There are no activity tags here").arg(act->teachersNames.join(",")).arg(act->subjectName).arg(act->studentsNames.join(","));
00108        }
00109        return s;
00110 }
00111 
00112 TimeConstraint::TimeConstraint()
00113 {
00114        type=CONSTRAINT_GENERIC_TIME;
00115        
00116        active=true;
00117        comments=QString("");
00118 }
00119 
00120 TimeConstraint::~TimeConstraint()
00121 {
00122 }
00123 
00124 TimeConstraint::TimeConstraint(double wp)
00125 {
00126        type=CONSTRAINT_GENERIC_TIME;
00127 
00128        weightPercentage=wp;
00129        assert(wp<=100 && wp>=0);
00130 
00131        active=true;
00132        comments=QString("");
00133 }
00134 
00137 
00138 ConstraintBasicCompulsoryTime::ConstraintBasicCompulsoryTime(): TimeConstraint()
00139 {
00140        this->type=CONSTRAINT_BASIC_COMPULSORY_TIME;
00141        this->weightPercentage=100;
00142 }
00143 
00144 ConstraintBasicCompulsoryTime::ConstraintBasicCompulsoryTime(double wp): TimeConstraint(wp)
00145 {
00146        this->type=CONSTRAINT_BASIC_COMPULSORY_TIME;
00147 }
00148 
00149 bool ConstraintBasicCompulsoryTime::computeInternalStructure(QWidget* parent, Rules& r)
00150 {
00151        Q_UNUSED(parent);
00152        Q_UNUSED(r);
00153        
00154        return true;
00155 }
00156 
00157 bool ConstraintBasicCompulsoryTime::hasInactiveActivities(Rules& r)
00158 {
00159        Q_UNUSED(r);
00160        return false;
00161 }
00162 
00163 QString ConstraintBasicCompulsoryTime::getXmlDescription(Rules& r)
00164 {
00165        Q_UNUSED(r);
00166 
00167        QString s = "<ConstraintBasicCompulsoryTime>\n";
00168        assert(this->weightPercentage==100.0);
00169        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
00170        s+="   <Active>"+trueFalse(active)+"</Active>\n";
00171        s+="   <Comments>"+protect(comments)+"</Comments>\n";
00172        s+="</ConstraintBasicCompulsoryTime>\n";
00173        return s;
00174 }
00175 
00176 QString ConstraintBasicCompulsoryTime::getDescription(Rules& r)
00177 {
00178        Q_UNUSED(r);
00179 
00180        QString begin=QString("");
00181        if(!active)
00182               begin="X - ";
00183               
00184        QString end=QString("");
00185        if(!comments.isEmpty())
00186               end=", "+tr("C: %1", "Comments").arg(comments);
00187               
00188        return begin+tr("Basic compulsory constraints (time)") + ", " + tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage))+end;
00189 }
00190 
00191 QString ConstraintBasicCompulsoryTime::getDetailedDescription(Rules& r)
00192 {
00193        Q_UNUSED(r);
00194 
00195        QString s=tr("These are the basic compulsory constraints (referring to time allocation) for any timetable");
00196        s+="\n";
00197 
00198        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
00199        s+=tr("The basic time constraints try to avoid:");s+="\n";
00200        s+=QString("- ");s+=tr("teachers assigned to more than one activity simultaneously");s+="\n";
00201        s+=QString("- ");s+=tr("students assigned to more than one activity simultaneously");s+="\n";
00202        
00203        if(!active){
00204               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
00205               s+="\n";
00206        }
00207        if(!comments.isEmpty()){
00208               s+=tr("Comments=%1").arg(comments);
00209               s+="\n";
00210        }
00211 
00212        return s;
00213 }
00214 
00215 double ConstraintBasicCompulsoryTime::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString){
00216        assert(r.internalStructureComputed);
00217 
00218        int teachersConflicts, subgroupsConflicts;
00219        
00220        assert(weightPercentage==100.0);
00221 
00222        //This constraint fitness calculation routine is called firstly,
00223        //so we can compute the teacher and subgroups conflicts faster this way.
00224        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
00225               c.teachersMatrixReady=true;
00226               c.subgroupsMatrixReady=true;
00227        
00228               subgroups_conflicts = subgroupsConflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
00229               teachers_conflicts = teachersConflicts = c.getTeachersMatrix(r, teachersMatrix);
00230 
00231               c.changedForMatrixCalculation=false;
00232        }
00233        else{
00234               assert(subgroups_conflicts>=0);
00235               assert(teachers_conflicts>=0);
00236               subgroupsConflicts = subgroups_conflicts;
00237               teachersConflicts = teachers_conflicts;
00238        }
00239 
00240        int i,dd;
00241 
00242        int unallocated; //unallocated activities
00243        int late; //late activities
00244        int nte; //number of teacher exhaustions
00245        int nse; //number of students exhaustions
00246 
00247        //Part without logging..................................................................
00248        if(conflictsString==NULL){
00249               //Unallocated or late activities
00250               unallocated=0;
00251               late=0;
00252               for(i=0; i<r.nInternalActivities; i++){
00253                      if(c.times[i]==UNALLOCATED_TIME){
00254                             //Firstly, we consider a big clash each unallocated activity.
00255                             //Needs to be very a large constant, bigger than any other broken constraint.
00256                             unallocated += /*r.internalActivitiesList[i].duration * r.internalActivitiesList[i].nSubgroups * */ 10000;
00257                             //(an unallocated activity for a year is more important than an unallocated activity for a subgroup)
00258                      }
00259                      else{
00260                             //Calculates the number of activities that are scheduled too late (in fact we
00261                             //calculate a function that increases as the activity is getting late)
00262                             int h=c.times[i]/r.nDaysPerWeek;
00263                             dd=r.internalActivitiesList[i].duration;
00264                             if(h+dd>r.nHoursPerDay){
00265                                    int tmp;
00266                                    tmp=1;
00267                                    late += (h+dd-r.nHoursPerDay) * tmp * r.internalActivitiesList[i].iSubgroupsList.count();
00268                                    //multiplied with the number
00269                                    //of subgroups implied, for seeing the importance of the
00270                                    //activity
00271                             }
00272                      }
00273               }
00274               
00275               assert(late==0);
00276 
00277               //Below, for teachers and students, please remember that 2 means a weekly activity
00278               //and 1 fortnightly one. So, if the matrix teachersMatrix[teacher][day][hour]==2, it is ok.
00279 
00280               //Calculates the number of teachers exhaustion (when he has to teach more than
00281               //one activity at the same time)
00282               /*nte=0;
00283               for(i=0; i<r.nInternalTeachers; i++)
00284                      for(int j=0; j<r.nDaysPerWeek; j++)
00285                             for(int k=0; k<r.nHoursPerDay; k++){
00286                                    int tmp=teachersMatrix[i][j][k]-2;
00287                                    if(tmp>0)
00288                                           nte+=tmp;
00289                             }*/
00290               nte = teachersConflicts; //faster
00291               
00292               assert(nte==0);
00293 
00294               //Calculates the number of subgroups exhaustion (a subgroup cannot attend two
00295               //activities at the same time)
00296               /*nse=0;
00297               for(i=0; i<r.nInternalSubgroups; i++)
00298                      for(int j=0; j<r.nDaysPerWeek; j++)
00299                             for(int k=0; k<r.nHoursPerDay; k++){
00300                                    int tmp=subgroupsMatrix[i][j][k]-2;
00301                                    if(tmp>0)
00302                                           nse += tmp;
00303                             }*/
00304               nse = subgroupsConflicts; //faster
00305               
00306               assert(nse==0);                    
00307        }
00308        //part with logging....................................................................
00309        else{
00310               //Unallocated or late activities
00311               unallocated=0;
00312               late=0;
00313               for(i=0; i<r.nInternalActivities; i++){
00314                      if(c.times[i]==UNALLOCATED_TIME){
00315                             //Firstly, we consider a big clash each unallocated activity.
00316                             //Needs to be very a large constant, bigger than any other broken constraint.
00317                             unallocated += /*r.internalActivitiesList[i].duration * r.internalActivitiesList[i].nSubgroups * */ 10000;
00318                             //(an unallocated activity for a year is more important than an unallocated activity for a subgroup)
00319                             if(conflictsString!=NULL){
00320                                    QString s= tr("Time constraint basic compulsory broken: unallocated activity with id=%1 (%2)",
00321                                           "%2 is the detailed description of activity - teachers, subject, students")
00322                                           .arg(r.internalActivitiesList[i].id).arg(getActivityDetailedDescription(r, r.internalActivitiesList[i].id));
00323                                    s+=" - ";
00324                                    s += tr("this increases the conflicts total by %1")
00325                                     .arg(CustomFETString::number(weightPercentage/100 * 10000));
00326                                    //s += "\n";
00327                                    
00328                                    dl.append(s);
00329                                    cl.append(weightPercentage/100 * 10000);
00330 
00331                                    (*conflictsString) += s + "\n";
00332                             }
00333                      }
00334                      else{
00335                             //Calculates the number of activities that are scheduled too late (in fact we
00336                             //calculate a function that increases as the activity is getting late)
00337                             int h=c.times[i]/r.nDaysPerWeek;
00338                             dd=r.internalActivitiesList[i].duration;
00339                             if(h+dd>r.nHoursPerDay){
00340                                    assert(0);    
00341                             
00342                                    int tmp;
00343                                    tmp=1;
00344                                    late += (h+dd-r.nHoursPerDay) * tmp * r.internalActivitiesList[i].iSubgroupsList.count();
00345                                    //multiplied with the number
00346                                    //of subgroups implied, for seeing the importance of the
00347                                    //activity
00348 
00349                                    if(conflictsString!=NULL){
00350                                           QString s=tr("Time constraint basic compulsory");
00351                                           s+=": ";
00352                                           s+=tr("activity with id=%1 is late.")
00353                                            .arg(r.internalActivitiesList[i].id);
00354                                           s+=" ";
00355                                           s+=tr("This increases the conflicts total by %1")
00356                                            .arg(CustomFETString::number((h+dd-r.nHoursPerDay)*tmp*r.internalActivitiesList[i].iSubgroupsList.count()*weightPercentage/100));
00357                                           s+="\n";
00358                                           
00359                                           dl.append(s);
00360                                           cl.append((h+dd-r.nHoursPerDay)*tmp*r.internalActivitiesList[i].iSubgroupsList.count()*weightPercentage/100);
00361 
00362                                           (*conflictsString) += s+"\n";
00363                                    }
00364                             }
00365                      }
00366               }
00367 
00368               //Below, for teachers and students, please remember that 2 means a weekly activity
00369               //and 1 fortnightly one. So, if the matrix teachersMatrix[teacher][day][hour]==2,
00370               //that is ok.
00371 
00372               //Calculates the number of teachers exhaustion (when he has to teach more than
00373               //one activity at the same time)
00374               nte=0;
00375               for(i=0; i<r.nInternalTeachers; i++)
00376                      for(int j=0; j<r.nDaysPerWeek; j++)
00377                             for(int k=0; k<r.nHoursPerDay; k++){
00378                                    int tmp=teachersMatrix[i][j][k]-1;
00379                                    if(tmp>0){
00380                                           if(conflictsString!=NULL){
00381                                                  QString s=tr("Time constraint basic compulsory");
00382                                                  s+=": ";
00383                                                  s+=tr("teacher with name %1 has more than one allocated activity on day %2, hour %3")
00384                                                   .arg(r.internalTeachersList[i]->name)
00385                                                   .arg(r.daysOfTheWeek[j])
00386                                                   .arg(r.hoursOfTheDay[k]);
00387                                                  s+=". ";
00388                                                  s+=tr("This increases the conflicts total by %1")
00389                                                   .arg(CustomFETString::number(tmp*weightPercentage/100));
00390                                           
00391                                                  (*conflictsString)+= s+"\n";
00392                                                  
00393                                                  dl.append(s);
00394                                                  cl.append(tmp*weightPercentage/100);
00395                                           }
00396                                           nte+=tmp;
00397                                    }
00398                             }
00399 
00400               assert(nte==0);
00401               
00402               //Calculates the number of subgroups exhaustion (a subgroup cannot attend two
00403               //activities at the same time)
00404               nse=0;
00405               for(i=0; i<r.nInternalSubgroups; i++)
00406                      for(int j=0; j<r.nDaysPerWeek; j++)
00407                             for(int k=0; k<r.nHoursPerDay; k++){
00408                                    int tmp=subgroupsMatrix[i][j][k]-1;
00409                                    if(tmp>0){
00410                                           if(conflictsString!=NULL){
00411                                                  QString s=tr("Time constraint basic compulsory");
00412                                                  s+=": ";
00413                                                  s+=tr("subgroup %1 has more than one allocated activity on day %2, hour %3")
00414                                                   .arg(r.internalSubgroupsList[i]->name)
00415                                                   .arg(r.daysOfTheWeek[j])
00416                                                   .arg(r.hoursOfTheDay[k]);
00417                                                  s+=". ";
00418                                                  s+=tr("This increases the conflicts total by %1")
00419                                                   .arg(CustomFETString::number((subgroupsMatrix[i][j][k]-1)*weightPercentage/100));
00420                                                   
00421                                                  dl.append(s);
00422                                                  cl.append((subgroupsMatrix[i][j][k]-1)*weightPercentage/100);
00423                                           
00424                                                  *conflictsString += s+"\n";
00425                                           }
00426                                           nse += tmp;
00427                                    }
00428                             }
00429                      
00430               assert(nse==0);
00431        }
00432 
00433        /*if(nte!=teachersConflicts){
00434               cout<<"nte=="<<nte<<", teachersConflicts=="<<teachersConflicts<<endl;
00435               cout<<c.getTeachersMatrix(r, teachersMatrix)<<endl;
00436        }
00437        if(nse!=subgroupsConflicts){
00438               cout<<"nse=="<<nse<<", subgroupsConflicts=="<<subgroupsConflicts<<endl;
00439               cout<<c.getSubgroupsMatrix(r, subgroupsMatrix)<<endl;
00440        }*/
00441        
00442        /*assert(nte==teachersConflicts); //just a check, works only on logged fitness calculation
00443        assert(nse==subgroupsConflicts);*/
00444 
00445        //return int (ceil ( weight * (unallocated + late + nte + nse) ) ); //conflicts factor
00446        return weightPercentage/100 * (unallocated + late + nte + nse); //conflicts factor
00447 }
00448 
00449 bool ConstraintBasicCompulsoryTime::isRelatedToActivity(Rules& r, Activity* a)
00450 {
00451        Q_UNUSED(a);
00452        Q_UNUSED(r);
00453 
00454        return false;
00455 }
00456 
00457 bool ConstraintBasicCompulsoryTime::isRelatedToTeacher(Teacher* t)
00458 {
00459        Q_UNUSED(t);
00460 
00461        return false;
00462 }
00463 
00464 bool ConstraintBasicCompulsoryTime::isRelatedToSubject(Subject* s)
00465 {
00466        Q_UNUSED(s);
00467 
00468        return false;
00469 }
00470 
00471 bool ConstraintBasicCompulsoryTime::isRelatedToActivityTag(ActivityTag* s)
00472 {
00473        Q_UNUSED(s);
00474 
00475        return false;
00476 }
00477 
00478 bool ConstraintBasicCompulsoryTime::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
00479 {
00480        Q_UNUSED(r);
00481        Q_UNUSED(s);
00482 
00483        return false;
00484 }
00485 
00486 bool ConstraintBasicCompulsoryTime::hasWrongDayOrHour(Rules& r)
00487 {
00488        Q_UNUSED(r);
00489        return false;
00490 }
00491 
00492 bool ConstraintBasicCompulsoryTime::canRepairWrongDayOrHour(Rules& r)
00493 {
00494        Q_UNUSED(r);
00495        assert(0);
00496        
00497        return true;
00498 }
00499 
00500 bool ConstraintBasicCompulsoryTime::repairWrongDayOrHour(Rules& r)
00501 {
00502        Q_UNUSED(r);
00503        assert(0); //should check hasWrongDayOrHour, firstly
00504 
00505        return true;
00506 }
00507 
00510 
00511 ConstraintTeacherNotAvailableTimes::ConstraintTeacherNotAvailableTimes()
00512        : TimeConstraint()
00513 {
00514        this->type=CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES;
00515 }
00516 
00517 ConstraintTeacherNotAvailableTimes::ConstraintTeacherNotAvailableTimes(double wp, const QString& tn, QList<int> d, QList<int> h)
00518        : TimeConstraint(wp)
00519 {
00520        this->teacher=tn;
00521        assert(d.count()==h.count());
00522        this->days=d;
00523        this->hours=h;
00524        this->type=CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES;
00525 }
00526 
00527 QString ConstraintTeacherNotAvailableTimes::getXmlDescription(Rules& r){
00528        QString s="<ConstraintTeacherNotAvailableTimes>\n";
00529        s+="   <Weight_Percentage>"+CustomFETString::number(weightPercentage)+"</Weight_Percentage>\n";
00530        s+="   <Teacher>"+protect(this->teacher)+"</Teacher>\n";
00531 
00532        s+="   <Number_of_Not_Available_Times>"+CustomFETString::number(this->days.count())+"</Number_of_Not_Available_Times>\n";
00533        assert(days.count()==hours.count());
00534        for(int i=0; i<days.count(); i++){
00535               s+="   <Not_Available_Time>\n";
00536               if(this->days.at(i)>=0)
00537                      s+="          <Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
00538               if(this->hours.at(i)>=0)
00539                      s+="          <Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
00540               s+="   </Not_Available_Time>\n";
00541        }
00542 
00543        s+="   <Active>"+trueFalse(active)+"</Active>\n";
00544        s+="   <Comments>"+protect(comments)+"</Comments>\n";
00545        s+="</ConstraintTeacherNotAvailableTimes>\n";
00546        return s;
00547 }
00548 
00549 QString ConstraintTeacherNotAvailableTimes::getDescription(Rules& r){
00550        QString begin=QString("");
00551        if(!active)
00552               begin="X - ";
00553               
00554        QString end=QString("");
00555        if(!comments.isEmpty())
00556               end=", "+tr("C: %1", "Comments").arg(comments);
00557               
00558        QString s=tr("Teacher not available");s+=", ";
00559        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
00560        s+=tr("T:%1", "Teacher").arg(this->teacher);s+=", ";
00561 
00562        s+=tr("NA at:", "Not available at");
00563        s+=" ";
00564        assert(days.count()==hours.count());
00565        for(int i=0; i<days.count(); i++){
00566               if(this->days.at(i)>=0){
00567                      s+=r.daysOfTheWeek[this->days.at(i)];
00568                      s+=" ";
00569               }
00570               if(this->hours.at(i)>=0){
00571                      s+=r.hoursOfTheDay[this->hours.at(i)];
00572               }
00573               if(i<days.count()-1)
00574                      s+="; ";
00575        }
00576 
00577        return begin+s+end;
00578 }
00579 
00580 QString ConstraintTeacherNotAvailableTimes::getDetailedDescription(Rules& r){
00581        QString s=tr("Time constraint");s+="\n";
00582        s+=tr("A teacher is not available");s+="\n";
00583        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
00584        s+=tr("Teacher=%1").arg(this->teacher);s+="\n";
00585 
00586        s+=tr("Not available at:");
00587        s+="\n";
00588        assert(days.count()==hours.count());
00589        for(int i=0; i<days.count(); i++){
00590               if(this->days.at(i)>=0){
00591                      s+=r.daysOfTheWeek[this->days.at(i)];
00592                      s+=" ";
00593               }
00594               if(this->hours.at(i)>=0){
00595                      s+=r.hoursOfTheDay[this->hours.at(i)];
00596               }
00597               if(i<days.count()-1)
00598                      s+="; ";
00599        }
00600        s+="\n";
00601 
00602        if(!active){
00603               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
00604               s+="\n";
00605        }
00606        if(!comments.isEmpty()){
00607               s+=tr("Comments=%1").arg(comments);
00608               s+="\n";
00609        }
00610 
00611        return s;
00612 }
00613 
00614 bool ConstraintTeacherNotAvailableTimes::computeInternalStructure(QWidget* parent, Rules& r){
00615        this->teacher_ID=r.searchTeacher(this->teacher);
00616 
00617        if(this->teacher_ID<0){
00618               QMessageBox::warning(parent, tr("FET warning"),
00619                tr("Constraint teacher not available times is wrong because it refers to inexistent teacher."
00620                " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
00621                
00622               return false;
00623        }      
00624 
00625        assert(days.count()==hours.count());
00626        for(int k=0; k<days.count(); k++){
00627               if(this->days.at(k) >= r.nDaysPerWeek){
00628                      QMessageBox::information(parent, tr("FET information"),
00629                       tr("Constraint teacher not available times is wrong because it refers to removed day. Please correct"
00630                       " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
00631                
00632                      return false;
00633               }             
00634               if(this->hours.at(k) >= r.nHoursPerDay){
00635                      QMessageBox::information(parent, tr("FET information"),
00636                       tr("Constraint teacher not available times is wrong because an hour is too late (after the last acceptable slot). Please correct"
00637                       " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
00638                
00639                      return false;
00640               }
00641        }
00642 
00643        assert(this->teacher_ID>=0);
00644        return true;
00645 }
00646 
00647 bool ConstraintTeacherNotAvailableTimes::hasInactiveActivities(Rules& r)
00648 {
00649        Q_UNUSED(r);
00650        return false;
00651 }
00652 
00653 double ConstraintTeacherNotAvailableTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString *conflictsString)
00654 {
00655        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
00656        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
00657               c.teachersMatrixReady=true;
00658               c.subgroupsMatrixReady=true;
00659 
00660               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
00661               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
00662 
00663               c.changedForMatrixCalculation=false;
00664        }
00665 
00666        //Calculates the number of hours when the teacher is supposed to be teaching, but he is not available
00667        //This function consideres all the hours, I mean if there are for example 5 weekly courses
00668        //scheduled on that hour (which is already a broken compulsory restriction - we only
00669        //are allowed 1 weekly course for a certain teacher at a certain hour) we calculate
00670        //5 broken restrictions for that function.
00671        //TODO: decide if it is better to consider only 2 or 10 as a return value in this particular case
00672        //(currently it is 10)
00673        int tch=this->teacher_ID;
00674 
00675        int nbroken;
00676 
00677        nbroken=0;
00678 
00679        assert(days.count()==hours.count());
00680        for(int k=0; k<days.count(); k++){
00681               int d=days.at(k);
00682               int h=hours.at(k);
00683               
00684               if(teachersMatrix[tch][d][h]>0){
00685                      nbroken+=teachersMatrix[tch][d][h];
00686        
00687                      if(conflictsString!=NULL){
00688                             QString s= tr("Time constraint teacher not available");
00689                             s += " ";
00690                             s += tr("broken for teacher: %1 on day %2, hour %3")
00691                              .arg(r.internalTeachersList[tch]->name)
00692                              .arg(r.daysOfTheWeek[d])
00693                              .arg(r.hoursOfTheDay[h]);
00694                             s += ". ";
00695                             s += tr("This increases the conflicts total by %1")
00696                              .arg(CustomFETString::number(teachersMatrix[tch][d][h]*weightPercentage/100));
00697                              
00698                             dl.append(s);
00699                             cl.append(teachersMatrix[tch][d][h]*weightPercentage/100);
00700                      
00701                             *conflictsString += s+"\n";
00702                      }
00703               }
00704        }
00705 
00706        if(weightPercentage==100.0)
00707               assert(nbroken==0);
00708        return weightPercentage/100 * nbroken;
00709 }
00710 
00711 bool ConstraintTeacherNotAvailableTimes::isRelatedToActivity(Rules& r, Activity* a)
00712 {
00713        Q_UNUSED(a);
00714        Q_UNUSED(r);
00715 
00716        return false;
00717 }
00718 
00719 bool ConstraintTeacherNotAvailableTimes::isRelatedToTeacher(Teacher* t)
00720 {
00721        if(this->teacher==t->name)
00722               return true;
00723        return false;
00724 }
00725 
00726 bool ConstraintTeacherNotAvailableTimes::isRelatedToSubject(Subject* s)
00727 {
00728        Q_UNUSED(s);
00729 
00730        return false;
00731 }
00732 
00733 bool ConstraintTeacherNotAvailableTimes::isRelatedToActivityTag(ActivityTag* s)
00734 {
00735        Q_UNUSED(s);
00736 
00737        return false;
00738 }
00739 
00740 bool ConstraintTeacherNotAvailableTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
00741 {
00742        Q_UNUSED(r);
00743        Q_UNUSED(s);
00744 
00745        return false;
00746 }
00747 
00748 bool ConstraintTeacherNotAvailableTimes::hasWrongDayOrHour(Rules& r)
00749 {
00750        assert(days.count()==hours.count());
00751        
00752        for(int i=0; i<days.count(); i++)
00753               if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
00754                || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
00755                      return true;
00756 
00757        return false;
00758 }
00759 
00760 bool ConstraintTeacherNotAvailableTimes::canRepairWrongDayOrHour(Rules& r)
00761 {
00762        assert(hasWrongDayOrHour(r));
00763        
00764        return true;
00765 }
00766 
00767 bool ConstraintTeacherNotAvailableTimes::repairWrongDayOrHour(Rules& r)
00768 {
00769        assert(hasWrongDayOrHour(r));
00770        
00771        assert(days.count()==hours.count());
00772        
00773        QList<int> newDays;
00774        QList<int> newHours;
00775        
00776        for(int i=0; i<days.count(); i++)
00777               if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
00778                && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
00779                      newDays.append(days.at(i));
00780                      newHours.append(hours.at(i));
00781               }
00782        
00783        days=newDays;
00784        hours=newHours;
00785        
00786        r.internalStructureComputed=false;
00787        setRulesModifiedAndOtherThings(&r);
00788 
00789        return true;
00790 }
00791 
00794 
00795 ConstraintStudentsSetNotAvailableTimes::ConstraintStudentsSetNotAvailableTimes()
00796        : TimeConstraint()
00797 {
00798        this->type=CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES;
00799 }
00800 
00801 ConstraintStudentsSetNotAvailableTimes::ConstraintStudentsSetNotAvailableTimes(double wp, const QString& sn, QList<int> d, QList<int> h)
00802         : TimeConstraint(wp){
00803        this->students = sn;
00804        assert(d.count()==h.count());
00805        this->days=d;
00806        this->hours=h;
00807        this->type=CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES;
00808 }
00809 
00810 bool ConstraintStudentsSetNotAvailableTimes::computeInternalStructure(QWidget* parent, Rules& r){
00811        StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
00812        
00813        if(ss==NULL){
00814               QMessageBox::warning(parent, tr("FET warning"),
00815                tr("Constraint students set not available is wrong because it refers to inexistent students set."
00816                " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
00817                
00818               return false;
00819        }      
00820        
00821        assert(days.count()==hours.count());
00822        for(int k=0; k<days.count(); k++){
00823               if(this->days.at(k) >= r.nDaysPerWeek){
00824                      QMessageBox::information(parent, tr("FET information"),
00825                       tr("Constraint students set not available times is wrong because it refers to removed day. Please correct"
00826                       " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
00827                
00828                      return false;
00829               }             
00830               if(this->hours.at(k) >= r.nHoursPerDay){
00831                      QMessageBox::information(parent, tr("FET information"),
00832                       tr("Constraint students set not available times is wrong because an hour is too late (after the last acceptable slot). Please correct"
00833                       " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
00834                
00835                      return false;
00836               }
00837        }
00838        
00839        assert(ss);
00840 
00841        this->iSubgroupsList.clear();
00842        if(ss->type==STUDENTS_SUBGROUP){
00843               int tmp;
00844               tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
00845               assert(tmp>=0);
00846               assert(tmp<r.nInternalSubgroups);
00847               if(!this->iSubgroupsList.contains(tmp))
00848                      this->iSubgroupsList.append(tmp);
00849        }
00850        else if(ss->type==STUDENTS_GROUP){
00851               StudentsGroup* stg=(StudentsGroup*)ss;
00852               for(int i=0; i<stg->subgroupsList.size(); i++){
00853                      StudentsSubgroup* sts=stg->subgroupsList[i];
00854                      int tmp;
00855                      tmp=sts->indexInInternalSubgroupsList;
00856                      assert(tmp>=0);
00857                      assert(tmp<r.nInternalSubgroups);
00858                      if(!this->iSubgroupsList.contains(tmp))
00859                             this->iSubgroupsList.append(tmp);
00860               }
00861        }
00862        else if(ss->type==STUDENTS_YEAR){
00863               StudentsYear* sty=(StudentsYear*)ss;
00864               for(int i=0; i<sty->groupsList.size(); i++){
00865                      StudentsGroup* stg=sty->groupsList[i];
00866                      for(int j=0; j<stg->subgroupsList.size(); j++){
00867                             StudentsSubgroup* sts=stg->subgroupsList[j];
00868                             int tmp;
00869                             tmp=sts->indexInInternalSubgroupsList;
00870                             assert(tmp>=0);
00871                             assert(tmp<r.nInternalSubgroups);
00872                             if(!this->iSubgroupsList.contains(tmp))
00873                                    this->iSubgroupsList.append(tmp);
00874                      }
00875               }
00876        }
00877        else
00878               assert(0);
00879        return true;
00880 }
00881 
00882 bool ConstraintStudentsSetNotAvailableTimes::hasInactiveActivities(Rules& r)
00883 {
00884        Q_UNUSED(r);
00885        return false;
00886 }
00887 
00888 QString ConstraintStudentsSetNotAvailableTimes::getXmlDescription(Rules& r){
00889        QString s="<ConstraintStudentsSetNotAvailableTimes>\n";
00890        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
00891        s+="   <Students>"+protect(this->students)+"</Students>\n";
00892 
00893        s+="   <Number_of_Not_Available_Times>"+CustomFETString::number(this->days.count())+"</Number_of_Not_Available_Times>\n";
00894        assert(days.count()==hours.count());
00895        for(int i=0; i<days.count(); i++){
00896               s+="   <Not_Available_Time>\n";
00897               if(this->days.at(i)>=0)
00898                      s+="          <Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
00899               if(this->hours.at(i)>=0)
00900                      s+="          <Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
00901               s+="   </Not_Available_Time>\n";
00902        }
00903 
00904        s+="   <Active>"+trueFalse(active)+"</Active>\n";
00905        s+="   <Comments>"+protect(comments)+"</Comments>\n";
00906        s+="</ConstraintStudentsSetNotAvailableTimes>\n";
00907        return s;
00908 }
00909 
00910 QString ConstraintStudentsSetNotAvailableTimes::getDescription(Rules& r){
00911        QString begin=QString("");
00912        if(!active)
00913               begin="X - ";
00914               
00915        QString end=QString("");
00916        if(!comments.isEmpty())
00917               end=", "+tr("C: %1", "Comments").arg(comments);
00918               
00919        QString s;
00920        s=tr("Students set not available");s+=", ";
00921        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
00922        s+=tr("St:%1", "Students").arg(this->students);s+=", ";
00923 
00924        s+=tr("NA at:", "Not available at");
00925        s+=" ";
00926        assert(days.count()==hours.count());
00927        for(int i=0; i<days.count(); i++){
00928               if(this->days.at(i)>=0){
00929                      s+=r.daysOfTheWeek[this->days.at(i)];
00930                      s+=" ";
00931               }
00932               if(this->hours.at(i)>=0){
00933                      s+=r.hoursOfTheDay[this->hours.at(i)];
00934               }
00935               if(i<days.count()-1)
00936                      s+="; ";
00937        }
00938 
00939        return begin+s+end;
00940 }
00941 
00942 QString ConstraintStudentsSetNotAvailableTimes::getDetailedDescription(Rules& r){
00943        QString s=tr("Time constraint");s+="\n";
00944        s+=tr("A students set is not available");s+="\n";
00945        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
00946 
00947        s+=tr("Students=%1").arg(this->students);s+="\n";
00948 
00949        s+=tr("Not available at:");s+="\n";
00950        
00951        assert(days.count()==hours.count());
00952        for(int i=0; i<days.count(); i++){
00953               if(this->days.at(i)>=0){
00954                      s+=r.daysOfTheWeek[this->days.at(i)];
00955                      s+=" ";
00956               }
00957               if(this->hours.at(i)>=0){
00958                      s+=r.hoursOfTheDay[this->hours.at(i)];
00959               }
00960               if(i<days.count()-1)
00961                      s+="; ";
00962        }
00963        s+="\n";
00964 
00965        if(!active){
00966               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
00967               s+="\n";
00968        }
00969        if(!comments.isEmpty()){
00970               s+=tr("Comments=%1").arg(comments);
00971               s+="\n";
00972        }
00973 
00974        return s;
00975 }
00976 
00977 double ConstraintStudentsSetNotAvailableTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString *conflictsString)
00978 {
00979        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
00980        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
00981               c.teachersMatrixReady=true;
00982               c.subgroupsMatrixReady=true;
00983               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
00984               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
00985 
00986               c.changedForMatrixCalculation=false;
00987        }
00988 
00989        int nbroken;
00990 
00991        nbroken=0;
00992        for(int m=0; m<this->iSubgroupsList.count(); m++){
00993               int sbg=this->iSubgroupsList.at(m);
00994               
00995               assert(days.count()==hours.count());
00996               for(int k=0; k<days.count(); k++){
00997                      int d=days.at(k);
00998                      int h=hours.at(k);
00999                      
01000                      if(subgroupsMatrix[sbg][d][h]>0){
01001                             nbroken+=subgroupsMatrix[sbg][d][h];
01002 
01003                             if(conflictsString!=NULL){
01004                                    QString s= tr("Time constraint students set not available");
01005                                    s += " ";
01006                                    s += tr("broken for subgroup: %1 on day %2, hour %3")
01007                                     .arg(r.internalSubgroupsList[sbg]->name)
01008                                     .arg(r.daysOfTheWeek[d])
01009                                     .arg(r.hoursOfTheDay[h]);
01010                                    s += ". ";
01011                                    s += tr("This increases the conflicts total by %1")
01012                                     .arg(CustomFETString::number(subgroupsMatrix[sbg][d][h]*weightPercentage/100));
01013                                     
01014                                    dl.append(s);
01015                                    cl.append(subgroupsMatrix[sbg][d][h]*weightPercentage/100);
01016                             
01017                                    *conflictsString += s+"\n";
01018                             }
01019                      }
01020               }
01021        }
01022 
01023        if(weightPercentage==100.0)
01024               assert(nbroken==0);
01025        return weightPercentage/100 * nbroken;
01026 }
01027 
01028 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToActivity(Rules& r, Activity* a)
01029 {
01030        Q_UNUSED(a);
01031        Q_UNUSED(r);
01032 
01033        return false;
01034 }
01035 
01036 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToTeacher(Teacher* t)
01037 {
01038        Q_UNUSED(t);
01039 
01040        return false;
01041 }
01042 
01043 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToSubject(Subject* s)
01044 {
01045        Q_UNUSED(s);
01046 
01047        return false;
01048 }
01049 
01050 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToActivityTag(ActivityTag* s)
01051 {
01052        Q_UNUSED(s);
01053 
01054        return false;
01055 }
01056 
01057 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
01058 {
01059        return r.setsShareStudents(this->students, s->name);
01060 }
01061 
01062 bool ConstraintStudentsSetNotAvailableTimes::hasWrongDayOrHour(Rules& r)
01063 {
01064        assert(days.count()==hours.count());
01065        
01066        for(int i=0; i<days.count(); i++)
01067               if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
01068                || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
01069                      return true;
01070 
01071        return false;
01072 }
01073 
01074 bool ConstraintStudentsSetNotAvailableTimes::canRepairWrongDayOrHour(Rules& r)
01075 {
01076        assert(hasWrongDayOrHour(r));
01077        
01078        return true;
01079 }
01080 
01081 bool ConstraintStudentsSetNotAvailableTimes::repairWrongDayOrHour(Rules& r)
01082 {
01083        assert(hasWrongDayOrHour(r));
01084        
01085        assert(days.count()==hours.count());
01086        
01087        QList<int> newDays;
01088        QList<int> newHours;
01089        
01090        for(int i=0; i<days.count(); i++)
01091               if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
01092                && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
01093                      newDays.append(days.at(i));
01094                      newHours.append(hours.at(i));
01095               }
01096        
01097        days=newDays;
01098        hours=newHours;
01099        
01100        r.internalStructureComputed=false;
01101        setRulesModifiedAndOtherThings(&r);
01102 
01103        return true;
01104 }
01105 
01108 
01109 ConstraintActivitiesSameStartingTime::ConstraintActivitiesSameStartingTime()
01110        : TimeConstraint()
01111 {
01112        type=CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME;
01113 }
01114 
01115 ConstraintActivitiesSameStartingTime::ConstraintActivitiesSameStartingTime(double wp, int nact, const QList<int>& act)
01116  : TimeConstraint(wp)
01117  {
01118        assert(nact>=2);
01119        assert(act.count()==nact);
01120        this->n_activities=nact;
01121        this->activitiesId.clear();
01122        for(int i=0; i<nact; i++)
01123               this->activitiesId.append(act.at(i));
01124 
01125        this->type=CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME;
01126 }
01127 
01128 bool ConstraintActivitiesSameStartingTime::computeInternalStructure(QWidget* parent, Rules& r)
01129 {
01130        //compute the indices of the activities,
01131        //based on their unique ID
01132 
01133        assert(this->n_activities==this->activitiesId.count());
01134 
01135        this->_activities.clear();
01136        for(int i=0; i<this->n_activities; i++){
01137               int j;
01138               Activity* act;
01139               for(j=0; j<r.nInternalActivities; j++){
01140                      act=&r.internalActivitiesList[j];
01141                      if(act->id==this->activitiesId[i]){
01142                             this->_activities.append(j);
01143                             break;
01144                      }
01145               }
01146        }
01147        this->_n_activities=this->_activities.count();
01148        
01149        if(this->_n_activities<=1){
01150               QMessageBox::warning(parent, tr("FET error in data"), 
01151                      tr("Following constraint is wrong (because you need 2 or more activities. Please correct it):\n%1").arg(this->getDetailedDescription(r)));
01152               //assert(0);
01153               return false;
01154        }
01155 
01156        return true;
01157 }
01158 
01159 void ConstraintActivitiesSameStartingTime::removeUseless(Rules& r)
01160 {
01161        //remove the activitiesId which no longer exist (used after the deletion of an activity)
01162        
01163        assert(this->n_activities==this->activitiesId.count());
01164 
01165        QList<int> tmpList;
01166 
01167        for(int i=0; i<this->n_activities; i++){
01168               for(int k=0; k<r.activitiesList.size(); k++){
01169                      Activity* act=r.activitiesList[k];
01170                      if(act->id==this->activitiesId[i]){
01171                             tmpList.append(act->id);
01172                             break;
01173                      }
01174               }
01175        }
01176        
01177        this->activitiesId=tmpList;
01178        this->n_activities=this->activitiesId.count();
01179 
01180        r.internalStructureComputed=false;
01181 }
01182 
01183 bool ConstraintActivitiesSameStartingTime::hasInactiveActivities(Rules& r)
01184 {
01185        int count=0;
01186 
01187        for(int i=0; i<this->n_activities; i++)
01188               if(r.inactiveActivities.contains(this->activitiesId[i]))
01189                      count++;
01190 
01191        if(this->n_activities-count<=1)
01192               return true;
01193        else
01194               return false;
01195 }
01196 
01197 QString ConstraintActivitiesSameStartingTime::getXmlDescription(Rules& r){
01198        Q_UNUSED(r);
01199 
01200        QString s="<ConstraintActivitiesSameStartingTime>\n";
01201        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
01202        s+="   <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
01203        for(int i=0; i<this->n_activities; i++)
01204               s+="   <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
01205        s+="   <Active>"+trueFalse(active)+"</Active>\n";
01206        s+="   <Comments>"+protect(comments)+"</Comments>\n";
01207        s+="</ConstraintActivitiesSameStartingTime>\n";
01208        return s;
01209 }
01210 
01211 QString ConstraintActivitiesSameStartingTime::getDescription(Rules& r){
01212        Q_UNUSED(r);
01213 
01214        QString begin=QString("");
01215        if(!active)
01216               begin="X - ";
01217               
01218        QString end=QString("");
01219        if(!comments.isEmpty())
01220               end=", "+tr("C: %1", "Comments").arg(comments);
01221 
01222        QString s;
01223        s+=tr("Activities same starting time");s+=", ";
01224        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
01225        s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
01226        for(int i=0; i<this->n_activities; i++){
01227               s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
01228               if(i<this->n_activities-1)
01229                      s+=", ";
01230        }
01231 
01232        return begin+s+end;
01233 }
01234 
01235 QString ConstraintActivitiesSameStartingTime::getDetailedDescription(Rules& r){
01236        QString s;
01237        
01238        s=tr("Time constraint");s+="\n";
01239        s+=tr("Activities must have the same starting time");s+="\n";
01240        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
01241        s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
01242        for(int i=0; i<this->n_activities; i++){
01243               s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity").arg(this->activitiesId[i]).arg(getActivityDetailedDescription(r, this->activitiesId[i]));
01244               s+="\n";
01245        }
01246 
01247        if(!active){
01248               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
01249               s+="\n";
01250        }
01251        if(!comments.isEmpty()){
01252               s+=tr("Comments=%1").arg(comments);
01253               s+="\n";
01254        }
01255 
01256        return s;
01257 }
01258 
01259 double ConstraintActivitiesSameStartingTime::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
01260 {
01261        assert(r.internalStructureComputed);
01262 
01263        int nbroken;
01264 
01265        //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
01266 
01267        //sum the differences in the scheduled time for all pairs of activities.
01268 
01269        //without logging
01270        if(conflictsString==NULL){
01271               nbroken=0;
01272               for(int i=1; i<this->_n_activities; i++){
01273                      int t1=c.times[this->_activities[i]];
01274                      if(t1!=UNALLOCATED_TIME){
01275                             int day1=t1%r.nDaysPerWeek;
01276                             int hour1=t1/r.nDaysPerWeek;
01277                             for(int j=0; j<i; j++){
01278                                    int t2=c.times[this->_activities[j]];
01279                                    if(t2!=UNALLOCATED_TIME){
01280                                           int day2=t2%r.nDaysPerWeek;
01281                                           int hour2=t2/r.nDaysPerWeek;
01282                                           int tmp=0;
01283 
01284                                           tmp = abs(day1-day2) + abs(hour1-hour2);
01285                                                  
01286                                           if(tmp>0)
01287                                                  tmp=1;
01288 
01289                                           nbroken+=tmp;
01290                                    }
01291                             }
01292                      }
01293               }
01294        }
01295        //with logging
01296        else{
01297               nbroken=0;
01298               for(int i=1; i<this->_n_activities; i++){
01299                      int t1=c.times[this->_activities[i]];
01300                      if(t1!=UNALLOCATED_TIME){
01301                             int day1=t1%r.nDaysPerWeek;
01302                             int hour1=t1/r.nDaysPerWeek;
01303                             for(int j=0; j<i; j++){
01304                                    int t2=c.times[this->_activities[j]];
01305                                    if(t2!=UNALLOCATED_TIME){
01306                                           int day2=t2%r.nDaysPerWeek;
01307                                           int hour2=t2/r.nDaysPerWeek;
01308                                           int tmp=0;
01309 
01310                                           tmp = abs(day1-day2) + abs(hour1-hour2);
01311                                                  
01312                                           if(tmp>0)
01313                                                  tmp=1;
01314 
01315                                           nbroken+=tmp;
01316 
01317                                           if(tmp>0 && conflictsString!=NULL){
01318                                                  QString s=tr("Time constraint activities same starting time broken, because activity with id=%1 (%2) is not at the same starting time with activity with id=%3 (%4)",
01319                                                  "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
01320                                                   .arg(this->activitiesId[i])
01321                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
01322                                                   .arg(this->activitiesId[j])
01323                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[j]));
01324                                                  s+=". ";
01325                                                  s+=tr("Conflicts factor increase=%1").arg(CustomFETString::number(tmp*weightPercentage/100));
01326                                           
01327                                                  dl.append(s);
01328                                                  cl.append(tmp*weightPercentage/100);
01329                                                  
01330                                                  *conflictsString+= s+"\n";
01331                                           }
01332                                    }
01333                             }
01334                      }
01335               }
01336        }
01337 
01338        if(weightPercentage==100)
01339               assert(nbroken==0);
01340        return weightPercentage/100 * nbroken;
01341 }
01342 
01343 bool ConstraintActivitiesSameStartingTime::isRelatedToActivity(Rules& r, Activity* a)
01344 {
01345        Q_UNUSED(r);
01346 
01347        for(int i=0; i<this->n_activities; i++)
01348               if(this->activitiesId[i]==a->id)
01349                      return true;
01350        return false;
01351 }
01352 
01353 bool ConstraintActivitiesSameStartingTime::isRelatedToTeacher(Teacher* t)
01354 {
01355        Q_UNUSED(t);
01356 
01357        return false;
01358 }
01359 
01360 bool ConstraintActivitiesSameStartingTime::isRelatedToSubject(Subject* s)
01361 {
01362        Q_UNUSED(s);
01363 
01364        return false;
01365 }
01366 
01367 bool ConstraintActivitiesSameStartingTime::isRelatedToActivityTag(ActivityTag* s)
01368 {
01369        Q_UNUSED(s);
01370 
01371        return false;
01372 }
01373 
01374 bool ConstraintActivitiesSameStartingTime::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
01375 {
01376        Q_UNUSED(r);
01377        Q_UNUSED(s);
01378 
01379        return false;
01380 }
01381 
01382 bool ConstraintActivitiesSameStartingTime::hasWrongDayOrHour(Rules& r)
01383 {
01384        Q_UNUSED(r);
01385        return false;
01386 }
01387 
01388 bool ConstraintActivitiesSameStartingTime::canRepairWrongDayOrHour(Rules& r)
01389 {
01390        Q_UNUSED(r);
01391        assert(0);
01392        
01393        return true;
01394 }
01395 
01396 bool ConstraintActivitiesSameStartingTime::repairWrongDayOrHour(Rules& r)
01397 {
01398        Q_UNUSED(r);
01399        assert(0); //should check hasWrongDayOrHour, firstly
01400 
01401        return true;
01402 }
01403 
01406 
01407 ConstraintActivitiesNotOverlapping::ConstraintActivitiesNotOverlapping()
01408        : TimeConstraint()
01409 {
01410        type=CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING;
01411 }
01412 
01413 ConstraintActivitiesNotOverlapping::ConstraintActivitiesNotOverlapping(double wp, int nact, const QList<int>& act)
01414  : TimeConstraint(wp)
01415  {
01416        assert(nact>=2);
01417        assert(act.count()==nact);
01418        this->n_activities=nact;
01419        this->activitiesId.clear();
01420        for(int i=0; i<nact; i++)
01421               this->activitiesId.append(act.at(i));
01422 
01423        this->type=CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING;
01424 }
01425 
01426 bool ConstraintActivitiesNotOverlapping::computeInternalStructure(QWidget* parent, Rules& r)
01427 {
01428        //compute the indices of the activities,
01429        //based on their unique ID
01430 
01431        assert(this->n_activities==this->activitiesId.count());
01432 
01433        this->_activities.clear();
01434        for(int i=0; i<this->n_activities; i++){
01435               int j;
01436               Activity* act;
01437               for(j=0; j<r.nInternalActivities; j++){
01438                      act=&r.internalActivitiesList[j];
01439                      if(act->id==this->activitiesId[i]){
01440                             this->_activities.append(j);
01441                             break;
01442                      }
01443               }
01444        }
01445        this->_n_activities=this->_activities.count();
01446        
01447        if(this->_n_activities<=1){
01448               QMessageBox::warning(parent, tr("FET error in data"), 
01449                      tr("Following constraint is wrong (because you need 2 or more activities. Please correct it):\n%1").arg(this->getDetailedDescription(r)));
01450               //assert(0);
01451               return false;
01452        }
01453 
01454        return true;
01455 }
01456 
01457 void ConstraintActivitiesNotOverlapping::removeUseless(Rules& r)
01458 {
01459        //remove the activitiesId which no longer exist (used after the deletion of an activity)
01460        
01461        assert(this->n_activities==this->activitiesId.count());
01462 
01463        QList<int> tmpList;
01464 
01465        for(int i=0; i<this->n_activities; i++){
01466               for(int k=0; k<r.activitiesList.size(); k++){
01467                      Activity* act=r.activitiesList[k];
01468                      if(act->id==this->activitiesId[i]){
01469                             tmpList.append(act->id);
01470                             break;
01471                      }
01472               }
01473        }
01474        
01475        this->activitiesId=tmpList;
01476        this->n_activities=this->activitiesId.count();
01477 
01478        r.internalStructureComputed=false;
01479 }
01480 
01481 bool ConstraintActivitiesNotOverlapping::hasInactiveActivities(Rules& r)
01482 {
01483        int count=0;
01484 
01485        for(int i=0; i<this->n_activities; i++)
01486               if(r.inactiveActivities.contains(this->activitiesId[i]))
01487                      count++;
01488 
01489        if(this->n_activities-count<=1)
01490               return true;
01491        else
01492               return false;
01493 }
01494 
01495 QString ConstraintActivitiesNotOverlapping::getXmlDescription(Rules& r){
01496        Q_UNUSED(r);
01497 
01498        QString s="<ConstraintActivitiesNotOverlapping>\n";
01499        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
01500        s+="   <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
01501        for(int i=0; i<this->n_activities; i++)
01502               s+="   <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
01503        s+="   <Active>"+trueFalse(active)+"</Active>\n";
01504        s+="   <Comments>"+protect(comments)+"</Comments>\n";
01505        s+="</ConstraintActivitiesNotOverlapping>\n";
01506        return s;
01507 }
01508 
01509 QString ConstraintActivitiesNotOverlapping::getDescription(Rules& r){
01510        Q_UNUSED(r);
01511 
01512        QString begin=QString("");
01513        if(!active)
01514               begin="X - ";
01515               
01516        QString end=QString("");
01517        if(!comments.isEmpty())
01518               end=", "+tr("C: %1", "Comments").arg(comments);
01519               
01520        QString s;
01521        s+=tr("Activities not overlapping");s+=", ";
01522        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
01523        s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
01524        for(int i=0; i<this->n_activities; i++){
01525               s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
01526               if(i<this->n_activities-1)
01527                      s+=", ";
01528        }
01529 
01530        return begin+s+end;
01531 }
01532 
01533 QString ConstraintActivitiesNotOverlapping::getDetailedDescription(Rules& r){
01534        QString s=tr("Time constraint");s+="\n";
01535        s+=tr("Activities must not overlap");s+="\n";
01536        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
01537        s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
01538        for(int i=0; i<this->n_activities; i++){
01539               s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
01540                      .arg(this->activitiesId[i]).arg(getActivityDetailedDescription(r, this->activitiesId[i]));
01541               s+="\n";
01542        }
01543 
01544        if(!active){
01545               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
01546               s+="\n";
01547        }
01548        if(!comments.isEmpty()){
01549               s+=tr("Comments=%1").arg(comments);
01550               s+="\n";
01551        }
01552 
01553        return s;
01554 }
01555 
01556 double ConstraintActivitiesNotOverlapping::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
01557 {
01558        assert(r.internalStructureComputed);
01559 
01560        int nbroken;
01561 
01562        //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
01563 
01564        //sum the overlapping hours for all pairs of activities.
01565        //without logging
01566        if(conflictsString==NULL){
01567               nbroken=0;
01568               for(int i=1; i<this->_n_activities; i++){
01569                      int t1=c.times[this->_activities[i]];
01570                      if(t1!=UNALLOCATED_TIME){
01571                             int day1=t1%r.nDaysPerWeek;
01572                             int hour1=t1/r.nDaysPerWeek;
01573                             int duration1=r.internalActivitiesList[this->_activities[i]].duration;
01574 
01575                             for(int j=0; j<i; j++){
01576                                    int t2=c.times[this->_activities[j]];
01577                                    if(t2!=UNALLOCATED_TIME){
01578                                           int day2=t2%r.nDaysPerWeek;
01579                                           int hour2=t2/r.nDaysPerWeek;
01580                                           int duration2=r.internalActivitiesList[this->_activities[j]].duration;
01581 
01582                                           //the number of overlapping hours
01583                                           int tt=0;
01584                                           if(day1==day2){
01585                                                  int start=max(hour1, hour2);
01586                                                  int stop=min(hour1+duration1, hour2+duration2);
01587                                                  if(stop>start)
01588                                                         tt+=stop-start;
01589                                           }
01590                                           
01591                                           nbroken+=tt;
01592                                    }
01593                             }
01594                      }
01595               }
01596        }
01597        //with logging
01598        else{
01599               nbroken=0;
01600               for(int i=1; i<this->_n_activities; i++){
01601                      int t1=c.times[this->_activities[i]];
01602                      if(t1!=UNALLOCATED_TIME){
01603                             int day1=t1%r.nDaysPerWeek;
01604                             int hour1=t1/r.nDaysPerWeek;
01605                             int duration1=r.internalActivitiesList[this->_activities[i]].duration;
01606 
01607                             for(int j=0; j<i; j++){
01608                                    int t2=c.times[this->_activities[j]];
01609                                    if(t2!=UNALLOCATED_TIME){
01610                                           int day2=t2%r.nDaysPerWeek;
01611                                           int hour2=t2/r.nDaysPerWeek;
01612                                           int duration2=r.internalActivitiesList[this->_activities[j]].duration;
01613        
01614                                           //the number of overlapping hours
01615                                           int tt=0;
01616                                           if(day1==day2){
01617                                                  int start=max(hour1, hour2);
01618                                                  int stop=min(hour1+duration1, hour2+duration2);
01619                                                  if(stop>start)
01620                                                         tt+=stop-start;
01621                                           }
01622 
01623                                           //The overlapping hours, considering weekly activities more important than fortnightly ones
01624                                           int tmp=tt;
01625 
01626                                           nbroken+=tmp;
01627 
01628                                           if(tt>0 && conflictsString!=NULL){
01629 
01630                                                  QString s=tr("Time constraint activities not overlapping broken: activity with id=%1 (%2) overlaps with activity with id=%3 (%4) on a number of %5 periods",
01631                                                   "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
01632                                                   .arg(this->activitiesId[i])
01633                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
01634                                                   .arg(this->activitiesId[j])
01635                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
01636                                                   .arg(tt);
01637                                                  s+=", ";
01638                                                  s+=tr("conflicts factor increase=%1").arg(CustomFETString::number(tmp*weightPercentage/100));
01639                                                  
01640                                                  dl.append(s);
01641                                                  cl.append(tmp*weightPercentage/100);
01642                                           
01643                                                  *conflictsString+= s+"\n";
01644                                           }
01645                                    }
01646                             }
01647                      }
01648               }
01649        }
01650 
01651        if(weightPercentage==100)
01652               assert(nbroken==0);
01653        return weightPercentage/100 * nbroken;
01654 }
01655 
01656 bool ConstraintActivitiesNotOverlapping::isRelatedToActivity(Rules& r, Activity* a)
01657 {
01658        Q_UNUSED(r);
01659 
01660        for(int i=0; i<this->n_activities; i++)
01661               if(this->activitiesId[i]==a->id)
01662                      return true;
01663        return false;
01664 }
01665 
01666 bool ConstraintActivitiesNotOverlapping::isRelatedToTeacher(Teacher* t)
01667 {
01668        Q_UNUSED(t);
01669 
01670        return false;
01671 }
01672 
01673 bool ConstraintActivitiesNotOverlapping::isRelatedToSubject(Subject* s)
01674 {
01675        Q_UNUSED(s);
01676 
01677        return false;
01678 }
01679 
01680 bool ConstraintActivitiesNotOverlapping::isRelatedToActivityTag(ActivityTag* s)
01681 {
01682        Q_UNUSED(s);
01683 
01684        return false;
01685 }
01686 
01687 bool ConstraintActivitiesNotOverlapping::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
01688 {
01689        Q_UNUSED(r);
01690        Q_UNUSED(s);
01691 
01692        return false;
01693 }
01694 
01695 bool ConstraintActivitiesNotOverlapping::hasWrongDayOrHour(Rules& r)
01696 {
01697        Q_UNUSED(r);
01698        return false;
01699 }
01700 
01701 bool ConstraintActivitiesNotOverlapping::canRepairWrongDayOrHour(Rules& r)
01702 {
01703        Q_UNUSED(r);
01704        assert(0);
01705        
01706        return true;
01707 }
01708 
01709 bool ConstraintActivitiesNotOverlapping::repairWrongDayOrHour(Rules& r)
01710 {
01711        Q_UNUSED(r);
01712        assert(0); //should check hasWrongDayOrHour, firstly
01713 
01714        return true;
01715 }
01716 
01719 
01720 ConstraintMinDaysBetweenActivities::ConstraintMinDaysBetweenActivities()
01721        : TimeConstraint()
01722 {
01723        type=CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES;
01724 }
01725 
01726 ConstraintMinDaysBetweenActivities::ConstraintMinDaysBetweenActivities(double wp, bool cisd, int nact, const QList<int>& act, int n)
01727  : TimeConstraint(wp)
01728  {
01729        this->consecutiveIfSameDay=cisd;
01730 
01731        assert(nact>=2);
01732        assert(act.count()==nact);
01733        this->n_activities=nact;
01734        this->activitiesId.clear();
01735        for(int i=0; i<nact; i++)
01736               this->activitiesId.append(act.at(i));
01737 
01738        assert(n>0);
01739        this->minDays=n;
01740 
01741        this->type=CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES;
01742 }
01743 
01744 bool ConstraintMinDaysBetweenActivities::operator==(ConstraintMinDaysBetweenActivities& c){
01745        assert(this->n_activities==this->activitiesId.count());
01746        assert(c.n_activities==c.activitiesId.count());
01747 
01748        if(this->n_activities!=c.n_activities)
01749               return false;
01750        for(int i=0; i<this->n_activities; i++)
01751               if(this->activitiesId[i]!=c.activitiesId[i])
01752                      return false;
01753        if(this->minDays!=c.minDays)
01754               return false;
01755        if(this->weightPercentage!=c.weightPercentage)
01756               return false;
01757        if(this->consecutiveIfSameDay!=c.consecutiveIfSameDay)
01758               return false;
01759        return true;
01760 }
01761 
01762 bool ConstraintMinDaysBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
01763 {
01764        //compute the indices of the activities,
01765        //based on their unique ID
01766 
01767        assert(this->n_activities==this->activitiesId.count());
01768 
01769        this->_activities.clear();
01770        for(int i=0; i<this->n_activities; i++){
01771               int j;
01772               Activity* act;
01773               for(j=0; j<r.nInternalActivities; j++){
01774                      act=&r.internalActivitiesList[j];
01775                      if(act->id==this->activitiesId[i]){
01776                             this->_activities.append(j);
01777                             break;
01778                      }
01779               }
01780        }
01781        this->_n_activities=this->_activities.count();
01782        
01783        if(this->_n_activities<=1){
01784               QMessageBox::warning(parent, tr("FET error in data"), 
01785                      tr("Following constraint is wrong (because you need 2 or more activities. Please correct it):\n%1").arg(this->getDetailedDescription(r)));
01786               //assert(0);
01787               return false;
01788        }
01789 
01790        return true;
01791 }
01792 
01793 void ConstraintMinDaysBetweenActivities::removeUseless(Rules& r)
01794 {
01795        //remove the activitiesId which no longer exist (used after the deletion of an activity)
01796        
01797        assert(this->n_activities==this->activitiesId.count());
01798 
01799        QList<int> tmpList;
01800 
01801        for(int i=0; i<this->n_activities; i++){
01802               for(int k=0; k<r.activitiesList.size(); k++){
01803                      Activity* act=r.activitiesList[k];
01804                      if(act->id==this->activitiesId[i]){
01805                             tmpList.append(act->id);
01806                             break;
01807                      }
01808               }
01809        }
01810        
01811        this->activitiesId=tmpList;
01812        this->n_activities=this->activitiesId.count();
01813 
01814        r.internalStructureComputed=false;
01815 }
01816 
01817 bool ConstraintMinDaysBetweenActivities::hasInactiveActivities(Rules& r)
01818 {
01819        int count=0;
01820 
01821        for(int i=0; i<this->n_activities; i++)
01822               if(r.inactiveActivities.contains(this->activitiesId[i]))
01823                      count++;
01824 
01825        if(this->n_activities-count<=1)
01826               return true;
01827        else
01828               return false;
01829 }
01830 
01831 QString ConstraintMinDaysBetweenActivities::getXmlDescription(Rules& r){
01832        Q_UNUSED(r);
01833 
01834        QString s="<ConstraintMinDaysBetweenActivities>\n";
01835        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
01836        s+="   <Consecutive_If_Same_Day>";s+=trueFalse(this->consecutiveIfSameDay);s+="</Consecutive_If_Same_Day>\n";
01837        s+="   <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
01838        for(int i=0; i<this->n_activities; i++)
01839               s+="   <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
01840        s+="   <MinDays>"+CustomFETString::number(this->minDays)+"</MinDays>\n";
01841        s+="   <Active>"+trueFalse(active)+"</Active>\n";
01842        s+="   <Comments>"+protect(comments)+"</Comments>\n";
01843        s+="</ConstraintMinDaysBetweenActivities>\n";
01844        return s;
01845 }
01846 
01847 QString ConstraintMinDaysBetweenActivities::getDescription(Rules& r){
01848        Q_UNUSED(r);
01849 
01850        QString begin=QString("");
01851        if(!active)
01852               begin="X - ";
01853               
01854        QString end=QString("");
01855        if(!comments.isEmpty())
01856               end=", "+tr("C: %1", "Comments").arg(comments);
01857               
01858        QString s;
01859        s+=tr("Min days between activities");s+=", ";
01860        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
01861        s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
01862        for(int i=0; i<this->n_activities; i++){
01863               s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
01864        }
01865        s+=tr("mD:%1", "Min days").arg(this->minDays);s+=", ";
01866        s+=tr("CSD:%1", "Consecutive if same day").arg(yesNoTranslated(this->consecutiveIfSameDay));
01867 
01868        return begin+s+end;
01869 }
01870 
01871 QString ConstraintMinDaysBetweenActivities::getDetailedDescription(Rules& r){
01872        QString s=tr("Time constraint");s+="\n";
01873        s+=tr("Minimum number of days between activities");s+="\n";
01874        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
01875        s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
01876        for(int i=0; i<this->n_activities; i++){
01877               s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
01878                      .arg(this->activitiesId[i])
01879                      .arg(getActivityDetailedDescription(r, this->activitiesId[i]));
01880               s+="\n";
01881        }
01882        s+=tr("Minimum number of days=%1").arg(this->minDays);s+="\n";
01883        s+=tr("Consecutive if same day=%1").arg(yesNoTranslated(this->consecutiveIfSameDay));s+="\n";
01884 
01885        if(!active){
01886               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
01887               s+="\n";
01888        }
01889        if(!comments.isEmpty()){
01890               s+=tr("Comments=%1").arg(comments);
01891               s+="\n";
01892        }
01893 
01894        return s;
01895 }
01896 
01897 double ConstraintMinDaysBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
01898 {
01899        assert(r.internalStructureComputed);
01900 
01901        int nbroken;
01902 
01903        //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
01904 
01905        //sum the overlapping hours for all pairs of activities.
01906        //without logging
01907        if(conflictsString==NULL){
01908               nbroken=0;
01909               for(int i=1; i<this->_n_activities; i++){
01910                      int t1=c.times[this->_activities[i]];
01911                      if(t1!=UNALLOCATED_TIME){
01912                             int day1=t1%r.nDaysPerWeek;
01913                             int hour1=t1/r.nDaysPerWeek;
01914                             int duration1=r.internalActivitiesList[this->_activities[i]].duration;
01915 
01916                             for(int j=0; j<i; j++){
01917                                    int t2=c.times[this->_activities[j]];
01918                                    if(t2!=UNALLOCATED_TIME){
01919                                           int day2=t2%r.nDaysPerWeek;
01920                                           int hour2=t2/r.nDaysPerWeek;
01921                                           int duration2=r.internalActivitiesList[this->_activities[j]].duration;
01922                                    
01923                                           int tmp;
01924                                           int tt=0;
01925                                           int dist=abs(day1-day2);
01926                                           if(dist<minDays){
01927                                                  tt=minDays-dist;
01928                                                  
01929                                                  if(this->consecutiveIfSameDay && day1==day2)
01930                                                         assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
01931                                           }
01932                                           
01933                                           tmp=tt;
01934        
01935                                           nbroken+=tmp;
01936                                    }
01937                             }
01938                      }
01939               }
01940        }
01941        //with logging
01942        else{
01943               nbroken=0;
01944               for(int i=1; i<this->_n_activities; i++){
01945                      int t1=c.times[this->_activities[i]];
01946                      if(t1!=UNALLOCATED_TIME){
01947                             int day1=t1%r.nDaysPerWeek;
01948                             int hour1=t1/r.nDaysPerWeek;
01949                             int duration1=r.internalActivitiesList[this->_activities[i]].duration;
01950 
01951                             for(int j=0; j<i; j++){
01952                                    int t2=c.times[this->_activities[j]];
01953                                    if(t2!=UNALLOCATED_TIME){
01954                                           int day2=t2%r.nDaysPerWeek;
01955                                           int hour2=t2/r.nDaysPerWeek;
01956                                           int duration2=r.internalActivitiesList[this->_activities[j]].duration;
01957                                    
01958                                           int tmp;
01959                                           int tt=0;
01960                                           int dist=abs(day1-day2);
01961 
01962                                           if(dist<minDays){
01963                                                  tt=minDays-dist;
01964                                                  
01965                                                  if(this->consecutiveIfSameDay && day1==day2)
01966                                                         assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
01967                                           }
01968 
01969                                           tmp=tt;
01970        
01971                                           nbroken+=tmp;
01972 
01973                                           if(tt>0 && conflictsString!=NULL){
01974                                                  QString s=tr("Time constraint min days between activities broken: activity with id=%1 (%2) conflicts with activity with id=%3 (%4), being %5 days too close, on days %6 and %7",
01975                                                   "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr. Close here means near")
01976                                                   .arg(this->activitiesId[i])
01977                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
01978                                                   .arg(this->activitiesId[j])
01979                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
01980                                                   .arg(tt)
01981                                                   .arg(r.daysOfTheWeek[day1])
01982                                                   .arg(r.daysOfTheWeek[day2]);
01983                                                   ;
01984 
01985                                                  s+=", ";
01986                                                  s+=tr("conflicts factor increase=%1").arg(CustomFETString::number(tmp*weightPercentage/100));
01987                                                  s+=".";
01988                                                  
01989                                                  if(this->consecutiveIfSameDay && day1==day2){
01990                                                         s+=" ";
01991                                                         s+=tr("The activities are placed consecutively in the timetable, because you selected this option"
01992                                                          " in case the activities are in the same day");
01993                                                  }
01994                                                  
01995                                                  dl.append(s);
01996                                                  cl.append(tmp*weightPercentage/100);
01997                                                  
01998                                                  *conflictsString+= s+"\n";
01999                                           }
02000                                    }
02001                             }
02002                      }
02003               }
02004        }
02005 
02006        if(weightPercentage==100)
02007               assert(nbroken==0);
02008        return weightPercentage/100 * nbroken;
02009 }
02010 
02011 bool ConstraintMinDaysBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
02012 {
02013        Q_UNUSED(r);
02014 
02015        for(int i=0; i<this->n_activities; i++)
02016               if(this->activitiesId[i]==a->id)
02017                      return true;
02018        return false;
02019 }
02020 
02021 bool ConstraintMinDaysBetweenActivities::isRelatedToTeacher(Teacher* t)
02022 {
02023        Q_UNUSED(t);
02024 
02025        return false;
02026 }
02027 
02028 bool ConstraintMinDaysBetweenActivities::isRelatedToSubject(Subject* s)
02029 {
02030        Q_UNUSED(s);
02031 
02032        return false;
02033 }
02034 
02035 bool ConstraintMinDaysBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
02036 {
02037        Q_UNUSED(s);
02038 
02039        return false;
02040 }
02041 
02042 bool ConstraintMinDaysBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
02043 {
02044        Q_UNUSED(r);
02045        Q_UNUSED(s);
02046 
02047        return false;
02048 }
02049 
02050 bool ConstraintMinDaysBetweenActivities::hasWrongDayOrHour(Rules& r)
02051 {
02052        if(minDays>=r.nDaysPerWeek)
02053               return true;
02054               
02055        return false;
02056 }
02057 
02058 bool ConstraintMinDaysBetweenActivities::canRepairWrongDayOrHour(Rules& r)
02059 {
02060        assert(hasWrongDayOrHour(r));
02061        
02062        return true;
02063 }
02064 
02065 bool ConstraintMinDaysBetweenActivities::repairWrongDayOrHour(Rules& r)
02066 {
02067        assert(hasWrongDayOrHour(r));
02068        
02069        if(minDays>=r.nDaysPerWeek)
02070               minDays=r.nDaysPerWeek-1;
02071 
02072        return true;
02073 }
02074 
02077 
02078 ConstraintMaxDaysBetweenActivities::ConstraintMaxDaysBetweenActivities()
02079        : TimeConstraint()
02080 {
02081        type=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES;
02082 }
02083 
02084 ConstraintMaxDaysBetweenActivities::ConstraintMaxDaysBetweenActivities(double wp, int nact, const QList<int>& act, int n)
02085  : TimeConstraint(wp)
02086  {
02087        assert(nact>=2);
02088        assert(act.count()==nact);
02089        this->n_activities=nact;
02090        this->activitiesId.clear();
02091        for(int i=0; i<nact; i++)
02092               this->activitiesId.append(act.at(i));
02093 
02094        assert(n>=0);
02095        this->maxDays=n;
02096 
02097        this->type=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES;
02098 }
02099 
02100 bool ConstraintMaxDaysBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
02101 {
02102        //compute the indices of the activities,
02103        //based on their unique ID
02104 
02105        assert(this->n_activities==this->activitiesId.count());
02106 
02107        this->_activities.clear();
02108        for(int i=0; i<this->n_activities; i++){
02109               int j;
02110               Activity* act;
02111               for(j=0; j<r.nInternalActivities; j++){
02112                      act=&r.internalActivitiesList[j];
02113                      if(act->id==this->activitiesId[i]){
02114                             this->_activities.append(j);
02115                             break;
02116                      }
02117               }
02118        }
02119        this->_n_activities=this->_activities.count();
02120        
02121        if(this->_n_activities<=1){
02122               QMessageBox::warning(parent, tr("FET error in data"), 
02123                      tr("Following constraint is wrong (because you need 2 or more activities. Please correct it):\n%1").arg(this->getDetailedDescription(r)));
02124               //assert(0);
02125               return false;
02126        }
02127 
02128        return true;
02129 }
02130 
02131 void ConstraintMaxDaysBetweenActivities::removeUseless(Rules& r)
02132 {
02133        //remove the activitiesId which no longer exist (used after the deletion of an activity)
02134        
02135        assert(this->n_activities==this->activitiesId.count());
02136 
02137        QList<int> tmpList;
02138 
02139        for(int i=0; i<this->n_activities; i++){
02140               for(int k=0; k<r.activitiesList.size(); k++){
02141                      Activity* act=r.activitiesList[k];
02142                      if(act->id==this->activitiesId[i]){
02143                             tmpList.append(act->id);
02144                             break;
02145                      }
02146               }
02147        }
02148        
02149        this->activitiesId=tmpList;
02150        this->n_activities=this->activitiesId.count();
02151 
02152        r.internalStructureComputed=false;
02153 }
02154 
02155 bool ConstraintMaxDaysBetweenActivities::hasInactiveActivities(Rules& r)
02156 {
02157        int count=0;
02158 
02159        for(int i=0; i<this->n_activities; i++)
02160               if(r.inactiveActivities.contains(this->activitiesId[i]))
02161                      count++;
02162 
02163        if(this->n_activities-count<=1)
02164               return true;
02165        else
02166               return false;
02167 }
02168 
02169 QString ConstraintMaxDaysBetweenActivities::getXmlDescription(Rules& r){
02170        Q_UNUSED(r);
02171 
02172        QString s="<ConstraintMaxDaysBetweenActivities>\n";
02173        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
02174        s+="   <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
02175        for(int i=0; i<this->n_activities; i++)
02176               s+="   <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
02177        s+="   <MaxDays>"+CustomFETString::number(this->maxDays)+"</MaxDays>\n";
02178        s+="   <Active>"+trueFalse(active)+"</Active>\n";
02179        s+="   <Comments>"+protect(comments)+"</Comments>\n";
02180        s+="</ConstraintMaxDaysBetweenActivities>\n";
02181        return s;
02182 }
02183 
02184 QString ConstraintMaxDaysBetweenActivities::getDescription(Rules& r){
02185        Q_UNUSED(r);
02186 
02187        QString begin=QString("");
02188        if(!active)
02189               begin="X - ";
02190               
02191        QString end=QString("");
02192        if(!comments.isEmpty())
02193               end=", "+tr("C: %1", "Comments").arg(comments);
02194               
02195        QString s;
02196        s+=tr("Max days between activities");s+=", ";
02197        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
02198        s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
02199        for(int i=0; i<this->n_activities; i++){
02200               s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
02201        }
02202        s+=tr("MD:%1", "Abbreviation for maximum days").arg(this->maxDays);
02203 
02204        return begin+s+end;
02205 }
02206 
02207 QString ConstraintMaxDaysBetweenActivities::getDetailedDescription(Rules& r){
02208        QString s=tr("Time constraint");s+="\n";
02209        s+=tr("Maximum number of days between activities");s+="\n";
02210        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
02211        s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
02212        for(int i=0; i<this->n_activities; i++){
02213               s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
02214                      .arg(this->activitiesId[i])
02215                      .arg(getActivityDetailedDescription(r, this->activitiesId[i]));
02216               s+="\n";
02217        }
02218        s+=tr("Maximum number of days=%1").arg(this->maxDays);s+="\n";
02219 
02220        if(!active){
02221               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
02222               s+="\n";
02223        }
02224        if(!comments.isEmpty()){
02225               s+=tr("Comments=%1").arg(comments);
02226               s+="\n";
02227        }
02228 
02229        return s;
02230 }
02231 
02232 double ConstraintMaxDaysBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
02233 {
02234        assert(r.internalStructureComputed);
02235 
02236        int nbroken;
02237 
02238        //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
02239 
02240        //sum the overlapping hours for all pairs of activities.
02241        //without logging
02242        if(conflictsString==NULL){
02243               nbroken=0;
02244               for(int i=1; i<this->_n_activities; i++){
02245                      int t1=c.times[this->_activities[i]];
02246                      if(t1!=UNALLOCATED_TIME){
02247                             int day1=t1%r.nDaysPerWeek;
02248                             //int hour1=t1/r.nDaysPerWeek;
02249                             //int duration1=r.internalActivitiesList[this->_activities[i]].duration;
02250 
02251                             for(int j=0; j<i; j++){
02252                                    int t2=c.times[this->_activities[j]];
02253                                    if(t2!=UNALLOCATED_TIME){
02254                                           int day2=t2%r.nDaysPerWeek;
02255                                           //int hour2=t2/r.nDaysPerWeek;
02256                                           //int duration2=r.internalActivitiesList[this->_activities[j]].duration;
02257                                    
02258                                           int tmp;
02259                                           int tt=0;
02260                                           int dist=abs(day1-day2);
02261                                           if(dist>maxDays){
02262                                                  tt=dist-maxDays;
02263                                                  
02264                                                  //if(this->consecutiveIfSameDay && day1==day2)
02265                                                  //     assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
02266                                           }
02267                                           
02268                                           tmp=tt;
02269        
02270                                           nbroken+=tmp;
02271                                    }
02272                             }
02273                      }
02274               }
02275        }
02276        //with logging
02277        else{
02278               nbroken=0;
02279               for(int i=1; i<this->_n_activities; i++){
02280                      int t1=c.times[this->_activities[i]];
02281                      if(t1!=UNALLOCATED_TIME){
02282                             int day1=t1%r.nDaysPerWeek;
02283                             //int hour1=t1/r.nDaysPerWeek;
02284                             //int duration1=r.internalActivitiesList[this->_activities[i]].duration;
02285 
02286                             for(int j=0; j<i; j++){
02287                                    int t2=c.times[this->_activities[j]];
02288                                    if(t2!=UNALLOCATED_TIME){
02289                                           int day2=t2%r.nDaysPerWeek;
02290                                           //int hour2=t2/r.nDaysPerWeek;
02291                                           //int duration2=r.internalActivitiesList[this->_activities[j]].duration;
02292                                    
02293                                           int tmp;
02294                                           int tt=0;
02295                                           int dist=abs(day1-day2);
02296 
02297                                           if(dist>maxDays){
02298                                                  tt=dist-maxDays;
02299                                                  
02300                                                  //if(this->consecutiveIfSameDay && day1==day2)
02301                                                  //     assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
02302                                           }
02303 
02304                                           tmp=tt;
02305        
02306                                           nbroken+=tmp;
02307 
02308                                           if(tt>0 && conflictsString!=NULL){
02309                                                  QString s=tr("Time constraint max days between activities broken: activity with id=%1 (%2) conflicts with activity with id=%3 (%4), being %5 days too far away"
02310                                                   ", on days %6 and %7", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
02311                                                   .arg(this->activitiesId[i])
02312                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
02313                                                   .arg(this->activitiesId[j])
02314                                                   .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
02315                                                   .arg(tt)
02316                                                   .arg(r.daysOfTheWeek[day1])
02317                                                   .arg(r.daysOfTheWeek[day2]);
02318                                                   
02319                                                  s+=", ";
02320                                                  s+=tr("conflicts factor increase=%1").arg(CustomFETString::number(tmp*weightPercentage/100));
02321                                                  s+=".";
02322                                                  
02323                                                  dl.append(s);
02324                                                  cl.append(tmp*weightPercentage/100);
02325                                                  
02326                                                  *conflictsString+= s+"\n";
02327                                           }
02328                                    }
02329                             }
02330                      }
02331               }
02332        }
02333 
02334        if(weightPercentage==100)
02335               assert(nbroken==0);
02336        return weightPercentage/100 * nbroken;
02337 }
02338 
02339 bool ConstraintMaxDaysBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
02340 {
02341        Q_UNUSED(r);
02342 
02343        for(int i=0; i<this->n_activities; i++)
02344               if(this->activitiesId[i]==a->id)
02345                      return true;
02346        return false;
02347 }
02348 
02349 bool ConstraintMaxDaysBetweenActivities::isRelatedToTeacher(Teacher* t)
02350 {
02351        Q_UNUSED(t);
02352 
02353        return false;
02354 }
02355 
02356 bool ConstraintMaxDaysBetweenActivities::isRelatedToSubject(Subject* s)
02357 {
02358        Q_UNUSED(s);
02359 
02360        return false;
02361 }
02362 
02363 bool ConstraintMaxDaysBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
02364 {
02365        Q_UNUSED(s);
02366 
02367        return false;
02368 }
02369 
02370 bool ConstraintMaxDaysBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
02371 {
02372        Q_UNUSED(r);
02373        Q_UNUSED(s);
02374 
02375        return false;
02376 }
02377 
02378 bool ConstraintMaxDaysBetweenActivities::hasWrongDayOrHour(Rules& r)
02379 {
02380        if(maxDays>=r.nDaysPerWeek)
02381               return true;
02382               
02383        return false;
02384 }
02385 
02386 bool ConstraintMaxDaysBetweenActivities::canRepairWrongDayOrHour(Rules& r)
02387 {
02388        assert(hasWrongDayOrHour(r));
02389        
02390        return true;
02391 }
02392 
02393 bool ConstraintMaxDaysBetweenActivities::repairWrongDayOrHour(Rules& r)
02394 {
02395        assert(hasWrongDayOrHour(r));
02396        
02397        if(maxDays>=r.nDaysPerWeek)
02398               maxDays=r.nDaysPerWeek-1;
02399 
02400        return true;
02401 }
02402 
02405 
02406 ConstraintMinGapsBetweenActivities::ConstraintMinGapsBetweenActivities()
02407        : TimeConstraint()
02408 {
02409        type=CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES;
02410 }
02411 
02412 ConstraintMinGapsBetweenActivities::ConstraintMinGapsBetweenActivities(double wp, int nact, const QList<int>& actList, int ngaps)
02413  : TimeConstraint(wp)
02414  {
02415        this->n_activities=nact;
02416        assert(nact==actList.count());
02417        this->activitiesId.clear();
02418        for(int i=0; i<nact; i++)
02419               this->activitiesId.append(actList.at(i));
02420 
02421        assert(ngaps>0);
02422        this->minGaps=ngaps;
02423 
02424        this->type=CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES;
02425 }
02426 
02427 bool ConstraintMinGapsBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
02428 {
02429        //compute the indices of the activities,
02430        //based on their unique ID
02431 
02432        assert(this->n_activities==this->activitiesId.count());
02433 
02434        this->_activities.clear();
02435        for(int i=0; i<this->n_activities; i++){
02436               int j;
02437               Activity* act;
02438               for(j=0; j<r.nInternalActivities; j++){
02439                      act=&r.internalActivitiesList[j];
02440                      if(act->id==this->activitiesId[i]){
02441                             this->_activities.append(j);
02442                             break;
02443                      }
02444               }
02445        }
02446        this->_n_activities=this->_activities.count();
02447        
02448        if(this->_n_activities<=1){
02449               QMessageBox::warning(parent, tr("FET error in data"), 
02450                      tr("Following constraint is wrong (because you need 2 or more activities. Please correct it):\n%1").arg(this->getDetailedDescription(r)));
02451               //assert(0);
02452               return false;
02453        }
02454 
02455        return true;
02456 }
02457 
02458 void ConstraintMinGapsBetweenActivities::removeUseless(Rules& r)
02459 {
02460        //remove the activitiesId which no longer exist (used after the deletion of an activity)
02461        
02462        assert(this->n_activities==this->activitiesId.count());
02463 
02464        QList<int> tmpList;
02465 
02466        for(int i=0; i<this->n_activities; i++){
02467               for(int k=0; k<r.activitiesList.size(); k++){
02468                      Activity* act=r.activitiesList[k];
02469                      if(act->id==this->activitiesId[i]){
02470                             tmpList.append(act->id);
02471                             break;
02472                      }
02473               }
02474        }
02475        
02476        this->activitiesId=tmpList;
02477        this->n_activities=this->activitiesId.count();
02478 
02479        r.internalStructureComputed=false;
02480 }
02481 
02482 bool ConstraintMinGapsBetweenActivities::hasInactiveActivities(Rules& r)
02483 {
02484        int count=0;
02485 
02486        for(int i=0; i<this->n_activities; i++)
02487               if(r.inactiveActivities.contains(this->activitiesId[i]))
02488                      count++;
02489 
02490        if(this->n_activities-count<=1)
02491               return true;
02492        else
02493               return false;
02494 }
02495 
02496 QString ConstraintMinGapsBetweenActivities::getXmlDescription(Rules& r){
02497        Q_UNUSED(r);
02498 
02499        QString s="<ConstraintMinGapsBetweenActivities>\n";
02500        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
02501        s+="   <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
02502        for(int i=0; i<this->n_activities; i++)
02503               s+="   <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
02504        s+="   <MinGaps>"+CustomFETString::number(this->minGaps)+"</MinGaps>\n";
02505        s+="   <Active>"+trueFalse(active)+"</Active>\n";
02506        s+="   <Comments>"+protect(comments)+"</Comments>\n";
02507        s+="</ConstraintMinGapsBetweenActivities>\n";
02508        return s;
02509 }
02510 
02511 QString ConstraintMinGapsBetweenActivities::getDescription(Rules& r){
02512        Q_UNUSED(r);
02513 
02514        QString begin=QString("");
02515        if(!active)
02516               begin="X - ";
02517               
02518        QString end=QString("");
02519        if(!comments.isEmpty())
02520               end=", "+tr("C: %1", "Comments").arg(comments);
02521               
02522        QString s;
02523        s+=tr("Min gaps between activities");s+=", ";
02524        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
02525        s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
02526        for(int i=0; i<this->n_activities; i++){
02527               s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
02528        }
02529        s+=tr("mG:%1", "Minimum number of gaps").arg(this->minGaps);
02530 
02531        return begin+s+end;
02532 }
02533 
02534 QString ConstraintMinGapsBetweenActivities::getDetailedDescription(Rules& r){
02535        QString s=tr("Time constraint");s+="\n";
02536        s+=tr("Minimum gaps between activities (if activities on the same day)");s+="\n";
02537        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
02538        s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
02539        for(int i=0; i<this->n_activities; i++){
02540               s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
02541                      .arg(this->activitiesId[i])
02542                      .arg(getActivityDetailedDescription(r, this->activitiesId[i]));
02543               s+="\n";
02544        }
02545        s+=tr("Minimum number of gaps=%1").arg(this->minGaps);s+="\n";
02546 
02547        if(!active){
02548               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
02549               s+="\n";
02550        }
02551        if(!comments.isEmpty()){
02552               s+=tr("Comments=%1").arg(comments);
02553               s+="\n";
02554        }
02555 
02556        return s;
02557 }
02558 
02559 double ConstraintMinGapsBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
02560 {
02561        assert(r.internalStructureComputed);
02562 
02563        int nbroken;
02564 
02565        //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
02566 
02567        nbroken=0;
02568        for(int i=1; i<this->_n_activities; i++){
02569               int t1=c.times[this->_activities[i]];
02570               if(t1!=UNALLOCATED_TIME){
02571                      int day1=t1%r.nDaysPerWeek;
02572                      int hour1=t1/r.nDaysPerWeek;
02573                      int duration1=r.internalActivitiesList[this->_activities[i]].duration;
02574 
02575                      for(int j=0; j<i; j++){
02576                             int t2=c.times[this->_activities[j]];
02577                             if(t2!=UNALLOCATED_TIME){
02578                                    int day2=t2%r.nDaysPerWeek;
02579                                    int hour2=t2/r.nDaysPerWeek;
02580                                    int duration2=r.internalActivitiesList[this->_activities[j]].duration;
02581                             
02582                                    int tmp;
02583                                    int tt=0;
02584                                    int dist=abs(day1-day2);
02585                                    
02586                                    if(dist==0){ //same day
02587                                           assert(day1==day2);
02588                                           if(hour2>=hour1){
02589                                                  //assert(hour1+duration1<=hour2); not true for activities which are not incompatible
02590                                                  if(hour1+duration1+minGaps > hour2)
02591                                                         tt = (hour1+duration1+minGaps) - hour2;
02592                                           }
02593                                           else{
02594                                                  //assert(hour2+duration2<=hour1); not true for activities which are not incompatible
02595                                                  if(hour2+duration2+minGaps > hour1)
02596                                                         tt = (hour2+duration2+minGaps) - hour1;
02597                                           }
02598                                    }
02599 
02600                                    tmp=tt;
02601        
02602                                    nbroken+=tmp;
02603 
02604                                    if(tt>0 && conflictsString!=NULL){
02605                                           QString s=tr("Time constraint min gaps between activities broken: activity with id=%1 (%2) conflicts with activity with id=%3 (%4), they are on the same day %5 and there are %6 extra hours between them",
02606                                                  "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
02607                                            .arg(this->activitiesId[i])
02608                                            .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
02609                                            .arg(this->activitiesId[j])
02610                                            .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
02611                                            .arg(r.daysOfTheWeek[day1])
02612                                            .arg(tt);
02613 
02614                                           s+=", ";
02615                                           s+=tr("conflicts factor increase=%1").arg(CustomFETString::number(tmp*weightPercentage/100));
02616                                           s+=".";
02617                                                  
02618                                           dl.append(s);
02619                                           cl.append(tmp*weightPercentage/100);
02620                                                  
02621                                           *conflictsString+= s+"\n";
02622                                    }
02623                             }
02624                      }
02625               }
02626        }
02627 
02628        if(weightPercentage==100)
02629               assert(nbroken==0);
02630        return weightPercentage/100 * nbroken;
02631 }
02632 
02633 bool ConstraintMinGapsBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
02634 {
02635        Q_UNUSED(r);
02636 
02637        for(int i=0; i<this->n_activities; i++)
02638               if(this->activitiesId[i]==a->id)
02639                      return true;
02640        return false;
02641 }
02642 
02643 bool ConstraintMinGapsBetweenActivities::isRelatedToTeacher(Teacher* t)
02644 {
02645        Q_UNUSED(t);
02646 
02647        return false;
02648 }
02649 
02650 bool ConstraintMinGapsBetweenActivities::isRelatedToSubject(Subject* s)
02651 {
02652        Q_UNUSED(s);
02653 
02654        return false;
02655 }
02656 
02657 bool ConstraintMinGapsBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
02658 {
02659        Q_UNUSED(s);
02660 
02661        return false;
02662 }
02663 
02664 bool ConstraintMinGapsBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
02665 {
02666        Q_UNUSED(r);
02667        Q_UNUSED(s);
02668 
02669        return false;
02670 }
02671 
02672 bool ConstraintMinGapsBetweenActivities::hasWrongDayOrHour(Rules& r)
02673 {
02674        if(minGaps>r.nHoursPerDay)
02675               return true;
02676               
02677        return false;
02678 }
02679 
02680 bool ConstraintMinGapsBetweenActivities::canRepairWrongDayOrHour(Rules& r)
02681 {
02682        assert(hasWrongDayOrHour(r));
02683        
02684        return true;
02685 }
02686 
02687 bool ConstraintMinGapsBetweenActivities::repairWrongDayOrHour(Rules& r)
02688 {
02689        assert(hasWrongDayOrHour(r));
02690        
02691        if(minGaps>r.nHoursPerDay)
02692               minGaps=r.nHoursPerDay;
02693 
02694        return true;
02695 }
02696 
02699 
02700 ConstraintTeachersMaxHoursDaily::ConstraintTeachersMaxHoursDaily()
02701        : TimeConstraint()
02702 {
02703        this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY;
02704 }
02705 
02706 ConstraintTeachersMaxHoursDaily::ConstraintTeachersMaxHoursDaily(double wp, int maxhours)
02707  : TimeConstraint(wp)
02708  {
02709        assert(maxhours>0);
02710        this->maxHoursDaily=maxhours;
02711 
02712        this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY;
02713 }
02714 
02715 bool ConstraintTeachersMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
02716 {
02717        Q_UNUSED(parent);
02718        Q_UNUSED(r);
02719        
02720        return true;
02721 }
02722 
02723 bool ConstraintTeachersMaxHoursDaily::hasInactiveActivities(Rules& r)
02724 {
02725        Q_UNUSED(r);
02726        return false;
02727 }
02728 
02729 QString ConstraintTeachersMaxHoursDaily::getXmlDescription(Rules& r){
02730        Q_UNUSED(r);
02731 
02732        QString s="<ConstraintTeachersMaxHoursDaily>\n";
02733        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
02734        s+="   <Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
02735        s+="   <Active>"+trueFalse(active)+"</Active>\n";
02736        s+="   <Comments>"+protect(comments)+"</Comments>\n";
02737        s+="</ConstraintTeachersMaxHoursDaily>\n";
02738        return s;
02739 }
02740 
02741 QString ConstraintTeachersMaxHoursDaily::getDescription(Rules& r){
02742        Q_UNUSED(r);
02743 
02744        QString begin=QString("");
02745        if(!active)
02746               begin="X - ";
02747               
02748        QString end=QString("");
02749        if(!comments.isEmpty())
02750               end=", "+tr("C: %1", "Comments").arg(comments);
02751               
02752        QString s;
02753        s+=tr("Teachers max hours daily"), s+=", ";
02754        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
02755        s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
02756 
02757        return begin+s+end;
02758 }
02759 
02760 QString ConstraintTeachersMaxHoursDaily::getDetailedDescription(Rules& r){
02761        Q_UNUSED(r);
02762 
02763        QString s=tr("Time constraint");s+="\n";
02764        s+=tr("All teachers must respect the maximum number of hours daily");s+="\n";
02765        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
02766        s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
02767 
02768        if(!active){
02769               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
02770               s+="\n";
02771        }
02772        if(!comments.isEmpty()){
02773               s+=tr("Comments=%1").arg(comments);
02774               s+="\n";
02775        }
02776 
02777        return s;
02778 }
02779 
02780 double ConstraintTeachersMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
02781 {
02782        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
02783        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
02784               c.teachersMatrixReady=true;
02785               c.subgroupsMatrixReady=true;
02786               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
02787               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
02788 
02789               c.changedForMatrixCalculation=false;
02790        }
02791 
02792        int nbroken;
02793 
02794        //without logging
02795        if(conflictsString==NULL){
02796               nbroken=0;
02797               for(int i=0; i<r.nInternalTeachers; i++){
02798                      for(int d=0; d<r.nDaysPerWeek; d++){
02799                             int n_hours_daily=0;
02800                             for(int h=0; h<r.nHoursPerDay; h++)
02801                                    if(teachersMatrix[i][d][h]>0)
02802                                           n_hours_daily++;
02803 
02804                             if(n_hours_daily>this->maxHoursDaily)
02805                                    nbroken++;
02806                      }
02807               }
02808        }
02809        //with logging
02810        else{
02811               nbroken=0;
02812               for(int i=0; i<r.nInternalTeachers; i++){
02813                      for(int d=0; d<r.nDaysPerWeek; d++){
02814                             int n_hours_daily=0;
02815                             for(int h=0; h<r.nHoursPerDay; h++)
02816                                    if(teachersMatrix[i][d][h]>0)
02817                                           n_hours_daily++;
02818 
02819                             if(n_hours_daily>this->maxHoursDaily){
02820                                    nbroken++;
02821 
02822                                    if(conflictsString!=NULL){
02823                                           QString s=(tr(
02824                                            "Time constraint teachers max %1 hours daily broken for teacher %2, on day %3, length=%4.")
02825                                            .arg(CustomFETString::number(this->maxHoursDaily))
02826                                            .arg(r.internalTeachersList[i]->name)
02827                                            .arg(r.daysOfTheWeek[d])
02828                                            .arg(n_hours_daily)
02829                                            )
02830                                            +
02831                                            " "
02832                                            +
02833                                            (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
02834                                           
02835                                           dl.append(s);
02836                                           cl.append(weightPercentage/100);
02837                                    
02838                                           *conflictsString+= s+"\n";
02839                                    }
02840                             }
02841                      }
02842               }
02843        }
02844 
02845        if(weightPercentage==100)
02846               assert(nbroken==0);
02847        return weightPercentage/100 * nbroken;
02848 }
02849 
02850 bool ConstraintTeachersMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
02851 {
02852        Q_UNUSED(r);
02853        Q_UNUSED(a);
02854 
02855        return false;
02856 }
02857 
02858 bool ConstraintTeachersMaxHoursDaily::isRelatedToTeacher(Teacher* t)
02859 {
02860        Q_UNUSED(t);
02861 
02862        return true;
02863 }
02864 
02865 bool ConstraintTeachersMaxHoursDaily::isRelatedToSubject(Subject* s)
02866 {
02867        Q_UNUSED(s);
02868 
02869        return false;
02870 }
02871 
02872 bool ConstraintTeachersMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
02873 {
02874        Q_UNUSED(s);
02875 
02876        return false;
02877 }
02878 
02879 bool ConstraintTeachersMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
02880 {
02881        Q_UNUSED(r);
02882        Q_UNUSED(s);
02883 
02884        return false;
02885 }
02886 
02887 bool ConstraintTeachersMaxHoursDaily::hasWrongDayOrHour(Rules& r)
02888 {
02889        if(maxHoursDaily>r.nHoursPerDay)
02890               return true;
02891               
02892        return false;
02893 }
02894 
02895 bool ConstraintTeachersMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
02896 {
02897        assert(hasWrongDayOrHour(r));
02898        
02899        return true;
02900 }
02901 
02902 bool ConstraintTeachersMaxHoursDaily::repairWrongDayOrHour(Rules& r)
02903 {
02904        assert(hasWrongDayOrHour(r));
02905        
02906        if(maxHoursDaily>r.nHoursPerDay)
02907               maxHoursDaily=r.nHoursPerDay;
02908 
02909        return true;
02910 }
02911 
02914 
02915 ConstraintTeacherMaxHoursDaily::ConstraintTeacherMaxHoursDaily()
02916        : TimeConstraint()
02917 {
02918        this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY;
02919 }
02920 
02921 ConstraintTeacherMaxHoursDaily::ConstraintTeacherMaxHoursDaily(double wp, int maxhours, const QString& teacher)
02922  : TimeConstraint(wp)
02923  {
02924        assert(maxhours>0);
02925        this->maxHoursDaily=maxhours;
02926        this->teacherName=teacher;
02927 
02928        this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY;
02929 }
02930 
02931 bool ConstraintTeacherMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
02932 {
02933        Q_UNUSED(parent);
02934 
02935        this->teacher_ID=r.searchTeacher(this->teacherName);
02936        assert(this->teacher_ID>=0);
02937        return true;
02938 }
02939 
02940 bool ConstraintTeacherMaxHoursDaily::hasInactiveActivities(Rules& r)
02941 {
02942        Q_UNUSED(r);
02943        return false;
02944 }
02945 
02946 QString ConstraintTeacherMaxHoursDaily::getXmlDescription(Rules& r){
02947        Q_UNUSED(r);
02948 
02949        QString s="<ConstraintTeacherMaxHoursDaily>\n";
02950        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
02951        s+="   <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
02952        s+="   <Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
02953        s+="   <Active>"+trueFalse(active)+"</Active>\n";
02954        s+="   <Comments>"+protect(comments)+"</Comments>\n";
02955        s+="</ConstraintTeacherMaxHoursDaily>\n";
02956        return s;
02957 }
02958 
02959 QString ConstraintTeacherMaxHoursDaily::getDescription(Rules& r){
02960        Q_UNUSED(r);
02961 
02962        QString begin=QString("");
02963        if(!active)
02964               begin="X - ";
02965               
02966        QString end=QString("");
02967        if(!comments.isEmpty())
02968               end=", "+tr("C: %1", "Comments").arg(comments);
02969               
02970        QString s;
02971        s+=tr("Teacher max hours daily");s+=", ";
02972        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
02973        s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
02974        s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
02975 
02976        return begin+s+end;
02977 }
02978 
02979 QString ConstraintTeacherMaxHoursDaily::getDetailedDescription(Rules& r){
02980        Q_UNUSED(r);
02981 
02982        QString s=tr("Time constraint");s+="\n";
02983        s+=tr("A teacher must respect the maximum number of hours daily");s+="\n";
02984        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
02985        s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
02986        s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
02987 
02988        if(!active){
02989               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
02990               s+="\n";
02991        }
02992        if(!comments.isEmpty()){
02993               s+=tr("Comments=%1").arg(comments);
02994               s+="\n";
02995        }
02996 
02997        return s;
02998 }
02999 
03000 double ConstraintTeacherMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
03001 {
03002        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
03003        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
03004               c.teachersMatrixReady=true;
03005               c.subgroupsMatrixReady=true;
03006               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
03007               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
03008 
03009               c.changedForMatrixCalculation=false;
03010        }
03011 
03012        int nbroken;
03013 
03014        //without logging
03015        if(conflictsString==NULL){
03016               nbroken=0;
03017               int i=this->teacher_ID;
03018               for(int d=0; d<r.nDaysPerWeek; d++){
03019                      int n_hours_daily=0;
03020                      for(int h=0; h<r.nHoursPerDay; h++)
03021                             if(teachersMatrix[i][d][h]>0)
03022                                    n_hours_daily++;
03023 
03024                      if(n_hours_daily>this->maxHoursDaily){
03025                             nbroken++;
03026                      }
03027               }
03028        }
03029        //with logging
03030        else{
03031               nbroken=0;
03032               int i=this->teacher_ID;
03033               for(int d=0; d<r.nDaysPerWeek; d++){
03034                      int n_hours_daily=0;
03035                      for(int h=0; h<r.nHoursPerDay; h++)
03036                             if(teachersMatrix[i][d][h]>0)
03037                                    n_hours_daily++;
03038 
03039                      if(n_hours_daily>this->maxHoursDaily){
03040                             nbroken++;
03041 
03042                             if(conflictsString!=NULL){
03043                                    QString s=(tr(
03044                                     "Time constraint teacher max %1 hours daily broken for teacher %2, on day %3, length=%4.")
03045                                     .arg(CustomFETString::number(this->maxHoursDaily))
03046                                     .arg(r.internalTeachersList[i]->name)
03047                                     .arg(r.daysOfTheWeek[d])
03048                                     .arg(n_hours_daily)
03049                                     )
03050                                     +" "
03051                                     +
03052                                     (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
03053                                           
03054                                    dl.append(s);
03055                                    cl.append(weightPercentage/100);
03056                             
03057                                    *conflictsString+= s+"\n";
03058                             }
03059                      }
03060               }
03061        }
03062 
03063        if(weightPercentage==100)
03064               assert(nbroken==0);
03065        return weightPercentage/100 * nbroken;
03066 }
03067 
03068 bool ConstraintTeacherMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
03069 {
03070        Q_UNUSED(r);
03071        Q_UNUSED(a);
03072 
03073        return false;
03074 }
03075 
03076 bool ConstraintTeacherMaxHoursDaily::isRelatedToTeacher(Teacher* t)
03077 {
03078        if(this->teacherName==t->name)
03079               return true;
03080        return false;
03081 }
03082 
03083 bool ConstraintTeacherMaxHoursDaily::isRelatedToSubject(Subject* s)
03084 {
03085        Q_UNUSED(s);
03086 
03087        return false;
03088 }
03089 
03090 bool ConstraintTeacherMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
03091 {
03092        Q_UNUSED(s);
03093 
03094        return false;
03095 }
03096 
03097 bool ConstraintTeacherMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
03098 {
03099        Q_UNUSED(r);
03100        Q_UNUSED(s);
03101 
03102        return false;
03103 }
03104 
03105 bool ConstraintTeacherMaxHoursDaily::hasWrongDayOrHour(Rules& r)
03106 {
03107        if(maxHoursDaily>r.nHoursPerDay)
03108               return true;
03109               
03110        return false;
03111 }
03112 
03113 bool ConstraintTeacherMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
03114 {
03115        assert(hasWrongDayOrHour(r));
03116        
03117        return true;
03118 }
03119 
03120 bool ConstraintTeacherMaxHoursDaily::repairWrongDayOrHour(Rules& r)
03121 {
03122        assert(hasWrongDayOrHour(r));
03123        
03124        if(maxHoursDaily>r.nHoursPerDay)
03125               maxHoursDaily=r.nHoursPerDay;
03126 
03127        return true;
03128 }
03129 
03132 
03133 ConstraintTeachersMaxHoursContinuously::ConstraintTeachersMaxHoursContinuously()
03134        : TimeConstraint()
03135 {
03136        this->type=CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY;
03137 }
03138 
03139 ConstraintTeachersMaxHoursContinuously::ConstraintTeachersMaxHoursContinuously(double wp, int maxhours)
03140  : TimeConstraint(wp)
03141  {
03142        assert(maxhours>0);
03143        this->maxHoursContinuously=maxhours;
03144 
03145        this->type=CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY;
03146 }
03147 
03148 bool ConstraintTeachersMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
03149 {
03150        Q_UNUSED(parent);
03151        Q_UNUSED(r);
03152 
03153        return true;
03154 }
03155 
03156 bool ConstraintTeachersMaxHoursContinuously::hasInactiveActivities(Rules& r)
03157 {
03158        Q_UNUSED(r);
03159        return false;
03160 }
03161 
03162 QString ConstraintTeachersMaxHoursContinuously::getXmlDescription(Rules& r){
03163        Q_UNUSED(r);
03164 
03165        QString s="<ConstraintTeachersMaxHoursContinuously>\n";
03166        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
03167        s+="   <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
03168        s+="   <Active>"+trueFalse(active)+"</Active>\n";
03169        s+="   <Comments>"+protect(comments)+"</Comments>\n";
03170        s+="</ConstraintTeachersMaxHoursContinuously>\n";
03171        return s;
03172 }
03173 
03174 QString ConstraintTeachersMaxHoursContinuously::getDescription(Rules& r){
03175        Q_UNUSED(r);
03176 
03177        QString begin=QString("");
03178        if(!active)
03179               begin="X - ";
03180               
03181        QString end=QString("");
03182        if(!comments.isEmpty())
03183               end=", "+tr("C: %1", "Comments").arg(comments);
03184               
03185        QString s;
03186        s+=tr("Teachers max hours continuously");s+=", ";
03187        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
03188        s+=tr("MH:%1", "Maximum hours (continuously)").arg(this->maxHoursContinuously);
03189 
03190        return begin+s+end;
03191 }
03192 
03193 QString ConstraintTeachersMaxHoursContinuously::getDetailedDescription(Rules& r){
03194        Q_UNUSED(r);
03195 
03196        QString s=tr("Time constraint");s+="\n";
03197        s+=tr("All teachers must respect the maximum number of hours continuously");s+="\n";
03198        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
03199        s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
03200 
03201        if(!active){
03202               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
03203               s+="\n";
03204        }
03205        if(!comments.isEmpty()){
03206               s+=tr("Comments=%1").arg(comments);
03207               s+="\n";
03208        }
03209 
03210        return s;
03211 }
03212 
03213 double ConstraintTeachersMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
03214 {
03215        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
03216        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
03217               c.teachersMatrixReady=true;
03218               c.subgroupsMatrixReady=true;
03219               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
03220               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
03221 
03222               c.changedForMatrixCalculation=false;
03223        }
03224 
03225        int nbroken;
03226 
03227        nbroken=0;
03228        for(int i=0; i<r.nInternalTeachers; i++){
03229               for(int d=0; d<r.nDaysPerWeek; d++){
03230                      int nc=0;
03231                      for(int h=0; h<r.nHoursPerDay; h++){
03232                             if(teachersMatrix[i][d][h]>0)
03233                                    nc++;
03234                             else{
03235                                    if(nc>this->maxHoursContinuously){
03236                                           nbroken++;
03237 
03238                                           if(conflictsString!=NULL){
03239                                                  QString s=(tr(
03240                                                   "Time constraint teachers max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
03241                                                   .arg(CustomFETString::number(this->maxHoursContinuously))
03242                                                   .arg(r.internalTeachersList[i]->name)
03243                                                   .arg(r.daysOfTheWeek[d])
03244                                                   .arg(nc)
03245                                                   )
03246                                                   +
03247                                                   " "
03248                                                   +
03249                                                   (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
03250                                                  
03251                                                  dl.append(s);
03252                                                  cl.append(weightPercentage/100);
03253                             
03254                                                  *conflictsString+= s+"\n";
03255                                           }
03256                                    }
03257                             
03258                                    nc=0;
03259                             }
03260                      }
03261 
03262                      if(nc>this->maxHoursContinuously){
03263                             nbroken++;
03264 
03265                             if(conflictsString!=NULL){
03266                                    QString s=(tr(
03267                                     "Time constraint teachers max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
03268                                     .arg(CustomFETString::number(this->maxHoursContinuously))
03269                                     .arg(r.internalTeachersList[i]->name)
03270                                     .arg(r.daysOfTheWeek[d])
03271                                     .arg(nc)
03272                                     )
03273                                     +
03274                                     " "
03275                                     +
03276                                     (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
03277                                                  
03278                                    dl.append(s);
03279                                    cl.append(weightPercentage/100);
03280                             
03281                                    *conflictsString+= s+"\n";
03282                             }
03283                      }
03284               }
03285        }
03286 
03287        if(weightPercentage==100)   
03288               assert(nbroken==0);
03289        return weightPercentage/100 * nbroken;
03290 }
03291 
03292 bool ConstraintTeachersMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
03293 {
03294        Q_UNUSED(r);
03295        Q_UNUSED(a);
03296 
03297        return false;
03298 }
03299 
03300 bool ConstraintTeachersMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
03301 {
03302        Q_UNUSED(t);
03303 
03304        return true;
03305 }
03306 
03307 bool ConstraintTeachersMaxHoursContinuously::isRelatedToSubject(Subject* s)
03308 {
03309        Q_UNUSED(s);
03310 
03311        return false;
03312 }
03313 
03314 bool ConstraintTeachersMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
03315 {
03316        Q_UNUSED(s);
03317 
03318        return false;
03319 }
03320 
03321 bool ConstraintTeachersMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
03322 {
03323        Q_UNUSED(r);
03324        Q_UNUSED(s);
03325 
03326        return false;
03327 }
03328 
03329 bool ConstraintTeachersMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
03330 {
03331        if(maxHoursContinuously>r.nHoursPerDay)
03332               return true;
03333        
03334        return false;
03335 }
03336 
03337 bool ConstraintTeachersMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
03338 {
03339        assert(hasWrongDayOrHour(r));
03340        
03341        return true;
03342 }
03343 
03344 bool ConstraintTeachersMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
03345 {
03346        assert(hasWrongDayOrHour(r));
03347        
03348        if(maxHoursContinuously>r.nHoursPerDay)
03349               maxHoursContinuously=r.nHoursPerDay;
03350 
03351        return true;
03352 }
03353 
03356 
03357 ConstraintTeacherMaxHoursContinuously::ConstraintTeacherMaxHoursContinuously()
03358        : TimeConstraint()
03359 {
03360        this->type=CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY;
03361 }
03362 
03363 ConstraintTeacherMaxHoursContinuously::ConstraintTeacherMaxHoursContinuously(double wp, int maxhours, const QString& teacher)
03364  : TimeConstraint(wp)
03365  {
03366        assert(maxhours>0);
03367        this->maxHoursContinuously=maxhours;
03368        this->teacherName=teacher;
03369 
03370        this->type=CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY;
03371 }
03372 
03373 bool ConstraintTeacherMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
03374 {
03375        Q_UNUSED(parent);
03376 
03377        this->teacher_ID=r.searchTeacher(this->teacherName);
03378        assert(this->teacher_ID>=0);
03379        return true;
03380 }
03381 
03382 bool ConstraintTeacherMaxHoursContinuously::hasInactiveActivities(Rules& r)
03383 {
03384        Q_UNUSED(r);
03385        return false;
03386 }
03387 
03388 QString ConstraintTeacherMaxHoursContinuously::getXmlDescription(Rules& r){
03389        Q_UNUSED(r);
03390 
03391        QString s="<ConstraintTeacherMaxHoursContinuously>\n";
03392        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
03393        s+="   <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
03394        s+="   <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
03395        s+="   <Active>"+trueFalse(active)+"</Active>\n";
03396        s+="   <Comments>"+protect(comments)+"</Comments>\n";
03397        s+="</ConstraintTeacherMaxHoursContinuously>\n";
03398        return s;
03399 }
03400 
03401 QString ConstraintTeacherMaxHoursContinuously::getDescription(Rules& r){
03402        Q_UNUSED(r);
03403 
03404        QString begin=QString("");
03405        if(!active)
03406               begin="X - ";
03407               
03408        QString end=QString("");
03409        if(!comments.isEmpty())
03410               end=", "+tr("C: %1", "Comments").arg(comments);
03411               
03412        QString s;
03413        s+=tr("Teacher max hours continuously");s+=", ";
03414        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
03415        s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
03416        s+=tr("MH:%1", "Maximum hours continuously").arg(this->maxHoursContinuously);
03417 
03418        return begin+s+end;
03419 }
03420 
03421 QString ConstraintTeacherMaxHoursContinuously::getDetailedDescription(Rules& r){
03422        Q_UNUSED(r);
03423 
03424        QString s=tr("Time constraint");s+="\n";
03425        s+=tr("A teacher must respect the maximum number of hours continuously");s+="\n";
03426        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
03427        s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
03428        s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
03429 
03430        if(!active){
03431               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
03432               s+="\n";
03433        }
03434        if(!comments.isEmpty()){
03435               s+=tr("Comments=%1").arg(comments);
03436               s+="\n";
03437        }
03438 
03439        return s;
03440 }
03441 
03442 double ConstraintTeacherMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
03443 {
03444        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
03445        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
03446               c.teachersMatrixReady=true;
03447               c.subgroupsMatrixReady=true;
03448               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
03449               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
03450 
03451               c.changedForMatrixCalculation=false;
03452        }
03453 
03454        int nbroken;
03455 
03456        nbroken=0;
03457        int i=this->teacher_ID;
03458        for(int d=0; d<r.nDaysPerWeek; d++){
03459               int nc=0;
03460               for(int h=0; h<r.nHoursPerDay; h++){
03461                      if(teachersMatrix[i][d][h]>0)
03462                             nc++;
03463                      else{
03464                             if(nc>this->maxHoursContinuously){
03465                                    nbroken++;
03466 
03467                                    if(conflictsString!=NULL){
03468                                           QString s=(tr(
03469                                            "Time constraint teacher max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
03470                                            .arg(CustomFETString::number(this->maxHoursContinuously))
03471                                            .arg(r.internalTeachersList[i]->name)
03472                                            .arg(r.daysOfTheWeek[d])
03473                                            .arg(nc)
03474                                            )
03475                                            +
03476                                            " "
03477                                            +
03478                                            (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
03479                                           
03480                                           dl.append(s);
03481                                           cl.append(weightPercentage/100);
03482                      
03483                                           *conflictsString+= s+"\n";
03484                                    }
03485                             }
03486                      
03487                             nc=0;
03488                      }
03489               }
03490 
03491               if(nc>this->maxHoursContinuously){
03492                      nbroken++;
03493 
03494                      if(conflictsString!=NULL){
03495                             QString s=(tr(
03496                              "Time constraint teacher max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
03497                              .arg(CustomFETString::number(this->maxHoursContinuously))
03498                              .arg(r.internalTeachersList[i]->name)
03499                              .arg(r.daysOfTheWeek[d])
03500                              .arg(nc)
03501                              )
03502                              +
03503                              " "
03504                              +
03505                              (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
03506                                           
03507                             dl.append(s);
03508                             cl.append(weightPercentage/100);
03509                      
03510                             *conflictsString+= s+"\n";
03511                      }
03512               }
03513        }
03514 
03515        if(weightPercentage==100)
03516               assert(nbroken==0);
03517        return weightPercentage/100 * nbroken;
03518 }
03519 
03520 bool ConstraintTeacherMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
03521 {
03522        Q_UNUSED(r);
03523        Q_UNUSED(a);
03524 
03525        return false;
03526 }
03527 
03528 bool ConstraintTeacherMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
03529 {
03530        if(this->teacherName==t->name)
03531               return true;
03532        return false;
03533 }
03534 
03535 bool ConstraintTeacherMaxHoursContinuously::isRelatedToSubject(Subject* s)
03536 {
03537        Q_UNUSED(s);
03538 
03539        return false;
03540 }
03541 
03542 bool ConstraintTeacherMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
03543 {
03544        Q_UNUSED(s);
03545 
03546        return false;
03547 }
03548 
03549 bool ConstraintTeacherMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
03550 {
03551        Q_UNUSED(r);
03552        Q_UNUSED(s);
03553 
03554        return false;
03555 }
03556 
03557 bool ConstraintTeacherMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
03558 {
03559        if(maxHoursContinuously>r.nHoursPerDay)
03560               return true;
03561        
03562        return false;
03563 }
03564 
03565 bool ConstraintTeacherMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
03566 {
03567        assert(hasWrongDayOrHour(r));
03568        
03569        return true;
03570 }
03571 
03572 bool ConstraintTeacherMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
03573 {
03574        assert(hasWrongDayOrHour(r));
03575        
03576        if(maxHoursContinuously>r.nHoursPerDay)
03577               maxHoursContinuously=r.nHoursPerDay;
03578 
03579        return true;
03580 }
03581 
03584 
03585 ConstraintTeachersActivityTagMaxHoursContinuously::ConstraintTeachersActivityTagMaxHoursContinuously()
03586        : TimeConstraint()
03587 {
03588        this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
03589 }
03590 
03591 ConstraintTeachersActivityTagMaxHoursContinuously::ConstraintTeachersActivityTagMaxHoursContinuously(double wp, int maxhours, const QString& activityTag)
03592  : TimeConstraint(wp)
03593  {
03594        assert(maxhours>0);
03595        this->maxHoursContinuously=maxhours;
03596        this->activityTagName=activityTag;
03597 
03598        this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
03599 }
03600 
03601 bool ConstraintTeachersActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
03602 {
03603        Q_UNUSED(parent);
03604 
03605        this->activityTagIndex=r.searchActivityTag(this->activityTagName);
03606        assert(this->activityTagIndex>=0);
03607        
03608        this->canonicalTeachersList.clear();
03609        for(int i=0; i<r.nInternalTeachers; i++){
03610               bool found=false;
03611        
03612               Teacher* tch=r.internalTeachersList[i];
03613               foreach(int actIndex, tch->activitiesForTeacher){
03614                      if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
03615                             found=true;
03616                             break;
03617                      }
03618               }
03619               
03620               if(found)
03621                      this->canonicalTeachersList.append(i);
03622        }
03623 
03624        return true;
03625 }
03626 
03627 bool ConstraintTeachersActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
03628 {
03629        Q_UNUSED(r);
03630        return false;
03631 }
03632 
03633 QString ConstraintTeachersActivityTagMaxHoursContinuously::getXmlDescription(Rules& r){
03634        Q_UNUSED(r);
03635 
03636        QString s="<ConstraintTeachersActivityTagMaxHoursContinuously>\n";
03637        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
03638        s+="   <Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
03639        s+="   <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
03640        s+="   <Active>"+trueFalse(active)+"</Active>\n";
03641        s+="   <Comments>"+protect(comments)+"</Comments>\n";
03642        s+="</ConstraintTeachersActivityTagMaxHoursContinuously>\n";
03643        return s;
03644 }
03645 
03646 QString ConstraintTeachersActivityTagMaxHoursContinuously::getDescription(Rules& r){
03647        Q_UNUSED(r);
03648 
03649        QString begin=QString("");
03650        if(!active)
03651               begin="X - ";
03652               
03653        QString end=QString("");
03654        if(!comments.isEmpty())
03655               end=", "+tr("C: %1", "Comments").arg(comments);
03656               
03657        QString s;
03658        s+=tr("Teachers for activity tag %1 have max %2 hours continuously").arg(this->activityTagName).arg(this->maxHoursContinuously);s+=", ";
03659        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
03660 
03661        return begin+s+end;
03662 }
03663 
03664 QString ConstraintTeachersActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r){
03665        Q_UNUSED(r);
03666 
03667        QString s=tr("Time constraint");s+="\n";
03668        s+=tr("All teachers, for an activity tag, must respect the maximum number of hours continuously");s+="\n";
03669        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
03670        s+=tr("Activity tag=%1").arg(this->activityTagName); s+="\n";
03671        s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously); s+="\n";
03672 
03673        if(!active){
03674               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
03675               s+="\n";
03676        }
03677        if(!comments.isEmpty()){
03678               s+=tr("Comments=%1").arg(comments);
03679               s+="\n";
03680        }
03681 
03682        return s;
03683 }
03684 
03685 double ConstraintTeachersActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
03686 {
03687        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
03688        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
03689               c.teachersMatrixReady=true;
03690               c.subgroupsMatrixReady=true;
03691               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
03692               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
03693 
03694               c.changedForMatrixCalculation=false;
03695        }
03696 
03697        int nbroken;
03698 
03699        nbroken=0;
03700        foreach(int i, this->canonicalTeachersList){
03701               Teacher* tch=r.internalTeachersList[i];
03702               int crtTeacherTimetableActivityTag[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
03703               for(int d=0; d<r.nDaysPerWeek; d++)
03704                      for(int h=0; h<r.nHoursPerDay; h++)
03705                             crtTeacherTimetableActivityTag[d][h]=-1;
03706               foreach(int ai, tch->activitiesForTeacher)if(c.times[ai]!=UNALLOCATED_TIME){
03707                      int d=c.times[ai]%r.nDaysPerWeek;
03708                      int h=c.times[ai]/r.nDaysPerWeek;
03709                      for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
03710                             assert(h+dur<r.nHoursPerDay);
03711                             assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
03712                             if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
03713                                    crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
03714                      }
03715               }
03716        
03717               for(int d=0; d<r.nDaysPerWeek; d++){
03718                      int nc=0;
03719                      for(int h=0; h<r.nHoursPerDay; h++){
03720                             bool inc=false;
03721                             if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
03722                                    inc=true;
03723                             
03724                             if(inc){
03725                                    nc++;
03726                             }
03727                             else{
03728                                    if(nc>this->maxHoursContinuously){
03729                                           nbroken++;
03730 
03731                                           if(conflictsString!=NULL){
03732                                                  QString s=(tr(
03733                                                   "Time constraint teachers activity tag %1 max %2 hours continuously broken for teacher %3, on day %4, length=%5.")
03734                                                   .arg(this->activityTagName)
03735                                                   .arg(CustomFETString::number(this->maxHoursContinuously))
03736                                                   .arg(r.internalTeachersList[i]->name)
03737                                                   .arg(r.daysOfTheWeek[d])
03738                                                   .arg(nc)
03739                                                   )
03740                                                   +
03741                                                   " "
03742                                                   +
03743                                                   (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
03744                                                  
03745                                                  dl.append(s);
03746                                                  cl.append(weightPercentage/100);
03747                             
03748                                                  *conflictsString+= s+"\n";
03749                                           }
03750                                    }
03751                             
03752                                    nc=0;
03753                             }
03754                      }
03755 
03756                      if(nc>this->maxHoursContinuously){
03757                             nbroken++;
03758 
03759                             if(conflictsString!=NULL){
03760                                    QString s=(tr(
03761                                     "Time constraint teachers activity tag %1 max %2 hours continuously broken for teacher %3, on day %4, length=%5.")
03762                                     .arg(this->activityTagName)
03763                                     .arg(CustomFETString::number(this->maxHoursContinuously))
03764                                     .arg(r.internalTeachersList[i]->name)
03765                                     .arg(r.daysOfTheWeek[d])
03766                                     .arg(nc)
03767                                     )
03768                                     +
03769                                     " "
03770                                     +
03771                                     (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
03772                                                  
03773                                    dl.append(s);
03774                                    cl.append(weightPercentage/100);
03775                             
03776                                    *conflictsString+= s+"\n";
03777                             }
03778                      }
03779               }
03780        }
03781 
03782        if(weightPercentage==100)   
03783               assert(nbroken==0);
03784        return weightPercentage/100 * nbroken;
03785 }
03786 
03787 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
03788 {
03789        Q_UNUSED(r);
03790        Q_UNUSED(a);
03791 
03792        return false;
03793 }
03794 
03795 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
03796 {
03797        Q_UNUSED(t);
03798 
03799        return true;
03800 }
03801 
03802 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
03803 {
03804        Q_UNUSED(s);
03805 
03806        return false;
03807 }
03808 
03809 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
03810 {
03811        return s->name==this->activityTagName;
03812 }
03813 
03814 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
03815 {
03816        Q_UNUSED(r);
03817        Q_UNUSED(s);
03818 
03819        return false;
03820 }
03821 
03822 bool ConstraintTeachersActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
03823 {
03824        if(maxHoursContinuously>r.nHoursPerDay)
03825               return true;
03826        
03827        return false;
03828 }
03829 
03830 bool ConstraintTeachersActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
03831 {
03832        assert(hasWrongDayOrHour(r));
03833        
03834        return true;
03835 }
03836 
03837 bool ConstraintTeachersActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
03838 {
03839        assert(hasWrongDayOrHour(r));
03840        
03841        if(maxHoursContinuously>r.nHoursPerDay)
03842               maxHoursContinuously=r.nHoursPerDay;
03843 
03844        return true;
03845 }
03846 
03849 ConstraintTeacherActivityTagMaxHoursContinuously::ConstraintTeacherActivityTagMaxHoursContinuously()
03850        : TimeConstraint()
03851 {
03852        this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
03853 }
03854 
03855 ConstraintTeacherActivityTagMaxHoursContinuously::ConstraintTeacherActivityTagMaxHoursContinuously(double wp, int maxhours, const QString& teacher, const QString& activityTag)
03856  : TimeConstraint(wp)
03857  {
03858        assert(maxhours>0);
03859        this->maxHoursContinuously=maxhours;
03860        this->teacherName=teacher;
03861        this->activityTagName=activityTag;
03862 
03863        this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
03864 }
03865 
03866 bool ConstraintTeacherActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
03867 {
03868        Q_UNUSED(parent);
03869 
03870        this->teacher_ID=r.searchTeacher(this->teacherName);
03871        assert(this->teacher_ID>=0);
03872 
03873        this->activityTagIndex=r.searchActivityTag(this->activityTagName);
03874        assert(this->activityTagIndex>=0);
03875 
03876        this->canonicalTeachersList.clear();
03877        int i=this->teacher_ID;
03878        bool found=false;
03879        
03880        Teacher* tch=r.internalTeachersList[i];
03881        foreach(int actIndex, tch->activitiesForTeacher){
03882               if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
03883                      found=true;
03884                      break;
03885               }
03886        }
03887               
03888        if(found)
03889               this->canonicalTeachersList.append(i);
03890 
03891        return true;
03892 }
03893 
03894 bool ConstraintTeacherActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
03895 {
03896        Q_UNUSED(r);
03897        return false;
03898 }
03899 
03900 QString ConstraintTeacherActivityTagMaxHoursContinuously::getXmlDescription(Rules& r){
03901        Q_UNUSED(r);
03902 
03903        QString s="<ConstraintTeacherActivityTagMaxHoursContinuously>\n";
03904        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
03905        s+="   <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
03906        s+="   <Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
03907        s+="   <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
03908        s+="   <Active>"+trueFalse(active)+"</Active>\n";
03909        s+="   <Comments>"+protect(comments)+"</Comments>\n";
03910        s+="</ConstraintTeacherActivityTagMaxHoursContinuously>\n";
03911        return s;
03912 }
03913 
03914 QString ConstraintTeacherActivityTagMaxHoursContinuously::getDescription(Rules& r){
03915        Q_UNUSED(r);
03916 
03917        QString begin=QString("");
03918        if(!active)
03919               begin="X - ";
03920               
03921        QString end=QString("");
03922        if(!comments.isEmpty())
03923               end=", "+tr("C: %1", "Comments").arg(comments);
03924               
03925        QString s;
03926        s+=tr("Teacher %1 for activity tag %2 has max %3 hours continuously").arg(this->teacherName).arg(this->activityTagName).arg(this->maxHoursContinuously);s+=", ";
03927        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
03928 
03929        return begin+s+end;
03930 }
03931 
03932 QString ConstraintTeacherActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r){
03933        Q_UNUSED(r);
03934 
03935        QString s=tr("Time constraint");s+="\n";
03936        s+=tr("A teacher for an activity tag must respect the maximum number of hours continuously");s+="\n";
03937        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
03938        s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
03939        s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
03940        s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously); s+="\n";
03941 
03942        if(!active){
03943               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
03944               s+="\n";
03945        }
03946        if(!comments.isEmpty()){
03947               s+=tr("Comments=%1").arg(comments);
03948               s+="\n";
03949        }
03950 
03951        return s;
03952 }
03953 
03954 double ConstraintTeacherActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
03955 {
03956        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
03957        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
03958               c.teachersMatrixReady=true;
03959               c.subgroupsMatrixReady=true;
03960               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
03961               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
03962 
03963               c.changedForMatrixCalculation=false;
03964        }
03965 
03966        int nbroken;
03967 
03968        nbroken=0;
03969        foreach(int i, this->canonicalTeachersList){
03970               Teacher* tch=r.internalTeachersList[i];
03971               int crtTeacherTimetableActivityTag[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
03972               for(int d=0; d<r.nDaysPerWeek; d++)
03973                      for(int h=0; h<r.nHoursPerDay; h++)
03974                             crtTeacherTimetableActivityTag[d][h]=-1;
03975               foreach(int ai, tch->activitiesForTeacher)if(c.times[ai]!=UNALLOCATED_TIME){
03976                      int d=c.times[ai]%r.nDaysPerWeek;
03977                      int h=c.times[ai]/r.nDaysPerWeek;
03978                      for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
03979                             assert(h+dur<r.nHoursPerDay);
03980                             assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
03981                             if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
03982                                    crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
03983                      }
03984               }
03985 
03986               for(int d=0; d<r.nDaysPerWeek; d++){
03987                      int nc=0;
03988                      for(int h=0; h<r.nHoursPerDay; h++){
03989                             bool inc=false;
03990 
03991                             if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
03992                                    inc=true;
03993                             
03994                             if(inc)
03995                                    nc++;
03996                             else{
03997                                    if(nc>this->maxHoursContinuously){
03998                                           nbroken++;
03999 
04000                                           if(conflictsString!=NULL){
04001                                                  QString s=(tr(
04002                                                   "Time constraint teacher activity tag max %1 hours continuously broken for teacher %2, activity tag %3, on day %4, length=%5.")
04003                                                   .arg(CustomFETString::number(this->maxHoursContinuously))
04004                                                   .arg(r.internalTeachersList[i]->name)
04005                                                   .arg(this->activityTagName)
04006                                                   .arg(r.daysOfTheWeek[d])
04007                                                   .arg(nc)
04008                                                   )
04009                                                   +
04010                                                   " "
04011                                                   +
04012                                                   (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
04013                                                  
04014                                                  dl.append(s);
04015                                                  cl.append(weightPercentage/100);
04016                             
04017                                                  *conflictsString+= s+"\n";
04018                                           }
04019                                    }
04020                             
04021                                    nc=0;
04022                             }
04023                      }
04024 
04025                      if(nc>this->maxHoursContinuously){
04026                             nbroken++;
04027 
04028                             if(conflictsString!=NULL){
04029                                    QString s=(tr(
04030                                     "Time constraint teacher activity tag max %1 hours continuously broken for teacher %2, activity tag %3, on day %4, length=%5.")
04031                                     .arg(CustomFETString::number(this->maxHoursContinuously))
04032                                     .arg(r.internalTeachersList[i]->name)
04033                                     .arg(this->activityTagName)
04034                                     .arg(r.daysOfTheWeek[d])
04035                                     .arg(nc)
04036                                     )
04037                                     +
04038                                     " "
04039                                     +
04040                                     (tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100)));
04041                                                  
04042                                    dl.append(s);
04043                                    cl.append(weightPercentage/100);
04044                             
04045                                    *conflictsString+= s+"\n";
04046                             }
04047                      }
04048               }
04049        }
04050 
04051        if(weightPercentage==100)   
04052               assert(nbroken==0);
04053        return weightPercentage/100 * nbroken;
04054 }
04055 
04056 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
04057 {
04058        Q_UNUSED(r);
04059        Q_UNUSED(a);
04060 
04061        return false;
04062 }
04063 
04064 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
04065 {
04066        if(this->teacherName==t->name)
04067               return true;
04068        return false;
04069 }
04070 
04071 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
04072 {
04073        Q_UNUSED(s);
04074 
04075        return false;
04076 }
04077 
04078 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
04079 {
04080        return this->activityTagName==s->name;
04081 }
04082 
04083 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
04084 {
04085        Q_UNUSED(r);
04086        Q_UNUSED(s);
04087 
04088        return false;
04089 }
04090 
04091 bool ConstraintTeacherActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
04092 {
04093        if(maxHoursContinuously>r.nHoursPerDay)
04094               return true;
04095        
04096        return false;
04097 }
04098 
04099 bool ConstraintTeacherActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
04100 {
04101        assert(hasWrongDayOrHour(r));
04102        
04103        return true;
04104 }
04105 
04106 bool ConstraintTeacherActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
04107 {
04108        assert(hasWrongDayOrHour(r));
04109        
04110        if(maxHoursContinuously>r.nHoursPerDay)
04111               maxHoursContinuously=r.nHoursPerDay;
04112 
04113        return true;
04114 }
04115 
04118 
04119 ConstraintTeacherMaxDaysPerWeek::ConstraintTeacherMaxDaysPerWeek()
04120        : TimeConstraint()
04121 {
04122        this->type=CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK;
04123 }
04124 
04125 ConstraintTeacherMaxDaysPerWeek::ConstraintTeacherMaxDaysPerWeek(double wp, int maxnd, QString tn)
04126         : TimeConstraint(wp)
04127 {
04128        this->teacherName = tn;
04129        this->maxDaysPerWeek=maxnd;
04130        this->type=CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK;
04131 }
04132 
04133 bool ConstraintTeacherMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
04134 {
04135        Q_UNUSED(parent);
04136 
04137        this->teacher_ID=r.searchTeacher(this->teacherName);
04138        assert(this->teacher_ID>=0);
04139        return true;
04140 }
04141 
04142 bool ConstraintTeacherMaxDaysPerWeek::hasInactiveActivities(Rules& r)
04143 {
04144        Q_UNUSED(r);
04145        return false;
04146 }
04147 
04148 QString ConstraintTeacherMaxDaysPerWeek::getXmlDescription(Rules& r)
04149 {
04150        Q_UNUSED(r);
04151 
04152        QString s="<ConstraintTeacherMaxDaysPerWeek>\n";
04153        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
04154        s+="   <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
04155        s+="   <Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
04156        s+="   <Active>"+trueFalse(active)+"</Active>\n";
04157        s+="   <Comments>"+protect(comments)+"</Comments>\n";
04158        s+="</ConstraintTeacherMaxDaysPerWeek>\n";
04159        return s;
04160 }
04161 
04162 QString ConstraintTeacherMaxDaysPerWeek::getDescription(Rules& r){
04163        Q_UNUSED(r);
04164 
04165        QString begin=QString("");
04166        if(!active)
04167               begin="X - ";
04168               
04169        QString end=QString("");
04170        if(!comments.isEmpty())
04171               end=", "+tr("C: %1", "Comments").arg(comments);
04172               
04173        QString s=tr("Teacher max days per week");s+=", ";
04174        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
04175        s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
04176        s+=tr("MD:%1", "Max days (per week)").arg(this->maxDaysPerWeek);
04177 
04178        return begin+s+end;
04179 }
04180 
04181 QString ConstraintTeacherMaxDaysPerWeek::getDetailedDescription(Rules& r){
04182        Q_UNUSED(r);
04183 
04184        QString s=tr("Time constraint");s+="\n";
04185        s+=tr("A teacher must respect the maximum number of days per week");s+="\n";
04186        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
04187        s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
04188        s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
04189 
04190        if(!active){
04191               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
04192               s+="\n";
04193        }
04194        if(!comments.isEmpty()){
04195               s+=tr("Comments=%1").arg(comments);
04196               s+="\n";
04197        }
04198 
04199        return s;
04200 }
04201 
04202 double ConstraintTeacherMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString *conflictsString)
04203 {
04204        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
04205        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
04206               c.teachersMatrixReady=true;
04207               c.subgroupsMatrixReady=true;
04208               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
04209               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
04210 
04211               c.changedForMatrixCalculation=false;
04212        }
04213 
04214        int nbroken;
04215 
04216        //without logging
04217        if(conflictsString==NULL){
04218               nbroken=0;
04219               //count sort
04220               int t=this->teacher_ID;
04221               int nd[MAX_HOURS_PER_DAY + 1];
04222               for(int h=0; h<=r.nHoursPerDay; h++)
04223                      nd[h]=0;
04224               for(int d=0; d<r.nDaysPerWeek; d++){
04225                      int nh=0;
04226                      for(int h=0; h<r.nHoursPerDay; h++)
04227                             nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
04228                      nd[nh]++;
04229               }
04230               //return the minimum occupied days which do not respect this constraint
04231               int i = r.nDaysPerWeek - this->maxDaysPerWeek;
04232               for(int k=0; k<=r.nHoursPerDay; k++){
04233                      if(nd[k]>0){
04234                             if(i>nd[k]){
04235                                    i-=nd[k];
04236                                    nbroken+=nd[k]*k;
04237                             }
04238                             else{
04239                                    nbroken+=i*k;
04240                                    break;
04241                             }
04242                      }
04243               }
04244        }
04245        //with logging
04246        else{
04247               nbroken=0;
04248               //count sort
04249               int t=this->teacher_ID;
04250               int nd[MAX_HOURS_PER_DAY + 1];
04251               for(int h=0; h<=r.nHoursPerDay; h++)
04252                      nd[h]=0;
04253               for(int d=0; d<r.nDaysPerWeek; d++){
04254                      int nh=0;
04255                      for(int h=0; h<r.nHoursPerDay; h++)
04256                             nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
04257                      nd[nh]++;
04258               }
04259               //return the minimum occupied days which do not respect this constraint
04260               int i = r.nDaysPerWeek - this->maxDaysPerWeek;
04261               for(int k=0; k<=r.nHoursPerDay; k++){
04262                      if(nd[k]>0){
04263                             if(i>nd[k]){
04264                                    i-=nd[k];
04265                                    nbroken+=nd[k]*k;
04266                             }
04267                             else{
04268                                    nbroken+=i*k;
04269                                    break;
04270                             }
04271                      }
04272               }
04273 
04274               if(nbroken>0){
04275                      QString s= tr("Time constraint teacher max days per week broken for teacher: %1.")
04276                       .arg(r.internalTeachersList[t]->name);
04277                      s += tr("This increases the conflicts total by %1")
04278                       .arg(CustomFETString::number(nbroken*weightPercentage/100));
04279                       
04280                      dl.append(s);
04281                      cl.append(nbroken*weightPercentage/100);
04282               
04283                      *conflictsString += s+"\n";
04284               }
04285        }
04286 
04287        if(weightPercentage==100)
04288               assert(nbroken==0);
04289        return weightPercentage/100 * nbroken;
04290 }
04291 
04292 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
04293 {
04294        Q_UNUSED(r);
04295        Q_UNUSED(a);
04296 
04297        return false;
04298 }
04299 
04300 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
04301 {
04302        if(this->teacherName==t->name)
04303               return true;
04304        return false;
04305 }
04306 
04307 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToSubject(Subject* s)
04308 {
04309        Q_UNUSED(s);
04310 
04311        return false;
04312 }
04313 
04314 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
04315 {
04316        Q_UNUSED(s);
04317 
04318        return false;
04319 }
04320 
04321 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
04322 {
04323        Q_UNUSED(r);
04324        Q_UNUSED(s);
04325 
04326        return false;
04327 }
04328 
04329 bool ConstraintTeacherMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
04330 {
04331        if(maxDaysPerWeek>r.nDaysPerWeek)
04332               return true;
04333        
04334        return false;
04335 }
04336 
04337 bool ConstraintTeacherMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
04338 {
04339        assert(hasWrongDayOrHour(r));
04340        
04341        return true;
04342 }
04343 
04344 bool ConstraintTeacherMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
04345 {
04346        assert(hasWrongDayOrHour(r));
04347        
04348        if(maxDaysPerWeek>r.nDaysPerWeek)
04349               maxDaysPerWeek=r.nDaysPerWeek;
04350 
04351        return true;
04352 }
04353 
04356 
04357 ConstraintTeachersMaxDaysPerWeek::ConstraintTeachersMaxDaysPerWeek()
04358        : TimeConstraint()
04359 {
04360        this->type=CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK;
04361 }
04362 
04363 ConstraintTeachersMaxDaysPerWeek::ConstraintTeachersMaxDaysPerWeek(double wp, int maxnd)
04364         : TimeConstraint(wp)
04365 {
04366        this->maxDaysPerWeek=maxnd;
04367        this->type=CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK;
04368 }
04369 
04370 bool ConstraintTeachersMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
04371 {
04372        Q_UNUSED(parent);
04373        Q_UNUSED(r);
04374 
04375        return true;
04376 }
04377 
04378 bool ConstraintTeachersMaxDaysPerWeek::hasInactiveActivities(Rules& r)
04379 {
04380        Q_UNUSED(r);
04381        return false;
04382 }
04383 
04384 QString ConstraintTeachersMaxDaysPerWeek::getXmlDescription(Rules& r)
04385 {
04386        Q_UNUSED(r);
04387 
04388        QString s="<ConstraintTeachersMaxDaysPerWeek>\n";
04389        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
04390        s+="   <Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
04391        s+="   <Active>"+trueFalse(active)+"</Active>\n";
04392        s+="   <Comments>"+protect(comments)+"</Comments>\n";
04393        s+="</ConstraintTeachersMaxDaysPerWeek>\n";
04394        return s;
04395 }
04396 
04397 QString ConstraintTeachersMaxDaysPerWeek::getDescription(Rules& r){
04398        Q_UNUSED(r);
04399 
04400        QString begin=QString("");
04401        if(!active)
04402               begin="X - ";
04403               
04404        QString end=QString("");
04405        if(!comments.isEmpty())
04406               end=", "+tr("C: %1", "Comments").arg(comments);
04407               
04408        QString s=tr("Teachers max days per week");s+=", ";
04409        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
04410        s+=tr("MD:%1", "Max days (per week)").arg(this->maxDaysPerWeek);
04411 
04412        return begin+s+end;
04413 }
04414 
04415 QString ConstraintTeachersMaxDaysPerWeek::getDetailedDescription(Rules& r){
04416        Q_UNUSED(r);
04417 
04418        QString s=tr("Time constraint");s+="\n";
04419        s+=tr("All teachers must respect the maximum number of days per week");s+="\n";
04420        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
04421        s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
04422 
04423        if(!active){
04424               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
04425               s+="\n";
04426        }
04427        if(!comments.isEmpty()){
04428               s+=tr("Comments=%1").arg(comments);
04429               s+="\n";
04430        }
04431 
04432        return s;
04433 }
04434 
04435 double ConstraintTeachersMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString *conflictsString)
04436 {
04437        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
04438        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
04439               c.teachersMatrixReady=true;
04440               c.subgroupsMatrixReady=true;
04441               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
04442               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
04443 
04444               c.changedForMatrixCalculation=false;
04445        }
04446 
04447        int nbroken;
04448 
04449        //without logging
04450        if(conflictsString==NULL){
04451               nbroken=0;
04452               //count sort
04453               
04454               for(int t=0; t<r.nInternalTeachers; t++){
04455                      int nd[MAX_HOURS_PER_DAY + 1];
04456                      for(int h=0; h<=r.nHoursPerDay; h++)
04457                             nd[h]=0;
04458                      for(int d=0; d<r.nDaysPerWeek; d++){
04459                             int nh=0;
04460                             for(int h=0; h<r.nHoursPerDay; h++)
04461                                    nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
04462                             nd[nh]++;
04463                      }
04464                      //return the minimum occupied days which do not respect this constraint
04465                      int i = r.nDaysPerWeek - this->maxDaysPerWeek;
04466                      for(int k=0; k<=r.nHoursPerDay; k++){
04467                             if(nd[k]>0){
04468                                    if(i>nd[k]){
04469                                           i-=nd[k];
04470                                           nbroken+=nd[k]*k;
04471                                    }
04472                                    else{
04473                                           nbroken+=i*k;
04474                                           break;
04475                                    }
04476                             }
04477                      }
04478               
04479               }
04480        }
04481        //with logging
04482        else{
04483               nbroken=0;
04484 
04485               for(int t=0; t<r.nInternalTeachers; t++){
04486                      int nbr=0;
04487 
04488                      //count sort
04489                      int nd[MAX_HOURS_PER_DAY + 1];
04490                      for(int h=0; h<=r.nHoursPerDay; h++)
04491                             nd[h]=0;
04492                      for(int d=0; d<r.nDaysPerWeek; d++){
04493                             int nh=0;
04494                             for(int h=0; h<r.nHoursPerDay; h++)
04495                                    nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
04496                             nd[nh]++;
04497                      }
04498                      //return the minimum occupied days which do not respect this constraint
04499                      int i = r.nDaysPerWeek - this->maxDaysPerWeek;
04500                      for(int k=0; k<=r.nHoursPerDay; k++){
04501                             if(nd[k]>0){
04502                                    if(i>nd[k]){
04503                                           i-=nd[k];
04504                                           nbroken+=nd[k]*k;
04505                                           nbr+=nd[k]*k;
04506                                    }
04507                                    else{
04508                                           nbroken+=i*k;
04509                                           nbr+=i*k;
04510                                           break;
04511                                    }
04512                             }
04513                      }
04514 
04515                      if(nbr>0){
04516                             QString s= tr("Time constraint teachers max days per week broken for teacher: %1.")
04517                             .arg(r.internalTeachersList[t]->name);
04518                             s += tr("This increases the conflicts total by %1")
04519                             .arg(CustomFETString::number(nbr*weightPercentage/100));
04520                             
04521                             dl.append(s);
04522                             cl.append(nbr*weightPercentage/100);
04523                      
04524                             *conflictsString += s+"\n";
04525                      }
04526               
04527               }
04528               
04529        }
04530 
04531        if(weightPercentage==100)
04532               assert(nbroken==0);
04533        return weightPercentage/100 * nbroken;
04534 }
04535 
04536 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
04537 {
04538        Q_UNUSED(r);
04539        Q_UNUSED(a);
04540 
04541        return false;
04542 }
04543 
04544 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
04545 {
04546        Q_UNUSED(t);
04547 
04548        return true;
04549 }
04550 
04551 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToSubject(Subject* s)
04552 {
04553        Q_UNUSED(s);
04554 
04555        return false;
04556 }
04557 
04558 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
04559 {
04560        Q_UNUSED(s);
04561 
04562        return false;
04563 }
04564 
04565 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
04566 {
04567        Q_UNUSED(r);
04568        Q_UNUSED(s);
04569 
04570        return false;
04571 }
04572 
04573 bool ConstraintTeachersMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
04574 {
04575        if(maxDaysPerWeek>r.nDaysPerWeek)
04576               return true;
04577        
04578        return false;
04579 }
04580 
04581 bool ConstraintTeachersMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
04582 {
04583        assert(hasWrongDayOrHour(r));
04584        
04585        return true;
04586 }
04587 
04588 bool ConstraintTeachersMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
04589 {
04590        assert(hasWrongDayOrHour(r));
04591        
04592        if(maxDaysPerWeek>r.nDaysPerWeek)
04593               maxDaysPerWeek=r.nDaysPerWeek;
04594 
04595        return true;
04596 }
04597 
04600 
04601 ConstraintTeachersMaxGapsPerWeek::ConstraintTeachersMaxGapsPerWeek()
04602        : TimeConstraint()
04603 {
04604        this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK;
04605 }
04606 
04607 ConstraintTeachersMaxGapsPerWeek::ConstraintTeachersMaxGapsPerWeek(double wp, int mg)
04608        : TimeConstraint(wp)
04609 {
04610        this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK;
04611        this->maxGaps=mg;
04612 }
04613 
04614 bool ConstraintTeachersMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
04615 {
04616        Q_UNUSED(parent);
04617        Q_UNUSED(r);
04618        
04619        return true;
04620 }
04621 
04622 bool ConstraintTeachersMaxGapsPerWeek::hasInactiveActivities(Rules& r)
04623 {
04624        Q_UNUSED(r);
04625        return false;
04626 }
04627 
04628 QString ConstraintTeachersMaxGapsPerWeek::getXmlDescription(Rules& r){
04629        Q_UNUSED(r);
04630 
04631        QString s="<ConstraintTeachersMaxGapsPerWeek>\n";
04632        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
04633        s+="   <Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
04634        s+="   <Active>"+trueFalse(active)+"</Active>\n";
04635        s+="   <Comments>"+protect(comments)+"</Comments>\n";
04636        s+="</ConstraintTeachersMaxGapsPerWeek>\n";
04637        return s;
04638 }
04639 
04640 QString ConstraintTeachersMaxGapsPerWeek::getDescription(Rules& r){
04641        Q_UNUSED(r);
04642 
04643        QString begin=QString("");
04644        if(!active)
04645               begin="X - ";
04646               
04647        QString end=QString("");
04648        if(!comments.isEmpty())
04649               end=", "+tr("C: %1", "Comments").arg(comments);
04650               
04651        QString s;
04652        s+=tr("Teachers max gaps per week");s+=", ";
04653        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
04654        s+=tr("MG:%1", "Max gaps (per week)").arg(this->maxGaps);
04655 
04656        return begin+s+end;
04657 }
04658 
04659 QString ConstraintTeachersMaxGapsPerWeek::getDetailedDescription(Rules& r){
04660        Q_UNUSED(r);
04661 
04662        QString s=tr("Time constraint");s+="\n";
04663        s+=tr("All teachers must respect the maximum number of gaps per week");s+="\n";
04664        s+=tr("(breaks and teacher not available not counted)");s+="\n";
04665        s+=tr("Maximum gaps per week=%1").arg(this->maxGaps); s+="\n";
04666        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
04667 
04668        if(!active){
04669               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
04670               s+="\n";
04671        }
04672        if(!comments.isEmpty()){
04673               s+=tr("Comments=%1").arg(comments);
04674               s+="\n";
04675        }
04676 
04677        return s;
04678 }
04679 
04680 double ConstraintTeachersMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
04681 { 
04682        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
04683        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
04684               c.teachersMatrixReady=true;
04685               c.subgroupsMatrixReady=true;
04686               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
04687               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
04688               
04689               c.changedForMatrixCalculation=false;
04690        }
04691        
04692        int tg;
04693        int i, j, k;
04694        int totalGaps;
04695 
04696        totalGaps=0;
04697        for(i=0; i<r.nInternalTeachers; i++){
04698               tg=0;
04699               for(j=0; j<r.nDaysPerWeek; j++){
04700                      for(k=0; k<r.nHoursPerDay; k++)
04701                             if(teachersMatrix[i][j][k]>0){
04702                                    assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
04703                                    break;
04704                             }
04705 
04706                      int cnt=0;
04707                      for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
04708                             if(teachersMatrix[i][j][k]>0){
04709                                    tg+=cnt;
04710                                    cnt=0;
04711                             }
04712                             else
04713                                    cnt++;
04714                      }
04715               }
04716               if(tg>this->maxGaps){
04717                      totalGaps+=tg-maxGaps;
04718                      //assert(this->weightPercentage<100); partial solutions might break this rule
04719                      if(conflictsString!=NULL){
04720                             QString s=tr("Time constraint teachers max gaps per week broken for teacher: %1, conflicts factor increase=%2")
04721                                    .arg(r.internalTeachersList[i]->name)
04722                                    .arg(CustomFETString::number((tg-maxGaps)*weightPercentage/100));
04723                                    
04724                             *conflictsString+= s+"\n";
04725                                           
04726                             dl.append(s);
04727                             cl.append((tg-maxGaps)*weightPercentage/100);
04728                      }
04729               }
04730        }
04731        
04732        if(c.nPlacedActivities==r.nInternalActivities)
04733               if(weightPercentage==100){
04734                      assert(totalGaps==0); //for partial solutions this rule might be broken
04735               }
04736                      
04737        return weightPercentage/100 * totalGaps;
04738 }
04739 
04740 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
04741 {
04742        Q_UNUSED(r);
04743        Q_UNUSED(a);
04744 
04745        return false;
04746 }
04747 
04748 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
04749 {
04750        Q_UNUSED(t);
04751 
04752        return true;
04753 }
04754 
04755 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToSubject(Subject* s)
04756 {
04757        Q_UNUSED(s);
04758 
04759        return false;
04760 }
04761 
04762 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
04763 {
04764        Q_UNUSED(s);
04765 
04766        return false;
04767 }
04768 
04769 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
04770 {
04771        Q_UNUSED(r);
04772        Q_UNUSED(s);
04773 
04774        return false;
04775 }
04776 
04777 bool ConstraintTeachersMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
04778 {
04779        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
04780               return true;
04781        
04782        return false;
04783 }
04784 
04785 bool ConstraintTeachersMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
04786 {
04787        assert(hasWrongDayOrHour(r));
04788        
04789        return true;
04790 }
04791 
04792 bool ConstraintTeachersMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
04793 {
04794        assert(hasWrongDayOrHour(r));
04795        
04796        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
04797               maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
04798 
04799        return true;
04800 }
04801 
04804 
04805 ConstraintTeacherMaxGapsPerWeek::ConstraintTeacherMaxGapsPerWeek()
04806        : TimeConstraint()
04807 {
04808        this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK;
04809 }
04810 
04811 ConstraintTeacherMaxGapsPerWeek::ConstraintTeacherMaxGapsPerWeek(double wp, QString tn, int mg)
04812        : TimeConstraint(wp)
04813 {
04814        this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK;
04815        this->teacherName=tn;
04816        this->maxGaps=mg;
04817 }
04818 
04819 bool ConstraintTeacherMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
04820 {
04821        Q_UNUSED(parent);
04822 
04823        this->teacherIndex=r.searchTeacher(this->teacherName);
04824        assert(this->teacherIndex>=0);
04825        return true;
04826 }
04827 
04828 bool ConstraintTeacherMaxGapsPerWeek::hasInactiveActivities(Rules& r)
04829 {
04830        Q_UNUSED(r);
04831        return false;
04832 }
04833 
04834 QString ConstraintTeacherMaxGapsPerWeek::getXmlDescription(Rules& r){
04835        Q_UNUSED(r);
04836 
04837        QString s="<ConstraintTeacherMaxGapsPerWeek>\n";
04838        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
04839        s+="   <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
04840        s+="   <Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
04841        s+="   <Active>"+trueFalse(active)+"</Active>\n";
04842        s+="   <Comments>"+protect(comments)+"</Comments>\n";
04843        s+="</ConstraintTeacherMaxGapsPerWeek>\n";
04844        return s;
04845 }
04846 
04847 QString ConstraintTeacherMaxGapsPerWeek::getDescription(Rules& r){
04848        Q_UNUSED(r);
04849 
04850        QString begin=QString("");
04851        if(!active)
04852               begin="X - ";
04853               
04854        QString end=QString("");
04855        if(!comments.isEmpty())
04856               end=", "+tr("C: %1", "Comments").arg(comments);
04857               
04858        QString s;
04859        s+=tr("Teacher max gaps per week");s+=", ";
04860        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
04861        s+=tr("T:%1", "Teacher").arg(this->teacherName); s+=", ";
04862        s+=tr("MG:%1", "Max gaps (per week").arg(this->maxGaps);
04863 
04864        return begin+s+end;
04865 }
04866 
04867 QString ConstraintTeacherMaxGapsPerWeek::getDetailedDescription(Rules& r){
04868        Q_UNUSED(r);
04869 
04870        QString s=tr("Time constraint"); s+="\n";
04871        s+=tr("A teacher must respect the maximum number of gaps per week"); s+="\n";
04872        s+=tr("(breaks and teacher not available not counted)");s+="\n";
04873        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
04874        s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
04875        s+=tr("Maximum gaps per week=%1").arg(this->maxGaps); s+="\n";
04876 
04877        if(!active){
04878               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
04879               s+="\n";
04880        }
04881        if(!comments.isEmpty()){
04882               s+=tr("Comments=%1").arg(comments);
04883               s+="\n";
04884        }
04885 
04886        return s;
04887 }
04888 
04889 double ConstraintTeacherMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
04890 { 
04891        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
04892        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
04893               c.teachersMatrixReady=true;
04894               c.subgroupsMatrixReady=true;
04895               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
04896               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
04897 
04898               c.changedForMatrixCalculation=false;
04899        }
04900        
04901        int tg;
04902        int i, j, k;
04903        int totalGaps;
04904 
04905        totalGaps=0;
04906               
04907        i=this->teacherIndex;
04908        
04909        tg=0;
04910        for(j=0; j<r.nDaysPerWeek; j++){
04911               for(k=0; k<r.nHoursPerDay; k++)
04912                      if(teachersMatrix[i][j][k]>0){
04913                             assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
04914                             break;
04915                      }
04916 
04917               int cnt=0;
04918               for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
04919                      if(teachersMatrix[i][j][k]>0){
04920                             tg+=cnt;
04921                             cnt=0;
04922                      }
04923                      else
04924                             cnt++;
04925               }
04926        }
04927        if(tg>this->maxGaps){
04928               totalGaps+=tg-maxGaps;
04929               //assert(this->weightPercentage<100); partial solutions might break this rule
04930               if(conflictsString!=NULL){
04931                      QString s=tr("Time constraint teacher max gaps per week broken for teacher: %1, conflicts factor increase=%2")
04932                             .arg(r.internalTeachersList[i]->name)
04933                             .arg(CustomFETString::number((tg-maxGaps)*weightPercentage/100));
04934                                    
04935                      *conflictsString+= s+"\n";
04936                                           
04937                      dl.append(s);
04938                      cl.append((tg-maxGaps)*weightPercentage/100);
04939               }
04940        }
04941 
04942        if(c.nPlacedActivities==r.nInternalActivities)
04943               if(weightPercentage==100)
04944                      assert(totalGaps==0); //for partial solutions this rule might be broken
04945        return weightPercentage/100 * totalGaps;
04946 }
04947 
04948 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
04949 {
04950        Q_UNUSED(r);
04951        Q_UNUSED(a);
04952 
04953        return false;
04954 }
04955 
04956 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
04957 {
04958        if(this->teacherName==t->name)
04959               return true;
04960        return false;
04961 }
04962 
04963 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToSubject(Subject* s)
04964 {
04965        Q_UNUSED(s);
04966 
04967        return false;
04968 }
04969 
04970 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
04971 {
04972        Q_UNUSED(s);
04973 
04974        return false;
04975 }
04976 
04977 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
04978 {
04979        Q_UNUSED(r);
04980        Q_UNUSED(s);
04981 
04982        return false;
04983 }
04984 
04985 bool ConstraintTeacherMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
04986 {
04987        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
04988               return true;
04989        
04990        return false;
04991 }
04992 
04993 bool ConstraintTeacherMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
04994 {
04995        assert(hasWrongDayOrHour(r));
04996        
04997        return true;
04998 }
04999 
05000 bool ConstraintTeacherMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
05001 {
05002        assert(hasWrongDayOrHour(r));
05003        
05004        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
05005               maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
05006 
05007        return true;
05008 }
05009 
05012 
05013 ConstraintTeachersMaxGapsPerDay::ConstraintTeachersMaxGapsPerDay()
05014        : TimeConstraint()
05015 {
05016        this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY;
05017 }
05018 
05019 ConstraintTeachersMaxGapsPerDay::ConstraintTeachersMaxGapsPerDay(double wp, int mg)
05020        : TimeConstraint(wp)
05021 {
05022        this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY;
05023        this->maxGaps=mg;
05024 }
05025 
05026 bool ConstraintTeachersMaxGapsPerDay::computeInternalStructure(QWidget* parent, Rules& r)
05027 {
05028        Q_UNUSED(parent);
05029        Q_UNUSED(r);
05030        
05031        return true;
05032 }
05033 
05034 bool ConstraintTeachersMaxGapsPerDay::hasInactiveActivities(Rules& r)
05035 {
05036        Q_UNUSED(r);
05037        return false;
05038 }
05039 
05040 QString ConstraintTeachersMaxGapsPerDay::getXmlDescription(Rules& r){
05041        Q_UNUSED(r);
05042 
05043        QString s="<ConstraintTeachersMaxGapsPerDay>\n";
05044        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
05045        s+="   <Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
05046        s+="   <Active>"+trueFalse(active)+"</Active>\n";
05047        s+="   <Comments>"+protect(comments)+"</Comments>\n";
05048        s+="</ConstraintTeachersMaxGapsPerDay>\n";
05049        return s;
05050 }
05051 
05052 QString ConstraintTeachersMaxGapsPerDay::getDescription(Rules& r){
05053        Q_UNUSED(r);
05054 
05055        QString begin=QString("");
05056        if(!active)
05057               begin="X - ";
05058               
05059        QString end=QString("");
05060        if(!comments.isEmpty())
05061               end=", "+tr("C: %1", "Comments").arg(comments);
05062               
05063        QString s;
05064        s+=tr("Teachers max gaps per day");s+=", ";
05065        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
05066        s+=tr("MG:%1", "Max gaps (per day)").arg(this->maxGaps);
05067 
05068        return begin+s+end;
05069 }
05070 
05071 QString ConstraintTeachersMaxGapsPerDay::getDetailedDescription(Rules& r){
05072        Q_UNUSED(r);
05073 
05074        QString s=tr("Time constraint");s+="\n";
05075        s+=tr("All teachers must respect the maximum gaps per day");s+="\n";
05076        s+=tr("(breaks and teacher not available not counted)");s+="\n";
05077        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
05078        s+=tr("Maximum gaps per day=%1").arg(this->maxGaps); s+="\n";
05079 
05080        if(!active){
05081               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
05082               s+="\n";
05083        }
05084        if(!comments.isEmpty()){
05085               s+=tr("Comments=%1").arg(comments);
05086               s+="\n";
05087        }
05088 
05089        return s;
05090 }
05091 
05092 double ConstraintTeachersMaxGapsPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
05093 { 
05094        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
05095        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
05096               c.teachersMatrixReady=true;
05097               c.subgroupsMatrixReady=true;
05098               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
05099               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
05100 
05101               c.changedForMatrixCalculation=false;
05102        }
05103        
05104        int tg;
05105        int i, j, k;
05106        int totalGaps;
05107 
05108        totalGaps=0;
05109        for(i=0; i<r.nInternalTeachers; i++){
05110               for(j=0; j<r.nDaysPerWeek; j++){
05111                      tg=0;
05112                      for(k=0; k<r.nHoursPerDay; k++)
05113                             if(teachersMatrix[i][j][k]>0){
05114                                    assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
05115                                    break;
05116                             }
05117 
05118                      int cnt=0;
05119                      for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
05120                             if(teachersMatrix[i][j][k]>0){
05121                                    tg+=cnt;
05122                                    cnt=0;
05123                             }
05124                             else
05125                                    cnt++;
05126                      }
05127                      if(tg>this->maxGaps){
05128                             totalGaps+=tg-maxGaps;
05129                             //assert(this->weightPercentage<100); partial solutions might break this rule
05130                             if(conflictsString!=NULL){
05131                                    QString s=tr("Time constraint teachers max gaps per day broken for teacher: %1, day: %2, conflicts factor increase=%3")
05132                                           .arg(r.internalTeachersList[i]->name)
05133                                           .arg(r.daysOfTheWeek[j])
05134                                           .arg(CustomFETString::number((tg-maxGaps)*weightPercentage/100));
05135                                    
05136                                    *conflictsString+= s+"\n";
05137                                                         
05138                                    dl.append(s);
05139                                    cl.append((tg-maxGaps)*weightPercentage/100);
05140                             }
05141                      }
05142               }
05143        }
05144        
05145        if(c.nPlacedActivities==r.nInternalActivities)
05146               if(weightPercentage==100)
05147                      assert(totalGaps==0); //for partial solutions this rule might be broken
05148        return weightPercentage/100 * totalGaps;
05149 }
05150 
05151 bool ConstraintTeachersMaxGapsPerDay::isRelatedToActivity(Rules& r, Activity* a)
05152 {
05153        Q_UNUSED(r);
05154        Q_UNUSED(a);
05155 
05156        return false;
05157 }
05158 
05159 bool ConstraintTeachersMaxGapsPerDay::isRelatedToTeacher(Teacher* t)
05160 {      
05161        Q_UNUSED(t);
05162 
05163        return true;
05164 }
05165 
05166 bool ConstraintTeachersMaxGapsPerDay::isRelatedToSubject(Subject* s)
05167 {
05168        Q_UNUSED(s);
05169 
05170        return false;
05171 }
05172 
05173 bool ConstraintTeachersMaxGapsPerDay::isRelatedToActivityTag(ActivityTag* s)
05174 {
05175        Q_UNUSED(s);
05176 
05177        return false;
05178 }
05179 
05180 bool ConstraintTeachersMaxGapsPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
05181 {
05182        Q_UNUSED(r);
05183        Q_UNUSED(s);
05184 
05185        return false;
05186 }
05187 
05188 bool ConstraintTeachersMaxGapsPerDay::hasWrongDayOrHour(Rules& r)
05189 {
05190        if(maxGaps>r.nHoursPerDay)
05191               return true;
05192        
05193        return false;
05194 }
05195 
05196 bool ConstraintTeachersMaxGapsPerDay::canRepairWrongDayOrHour(Rules& r)
05197 {
05198        assert(hasWrongDayOrHour(r));
05199        
05200        return true;
05201 }
05202 
05203 bool ConstraintTeachersMaxGapsPerDay::repairWrongDayOrHour(Rules& r)
05204 {
05205        assert(hasWrongDayOrHour(r));
05206        
05207        if(maxGaps>r.nHoursPerDay)
05208               maxGaps=r.nHoursPerDay;
05209 
05210        return true;
05211 }
05212 
05215 
05216 ConstraintTeacherMaxGapsPerDay::ConstraintTeacherMaxGapsPerDay()
05217        : TimeConstraint()
05218 {
05219        this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY;
05220 }
05221 
05222 ConstraintTeacherMaxGapsPerDay::ConstraintTeacherMaxGapsPerDay(double wp, QString tn, int mg)
05223        : TimeConstraint(wp)
05224 {
05225        this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY;
05226        this->teacherName=tn;
05227        this->maxGaps=mg;
05228 }
05229 
05230 bool ConstraintTeacherMaxGapsPerDay::computeInternalStructure(QWidget* parent, Rules& r)
05231 {
05232        Q_UNUSED(parent);
05233 
05234        this->teacherIndex=r.searchTeacher(this->teacherName);
05235        assert(this->teacherIndex>=0);
05236        return true;
05237 }
05238 
05239 bool ConstraintTeacherMaxGapsPerDay::hasInactiveActivities(Rules& r)
05240 {
05241        Q_UNUSED(r);
05242        return false;
05243 }
05244 
05245 QString ConstraintTeacherMaxGapsPerDay::getXmlDescription(Rules& r){
05246        Q_UNUSED(r);
05247 
05248        QString s="<ConstraintTeacherMaxGapsPerDay>\n";
05249        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
05250        s+="   <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
05251        s+="   <Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
05252        s+="   <Active>"+trueFalse(active)+"</Active>\n";
05253        s+="   <Comments>"+protect(comments)+"</Comments>\n";
05254        s+="</ConstraintTeacherMaxGapsPerDay>\n";
05255        return s;
05256 }
05257 
05258 QString ConstraintTeacherMaxGapsPerDay::getDescription(Rules& r){
05259        Q_UNUSED(r);
05260 
05261        QString begin=QString("");
05262        if(!active)
05263               begin="X - ";
05264               
05265        QString end=QString("");
05266        if(!comments.isEmpty())
05267               end=", "+tr("C: %1", "Comments").arg(comments);
05268               
05269        QString s;
05270        s+=tr("Teacher max gaps per day");s+=", ";
05271        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
05272        s+=tr("T:%1", "Teacher").arg(this->teacherName); s+=", ";
05273        s+=tr("MG:%1", "Max gaps (per day)").arg(this->maxGaps);
05274 
05275        return begin+s+end;
05276 }
05277 
05278 QString ConstraintTeacherMaxGapsPerDay::getDetailedDescription(Rules& r){
05279        Q_UNUSED(r);
05280 
05281        QString s=tr("Time constraint"); s+="\n";
05282        s+=tr("A teacher must respect the maximum number of gaps per day"); s+="\n";
05283        s+=tr("(breaks and teacher not available not counted)");s+="\n";
05284        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
05285        s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
05286        s+=tr("Maximum gaps per day=%1").arg(this->maxGaps); s+="\n";
05287 
05288        if(!active){
05289               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
05290               s+="\n";
05291        }
05292        if(!comments.isEmpty()){
05293               s+=tr("Comments=%1").arg(comments);
05294               s+="\n";
05295        }
05296 
05297        return s;
05298 }
05299 
05300 double ConstraintTeacherMaxGapsPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
05301 { 
05302        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
05303        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
05304               c.teachersMatrixReady=true;
05305               c.subgroupsMatrixReady=true;
05306               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
05307               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
05308 
05309               c.changedForMatrixCalculation=false;
05310        }
05311        
05312        int tg;
05313        int i, j, k;
05314        int totalGaps;
05315 
05316        totalGaps=0;
05317               
05318        i=this->teacherIndex;
05319        
05320        for(j=0; j<r.nDaysPerWeek; j++){
05321               tg=0;
05322               for(k=0; k<r.nHoursPerDay; k++)
05323                      if(teachersMatrix[i][j][k]>0){
05324                             assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
05325                             break;
05326                      }
05327 
05328               int cnt=0;
05329               for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
05330                      if(teachersMatrix[i][j][k]>0){
05331                             tg+=cnt;
05332                             cnt=0;
05333                      }
05334                      else
05335                             cnt++;
05336               }
05337               if(tg>this->maxGaps){
05338                      totalGaps+=tg-maxGaps;
05339                      //assert(this->weightPercentage<100); partial solutions might break this rule
05340                      if(conflictsString!=NULL){
05341                             QString s=tr("Time constraint teacher max gaps per day broken for teacher: %1, day: %2, conflicts factor increase=%3")
05342                                    .arg(r.internalTeachersList[i]->name)
05343                                    .arg(r.daysOfTheWeek[j])
05344                                    .arg(CustomFETString::number((tg-maxGaps)*weightPercentage/100));
05345                                           
05346                             *conflictsString+= s+"\n";
05347                                                  
05348                             dl.append(s);
05349                             cl.append((tg-maxGaps)*weightPercentage/100);
05350                      }
05351               }
05352        }
05353 
05354        if(c.nPlacedActivities==r.nInternalActivities)
05355               if(weightPercentage==100)
05356                      assert(totalGaps==0); //for partial solutions this rule might be broken
05357        return weightPercentage/100 * totalGaps;
05358 }
05359 
05360 bool ConstraintTeacherMaxGapsPerDay::isRelatedToActivity(Rules& r, Activity* a)
05361 {
05362        Q_UNUSED(r);
05363        Q_UNUSED(a);
05364 
05365        return false;
05366 }
05367 
05368 bool ConstraintTeacherMaxGapsPerDay::isRelatedToTeacher(Teacher* t)
05369 {
05370        if(this->teacherName==t->name)
05371               return true;
05372        return false;
05373 }
05374 
05375 bool ConstraintTeacherMaxGapsPerDay::isRelatedToSubject(Subject* s)
05376 {
05377        Q_UNUSED(s);
05378 
05379        return false;
05380 }
05381 
05382 bool ConstraintTeacherMaxGapsPerDay::isRelatedToActivityTag(ActivityTag* s)
05383 {
05384        Q_UNUSED(s);
05385 
05386        return false;
05387 }
05388 
05389 bool ConstraintTeacherMaxGapsPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
05390 {
05391        Q_UNUSED(r);
05392        Q_UNUSED(s);
05393 
05394        return false;
05395 }
05396 
05397 bool ConstraintTeacherMaxGapsPerDay::hasWrongDayOrHour(Rules& r)
05398 {
05399        if(maxGaps>r.nHoursPerDay)
05400               return true;
05401        
05402        return false;
05403 }
05404 
05405 bool ConstraintTeacherMaxGapsPerDay::canRepairWrongDayOrHour(Rules& r)
05406 {
05407        assert(hasWrongDayOrHour(r));
05408        
05409        return true;
05410 }
05411 
05412 bool ConstraintTeacherMaxGapsPerDay::repairWrongDayOrHour(Rules& r)
05413 {
05414        assert(hasWrongDayOrHour(r));
05415        
05416        if(maxGaps>r.nHoursPerDay)
05417               maxGaps=r.nHoursPerDay;
05418 
05419        return true;
05420 }
05421 
05424 
05425 ConstraintBreakTimes::ConstraintBreakTimes()
05426        : TimeConstraint()
05427 {
05428        this->type = CONSTRAINT_BREAK_TIMES;
05429 }
05430 
05431 ConstraintBreakTimes::ConstraintBreakTimes(double wp, QList<int> d, QList<int> h)
05432        : TimeConstraint(wp)
05433 {
05434        this->days = d;
05435        this->hours = h;
05436        this->type = CONSTRAINT_BREAK_TIMES;
05437 }
05438 
05439 bool ConstraintBreakTimes::hasInactiveActivities(Rules& r)
05440 {
05441        Q_UNUSED(r);
05442        return false;
05443 }
05444 
05445 QString ConstraintBreakTimes::getXmlDescription(Rules& r){
05446        QString s="<ConstraintBreakTimes>\n";
05447        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
05448 
05449        s+="   <Number_of_Break_Times>"+CustomFETString::number(this->days.count())+"</Number_of_Break_Times>\n";
05450        assert(days.count()==hours.count());
05451        for(int i=0; i<days.count(); i++){
05452               s+="   <Break_Time>\n";
05453               if(this->days.at(i)>=0)
05454                      s+="          <Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
05455               if(this->hours.at(i)>=0)
05456                      s+="          <Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
05457               s+="   </Break_Time>\n";
05458        }
05459 
05460        s+="   <Active>"+trueFalse(active)+"</Active>\n";
05461        s+="   <Comments>"+protect(comments)+"</Comments>\n";
05462        s+="</ConstraintBreakTimes>\n";
05463        return s;
05464 }
05465 
05466 QString ConstraintBreakTimes::getDescription(Rules& r){
05467        QString begin=QString("");
05468        if(!active)
05469               begin="X - ";
05470               
05471        QString end=QString("");
05472        if(!comments.isEmpty())
05473               end=", "+tr("C: %1", "Comments").arg(comments);
05474               
05475        QString s;
05476        s+=tr("Break times");s+=", ";
05477        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
05478 
05479        s+=tr("B at:", "Break at");
05480        s+=" ";
05481        assert(days.count()==hours.count());
05482        for(int i=0; i<days.count(); i++){
05483               if(this->days.at(i)>=0){
05484                      s+=r.daysOfTheWeek[this->days.at(i)];
05485                      s+=" ";
05486               }
05487               if(this->hours.at(i)>=0){
05488                      s+=r.hoursOfTheDay[this->hours.at(i)];
05489               }
05490               if(i<days.count()-1)
05491                      s+="; ";
05492        }
05493        
05494        return begin+s+end;
05495 }
05496 
05497 QString ConstraintBreakTimes::getDetailedDescription(Rules& r){
05498        QString s=tr("Time constraint");s+="\n";
05499        s+=tr("Break times");s+="\n";
05500        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
05501 
05502        s+=tr("Break at:"); s+="\n";
05503        assert(days.count()==hours.count());
05504        for(int i=0; i<days.count(); i++){
05505               if(this->days.at(i)>=0){
05506                      s+=r.daysOfTheWeek[this->days.at(i)];
05507                      s+=" ";
05508               }
05509               if(this->hours.at(i)>=0){
05510                      s+=r.hoursOfTheDay[this->hours.at(i)];
05511               }
05512               if(i<days.count()-1)
05513                      s+="; ";
05514        }
05515        s+="\n";
05516 
05517        if(!active){
05518               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
05519               s+="\n";
05520        }
05521        if(!comments.isEmpty()){
05522               s+=tr("Comments=%1").arg(comments);
05523               s+="\n";
05524        }
05525        
05526        return s;
05527 }
05528 
05529 bool ConstraintBreakTimes::computeInternalStructure(QWidget* parent, Rules& r)
05530 {
05531        Q_UNUSED(r);
05532        
05533        assert(days.count()==hours.count());
05534        for(int k=0; k<days.count(); k++){
05535               if(this->days.at(k) >= r.nDaysPerWeek){
05536                      QMessageBox::information(parent, tr("FET information"),
05537                       tr("Constraint break times is wrong because it refers to removed day. Please correct"
05538                       " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
05539                
05540                      return false;
05541               }             
05542               if(this->hours.at(k) >= r.nHoursPerDay){
05543                      QMessageBox::information(parent, tr("FET information"),
05544                       tr("Constraint break times is wrong because an hour is too late (after the last acceptable slot). Please correct"
05545                       " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
05546                
05547                      return false;
05548               }
05549        }
05550 
05551        return true;
05552 }
05553 
05554 double ConstraintBreakTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
05555 {
05556        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
05557        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
05558               c.teachersMatrixReady=true;
05559               c.subgroupsMatrixReady=true;
05560               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
05561               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
05562 
05563               c.changedForMatrixCalculation=false;
05564        }
05565 
05566        //DEPRECATED COMMENT
05567        //For the moment, this function sums the number of hours each teacher
05568        //is teaching in this break period.
05569        //This function consideres all the hours, I mean if there are for example 5 weekly courses
05570        //scheduled on that hour (which is already a broken hard restriction - we only
05571        //are allowed 1 weekly course for a certain teacher at a certain hour) we calculate
05572        //5 broken restrictions for this break period.
05573        //TODO: decide if it is better to consider only 2 or 10 as a return value in this particular case
05574        //(currently it is 10)
05575        
05576        int nbroken;
05577        
05578        nbroken=0;
05579               
05580        for(int i=0; i<r.nInternalActivities; i++){
05581               int dayact=c.times[i]%r.nDaysPerWeek;
05582               int houract=c.times[i]/r.nDaysPerWeek;
05583               
05584               assert(days.count()==hours.count());
05585               for(int kk=0; kk<days.count(); kk++){
05586                      int d=days.at(kk);
05587                      int h=hours.at(kk);
05588                      
05589                      int dur=r.internalActivitiesList[i].duration;
05590                      if(d==dayact && !(houract+dur<=h || houract>h))
05591                      {                    
05592                             nbroken++;
05593 
05594                             if(conflictsString!=NULL){
05595                                    QString s=tr("Time constraint break not respected for activity with id %1, on day %2, hours %3")
05596                                           .arg(r.internalActivitiesList[i].id)
05597                                           .arg(r.daysOfTheWeek[dayact])
05598                                           .arg(r.daysOfTheWeek[houract]);
05599                                    s+=". ";
05600                                    s+=tr("This increases the conflicts total by %1").arg(CustomFETString::number(weightPercentage/100));
05601                                    
05602                                    dl.append(s);
05603                                    cl.append(weightPercentage/100);
05604                             
05605                                    *conflictsString+= s+"\n";
05606                             }
05607                      }
05608               }
05609        }
05610 
05611        if(weightPercentage==100)
05612               assert(nbroken==0);
05613        return weightPercentage/100 * nbroken;
05614 }
05615 
05616 bool ConstraintBreakTimes::isRelatedToActivity(Rules& r, Activity* a)
05617 {
05618        Q_UNUSED(r);
05619        Q_UNUSED(a);
05620 
05621        return false;
05622 }
05623 
05624 bool ConstraintBreakTimes::isRelatedToTeacher(Teacher* t)
05625 {
05626        Q_UNUSED(t);
05627 
05628        return false;
05629 }
05630 
05631 bool ConstraintBreakTimes::isRelatedToSubject(Subject* s)
05632 {
05633        Q_UNUSED(s);
05634 
05635        return false;
05636 }
05637 
05638 bool ConstraintBreakTimes::isRelatedToActivityTag(ActivityTag* s)
05639 {
05640        Q_UNUSED(s);
05641 
05642        return false;
05643 }
05644 
05645 bool ConstraintBreakTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
05646 {
05647        Q_UNUSED(r);
05648        Q_UNUSED(s);
05649 
05650        return false;
05651 }
05652 
05653 bool ConstraintBreakTimes::hasWrongDayOrHour(Rules& r)
05654 {
05655        assert(days.count()==hours.count());
05656        
05657        for(int i=0; i<days.count(); i++)
05658               if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
05659                || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
05660                      return true;
05661 
05662        return false;
05663 }
05664 
05665 bool ConstraintBreakTimes::canRepairWrongDayOrHour(Rules& r)
05666 {
05667        assert(hasWrongDayOrHour(r));
05668        
05669        return true;
05670 }
05671 
05672 bool ConstraintBreakTimes::repairWrongDayOrHour(Rules& r)
05673 {
05674        assert(hasWrongDayOrHour(r));
05675        
05676        assert(days.count()==hours.count());
05677        
05678        QList<int> newDays;
05679        QList<int> newHours;
05680        
05681        for(int i=0; i<days.count(); i++)
05682               if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
05683                && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
05684                      newDays.append(days.at(i));
05685                      newHours.append(hours.at(i));
05686               }
05687        
05688        days=newDays;
05689        hours=newHours;
05690        
05691        r.internalStructureComputed=false;
05692        setRulesModifiedAndOtherThings(&r);
05693 
05694        return true;
05695 }
05696 
05699 
05700 ConstraintStudentsMaxGapsPerWeek::ConstraintStudentsMaxGapsPerWeek()
05701        : TimeConstraint()
05702 {
05703        this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK;
05704 }
05705 
05706 ConstraintStudentsMaxGapsPerWeek::ConstraintStudentsMaxGapsPerWeek(double wp, int mg)
05707        : TimeConstraint(wp)
05708 {
05709        this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK;
05710        this->maxGaps=mg;
05711 }
05712 
05713 bool ConstraintStudentsMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
05714 {
05715        Q_UNUSED(parent);
05716        Q_UNUSED(r);
05717        
05718        return true;
05719 }
05720 
05721 bool ConstraintStudentsMaxGapsPerWeek::hasInactiveActivities(Rules& r)
05722 {
05723        Q_UNUSED(r);
05724        return false;
05725 }
05726 
05727 QString ConstraintStudentsMaxGapsPerWeek::getXmlDescription(Rules& r)
05728 {
05729        Q_UNUSED(r);
05730 
05731        QString s="<ConstraintStudentsMaxGapsPerWeek>\n";
05732        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
05733        s+="   <Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
05734        s+="   <Active>"+trueFalse(active)+"</Active>\n";
05735        s+="   <Comments>"+protect(comments)+"</Comments>\n";
05736        s+="</ConstraintStudentsMaxGapsPerWeek>\n";
05737        return s;
05738 }
05739 
05740 QString ConstraintStudentsMaxGapsPerWeek::getDescription(Rules& r)
05741 {
05742        Q_UNUSED(r);
05743 
05744        QString begin=QString("");
05745        if(!active)
05746               begin="X - ";
05747               
05748        QString end=QString("");
05749        if(!comments.isEmpty())
05750               end=", "+tr("C: %1", "Comments").arg(comments);
05751               
05752        QString s;
05753        s+=tr("Students max gaps per week");s+=", ";
05754        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
05755        s+=tr("MG:%1", "Max gaps (per week)").arg(this->maxGaps);
05756 
05757        return begin+s+end;
05758 }
05759 
05760 QString ConstraintStudentsMaxGapsPerWeek::getDetailedDescription(Rules& r)
05761 {
05762        Q_UNUSED(r);
05763 
05764        QString s=tr("Time constraint");s+="\n";
05765        s+=tr("All students must respect the maximum number of gaps per week");s+="\n";
05766        s+=tr("(breaks and students set not available not counted)");s+="\n";
05767        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
05768        s+=tr("Maximum gaps per week=%1").arg(this->maxGaps);s+="\n";
05769        
05770        if(!active){
05771               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
05772               s+="\n";
05773        }
05774        if(!comments.isEmpty()){
05775               s+=tr("Comments=%1").arg(comments);
05776               s+="\n";
05777        }
05778 
05779        return s;
05780 }
05781 
05782 double ConstraintStudentsMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
05783 {
05784        //returns a number equal to the number of gaps of the subgroups (in hours)
05785 
05786        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
05787        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
05788               c.teachersMatrixReady=true;
05789               c.subgroupsMatrixReady=true;
05790               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
05791               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
05792               
05793               c.changedForMatrixCalculation=false;
05794        }
05795 
05796        int nGaps;
05797        int tmp;
05798        int i;
05799        
05800        int tIllegalGaps=0;
05801 
05802        for(i=0; i<r.nInternalSubgroups; i++){
05803               nGaps=0;
05804               for(int j=0; j<r.nDaysPerWeek; j++){
05805                      int k;
05806                      tmp=0;
05807                      for(k=0; k<r.nHoursPerDay; k++)
05808                             if(subgroupsMatrix[i][j][k]>0){
05809                                    assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
05810                                    break;
05811                             }
05812                      for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
05813                             if(subgroupsMatrix[i][j][k]>0){
05814                                    nGaps+=tmp;
05815                                    tmp=0;
05816                             }
05817                             else
05818                                    tmp++;
05819                      }
05820               }
05821               
05822               int illegalGaps=nGaps-this->maxGaps;
05823               if(illegalGaps<0)
05824                      illegalGaps=0;
05825 
05826               if(illegalGaps>0 && conflictsString!=NULL){
05827                      QString s=tr("Time constraint students max gaps per week broken for subgroup: %1, it has %2 extra gaps, conflicts increase=%3")
05828                       .arg(r.internalSubgroupsList[i]->name)
05829                       .arg(illegalGaps)
05830                       .arg(CustomFETString::number(illegalGaps*weightPercentage/100));
05831                                            
05832                      dl.append(s);
05833                      cl.append(illegalGaps*weightPercentage/100);
05834                                    
05835                      *conflictsString+= s+"\n";
05836               }
05837               
05838               tIllegalGaps+=illegalGaps;
05839        }
05840               
05841        if(c.nPlacedActivities==r.nInternalActivities)
05842               if(weightPercentage==100)    //for partial solutions it might be broken
05843                      assert(tIllegalGaps==0);
05844        return weightPercentage/100 * tIllegalGaps;
05845 }
05846 
05847 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
05848 {
05849        Q_UNUSED(r);
05850        Q_UNUSED(a);
05851 
05852        return false;
05853 }
05854 
05855 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
05856 {
05857        Q_UNUSED(t);
05858 
05859        return false;
05860 }
05861 
05862 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToSubject(Subject* s)
05863 {
05864        Q_UNUSED(s);
05865 
05866        return false;
05867 }
05868 
05869 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
05870 {
05871        Q_UNUSED(s);
05872 
05873        return false;
05874 }
05875 
05876 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
05877 {
05878        Q_UNUSED(r);
05879        Q_UNUSED(s);
05880 
05881        return true;
05882 }
05883 
05884 bool ConstraintStudentsMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
05885 {
05886        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
05887               return true;
05888        
05889        return false;
05890 }
05891 
05892 bool ConstraintStudentsMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
05893 {
05894        assert(hasWrongDayOrHour(r));
05895        
05896        return true;
05897 }
05898 
05899 bool ConstraintStudentsMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
05900 {
05901        assert(hasWrongDayOrHour(r));
05902        
05903        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
05904               maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
05905 
05906        return true;
05907 }
05908 
05911 
05912 ConstraintStudentsSetMaxGapsPerWeek::ConstraintStudentsSetMaxGapsPerWeek()
05913        : TimeConstraint()
05914 {
05915        this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK;
05916 }
05917 
05918 ConstraintStudentsSetMaxGapsPerWeek::ConstraintStudentsSetMaxGapsPerWeek(double wp, int mg, const QString& st )
05919        : TimeConstraint(wp)
05920 {
05921        this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK;
05922        this->maxGaps=mg;
05923        this->students = st;
05924 }
05925 
05926 bool ConstraintStudentsSetMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r){
05927        StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
05928 
05929        if(ss==NULL){
05930               QMessageBox::warning(parent, tr("FET warning"),
05931                tr("Constraint students set max gaps per week is wrong because it refers to inexistent students set."
05932                " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
05933                
05934               return false;
05935        }      
05936 
05937        assert(ss);
05938 
05939        this->iSubgroupsList.clear();
05940        if(ss->type==STUDENTS_SUBGROUP){
05941               int tmp;
05942               tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
05943               assert(tmp>=0);
05944               assert(tmp<r.nInternalSubgroups);
05945               if(!this->iSubgroupsList.contains(tmp))
05946                      this->iSubgroupsList.append(tmp);
05947        }
05948        else if(ss->type==STUDENTS_GROUP){
05949               StudentsGroup* stg=(StudentsGroup*)ss;
05950               for(int i=0; i<stg->subgroupsList.size(); i++){
05951                      StudentsSubgroup* sts=stg->subgroupsList[i];
05952                      int tmp;
05953                      tmp=sts->indexInInternalSubgroupsList;
05954                      assert(tmp>=0);
05955                      assert(tmp<r.nInternalSubgroups);
05956                      if(!this->iSubgroupsList.contains(tmp))
05957                             this->iSubgroupsList.append(tmp);
05958               }
05959        }
05960        else if(ss->type==STUDENTS_YEAR){
05961               StudentsYear* sty=(StudentsYear*)ss;
05962               for(int i=0; i<sty->groupsList.size(); i++){
05963                      StudentsGroup* stg=sty->groupsList[i];
05964                      for(int j=0; j<stg->subgroupsList.size(); j++){
05965                             StudentsSubgroup* sts=stg->subgroupsList[j];
05966                             int tmp;
05967                             tmp=sts->indexInInternalSubgroupsList;
05968                             assert(tmp>=0);
05969                             assert(tmp<r.nInternalSubgroups);
05970                             if(!this->iSubgroupsList.contains(tmp))
05971                                    this->iSubgroupsList.append(tmp);
05972                      }
05973               }
05974        }
05975        else
05976               assert(0);
05977               
05978        return true;
05979 }
05980 
05981 bool ConstraintStudentsSetMaxGapsPerWeek::hasInactiveActivities(Rules& r)
05982 {
05983        Q_UNUSED(r);
05984        return false;
05985 }
05986 
05987 QString ConstraintStudentsSetMaxGapsPerWeek::getXmlDescription(Rules& r){
05988        Q_UNUSED(r);
05989 
05990        QString s="<ConstraintStudentsSetMaxGapsPerWeek>\n";
05991        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
05992        s+="   <Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
05993        s+="   <Students>"; s+=protect(this->students); s+="</Students>\n";
05994        s+="   <Active>"+trueFalse(active)+"</Active>\n";
05995        s+="   <Comments>"+protect(comments)+"</Comments>\n";
05996        s+="</ConstraintStudentsSetMaxGapsPerWeek>\n";
05997        return s;
05998 }
05999 
06000 QString ConstraintStudentsSetMaxGapsPerWeek::getDescription(Rules& r){
06001        Q_UNUSED(r);
06002 
06003        QString begin=QString("");
06004        if(!active)
06005               begin="X - ";
06006               
06007        QString end=QString("");
06008        if(!comments.isEmpty())
06009               end=", "+tr("C: %1", "Comments").arg(comments);
06010               
06011        QString s;
06012        s+=tr("Students set max gaps per week"); s+=", ";
06013        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage)); s+=", ";
06014        s+=tr("MG:%1", "Max gaps (per week)").arg(this->maxGaps);s+=", ";
06015        s+=tr("St:%1", "Students").arg(this->students);
06016 
06017        return begin+s+end;
06018 }
06019 
06020 QString ConstraintStudentsSetMaxGapsPerWeek::getDetailedDescription(Rules& r){
06021        Q_UNUSED(r);
06022 
06023        QString s=tr("Time constraint");s+="\n";
06024        s+=tr("A students set must respect the maximum number of gaps per week");s+="\n";
06025        s+=tr("(breaks and students set not available not counted)");s+="\n";
06026        s+=tr("Weight (percentage)=%1").arg(CustomFETString::number(this->weightPercentage));s+="\n";
06027        s+=tr("Maximum gaps per week=%1").arg(this->maxGaps);s+="\n";
06028        s+=tr("Students=%1").arg(this->students); s+="\n";
06029        
06030        if(!active){
06031               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
06032               s+="\n";
06033        }
06034        if(!comments.isEmpty()){
06035               s+=tr("Comments=%1").arg(comments);
06036               s+="\n";
06037        }
06038        
06039        return s;
06040 }
06041 
06042 double ConstraintStudentsSetMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
06043 {
06044        //OLD COMMENT
06045        //returns a number equal to the number of gaps of the subgroups (in hours)
06046 
06047        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
06048        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
06049               c.teachersMatrixReady=true;
06050               c.subgroupsMatrixReady=true;
06051               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
06052               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
06053 
06054               c.changedForMatrixCalculation=false;
06055        }
06056        
06057        int nGaps;
06058        int tmp;
06059        
06060        int tIllegalGaps=0;
06061        
06062        for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
06063               nGaps=0;
06064               int i=this->iSubgroupsList.at(sg);
06065               for(int j=0; j<r.nDaysPerWeek; j++){
06066                      int k;
06067                      tmp=0;
06068                      for(k=0; k<r.nHoursPerDay; k++)
06069                             if(subgroupsMatrix[i][j][k]>0){
06070                                    assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
06071                                    break;
06072                             }
06073                      for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
06074                             if(subgroupsMatrix[i][j][k]>0){
06075                                    nGaps+=tmp;
06076                                    tmp=0;
06077                             }
06078                             else
06079                                    tmp++;
06080                      }
06081               }
06082               
06083               int illegalGaps=nGaps-this->maxGaps;
06084               if(illegalGaps<0)
06085                      illegalGaps=0;
06086 
06087               if(illegalGaps>0 && conflictsString!=NULL){
06088                      QString s=tr("Time constraint students set max gaps per week broken for subgroup: %1, extra gaps=%2, conflicts increase=%3")
06089                       .arg(r.internalSubgroupsList[i]->name)
06090                       .arg(illegalGaps)
06091                       .arg(CustomFETString::number(weightPercentage/100*illegalGaps));
06092                                            
06093                      dl.append(s);
06094                      cl.append(weightPercentage/100*illegalGaps);
06095                             
06096                      *conflictsString+= s+"\n";
06097               }
06098               
06099               tIllegalGaps+=illegalGaps;
06100        }
06101 
06102        if(c.nPlacedActivities==r.nInternalActivities)
06103               if(weightPercentage==100)     //for partial solutions it might be broken
06104                      assert(tIllegalGaps==0);
06105        return weightPercentage/100 * tIllegalGaps;
06106 }
06107 
06108 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
06109 {
06110        Q_UNUSED(r);
06111        Q_UNUSED(a);
06112 
06113        return false;
06114 }
06115 
06116 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
06117 {
06118        Q_UNUSED(t);
06119 
06120        return false;
06121 }
06122 
06123 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToSubject(Subject* s)
06124 {
06125        Q_UNUSED(s);
06126 
06127        return false;
06128 }
06129 
06130 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
06131 {
06132        Q_UNUSED(s);
06133 
06134        return false;
06135 }
06136 
06137 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
06138 {
06139        return r.setsShareStudents(this->students, s->name);
06140 }
06141 
06142 bool ConstraintStudentsSetMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
06143 {
06144        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
06145               return true;
06146        
06147        return false;
06148 }
06149 
06150 bool ConstraintStudentsSetMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
06151 {
06152        assert(hasWrongDayOrHour(r));
06153        
06154        return true;
06155 }
06156 
06157 bool ConstraintStudentsSetMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
06158 {
06159        assert(hasWrongDayOrHour(r));
06160        
06161        if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
06162               maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
06163 
06164        return true;
06165 }
06166 
06169 
06170 ConstraintStudentsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsEarlyMaxBeginningsAtSecondHour()
06171        : TimeConstraint()
06172 {
06173        this->type = CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
06174 }
06175 
06176 ConstraintStudentsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH)
06177        : TimeConstraint(wp)
06178 {
06179        this->type = CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
06180        this->maxBeginningsAtSecondHour=mBSH;
06181 }
06182 
06183 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
06184 {
06185        Q_UNUSED(parent);
06186        Q_UNUSED(r);
06187        
06188        return true;
06189 }
06190 
06191 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
06192 {
06193        Q_UNUSED(r);
06194        return false;
06195 }
06196 
06197 QString ConstraintStudentsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
06198 {
06199        Q_UNUSED(r);
06200 
06201        QString s="<ConstraintStudentsEarlyMaxBeginningsAtSecondHour>\n";
06202        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
06203        s+="   <Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
06204        s+="   <Active>"+trueFalse(active)+"</Active>\n";
06205        s+="   <Comments>"+protect(comments)+"</Comments>\n";
06206        s+="</ConstraintStudentsEarlyMaxBeginningsAtSecondHour>\n";
06207        return s;
06208 }
06209 
06210 QString ConstraintStudentsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
06211 {
06212        Q_UNUSED(r);
06213 
06214        QString begin=QString("");
06215        if(!active)
06216               begin="X - ";
06217               
06218        QString end=QString("");
06219        if(!comments.isEmpty())
06220               end=", "+tr("C: %1", "Comments").arg(comments);
06221               
06222        QString s;
06223        s+=tr("Students must arrive early, respecting maximum %1 arrivals at second hour")
06224         .arg(this->maxBeginningsAtSecondHour);
06225        s+=", ";
06226        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
06227 
06228        return begin+s+end;
06229 }
06230 
06231 QString ConstraintStudentsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
06232 {
06233        Q_UNUSED(r);
06234 
06235        QString s=tr("Time constraint");s+="\n";
06236        s+=tr("All students must begin their courses early, respecting maximum %1 later arrivals, at second hour")
06237         .arg(this->maxBeginningsAtSecondHour);s+="\n";
06238        s+=tr("(breaks and students set not available not counted)");s+="\n";
06239        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
06240 
06241        if(!active){
06242               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
06243               s+="\n";
06244        }
06245        if(!comments.isEmpty()){
06246               s+=tr("Comments=%1").arg(comments);
06247               s+="\n";
06248        }
06249 
06250        return s;
06251 }
06252 
06253 double ConstraintStudentsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
06254 {
06255        //considers the condition that the hours of subgroups begin as early as possible
06256 
06257        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
06258        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
06259               c.teachersMatrixReady=true;
06260               c.subgroupsMatrixReady=true;
06261               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
06262               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
06263 
06264               c.changedForMatrixCalculation=false;
06265        }
06266        
06267        int conflTotal=0;
06268        
06269        for(int i=0; i<r.nInternalSubgroups; i++){
06270               int nGapsFirstHour=0;
06271               for(int j=0; j<r.nDaysPerWeek; j++){
06272                      int k;
06273                      for(k=0; k<r.nHoursPerDay; k++)
06274                             if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
06275                                    break;
06276                             
06277                      bool firstHourOccupied=false;
06278                      if(k<r.nHoursPerDay && subgroupsMatrix[i][j][k]>0)
06279                             firstHourOccupied=true;
06280                                    
06281                      bool dayOccupied=firstHourOccupied;
06282                      
06283                      bool illegalGap=false;
06284                             
06285                      for(k++; k<r.nHoursPerDay && !dayOccupied; k++)
06286                             if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
06287                                    if(subgroupsMatrix[i][j][k]>0)
06288                                           dayOccupied=true;
06289                                    else
06290                                           illegalGap=true;
06291                             }
06292                             
06293                      if(dayOccupied && illegalGap){
06294                             if(conflictsString!=NULL){
06295                                    QString s=tr("Constraint students early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
06296                                     " because students have an illegal gap, increases conflicts total by %4")
06297                                     .arg(this->maxBeginningsAtSecondHour)
06298                                     .arg(r.internalSubgroupsList[i]->name)
06299                                     .arg(r.daysOfTheWeek[j])
06300                                     .arg(CustomFETString::number(1*weightPercentage/100));
06301                                     
06302                                    dl.append(s);
06303                                    cl.append(1*weightPercentage/100);
06304                                           
06305                                    *conflictsString+= s+"\n";
06306                                    
06307                                    conflTotal+=1;
06308                             }
06309                             
06310                             if(c.nPlacedActivities==r.nInternalActivities){
06311                                    assert(0);
06312                             }
06313                      }
06314                      
06315                      if(dayOccupied && !firstHourOccupied)
06316                             nGapsFirstHour++;
06317               }
06318               
06319               if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
06320                      if(conflictsString!=NULL){
06321                             QString s=tr("Constraint students early max %1 beginnings at second hour broken for subgroup %2,"
06322                              " because students have too many arrivals at second hour, increases conflicts total by %3")
06323                              .arg(this->maxBeginningsAtSecondHour)
06324                              .arg(r.internalSubgroupsList[i]->name)
06325                              .arg(CustomFETString::number((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
06326                              
06327                             dl.append(s);
06328                             cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
06329                                    
06330                             *conflictsString+= s+"\n";
06331                             
06332                             conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
06333                      }
06334                      
06335                      if(c.nPlacedActivities==r.nInternalActivities){
06336                             assert(0);
06337                      }
06338               }
06339        }
06340                                    
06341        if(c.nPlacedActivities==r.nInternalActivities)
06342               if(weightPercentage==100)    //might be broken for partial solutions
06343                      assert(conflTotal==0);
06344        return weightPercentage/100 * conflTotal;
06345 }
06346 
06347 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
06348 {
06349        Q_UNUSED(r);
06350        Q_UNUSED(a);
06351 
06352        return false;
06353 }
06354 
06355 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
06356 {
06357        Q_UNUSED(t);
06358 
06359        return false;
06360 }
06361 
06362 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
06363 {
06364        Q_UNUSED(s);
06365 
06366        return false;
06367 }
06368 
06369 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
06370 {
06371        Q_UNUSED(s);
06372 
06373        return false;
06374 }
06375 
06376 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
06377 {
06378        Q_UNUSED(r);
06379        Q_UNUSED(s);
06380 
06381        return true;
06382 }
06383 
06384 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
06385 {
06386        if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
06387               return true;
06388        
06389        return false;
06390 }
06391 
06392 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
06393 {
06394        assert(hasWrongDayOrHour(r));
06395        
06396        return true;
06397 }
06398 
06399 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
06400 {
06401        assert(hasWrongDayOrHour(r));
06402        
06403        if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
06404               maxBeginningsAtSecondHour=r.nDaysPerWeek;
06405 
06406        return true;
06407 }
06408 
06411 
06412 ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour()
06413        : TimeConstraint()
06414 {
06415        this->type = CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
06416 }
06417 
06418 ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour(double wp, int mBSH, const QString& students)
06419        : TimeConstraint(wp)
06420 {
06421        this->type = CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
06422        this->students=students;
06423        this->maxBeginningsAtSecondHour=mBSH;
06424 }
06425 
06426 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
06427 {
06428        StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
06429        
06430        if(ss==NULL){
06431               QMessageBox::warning(parent, tr("FET warning"),
06432                tr("Constraint students set early is wrong because it refers to inexistent students set."
06433                " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
06434                
06435               return false;
06436        }      
06437 
06438        assert(ss);
06439 
06440        this->iSubgroupsList.clear();
06441        if(ss->type==STUDENTS_SUBGROUP){
06442               int tmp;
06443               tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
06444               assert(tmp>=0);
06445               assert(tmp<r.nInternalSubgroups);
06446               if(!this->iSubgroupsList.contains(tmp))
06447                      this->iSubgroupsList.append(tmp);
06448        }
06449        else if(ss->type==STUDENTS_GROUP){
06450               StudentsGroup* stg=(StudentsGroup*)ss;
06451               for(int i=0; i<stg->subgroupsList.size(); i++){
06452                      StudentsSubgroup* sts=stg->subgroupsList[i];
06453                      int tmp;
06454                      tmp=sts->indexInInternalSubgroupsList;
06455                      assert(tmp>=0);
06456                      assert(tmp<r.nInternalSubgroups);
06457                      if(!this->iSubgroupsList.contains(tmp))
06458                             this->iSubgroupsList.append(tmp);
06459               }
06460        }
06461        else if(ss->type==STUDENTS_YEAR){
06462               StudentsYear* sty=(StudentsYear*)ss;
06463               for(int i=0; i<sty->groupsList.size(); i++){
06464                      StudentsGroup* stg=sty->groupsList[i];
06465                      for(int j=0; j<stg->subgroupsList.size(); j++){
06466                             StudentsSubgroup* sts=stg->subgroupsList[j];
06467                             int tmp;
06468                             tmp=sts->indexInInternalSubgroupsList;
06469                             assert(tmp>=0);
06470                             assert(tmp<r.nInternalSubgroups);
06471                             if(!this->iSubgroupsList.contains(tmp))
06472                                    this->iSubgroupsList.append(tmp);
06473                      }
06474               }
06475        }
06476        else
06477               assert(0);
06478        return true;
06479 }
06480 
06481 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
06482 {
06483        Q_UNUSED(r);
06484        return false;
06485 }
06486 
06487 QString ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
06488 {
06489        Q_UNUSED(r);
06490 
06491        QString s="<ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour>\n";
06492        s+="   <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
06493        s+="   <Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
06494        s+="   <Students>"+protect(this->students)+"</Students>\n";
06495        s+="   <Active>"+trueFalse(active)+"</Active>\n";
06496        s+="   <Comments>"+protect(comments)+"</Comments>\n";
06497        s+="</ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour>\n";
06498        return s;
06499 }
06500 
06501 QString ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
06502 {
06503        Q_UNUSED(r);
06504 
06505        QString begin=QString("");
06506        if(!active)
06507               begin="X - ";
06508               
06509        QString end=QString("");
06510        if(!comments.isEmpty())
06511               end=", "+tr("C: %1", "Comments").arg(comments);
06512               
06513        QString s;
06514 
06515        s+=tr("Students set must arrive early, respecting maximum %1 arrivals at second hour")
06516         .arg(this->maxBeginningsAtSecondHour); s+=", ";
06517        s+=tr("WP:%1\%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
06518        s+=tr("St:%1", "Students set").arg(this->students);
06519 
06520        return begin+s+end;
06521 }
06522 
06523 QString ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
06524 {
06525        Q_UNUSED(r);
06526 
06527        QString s=tr("Time constraint");s+="\n";
06528 
06529        s+=tr("A students set must begin its courses early, respecting a maximum number of later arrivals, at second hour"); s+="\n";
06530        s+=tr("(breaks and students set not available not counted)");s+="\n";
06531        s+=tr("Weight (percentage)=%1\%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
06532        s+=tr("Students set=%1").arg(this->students); s+="\n";
06533        s+=tr("Maximum number of arrivals at the second hour=%1").arg(this->maxBeginningsAtSecondHour);s+="\n";
06534 
06535        if(!active){
06536               s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
06537               s+="\n";
06538        }
06539        if(!comments.isEmpty()){
06540               s+=tr("Comments=%1").arg(comments);
06541               s+="\n";
06542        }
06543 
06544        return s;
06545 }
06546 
06547 double ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, QString* conflictsString)
06548 {
06549        //considers the condition that the hours of subgroups begin as early as possible
06550 
06551        //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
06552        if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
06553               c.teachersMatrixReady=true;
06554               c.subgroupsMatrixReady=true;
06555               subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
06556               teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
06557 
06558               c.changedForMatrixCalculation=false;
06559        }
06560        
06561        int conflTotal=0;
06562 
06563        foreach(int i, this->iSubgroupsList){
06564               int nGapsFirstHour=0;
06565               for(int j=0; j<r.nDaysPerWeek; j++){
06566                      int k;
06567                      for(k=0; k<r.nHoursPerDay; k++)
06568                             if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
06569                                    break;
06570                             
06571                      bool firstHourOccupied=false;
06572                      if(k<r.nHoursPerDay && subgroupsMatrix[i][j][k]>0)
06573                             firstHourOccupied=true;
06574                                    
06575                      bool dayOccupied=firstHourOccupied;
06576                      
06577                      bool illegalGap=false;
06578                             
06579                      for(k++; k<r.nHoursPerDay && !dayOccupied; k++)
06580                             if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
06581                                    if(subgroupsMatrix[i][j][k]>0)
06582                                           dayOccupied=true;
06583                                    else
06584                                           illegalGap=true;
06585                             }
06586                             
06587                      if(dayOccupied && illegalGap){
06588                             if(conflictsString!=NULL){
06589                                    QString s=tr("Constraint students set early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
06590                                     " because students have an illegal gap, increases conflicts total by %4")
06591                                     .arg(this->maxBeginningsAtSecondHour)
06592                                     .arg(r.internalSubgroupsList[i]->name)
06593                                     .arg(r.daysOfTheWeek[j])
06594                                     .arg(CustomFETString::number(1*weightPercentage/100));
06595                                     
06596                                    dl.append(s);
06597                                    cl.append(1*weightPercentage/100);
06598                                           
06599                                    *conflictsString+= s+"\n";
06600                                    
06601                                    conflTotal+=1;
06602                             }
06603                             
06604                             if(c.nPlacedActivities==r.nInternalActivities)
06605                                    assert(0);
06606                      }
06607                      
06608                      if(dayOccupied && !firstHourOccupied)
06609                             nGapsFirstHour++;
06610               }
06611               
06612               if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
06613                      if(conflictsString!=NULL){
06614                             QString s=tr("Constraint students set early max %1 beginnings at second hour broken for subgroup %2,"
06615                              " because students have too many arrivals at second hour, increases conflicts total by %3")
06616                              .arg(this->maxBeginningsAtSecondHour)
06617                              .arg(r.internalSubgroupsList[i]->name)
06618                              .arg(CustomFETString::number((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
06619                              
06620                             dl.append(s);
06621                             cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
06622                                    
06623                             *conflictsString+= s+"\n";
06624                             
06625                             conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
06626                      }
06627                      
06628                      if(c.nPlacedActivities==r.nInternalActivities)
06629                             assert(0);
06630               }
06631        }
06632                                    
06633        if(c.nPlacedActivities==r.nInternalActivities)
06634               if(weightPercentage==100)    //might be broken for partial solutions
06635                      assert(conflTotal==0);
06636        return weightPercentage/100 * conflTotal;
06637 }
06638 
06639 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
06640 {
06641        Q_UNUSED(r);
06642        Q_UNUSED(a);
06643 
06644        return false;
06645 }
06646 
06647 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
06648 {
06649        Q_UNUSED(t);
06650 
06651        return false;
06652 }
06653 
06654 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
06655 {
06656        Q_UNUSED(s);
06657 
06658        return false;
06659 }
06660 
06661 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
06662 {
06663        Q_UNUSED(s);
06664 
06665        return false;
06666 }
06667 
06668 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
06669 {
06670        return r.setsShareStudents(this->students, s->name);
06671 }
06672 
06673 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
06674 {
06675        if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
06676               return true;
06677        
06678        return false;
06679 }
06680 
06681 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
06682 {
06683        assert(hasWrongDayOrHour(r));
06684        
06685        return true;
06686 }
06687 
06688 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
06689 {
06690        assert(hasWrongDayOrHour(r));
06691        
06692        if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
06693               maxBeginningsAtSecondHour=r.nDaysPerWeek;
06694 
06695        return true;
06696 }
06697 
06700 
06701 ConstraintStudentsMaxHoursDaily::ConstraintStudentsMaxHoursDaily()
06702        : TimeConstraint()
06703 {
06704        this->type = CONSTRAINT_STUDENTS_MAX_HOURS_DAILY;
06705        this->maxHoursDaily = -1;
06706 }
06707 
06708 ConstraintStudentsMaxHoursDaily::ConstraintStudentsMaxHoursDaily(double wp, int maxnh)
06709        : TimeConstraint(wp)
06710 {
06711        this->maxHoursDaily = maxnh;
06712        this->type = CONSTRAINT_STUDENTS_MAX_HOURS_DAILY;
06713 }
06714 
06715 bool ConstraintStudentsMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
06716 {
06717        Q_UNUSED(parent);
06718        Q_UNUSED(r);
06719        
06720        return true;
06721 }
06722 
06723 bool ConstraintStudentsMaxHoursDaily::hasInactiveActivities(