"Fossies" - the Fresh Open Source Software Archive

Member "fet-5.45.1/src/engine/timeconstraint.cpp" (21 Jun 2020, 686230 Bytes) of package /linux/privat/fet-5.45.1.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "timeconstraint.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.45.0_vs_5.45.1.

    1 /*
    2 File timeconstraint.cpp
    3 */
    4 
    5 /***************************************************************************
    6                           timeconstraint.cpp  -  description
    7                              -------------------
    8     begin                : 2002
    9     copyright            : (C) 2002 by Lalescu Liviu
   10     email                : Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)
   11  ***************************************************************************/
   12 
   13 /***************************************************************************
   14  *                                                                         *
   15  *   This program is free software: you can redistribute it and/or modify  *
   16  *   it under the terms of the GNU Affero General Public License as        *
   17  *   published by the Free Software Foundation, either version 3 of the    *
   18  *   License, or (at your option) any later version.                       *
   19  *                                                                         *
   20  ***************************************************************************/
   21 
   22 #include "timetable_defs.h"
   23 #include "timeconstraint.h"
   24 #include "rules.h"
   25 #include "solution.h"
   26 #include "activity.h"
   27 #include "teacher.h"
   28 #include "subject.h"
   29 #include "activitytag.h"
   30 #include "studentsset.h"
   31 
   32 #include "matrix.h"
   33 
   34 #include <QString>
   35 
   36 #include "messageboxes.h"
   37 
   38 #include <QSet>
   39 
   40 //for min max functions
   41 #include <algorithm>
   42 //using namespace std;
   43 
   44 static QString trueFalse(bool x){
   45     if(!x)
   46         return QString("false");
   47     else
   48         return QString("true");
   49 }
   50 
   51 static QString yesNoTranslated(bool x){
   52     if(!x)
   53         return QCoreApplication::translate("TimeConstraint", "no", "no - meaning negation");
   54     else
   55         return QCoreApplication::translate("TimeConstraint", "yes", "yes - meaning affirmative");
   56 }
   57 
   58 //The following 2 matrices are kept to make the computation faster
   59 //They are calculated only at the beginning of the computation of the fitness
   60 //of the solution.
   61 /*static qint8 subgroupsMatrix[MAX_TOTAL_SUBGROUPS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
   62 static qint8 teachersMatrix[MAX_TEACHERS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];*/
   63 static Matrix3D<int> subgroupsMatrix;
   64 static Matrix3D<int> teachersMatrix;
   65 
   66 static int teachers_conflicts=-1;
   67 static int subgroups_conflicts=-1;
   68 
   69 //extern bool breakDayHour[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
   70 extern Matrix2D<bool> breakDayHour;
   71 
   72 /*extern bool teacherNotAvailableDayHour[MAX_TEACHERS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
   73 
   74 extern bool subgroupNotAvailableDayHour[MAX_TOTAL_SUBGROUPS][MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];*/
   75 extern Matrix3D<bool> teacherNotAvailableDayHour;
   76 
   77 extern Matrix3D<bool> subgroupNotAvailableDayHour;
   78 
   79 /////////////////////////////////////////////////////////////////////////////////////////////
   80 /////////////////////////////////////////////////////////////////////////////////////////////
   81 
   82 QString getActivityDetailedDescription(Rules& r, int id)
   83 {
   84     QString s;
   85 
   86     /*int ai;
   87     for(ai=0; ai<r.activitiesList.size(); ai++)
   88         if(r.activitiesList[ai]->id==id)
   89             break;
   90     
   91     if(ai==r.activitiesList.size()){
   92         s+=QCoreApplication::translate("Activity", "Invalid (inexistent) id for activity");
   93         return s;
   94     }
   95     assert(ai<r.activitiesList.size());
   96 
   97     Activity* act=r.activitiesList.at(ai);*/
   98 
   99     Activity* act=r.activitiesPointerHash.value(id, NULL);
  100     if(act==NULL){
  101         s+=QCoreApplication::translate("Activity", "Invalid (inexistent) id for activity");
  102         return s;
  103     }
  104 
  105     if(act->activityTagsNames.count()>0){
  106         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)."
  107          "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(","));
  108     }
  109     else{
  110         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)."
  111          "The abbreviations are: Teachers, Subject, Students. There are no activity tags here").arg(act->teachersNames.join(",")).arg(act->subjectName).arg(act->studentsNames.join(","));
  112     }
  113     return s;
  114 }
  115 
  116 void populateInternalSubgroupsList(const Rules& r, const StudentsSet* ss, QList<int>& iSubgroupsList){
  117     iSubgroupsList.clear();
  118     
  119     QSet<int> tmpSet;
  120     
  121     if(ss->type==STUDENTS_SUBGROUP){
  122         int tmp;
  123         tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
  124         assert(tmp>=0);
  125         assert(tmp<r.nInternalSubgroups);
  126         if(!tmpSet.contains(tmp)){
  127             tmpSet.insert(tmp);
  128             iSubgroupsList.append(tmp);
  129         }
  130     }
  131     else if(ss->type==STUDENTS_GROUP){
  132         StudentsGroup* stg=(StudentsGroup*)ss;
  133         for(int i=0; i<stg->subgroupsList.size(); i++){
  134             StudentsSubgroup* sts=stg->subgroupsList[i];
  135             int tmp;
  136             tmp=sts->indexInInternalSubgroupsList;
  137             assert(tmp>=0);
  138             assert(tmp<r.nInternalSubgroups);
  139             if(!tmpSet.contains(tmp)){
  140                 tmpSet.insert(tmp);
  141                 iSubgroupsList.append(tmp);
  142             }
  143         }
  144     }
  145     else if(ss->type==STUDENTS_YEAR){
  146         StudentsYear* sty=(StudentsYear*)ss;
  147         for(int i=0; i<sty->groupsList.size(); i++){
  148             StudentsGroup* stg=sty->groupsList[i];
  149             for(int j=0; j<stg->subgroupsList.size(); j++){
  150                 StudentsSubgroup* sts=stg->subgroupsList[j];
  151                 int tmp;
  152                 tmp=sts->indexInInternalSubgroupsList;
  153                 assert(tmp>=0);
  154                 assert(tmp<r.nInternalSubgroups);
  155                 if(!tmpSet.contains(tmp)){
  156                     tmpSet.insert(tmp);
  157                     iSubgroupsList.append(tmp);
  158                 }
  159             }
  160         }
  161     }
  162     else
  163         assert(0);
  164 }
  165 
  166 TimeConstraint::TimeConstraint()
  167 {
  168     type=CONSTRAINT_GENERIC_TIME;
  169     
  170     active=true;
  171     comments=QString("");
  172 }
  173 
  174 TimeConstraint::~TimeConstraint()
  175 {
  176 }
  177 
  178 TimeConstraint::TimeConstraint(double wp)
  179 {
  180     type=CONSTRAINT_GENERIC_TIME;
  181 
  182     weightPercentage=wp;
  183     assert(wp<=100 && wp>=0);
  184 
  185     active=true;
  186     comments=QString("");
  187 }
  188 
  189 /////////////////////////////////////////////////////////////////////////////////////////////
  190 /////////////////////////////////////////////////////////////////////////////////////////////
  191 
  192 ConstraintBasicCompulsoryTime::ConstraintBasicCompulsoryTime(): TimeConstraint()
  193 {
  194     this->type=CONSTRAINT_BASIC_COMPULSORY_TIME;
  195     this->weightPercentage=100;
  196 }
  197 
  198 ConstraintBasicCompulsoryTime::ConstraintBasicCompulsoryTime(double wp): TimeConstraint(wp)
  199 {
  200     this->type=CONSTRAINT_BASIC_COMPULSORY_TIME;
  201 }
  202 
  203 bool ConstraintBasicCompulsoryTime::computeInternalStructure(QWidget* parent, Rules& r)
  204 {
  205     Q_UNUSED(parent);
  206     Q_UNUSED(r);
  207     
  208     return true;
  209 }
  210 
  211 bool ConstraintBasicCompulsoryTime::hasInactiveActivities(Rules& r)
  212 {
  213     Q_UNUSED(r);
  214     return false;
  215 }
  216 
  217 QString ConstraintBasicCompulsoryTime::getXmlDescription(Rules& r)
  218 {
  219     Q_UNUSED(r);
  220 
  221     QString s = "<ConstraintBasicCompulsoryTime>\n";
  222     assert(this->weightPercentage==100.0);
  223     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
  224     s+="    <Active>"+trueFalse(active)+"</Active>\n";
  225     s+="    <Comments>"+protect(comments)+"</Comments>\n";
  226     s+="</ConstraintBasicCompulsoryTime>\n";
  227     return s;
  228 }
  229 
  230 QString ConstraintBasicCompulsoryTime::getDescription(Rules& r)
  231 {
  232     Q_UNUSED(r);
  233 
  234     QString begin=QString("");
  235     if(!active)
  236         begin="X - ";
  237         
  238     QString end=QString("");
  239     if(!comments.isEmpty())
  240         end=", "+tr("C: %1", "Comments").arg(comments);
  241         
  242     return begin+tr("Basic compulsory constraints (time)") + ", " + tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage))+end;
  243 }
  244 
  245 QString ConstraintBasicCompulsoryTime::getDetailedDescription(Rules& r)
  246 {
  247     Q_UNUSED(r);
  248 
  249     QString s=tr("These are the basic compulsory constraints (referring to time allocation) for any timetable");
  250     s+="\n";
  251 
  252     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
  253     s+=tr("The basic time constraints try to avoid:");s+="\n";
  254     s+=QString("- ");s+=tr("teachers assigned to more than one activity simultaneously");s+="\n";
  255     s+=QString("- ");s+=tr("students assigned to more than one activity simultaneously");s+="\n";
  256     
  257     if(!active){
  258         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
  259         s+="\n";
  260     }
  261     if(!comments.isEmpty()){
  262         s+=tr("Comments=%1").arg(comments);
  263         s+="\n";
  264     }
  265 
  266     return s;
  267 }
  268 
  269 double ConstraintBasicCompulsoryTime::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString){
  270     assert(r.internalStructureComputed);
  271 
  272     int teachersConflicts, subgroupsConflicts;
  273     
  274     assert(weightPercentage==100.0);
  275 
  276     //This constraint fitness calculation routine is called firstly,
  277     //so we can compute the teacher and subgroups conflicts faster this way.
  278     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
  279         c.teachersMatrixReady=true;
  280         c.subgroupsMatrixReady=true;
  281     
  282         subgroups_conflicts = subgroupsConflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
  283         teachers_conflicts = teachersConflicts = c.getTeachersMatrix(r, teachersMatrix);
  284 
  285         c.changedForMatrixCalculation=false;
  286     }
  287     else{
  288         assert(subgroups_conflicts>=0);
  289         assert(teachers_conflicts>=0);
  290         subgroupsConflicts = subgroups_conflicts;
  291         teachersConflicts = teachers_conflicts;
  292     }
  293 
  294     int i,dd;
  295 
  296     qint64 unallocated; //unallocated activities
  297     int late; //late activities
  298     int nte; //number of teacher exhaustions
  299     int nse; //number of students exhaustions
  300 
  301     //Part without logging..................................................................
  302     if(conflictsString==NULL){
  303         //Unallocated or late activities
  304         unallocated=0;
  305         late=0;
  306         for(i=0; i<r.nInternalActivities; i++){
  307             if(c.times[i]==UNALLOCATED_TIME){
  308                 //Firstly, we consider a big clash each unallocated activity.
  309                 //Needs to be very a large constant, bigger than any other broken constraint.
  310                 //Take care: MAX_ACTIVITIES*this_constant <= INT_MAX
  311                 unallocated += /*r.internalActivitiesList[i].duration * r.internalActivitiesList[i].nSubgroups * */ 10000;
  312                 //(an unallocated activity for a year is more important than an unallocated activity for a subgroup)
  313             }
  314             else{
  315                 //Calculates the number of activities that are scheduled too late (in fact we
  316                 //calculate a function that increases as the activity is getting late)
  317                 int h=c.times[i]/r.nDaysPerWeek;
  318                 dd=r.internalActivitiesList[i].duration;
  319                 if(h+dd>r.nHoursPerDay){
  320                     int tmp;
  321                     tmp=1;
  322                     late += (h+dd-r.nHoursPerDay) * tmp * r.internalActivitiesList[i].iSubgroupsList.count();
  323                     //multiplied with the number
  324                     //of subgroups implied, for seeing the importance of the
  325                     //activity
  326                 }
  327             }
  328         }
  329         
  330         assert(late==0);
  331 
  332         //Below, for teachers and students, please remember that 2 means a weekly activity
  333         //and 1 fortnightly one. So, if the matrix teachersMatrix[teacher][day][hour]==2, it is ok.
  334 
  335         //Calculates the number of teachers exhaustion (when he has to teach more than
  336         //one activity at the same time)
  337         /*nte=0;
  338         for(i=0; i<r.nInternalTeachers; i++)
  339             for(int j=0; j<r.nDaysPerWeek; j++)
  340                 for(int k=0; k<r.nHoursPerDay; k++){
  341                     int tmp=teachersMatrix[i][j][k]-2;
  342                     if(tmp>0)
  343                         nte+=tmp;
  344                 }*/
  345         nte = teachersConflicts; //faster
  346         
  347         assert(nte==0);
  348 
  349         //Calculates the number of subgroups exhaustion (a subgroup cannot attend two
  350         //activities at the same time)
  351         /*nse=0;
  352         for(i=0; i<r.nInternalSubgroups; i++)
  353             for(int j=0; j<r.nDaysPerWeek; j++)
  354                 for(int k=0; k<r.nHoursPerDay; k++){
  355                     int tmp=subgroupsMatrix[i][j][k]-2;
  356                     if(tmp>0)
  357                         nse += tmp;
  358                 }*/
  359         nse = subgroupsConflicts; //faster
  360         
  361         assert(nse==0);         
  362     }
  363     //part with logging....................................................................
  364     else{
  365         //Unallocated or late activities
  366         unallocated=0;
  367         late=0;
  368         for(i=0; i<r.nInternalActivities; i++){
  369             if(c.times[i]==UNALLOCATED_TIME){
  370                 //Firstly, we consider a big clash each unallocated activity.
  371                 //Needs to be very a large constant, bigger than any other broken constraint.
  372                 //Take care: MAX_ACTIVITIES*this_constant <= INT_MAX
  373                 unallocated += /*r.internalActivitiesList[i].duration * r.internalActivitiesList[i].nSubgroups * */ 10000;
  374                 //(an unallocated activity for a year is more important than an unallocated activity for a subgroup)
  375                 if(conflictsString!=NULL){
  376                     QString s= tr("Time constraint basic compulsory broken: unallocated activity with id=%1 (%2)",
  377                         "%2 is the detailed description of activity - teachers, subject, students")
  378                         .arg(r.internalActivitiesList[i].id).arg(getActivityDetailedDescription(r, r.internalActivitiesList[i].id));
  379                     s+=" - ";
  380                     s += tr("this increases the conflicts total by %1")
  381                      .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100 * 10000));
  382                     //s += "\n";
  383                     
  384                     dl.append(s);
  385                     cl.append(weightPercentage/100 * 10000);
  386 
  387                     (*conflictsString) += s + "\n";
  388                 }
  389             }
  390             else{
  391                 //Calculates the number of activities that are scheduled too late (in fact we
  392                 //calculate a function that increases as the activity is getting late)
  393                 int h=c.times[i]/r.nDaysPerWeek;
  394                 dd=r.internalActivitiesList[i].duration;
  395                 if(h+dd>r.nHoursPerDay){
  396                     assert(0);  
  397                 
  398                     int tmp;
  399                     tmp=1;
  400                     late += (h+dd-r.nHoursPerDay) * tmp * r.internalActivitiesList[i].iSubgroupsList.count();
  401                     //multiplied with the number
  402                     //of subgroups implied, for seeing the importance of the
  403                     //activity
  404 
  405                     if(conflictsString!=NULL){
  406                         QString s=tr("Time constraint basic compulsory");
  407                         s+=": ";
  408                         s+=tr("activity with id=%1 is late.")
  409                          .arg(r.internalActivitiesList[i].id);
  410                         s+=" ";
  411                         s+=tr("This increases the conflicts total by %1")
  412                          .arg(CustomFETString::numberPlusTwoDigitsPrecision((h+dd-r.nHoursPerDay)*tmp*r.internalActivitiesList[i].iSubgroupsList.count()*weightPercentage/100));
  413                         s+="\n";
  414                         
  415                         dl.append(s);
  416                         cl.append((h+dd-r.nHoursPerDay)*tmp*r.internalActivitiesList[i].iSubgroupsList.count()*weightPercentage/100);
  417 
  418                         (*conflictsString) += s+"\n";
  419                     }
  420                 }
  421             }
  422         }
  423 
  424         //Below, for teachers and students, please remember that 2 means a weekly activity
  425         //and 1 fortnightly one. So, if the matrix teachersMatrix[teacher][day][hour]==2,
  426         //that is ok.
  427 
  428         //Calculates the number of teachers exhaustion (when he has to teach more than
  429         //one activity at the same time)
  430         nte=0;
  431         for(i=0; i<r.nInternalTeachers; i++)
  432             for(int j=0; j<r.nDaysPerWeek; j++)
  433                 for(int k=0; k<r.nHoursPerDay; k++){
  434                     int tmp=teachersMatrix[i][j][k]-1;
  435                     if(tmp>0){
  436                         if(conflictsString!=NULL){
  437                             QString s=tr("Time constraint basic compulsory");
  438                             s+=": ";
  439                             s+=tr("teacher with name %1 has more than one allocated activity on day %2, hour %3")
  440                              .arg(r.internalTeachersList[i]->name)
  441                              .arg(r.daysOfTheWeek[j])
  442                              .arg(r.hoursOfTheDay[k]);
  443                             s+=". ";
  444                             s+=tr("This increases the conflicts total by %1")
  445                              .arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
  446                         
  447                             (*conflictsString)+= s+"\n";
  448                             
  449                             dl.append(s);
  450                             cl.append(tmp*weightPercentage/100);
  451                         }
  452                         nte+=tmp;
  453                     }
  454                 }
  455 
  456         assert(nte==0);
  457         
  458         //Calculates the number of subgroups exhaustion (a subgroup cannot attend two
  459         //activities at the same time)
  460         nse=0;
  461         for(i=0; i<r.nInternalSubgroups; i++)
  462             for(int j=0; j<r.nDaysPerWeek; j++)
  463                 for(int k=0; k<r.nHoursPerDay; k++){
  464                     int tmp=subgroupsMatrix[i][j][k]-1;
  465                     if(tmp>0){
  466                         if(conflictsString!=NULL){
  467                             QString s=tr("Time constraint basic compulsory");
  468                             s+=": ";
  469                             s+=tr("subgroup %1 has more than one allocated activity on day %2, hour %3")
  470                              .arg(r.internalSubgroupsList[i]->name)
  471                              .arg(r.daysOfTheWeek[j])
  472                              .arg(r.hoursOfTheDay[k]);
  473                             s+=". ";
  474                             s+=tr("This increases the conflicts total by %1")
  475                              .arg(CustomFETString::numberPlusTwoDigitsPrecision((subgroupsMatrix[i][j][k]-1)*weightPercentage/100));
  476                             
  477                             dl.append(s);
  478                             cl.append((subgroupsMatrix[i][j][k]-1)*weightPercentage/100);
  479                         
  480                             *conflictsString += s+"\n";
  481                         }
  482                         nse += tmp;
  483                     }
  484                 }
  485             
  486         assert(nse==0);
  487     }
  488 
  489     /*if(nte!=teachersConflicts){
  490         cout<<"nte=="<<nte<<", teachersConflicts=="<<teachersConflicts<<endl;
  491         cout<<c.getTeachersMatrix(r, teachersMatrix)<<endl;
  492     }
  493     if(nse!=subgroupsConflicts){
  494         cout<<"nse=="<<nse<<", subgroupsConflicts=="<<subgroupsConflicts<<endl;
  495         cout<<c.getSubgroupsMatrix(r, subgroupsMatrix)<<endl;
  496     }*/
  497     
  498     /*assert(nte==teachersConflicts); //just a check, works only on logged fitness calculation
  499     assert(nse==subgroupsConflicts);*/
  500 
  501     return weightPercentage/100 * (unallocated + qint64(late) + qint64(nte) + qint64(nse)); //conflicts factor
  502 }
  503 
  504 bool ConstraintBasicCompulsoryTime::isRelatedToActivity(Rules& r, Activity* a)
  505 {
  506     Q_UNUSED(a);
  507     Q_UNUSED(r);
  508 
  509     return false;
  510 }
  511 
  512 bool ConstraintBasicCompulsoryTime::isRelatedToTeacher(Teacher* t)
  513 {
  514     Q_UNUSED(t);
  515 
  516     return false;
  517 }
  518 
  519 bool ConstraintBasicCompulsoryTime::isRelatedToSubject(Subject* s)
  520 {
  521     Q_UNUSED(s);
  522 
  523     return false;
  524 }
  525 
  526 bool ConstraintBasicCompulsoryTime::isRelatedToActivityTag(ActivityTag* s)
  527 {
  528     Q_UNUSED(s);
  529 
  530     return false;
  531 }
  532 
  533 bool ConstraintBasicCompulsoryTime::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
  534 {
  535     Q_UNUSED(r);
  536     Q_UNUSED(s);
  537 
  538     return false;
  539 }
  540 
  541 bool ConstraintBasicCompulsoryTime::hasWrongDayOrHour(Rules& r)
  542 {
  543     Q_UNUSED(r);
  544     return false;
  545 }
  546 
  547 bool ConstraintBasicCompulsoryTime::canRepairWrongDayOrHour(Rules& r)
  548 {
  549     Q_UNUSED(r);
  550     assert(0);
  551     
  552     return true;
  553 }
  554 
  555 bool ConstraintBasicCompulsoryTime::repairWrongDayOrHour(Rules& r)
  556 {
  557     Q_UNUSED(r);
  558     assert(0); //should check hasWrongDayOrHour, firstly
  559 
  560     return true;
  561 }
  562 
  563 /////////////////////////////////////////////////////////////////////////////////////////////
  564 /////////////////////////////////////////////////////////////////////////////////////////////
  565 
  566 ConstraintTeacherNotAvailableTimes::ConstraintTeacherNotAvailableTimes()
  567     : TimeConstraint()
  568 {
  569     this->type=CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES;
  570 }
  571 
  572 ConstraintTeacherNotAvailableTimes::ConstraintTeacherNotAvailableTimes(double wp, const QString& tn, QList<int> d, QList<int> h)
  573     : TimeConstraint(wp)
  574 {
  575     this->teacher=tn;
  576     assert(d.count()==h.count());
  577     this->days=d;
  578     this->hours=h;
  579     this->type=CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES;
  580 }
  581 
  582 QString ConstraintTeacherNotAvailableTimes::getXmlDescription(Rules& r){
  583     QString s="<ConstraintTeacherNotAvailableTimes>\n";
  584     s+="    <Weight_Percentage>"+CustomFETString::number(weightPercentage)+"</Weight_Percentage>\n";
  585     s+="    <Teacher>"+protect(this->teacher)+"</Teacher>\n";
  586 
  587     s+="    <Number_of_Not_Available_Times>"+CustomFETString::number(this->days.count())+"</Number_of_Not_Available_Times>\n";
  588     assert(days.count()==hours.count());
  589     for(int i=0; i<days.count(); i++){
  590         s+="    <Not_Available_Time>\n";
  591         if(this->days.at(i)>=0)
  592             s+="        <Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
  593         if(this->hours.at(i)>=0)
  594             s+="        <Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
  595         s+="    </Not_Available_Time>\n";
  596     }
  597 
  598     s+="    <Active>"+trueFalse(active)+"</Active>\n";
  599     s+="    <Comments>"+protect(comments)+"</Comments>\n";
  600     s+="</ConstraintTeacherNotAvailableTimes>\n";
  601     return s;
  602 }
  603 
  604 QString ConstraintTeacherNotAvailableTimes::getDescription(Rules& r){
  605     QString begin=QString("");
  606     if(!active)
  607         begin="X - ";
  608         
  609     QString end=QString("");
  610     if(!comments.isEmpty())
  611         end=", "+tr("C: %1", "Comments").arg(comments);
  612         
  613     QString s=tr("Teacher not available");s+=", ";
  614     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
  615     s+=tr("T:%1", "Teacher").arg(this->teacher);s+=", ";
  616 
  617     s+=tr("NA at:", "Not available at");
  618     s+=" ";
  619     assert(days.count()==hours.count());
  620     for(int i=0; i<days.count(); i++){
  621         if(this->days.at(i)>=0){
  622             s+=r.daysOfTheWeek[this->days.at(i)];
  623             s+=" ";
  624         }
  625         if(this->hours.at(i)>=0){
  626             s+=r.hoursOfTheDay[this->hours.at(i)];
  627         }
  628         if(i<days.count()-1)
  629             s+="; ";
  630     }
  631 
  632     return begin+s+end;
  633 }
  634 
  635 QString ConstraintTeacherNotAvailableTimes::getDetailedDescription(Rules& r){
  636     QString s=tr("Time constraint");s+="\n";
  637     s+=tr("A teacher is not available");s+="\n";
  638     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
  639     s+=tr("Teacher=%1").arg(this->teacher);s+="\n";
  640 
  641     s+=tr("Not available at:", "It refers to a teacher");
  642     s+="\n";
  643     assert(days.count()==hours.count());
  644     for(int i=0; i<days.count(); i++){
  645         if(this->days.at(i)>=0){
  646             s+=r.daysOfTheWeek[this->days.at(i)];
  647             s+=" ";
  648         }
  649         if(this->hours.at(i)>=0){
  650             s+=r.hoursOfTheDay[this->hours.at(i)];
  651         }
  652         if(i<days.count()-1)
  653             s+="; ";
  654     }
  655     s+="\n";
  656 
  657     if(!active){
  658         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
  659         s+="\n";
  660     }
  661     if(!comments.isEmpty()){
  662         s+=tr("Comments=%1").arg(comments);
  663         s+="\n";
  664     }
  665 
  666     return s;
  667 }
  668 
  669 bool ConstraintTeacherNotAvailableTimes::computeInternalStructure(QWidget* parent, Rules& r){
  670     //this->teacher_ID=r.searchTeacher(this->teacher);
  671     teacher_ID=r.teachersHash.value(teacher, -1);
  672 
  673     if(this->teacher_ID<0){
  674         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
  675          tr("Constraint teacher not available times is wrong because it refers to inexistent teacher."
  676          " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
  677         
  678         return false;
  679     }
  680 
  681     assert(days.count()==hours.count());
  682     for(int k=0; k<days.count(); k++){
  683         if(this->days.at(k) >= r.nDaysPerWeek){
  684             TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
  685              tr("Constraint teacher not available times is wrong because it refers to removed day. Please correct"
  686              " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
  687         
  688             return false;
  689         }       
  690         if(this->hours.at(k) >= r.nHoursPerDay){
  691             TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
  692              tr("Constraint teacher not available times is wrong because an hour is too late (after the last acceptable slot). Please correct"
  693              " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
  694         
  695             return false;
  696         }
  697     }
  698 
  699     assert(this->teacher_ID>=0);
  700     return true;
  701 }
  702 
  703 bool ConstraintTeacherNotAvailableTimes::hasInactiveActivities(Rules& r)
  704 {
  705     Q_UNUSED(r);
  706     return false;
  707 }
  708 
  709 double ConstraintTeacherNotAvailableTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
  710 {
  711     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
  712     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
  713         c.teachersMatrixReady=true;
  714         c.subgroupsMatrixReady=true;
  715 
  716         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
  717         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
  718 
  719         c.changedForMatrixCalculation=false;
  720     }
  721 
  722     //Calculates the number of hours when the teacher is supposed to be teaching, but he is not available
  723     //This function consideres all the hours, I mean if there are for example 5 weekly courses
  724     //scheduled on that hour (which is already a broken compulsory restriction - we only
  725     //are allowed 1 weekly course for a certain teacher at a certain hour) we calculate
  726     //5 broken restrictions for that function.
  727     //TODO: decide if it is better to consider only 2 or 10 as a return value in this particular case
  728     //(currently it is 10)
  729     int tch=this->teacher_ID;
  730 
  731     int nbroken;
  732 
  733     nbroken=0;
  734 
  735     assert(days.count()==hours.count());
  736     for(int k=0; k<days.count(); k++){
  737         int d=days.at(k);
  738         int h=hours.at(k);
  739         
  740         if(teachersMatrix[tch][d][h]>0){
  741             nbroken+=teachersMatrix[tch][d][h];
  742     
  743             if(conflictsString!=NULL){
  744                 QString s= tr("Time constraint teacher not available");
  745                 s += " ";
  746                 s += tr("broken for teacher: %1 on day %2, hour %3")
  747                  .arg(r.internalTeachersList[tch]->name)
  748                  .arg(r.daysOfTheWeek[d])
  749                  .arg(r.hoursOfTheDay[h]);
  750                 s += ". ";
  751                 s += tr("This increases the conflicts total by %1")
  752                  .arg(CustomFETString::numberPlusTwoDigitsPrecision(teachersMatrix[tch][d][h]*weightPercentage/100));
  753                 
  754                 dl.append(s);
  755                 cl.append(teachersMatrix[tch][d][h]*weightPercentage/100);
  756             
  757                 *conflictsString += s+"\n";
  758             }
  759         }
  760     }
  761 
  762     if(weightPercentage==100.0)
  763         assert(nbroken==0);
  764     return weightPercentage/100 * nbroken;
  765 }
  766 
  767 bool ConstraintTeacherNotAvailableTimes::isRelatedToActivity(Rules& r, Activity* a)
  768 {
  769     Q_UNUSED(a);
  770     Q_UNUSED(r);
  771 
  772     return false;
  773 }
  774 
  775 bool ConstraintTeacherNotAvailableTimes::isRelatedToTeacher(Teacher* t)
  776 {
  777     if(this->teacher==t->name)
  778         return true;
  779     return false;
  780 }
  781 
  782 bool ConstraintTeacherNotAvailableTimes::isRelatedToSubject(Subject* s)
  783 {
  784     Q_UNUSED(s);
  785 
  786     return false;
  787 }
  788 
  789 bool ConstraintTeacherNotAvailableTimes::isRelatedToActivityTag(ActivityTag* s)
  790 {
  791     Q_UNUSED(s);
  792 
  793     return false;
  794 }
  795 
  796 bool ConstraintTeacherNotAvailableTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
  797 {
  798     Q_UNUSED(r);
  799     Q_UNUSED(s);
  800 
  801     return false;
  802 }
  803 
  804 bool ConstraintTeacherNotAvailableTimes::hasWrongDayOrHour(Rules& r)
  805 {
  806     assert(days.count()==hours.count());
  807     
  808     for(int i=0; i<days.count(); i++)
  809         if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
  810          || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
  811             return true;
  812 
  813     return false;
  814 }
  815 
  816 bool ConstraintTeacherNotAvailableTimes::canRepairWrongDayOrHour(Rules& r)
  817 {
  818     assert(hasWrongDayOrHour(r));
  819     
  820     return true;
  821 }
  822 
  823 bool ConstraintTeacherNotAvailableTimes::repairWrongDayOrHour(Rules& r)
  824 {
  825     assert(hasWrongDayOrHour(r));
  826     
  827     assert(days.count()==hours.count());
  828     
  829     QList<int> newDays;
  830     QList<int> newHours;
  831     
  832     for(int i=0; i<days.count(); i++)
  833         if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
  834          && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
  835             newDays.append(days.at(i));
  836             newHours.append(hours.at(i));
  837         }
  838     
  839     days=newDays;
  840     hours=newHours;
  841     
  842     r.internalStructureComputed=false;
  843     setRulesModifiedAndOtherThings(&r);
  844 
  845     return true;
  846 }
  847 
  848 /////////////////////////////////////////////////////////////////////////////////////////////
  849 /////////////////////////////////////////////////////////////////////////////////////////////
  850 
  851 ConstraintStudentsSetNotAvailableTimes::ConstraintStudentsSetNotAvailableTimes()
  852     : TimeConstraint()
  853 {
  854     this->type=CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES;
  855 }
  856 
  857 ConstraintStudentsSetNotAvailableTimes::ConstraintStudentsSetNotAvailableTimes(double wp, const QString& sn, QList<int> d, QList<int> h)
  858      : TimeConstraint(wp){
  859     this->students = sn;
  860     assert(d.count()==h.count());
  861     this->days=d;
  862     this->hours=h;
  863     this->type=CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES;
  864 }
  865 
  866 bool ConstraintStudentsSetNotAvailableTimes::computeInternalStructure(QWidget* parent, Rules& r){
  867     //StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
  868     StudentsSet* ss=r.studentsHash.value(students, NULL);
  869     
  870     if(ss==NULL){
  871         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
  872          tr("Constraint students set not available is wrong because it refers to inexistent students set."
  873          " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
  874         
  875         return false;
  876     }
  877     
  878     assert(days.count()==hours.count());
  879     for(int k=0; k<days.count(); k++){
  880         if(this->days.at(k) >= r.nDaysPerWeek){
  881             TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
  882              tr("Constraint students set not available times is wrong because it refers to removed day. Please correct"
  883              " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
  884         
  885             return false;
  886         }
  887         if(this->hours.at(k) >= r.nHoursPerDay){
  888             TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
  889              tr("Constraint students set not available times is wrong because an hour is too late (after the last acceptable slot). Please correct"
  890              " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
  891         
  892             return false;
  893         }
  894     }
  895     
  896     assert(ss);
  897 
  898     populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
  899     /*this->iSubgroupsList.clear();
  900     if(ss->type==STUDENTS_SUBGROUP){
  901         int tmp;
  902         tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
  903         assert(tmp>=0);
  904         assert(tmp<r.nInternalSubgroups);
  905         if(!this->iSubgroupsList.contains(tmp))
  906             this->iSubgroupsList.append(tmp);
  907     }
  908     else if(ss->type==STUDENTS_GROUP){
  909         StudentsGroup* stg=(StudentsGroup*)ss;
  910         for(int i=0; i<stg->subgroupsList.size(); i++){
  911             StudentsSubgroup* sts=stg->subgroupsList[i];
  912             int tmp;
  913             tmp=sts->indexInInternalSubgroupsList;
  914             assert(tmp>=0);
  915             assert(tmp<r.nInternalSubgroups);
  916             if(!this->iSubgroupsList.contains(tmp))
  917                 this->iSubgroupsList.append(tmp);
  918         }
  919     }
  920     else if(ss->type==STUDENTS_YEAR){
  921         StudentsYear* sty=(StudentsYear*)ss;
  922         for(int i=0; i<sty->groupsList.size(); i++){
  923             StudentsGroup* stg=sty->groupsList[i];
  924             for(int j=0; j<stg->subgroupsList.size(); j++){
  925                 StudentsSubgroup* sts=stg->subgroupsList[j];
  926                 int tmp;
  927                 tmp=sts->indexInInternalSubgroupsList;
  928                 assert(tmp>=0);
  929                 assert(tmp<r.nInternalSubgroups);
  930                 if(!this->iSubgroupsList.contains(tmp))
  931                     this->iSubgroupsList.append(tmp);
  932             }
  933         }
  934     }
  935     else
  936         assert(0);*/
  937     return true;
  938 }
  939 
  940 bool ConstraintStudentsSetNotAvailableTimes::hasInactiveActivities(Rules& r)
  941 {
  942     Q_UNUSED(r);
  943     return false;
  944 }
  945 
  946 QString ConstraintStudentsSetNotAvailableTimes::getXmlDescription(Rules& r){
  947     QString s="<ConstraintStudentsSetNotAvailableTimes>\n";
  948     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
  949     s+="    <Students>"+protect(this->students)+"</Students>\n";
  950 
  951     s+="    <Number_of_Not_Available_Times>"+CustomFETString::number(this->days.count())+"</Number_of_Not_Available_Times>\n";
  952     assert(days.count()==hours.count());
  953     for(int i=0; i<days.count(); i++){
  954         s+="    <Not_Available_Time>\n";
  955         if(this->days.at(i)>=0)
  956             s+="        <Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
  957         if(this->hours.at(i)>=0)
  958             s+="        <Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
  959         s+="    </Not_Available_Time>\n";
  960     }
  961 
  962     s+="    <Active>"+trueFalse(active)+"</Active>\n";
  963     s+="    <Comments>"+protect(comments)+"</Comments>\n";
  964     s+="</ConstraintStudentsSetNotAvailableTimes>\n";
  965     return s;
  966 }
  967 
  968 QString ConstraintStudentsSetNotAvailableTimes::getDescription(Rules& r){
  969     QString begin=QString("");
  970     if(!active)
  971         begin="X - ";
  972         
  973     QString end=QString("");
  974     if(!comments.isEmpty())
  975         end=", "+tr("C: %1", "Comments").arg(comments);
  976         
  977     QString s;
  978     s=tr("Students set not available");s+=", ";
  979     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
  980     s+=tr("St:%1", "Students").arg(this->students);s+=", ";
  981 
  982     s+=tr("NA at:", "Not available at");
  983     s+=" ";
  984     assert(days.count()==hours.count());
  985     for(int i=0; i<days.count(); i++){
  986         if(this->days.at(i)>=0){
  987             s+=r.daysOfTheWeek[this->days.at(i)];
  988             s+=" ";
  989         }
  990         if(this->hours.at(i)>=0){
  991             s+=r.hoursOfTheDay[this->hours.at(i)];
  992         }
  993         if(i<days.count()-1)
  994             s+="; ";
  995     }
  996 
  997     return begin+s+end;
  998 }
  999 
 1000 QString ConstraintStudentsSetNotAvailableTimes::getDetailedDescription(Rules& r){
 1001     QString s=tr("Time constraint");s+="\n";
 1002     s+=tr("A students set is not available");s+="\n";
 1003     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 1004 
 1005     s+=tr("Students=%1").arg(this->students);s+="\n";
 1006 
 1007     s+=tr("Not available at:", "It refers to a students set");s+="\n";
 1008     
 1009     assert(days.count()==hours.count());
 1010     for(int i=0; i<days.count(); i++){
 1011         if(this->days.at(i)>=0){
 1012             s+=r.daysOfTheWeek[this->days.at(i)];
 1013             s+=" ";
 1014         }
 1015         if(this->hours.at(i)>=0){
 1016             s+=r.hoursOfTheDay[this->hours.at(i)];
 1017         }
 1018         if(i<days.count()-1)
 1019             s+="; ";
 1020     }
 1021     s+="\n";
 1022 
 1023     if(!active){
 1024         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 1025         s+="\n";
 1026     }
 1027     if(!comments.isEmpty()){
 1028         s+=tr("Comments=%1").arg(comments);
 1029         s+="\n";
 1030     }
 1031 
 1032     return s;
 1033 }
 1034 
 1035 double ConstraintStudentsSetNotAvailableTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 1036 {
 1037     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 1038     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 1039         c.teachersMatrixReady=true;
 1040         c.subgroupsMatrixReady=true;
 1041         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 1042         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 1043 
 1044         c.changedForMatrixCalculation=false;
 1045     }
 1046 
 1047     int nbroken;
 1048 
 1049     nbroken=0;
 1050     for(int m=0; m<this->iSubgroupsList.count(); m++){
 1051         int sbg=this->iSubgroupsList.at(m);
 1052         
 1053         assert(days.count()==hours.count());
 1054         for(int k=0; k<days.count(); k++){
 1055             int d=days.at(k);
 1056             int h=hours.at(k);
 1057             
 1058             if(subgroupsMatrix[sbg][d][h]>0){
 1059                 nbroken+=subgroupsMatrix[sbg][d][h];
 1060 
 1061                 if(conflictsString!=NULL){
 1062                     QString s= tr("Time constraint students set not available");
 1063                     s += " ";
 1064                     s += tr("broken for subgroup: %1 on day %2, hour %3")
 1065                      .arg(r.internalSubgroupsList[sbg]->name)
 1066                      .arg(r.daysOfTheWeek[d])
 1067                      .arg(r.hoursOfTheDay[h]);
 1068                     s += ". ";
 1069                     s += tr("This increases the conflicts total by %1")
 1070                      .arg(CustomFETString::numberPlusTwoDigitsPrecision(subgroupsMatrix[sbg][d][h]*weightPercentage/100));
 1071                     
 1072                     dl.append(s);
 1073                     cl.append(subgroupsMatrix[sbg][d][h]*weightPercentage/100);
 1074                 
 1075                     *conflictsString += s+"\n";
 1076                 }
 1077             }
 1078         }
 1079     }
 1080 
 1081     if(weightPercentage==100.0)
 1082         assert(nbroken==0);
 1083     return weightPercentage/100 * nbroken;
 1084 }
 1085 
 1086 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToActivity(Rules& r, Activity* a)
 1087 {
 1088     Q_UNUSED(a);
 1089     Q_UNUSED(r);
 1090 
 1091     return false;
 1092 }
 1093 
 1094 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToTeacher(Teacher* t)
 1095 {
 1096     Q_UNUSED(t);
 1097 
 1098     return false;
 1099 }
 1100 
 1101 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToSubject(Subject* s)
 1102 {
 1103     Q_UNUSED(s);
 1104 
 1105     return false;
 1106 }
 1107 
 1108 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToActivityTag(ActivityTag* s)
 1109 {
 1110     Q_UNUSED(s);
 1111 
 1112     return false;
 1113 }
 1114 
 1115 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 1116 {
 1117     return r.setsShareStudents(this->students, s->name);
 1118 }
 1119 
 1120 bool ConstraintStudentsSetNotAvailableTimes::hasWrongDayOrHour(Rules& r)
 1121 {
 1122     assert(days.count()==hours.count());
 1123     
 1124     for(int i=0; i<days.count(); i++)
 1125         if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
 1126          || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
 1127             return true;
 1128 
 1129     return false;
 1130 }
 1131 
 1132 bool ConstraintStudentsSetNotAvailableTimes::canRepairWrongDayOrHour(Rules& r)
 1133 {
 1134     assert(hasWrongDayOrHour(r));
 1135     
 1136     return true;
 1137 }
 1138 
 1139 bool ConstraintStudentsSetNotAvailableTimes::repairWrongDayOrHour(Rules& r)
 1140 {
 1141     assert(hasWrongDayOrHour(r));
 1142     
 1143     assert(days.count()==hours.count());
 1144     
 1145     QList<int> newDays;
 1146     QList<int> newHours;
 1147     
 1148     for(int i=0; i<days.count(); i++)
 1149         if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
 1150          && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
 1151             newDays.append(days.at(i));
 1152             newHours.append(hours.at(i));
 1153         }
 1154     
 1155     days=newDays;
 1156     hours=newHours;
 1157     
 1158     r.internalStructureComputed=false;
 1159     setRulesModifiedAndOtherThings(&r);
 1160 
 1161     return true;
 1162 }
 1163 
 1164 /////////////////////////////////////////////////////////////////////////////////////////////////
 1165 /////////////////////////////////////////////////////////////////////////////////////////////////
 1166 
 1167 ConstraintActivitiesSameStartingTime::ConstraintActivitiesSameStartingTime()
 1168     : TimeConstraint()
 1169 {
 1170     type=CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME;
 1171 }
 1172 
 1173 ConstraintActivitiesSameStartingTime::ConstraintActivitiesSameStartingTime(double wp, int nact, const QList<int>& act)
 1174  : TimeConstraint(wp)
 1175  {
 1176     assert(nact>=2);
 1177     assert(act.count()==nact);
 1178     this->n_activities=nact;
 1179     this->activitiesId.clear();
 1180     for(int i=0; i<nact; i++)
 1181         this->activitiesId.append(act.at(i));
 1182 
 1183     this->type=CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME;
 1184 }
 1185 
 1186 bool ConstraintActivitiesSameStartingTime::computeInternalStructure(QWidget* parent, Rules& r)
 1187 {
 1188     //compute the indices of the activities,
 1189     //based on their unique ID
 1190 
 1191     assert(this->n_activities==this->activitiesId.count());
 1192 
 1193     this->_activities.clear();
 1194     for(int i=0; i<this->n_activities; i++){
 1195         int j=r.activitiesHash.value(activitiesId.at(i), -1);
 1196         //assert(j>=0);
 1197         if(j>=0)
 1198             _activities.append(j);
 1199         /*int j;
 1200         Activity* act;
 1201         for(j=0; j<r.nInternalActivities; j++){
 1202             act=&r.internalActivitiesList[j];
 1203             if(act->id==this->activitiesId[i]){
 1204                 this->_activities.append(j);
 1205                 break;
 1206             }
 1207         }*/
 1208     }
 1209     this->_n_activities=this->_activities.count();
 1210     
 1211     if(this->_n_activities<=1){
 1212         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
 1213             tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
 1214         //assert(0);
 1215         return false;
 1216     }
 1217 
 1218     return true;
 1219 }
 1220 
 1221 void ConstraintActivitiesSameStartingTime::removeUseless(Rules& r)
 1222 {
 1223     //remove the activitiesId which no longer exist (used after the deletion of an activity)
 1224     
 1225     assert(this->n_activities==this->activitiesId.count());
 1226 
 1227     QList<int> tmpList;
 1228 
 1229     for(int i=0; i<this->n_activities; i++){
 1230         Activity* act=r.activitiesPointerHash.value(activitiesId[i], NULL);
 1231         if(act!=NULL)
 1232             tmpList.append(act->id);
 1233         /*for(int k=0; k<r.activitiesList.size(); k++){
 1234             Activity* act=r.activitiesList[k];
 1235             if(act->id==this->activitiesId[i]){
 1236                 tmpList.append(act->id);
 1237                 break;
 1238             }
 1239         }*/
 1240     }
 1241     
 1242     this->activitiesId=tmpList;
 1243     this->n_activities=this->activitiesId.count();
 1244 
 1245     r.internalStructureComputed=false;
 1246 }
 1247 
 1248 bool ConstraintActivitiesSameStartingTime::hasInactiveActivities(Rules& r)
 1249 {
 1250     int count=0;
 1251 
 1252     for(int i=0; i<this->n_activities; i++)
 1253         if(r.inactiveActivities.contains(this->activitiesId[i]))
 1254             count++;
 1255 
 1256     if(this->n_activities-count<=1)
 1257         return true;
 1258     else
 1259         return false;
 1260 }
 1261 
 1262 QString ConstraintActivitiesSameStartingTime::getXmlDescription(Rules& r){
 1263     Q_UNUSED(r);
 1264 
 1265     QString s="<ConstraintActivitiesSameStartingTime>\n";
 1266     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 1267     s+="    <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
 1268     for(int i=0; i<this->n_activities; i++)
 1269         s+="    <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
 1270     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 1271     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 1272     s+="</ConstraintActivitiesSameStartingTime>\n";
 1273     return s;
 1274 }
 1275 
 1276 QString ConstraintActivitiesSameStartingTime::getDescription(Rules& r){
 1277     Q_UNUSED(r);
 1278 
 1279     QString begin=QString("");
 1280     if(!active)
 1281         begin="X - ";
 1282         
 1283     QString end=QString("");
 1284     if(!comments.isEmpty())
 1285         end=", "+tr("C: %1", "Comments").arg(comments);
 1286 
 1287     QString s;
 1288     s+=tr("Activities same starting time");s+=", ";
 1289     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 1290     s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
 1291     for(int i=0; i<this->n_activities; i++){
 1292         s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
 1293         if(i<this->n_activities-1)
 1294             s+=", ";
 1295     }
 1296 
 1297     return begin+s+end;
 1298 }
 1299 
 1300 QString ConstraintActivitiesSameStartingTime::getDetailedDescription(Rules& r){
 1301     QString s;
 1302     
 1303     s=tr("Time constraint");s+="\n";
 1304     s+=tr("Activities must have the same starting time");s+="\n";
 1305     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 1306     s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
 1307     for(int i=0; i<this->n_activities; i++){
 1308         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]));
 1309         s+="\n";
 1310     }
 1311 
 1312     if(!active){
 1313         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 1314         s+="\n";
 1315     }
 1316     if(!comments.isEmpty()){
 1317         s+=tr("Comments=%1").arg(comments);
 1318         s+="\n";
 1319     }
 1320 
 1321     return s;
 1322 }
 1323 
 1324 double ConstraintActivitiesSameStartingTime::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 1325 {
 1326     assert(r.internalStructureComputed);
 1327 
 1328     int nbroken;
 1329 
 1330     //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
 1331 
 1332     //sum the differences in the scheduled time for all pairs of activities.
 1333 
 1334     //without logging
 1335     if(conflictsString==NULL){
 1336         nbroken=0;
 1337         for(int i=1; i<this->_n_activities; i++){
 1338             int t1=c.times[this->_activities[i]];
 1339             if(t1!=UNALLOCATED_TIME){
 1340                 int day1=t1%r.nDaysPerWeek;
 1341                 int hour1=t1/r.nDaysPerWeek;
 1342                 for(int j=0; j<i; j++){
 1343                     int t2=c.times[this->_activities[j]];
 1344                     if(t2!=UNALLOCATED_TIME){
 1345                         int day2=t2%r.nDaysPerWeek;
 1346                         int hour2=t2/r.nDaysPerWeek;
 1347                         int tmp=0;
 1348 
 1349                         tmp = abs(day1-day2) + abs(hour1-hour2);
 1350                             
 1351                         if(tmp>0)
 1352                             tmp=1;
 1353 
 1354                         nbroken+=tmp;
 1355                     }
 1356                 }
 1357             }
 1358         }
 1359     }
 1360     //with logging
 1361     else{
 1362         nbroken=0;
 1363         for(int i=1; i<this->_n_activities; i++){
 1364             int t1=c.times[this->_activities[i]];
 1365             if(t1!=UNALLOCATED_TIME){
 1366                 int day1=t1%r.nDaysPerWeek;
 1367                 int hour1=t1/r.nDaysPerWeek;
 1368                 for(int j=0; j<i; j++){
 1369                     int t2=c.times[this->_activities[j]];
 1370                     if(t2!=UNALLOCATED_TIME){
 1371                         int day2=t2%r.nDaysPerWeek;
 1372                         int hour2=t2/r.nDaysPerWeek;
 1373                         int tmp=0;
 1374 
 1375                         tmp = abs(day1-day2) + abs(hour1-hour2);
 1376                             
 1377                         if(tmp>0)
 1378                             tmp=1;
 1379 
 1380                         nbroken+=tmp;
 1381 
 1382                         if(tmp>0 && conflictsString!=NULL){
 1383                             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)",
 1384                             "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
 1385                              .arg(this->activitiesId[i])
 1386                              .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
 1387                              .arg(this->activitiesId[j])
 1388                              .arg(getActivityDetailedDescription(r, this->activitiesId[j]));
 1389                             s+=". ";
 1390                             s+=tr("Conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
 1391                         
 1392                             dl.append(s);
 1393                             cl.append(tmp*weightPercentage/100);
 1394                             
 1395                             *conflictsString+= s+"\n";
 1396                         }
 1397                     }
 1398                 }
 1399             }
 1400         }
 1401     }
 1402 
 1403     if(weightPercentage==100)
 1404         assert(nbroken==0);
 1405     return weightPercentage/100 * nbroken;
 1406 }
 1407 
 1408 bool ConstraintActivitiesSameStartingTime::isRelatedToActivity(Rules& r, Activity* a)
 1409 {
 1410     Q_UNUSED(r);
 1411 
 1412     for(int i=0; i<this->n_activities; i++)
 1413         if(this->activitiesId[i]==a->id)
 1414             return true;
 1415     return false;
 1416 }
 1417 
 1418 bool ConstraintActivitiesSameStartingTime::isRelatedToTeacher(Teacher* t)
 1419 {
 1420     Q_UNUSED(t);
 1421 
 1422     return false;
 1423 }
 1424 
 1425 bool ConstraintActivitiesSameStartingTime::isRelatedToSubject(Subject* s)
 1426 {
 1427     Q_UNUSED(s);
 1428 
 1429     return false;
 1430 }
 1431 
 1432 bool ConstraintActivitiesSameStartingTime::isRelatedToActivityTag(ActivityTag* s)
 1433 {
 1434     Q_UNUSED(s);
 1435 
 1436     return false;
 1437 }
 1438 
 1439 bool ConstraintActivitiesSameStartingTime::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 1440 {
 1441     Q_UNUSED(r);
 1442     Q_UNUSED(s);
 1443 
 1444     return false;
 1445 }
 1446 
 1447 bool ConstraintActivitiesSameStartingTime::hasWrongDayOrHour(Rules& r)
 1448 {
 1449     Q_UNUSED(r);
 1450     return false;
 1451 }
 1452 
 1453 bool ConstraintActivitiesSameStartingTime::canRepairWrongDayOrHour(Rules& r)
 1454 {
 1455     Q_UNUSED(r);
 1456     assert(0);
 1457     
 1458     return true;
 1459 }
 1460 
 1461 bool ConstraintActivitiesSameStartingTime::repairWrongDayOrHour(Rules& r)
 1462 {
 1463     Q_UNUSED(r);
 1464     assert(0); //should check hasWrongDayOrHour, firstly
 1465 
 1466     return true;
 1467 }
 1468 
 1469 ////////////////////////////////////////////////////////////////////////////////////////////
 1470 ////////////////////////////////////////////////////////////////////////////////////////////
 1471 
 1472 ConstraintActivitiesNotOverlapping::ConstraintActivitiesNotOverlapping()
 1473     : TimeConstraint()
 1474 {
 1475     type=CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING;
 1476 }
 1477 
 1478 ConstraintActivitiesNotOverlapping::ConstraintActivitiesNotOverlapping(double wp, int nact, const QList<int>& act)
 1479  : TimeConstraint(wp)
 1480  {
 1481     assert(nact>=2);
 1482     assert(act.count()==nact);
 1483     this->n_activities=nact;
 1484     this->activitiesId.clear();
 1485     for(int i=0; i<nact; i++)
 1486         this->activitiesId.append(act.at(i));
 1487 
 1488     this->type=CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING;
 1489 }
 1490 
 1491 bool ConstraintActivitiesNotOverlapping::computeInternalStructure(QWidget* parent, Rules& r)
 1492 {
 1493     //compute the indices of the activities,
 1494     //based on their unique ID
 1495 
 1496     assert(this->n_activities==this->activitiesId.count());
 1497 
 1498     this->_activities.clear();
 1499     for(int i=0; i<this->n_activities; i++){
 1500         int j=r.activitiesHash.value(activitiesId.at(i), -1);
 1501         //assert(j>=0);
 1502         if(j>=0)
 1503             _activities.append(j);
 1504         /*int j;
 1505         Activity* act;
 1506         for(j=0; j<r.nInternalActivities; j++){
 1507             act=&r.internalActivitiesList[j];
 1508             if(act->id==this->activitiesId[i]){
 1509                 this->_activities.append(j);
 1510                 break;
 1511             }
 1512         }*/
 1513     }
 1514     this->_n_activities=this->_activities.count();
 1515     
 1516     if(this->_n_activities<=1){
 1517         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
 1518             tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
 1519         //assert(0);
 1520         return false;
 1521     }
 1522 
 1523     return true;
 1524 }
 1525 
 1526 void ConstraintActivitiesNotOverlapping::removeUseless(Rules& r)
 1527 {
 1528     //remove the activitiesId which no longer exist (used after the deletion of an activity)
 1529     
 1530     assert(this->n_activities==this->activitiesId.count());
 1531 
 1532     QList<int> tmpList;
 1533 
 1534     for(int i=0; i<this->n_activities; i++){
 1535         Activity* act=r.activitiesPointerHash.value(activitiesId[i], NULL);
 1536         if(act!=NULL)
 1537             tmpList.append(act->id);
 1538         /*for(int k=0; k<r.activitiesList.size(); k++){
 1539             Activity* act=r.activitiesList[k];
 1540             if(act->id==this->activitiesId[i]){
 1541                 tmpList.append(act->id);
 1542                 break;
 1543             }
 1544         }*/
 1545     }
 1546     
 1547     this->activitiesId=tmpList;
 1548     this->n_activities=this->activitiesId.count();
 1549 
 1550     r.internalStructureComputed=false;
 1551 }
 1552 
 1553 bool ConstraintActivitiesNotOverlapping::hasInactiveActivities(Rules& r)
 1554 {
 1555     int count=0;
 1556 
 1557     for(int i=0; i<this->n_activities; i++)
 1558         if(r.inactiveActivities.contains(this->activitiesId[i]))
 1559             count++;
 1560 
 1561     if(this->n_activities-count<=1)
 1562         return true;
 1563     else
 1564         return false;
 1565 }
 1566 
 1567 QString ConstraintActivitiesNotOverlapping::getXmlDescription(Rules& r){
 1568     Q_UNUSED(r);
 1569 
 1570     QString s="<ConstraintActivitiesNotOverlapping>\n";
 1571     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 1572     s+="    <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
 1573     for(int i=0; i<this->n_activities; i++)
 1574         s+="    <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
 1575     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 1576     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 1577     s+="</ConstraintActivitiesNotOverlapping>\n";
 1578     return s;
 1579 }
 1580 
 1581 QString ConstraintActivitiesNotOverlapping::getDescription(Rules& r){
 1582     Q_UNUSED(r);
 1583 
 1584     QString begin=QString("");
 1585     if(!active)
 1586         begin="X - ";
 1587         
 1588     QString end=QString("");
 1589     if(!comments.isEmpty())
 1590         end=", "+tr("C: %1", "Comments").arg(comments);
 1591         
 1592     QString s;
 1593     s+=tr("Activities not overlapping");s+=", ";
 1594     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 1595     s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
 1596     for(int i=0; i<this->n_activities; i++){
 1597         s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
 1598         if(i<this->n_activities-1)
 1599             s+=", ";
 1600     }
 1601 
 1602     return begin+s+end;
 1603 }
 1604 
 1605 QString ConstraintActivitiesNotOverlapping::getDetailedDescription(Rules& r){
 1606     QString s=tr("Time constraint");s+="\n";
 1607     s+=tr("Activities must not overlap");s+="\n";
 1608     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 1609     s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
 1610     for(int i=0; i<this->n_activities; i++){
 1611         s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
 1612             .arg(this->activitiesId[i]).arg(getActivityDetailedDescription(r, this->activitiesId[i]));
 1613         s+="\n";
 1614     }
 1615 
 1616     if(!active){
 1617         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 1618         s+="\n";
 1619     }
 1620     if(!comments.isEmpty()){
 1621         s+=tr("Comments=%1").arg(comments);
 1622         s+="\n";
 1623     }
 1624 
 1625     return s;
 1626 }
 1627 
 1628 double ConstraintActivitiesNotOverlapping::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 1629 {
 1630     assert(r.internalStructureComputed);
 1631 
 1632     int nbroken;
 1633 
 1634     //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
 1635 
 1636     //sum the overlapping hours for all pairs of activities.
 1637     //without logging
 1638     if(conflictsString==NULL){
 1639         nbroken=0;
 1640         for(int i=1; i<this->_n_activities; i++){
 1641             int t1=c.times[this->_activities[i]];
 1642             if(t1!=UNALLOCATED_TIME){
 1643                 int day1=t1%r.nDaysPerWeek;
 1644                 int hour1=t1/r.nDaysPerWeek;
 1645                 int duration1=r.internalActivitiesList[this->_activities[i]].duration;
 1646 
 1647                 for(int j=0; j<i; j++){
 1648                     int t2=c.times[this->_activities[j]];
 1649                     if(t2!=UNALLOCATED_TIME){
 1650                         int day2=t2%r.nDaysPerWeek;
 1651                         int hour2=t2/r.nDaysPerWeek;
 1652                         int duration2=r.internalActivitiesList[this->_activities[j]].duration;
 1653 
 1654                         //the number of overlapping hours
 1655                         int tt=0;
 1656                         if(day1==day2){
 1657                             int start=std::max(hour1, hour2);
 1658                             int stop=std::min(hour1+duration1, hour2+duration2);
 1659                             if(stop>start)
 1660                                 tt+=stop-start;
 1661                         }
 1662                         
 1663                         nbroken+=tt;
 1664                     }
 1665                 }
 1666             }
 1667         }
 1668     }
 1669     //with logging
 1670     else{
 1671         nbroken=0;
 1672         for(int i=1; i<this->_n_activities; i++){
 1673             int t1=c.times[this->_activities[i]];
 1674             if(t1!=UNALLOCATED_TIME){
 1675                 int day1=t1%r.nDaysPerWeek;
 1676                 int hour1=t1/r.nDaysPerWeek;
 1677                 int duration1=r.internalActivitiesList[this->_activities[i]].duration;
 1678 
 1679                 for(int j=0; j<i; j++){
 1680                     int t2=c.times[this->_activities[j]];
 1681                     if(t2!=UNALLOCATED_TIME){
 1682                         int day2=t2%r.nDaysPerWeek;
 1683                         int hour2=t2/r.nDaysPerWeek;
 1684                         int duration2=r.internalActivitiesList[this->_activities[j]].duration;
 1685     
 1686                         //the number of overlapping hours
 1687                         int tt=0;
 1688                         if(day1==day2){
 1689                             int start=std::max(hour1, hour2);
 1690                             int stop=std::min(hour1+duration1, hour2+duration2);
 1691                             if(stop>start)
 1692                                 tt+=stop-start;
 1693                         }
 1694 
 1695                         //The overlapping hours
 1696                         int tmp=tt;
 1697 
 1698                         nbroken+=tmp;
 1699 
 1700                         if(tt>0 && conflictsString!=NULL){
 1701 
 1702                             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",
 1703                              "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
 1704                              .arg(this->activitiesId[i])
 1705                              .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
 1706                              .arg(this->activitiesId[j])
 1707                              .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
 1708                              .arg(tt);
 1709                             s+=", ";
 1710                             s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
 1711                             
 1712                             dl.append(s);
 1713                             cl.append(tmp*weightPercentage/100);
 1714                         
 1715                             *conflictsString+= s+"\n";
 1716                         }
 1717                     }
 1718                 }
 1719             }
 1720         }
 1721     }
 1722 
 1723     if(weightPercentage==100)
 1724         assert(nbroken==0);
 1725     return weightPercentage/100 * nbroken;
 1726 }
 1727 
 1728 bool ConstraintActivitiesNotOverlapping::isRelatedToActivity(Rules& r, Activity* a)
 1729 {
 1730     Q_UNUSED(r);
 1731 
 1732     for(int i=0; i<this->n_activities; i++)
 1733         if(this->activitiesId[i]==a->id)
 1734             return true;
 1735     return false;
 1736 }
 1737 
 1738 bool ConstraintActivitiesNotOverlapping::isRelatedToTeacher(Teacher* t)
 1739 {
 1740     Q_UNUSED(t);
 1741 
 1742     return false;
 1743 }
 1744 
 1745 bool ConstraintActivitiesNotOverlapping::isRelatedToSubject(Subject* s)
 1746 {
 1747     Q_UNUSED(s);
 1748 
 1749     return false;
 1750 }
 1751 
 1752 bool ConstraintActivitiesNotOverlapping::isRelatedToActivityTag(ActivityTag* s)
 1753 {
 1754     Q_UNUSED(s);
 1755 
 1756     return false;
 1757 }
 1758 
 1759 bool ConstraintActivitiesNotOverlapping::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 1760 {
 1761     Q_UNUSED(r);
 1762     Q_UNUSED(s);
 1763 
 1764     return false;
 1765 }
 1766 
 1767 bool ConstraintActivitiesNotOverlapping::hasWrongDayOrHour(Rules& r)
 1768 {
 1769     Q_UNUSED(r);
 1770     return false;
 1771 }
 1772 
 1773 bool ConstraintActivitiesNotOverlapping::canRepairWrongDayOrHour(Rules& r)
 1774 {
 1775     Q_UNUSED(r);
 1776     assert(0);
 1777     
 1778     return true;
 1779 }
 1780 
 1781 bool ConstraintActivitiesNotOverlapping::repairWrongDayOrHour(Rules& r)
 1782 {
 1783     Q_UNUSED(r);
 1784     assert(0); //should check hasWrongDayOrHour, firstly
 1785 
 1786     return true;
 1787 }
 1788 
 1789 ////////////////////////////////////////////////////////////////////////////////////////////
 1790 ////////////////////////////////////////////////////////////////////////////////////////////
 1791 
 1792 ConstraintActivityTagsNotOverlapping::ConstraintActivityTagsNotOverlapping()
 1793     : TimeConstraint()
 1794 {
 1795     type=CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING;
 1796 }
 1797 
 1798 ConstraintActivityTagsNotOverlapping::ConstraintActivityTagsNotOverlapping(double wp, const QStringList& atl)
 1799  : TimeConstraint(wp)
 1800  {
 1801     activityTagsNames=atl;
 1802 
 1803     this->type=CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING;
 1804 }
 1805 
 1806 bool ConstraintActivityTagsNotOverlapping::computeInternalStructure(QWidget* parent, Rules& r)
 1807 {
 1808     if(activityTagsNames.count()<2){
 1809         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
 1810             tr("The following constraint is wrong (because it needs at least two activity tags). "
 1811             "Please correct it:\n%1").arg(this->getDetailedDescription(r)));
 1812         return false;
 1813     }
 1814 
 1815     activityTagsIndices.clear();
 1816     activitiesIndicesLists.clear();
 1817     for(const QString& activityTagName : qAsConst(activityTagsNames)){
 1818         int activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
 1819         assert(activityTagIndex>=0);
 1820         activityTagsIndices.append(activityTagIndex);
 1821         activitiesIndicesLists.append(QList<int>());
 1822     }
 1823     
 1824     for(int ai=0; ai<r.nInternalActivities; ai++){
 1825         Activity* act=&r.internalActivitiesList[ai];
 1826         for(int i=0; i<activityTagsIndices.count(); i++){
 1827             int at=activityTagsIndices.at(i);
 1828             if(act->iActivityTagsSet.contains(at))
 1829                 activitiesIndicesLists[i].append(ai);
 1830         }
 1831     }
 1832     
 1833     assert(activitiesIndicesLists.count()==activityTagsIndices.count());
 1834     assert(activityTagsNames.count()==activityTagsIndices.count());
 1835     for(int i=0; i<activityTagsIndices.count(); i++){
 1836         if(activitiesIndicesLists.at(i).count()<1){
 1837             TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
 1838                 tr("Following constraint is wrong (because you need at least one activity for each activity tag, but"
 1839                  " no activity has activity tag %1). Please correct it:\n%2").arg(activityTagsNames.at(i)).arg(this->getDetailedDescription(r)));
 1840             return false;
 1841         }
 1842     }
 1843 
 1844     return true;
 1845 }
 1846 
 1847 bool ConstraintActivityTagsNotOverlapping::hasInactiveActivities(Rules& r)
 1848 {
 1849     Q_UNUSED(r);
 1850     return false;
 1851 }
 1852 
 1853 QString ConstraintActivityTagsNotOverlapping::getXmlDescription(Rules& r){
 1854     Q_UNUSED(r);
 1855 
 1856     QString s="<ConstraintActivityTagsNotOverlapping>\n";
 1857     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 1858     s+="    <Number_of_Activity_Tags>"+CustomFETString::number(this->activityTagsNames.count())+"</Number_of_Activity_Tags>\n";
 1859     for(const QString& atn : qAsConst(activityTagsNames))
 1860         s+="    <Activity_Tag>"+protect(atn)+"</Activity_Tag>\n";
 1861     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 1862     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 1863     s+="</ConstraintActivityTagsNotOverlapping>\n";
 1864     return s;
 1865 }
 1866 
 1867 QString ConstraintActivityTagsNotOverlapping::getDescription(Rules& r){
 1868     Q_UNUSED(r);
 1869 
 1870     QString begin=QString("");
 1871     if(!active)
 1872         begin="X - ";
 1873         
 1874     QString end=QString("");
 1875     if(!comments.isEmpty())
 1876         end=", "+tr("C: %1", "Comments").arg(comments);
 1877         
 1878     QString s;
 1879     s+=tr("Activity tags not overlapping");s+=", ";
 1880     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 1881     s+=tr("NAT:%1", "Number of activity tags").arg(this->activityTagsNames.count());s+=", ";
 1882     int i=0;
 1883     for(const QString& atn : qAsConst(activityTagsNames)){
 1884         s+=tr("AT:%1", "Activity tag").arg(atn);
 1885         if(i<this->activityTagsNames.count()-1)
 1886             s+=", ";
 1887         i++;
 1888     }
 1889 
 1890     return begin+s+end;
 1891 }
 1892 
 1893 QString ConstraintActivityTagsNotOverlapping::getDetailedDescription(Rules& r){
 1894     Q_UNUSED(r);
 1895 
 1896     QString s=tr("Time constraint");s+="\n";
 1897     s+=tr("Activity tags must not overlap");s+="\n";
 1898     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 1899     s+=tr("Number of activity tags=%1").arg(this->activityTagsNames.count());s+="\n";
 1900     for(const QString& atn : qAsConst(activityTagsNames)){
 1901         s+=tr("Activity tag=%1").arg(atn);
 1902         s+="\n";
 1903     }
 1904 
 1905     if(!active){
 1906         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 1907         s+="\n";
 1908     }
 1909     if(!comments.isEmpty()){
 1910         s+=tr("Comments=%1").arg(comments);
 1911         s+="\n";
 1912     }
 1913 
 1914     return s;
 1915 }
 1916 
 1917 double ConstraintActivityTagsNotOverlapping::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 1918 {
 1919     assert(r.internalStructureComputed);
 1920 
 1921     int nbroken;
 1922 
 1923     //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
 1924 
 1925     //sum the overlapping hours for all pairs of activities.
 1926     nbroken=0;
 1927     
 1928     for(int k1=1; k1<activitiesIndicesLists.count(); k1++){
 1929         const QList<int>& l1=activitiesIndicesLists.at(k1);
 1930         for(int k2=0; k2<k1; k2++){
 1931             const QList<int>& l2=activitiesIndicesLists.at(k2);
 1932             
 1933             for(int i : qAsConst(l1)){
 1934                 int t1=c.times[i];
 1935                 if(t1!=UNALLOCATED_TIME){
 1936                     int day1=t1%r.nDaysPerWeek;
 1937                     int hour1=t1/r.nDaysPerWeek;
 1938                     int duration1=r.internalActivitiesList[i].duration;
 1939 
 1940                     for(int j : qAsConst(l2)){
 1941                         int t2=c.times[j];
 1942                         if(t2!=UNALLOCATED_TIME){
 1943                             int day2=t2%r.nDaysPerWeek;
 1944                             int hour2=t2/r.nDaysPerWeek;
 1945                             int duration2=r.internalActivitiesList[j].duration;
 1946 
 1947                             //the number of overlapping hours
 1948                             int tt=0;
 1949                             if(day1==day2){
 1950                                 int start=std::max(hour1, hour2);
 1951                                 int stop=std::min(hour1+duration1, hour2+duration2);
 1952                                 if(stop>start)
 1953                                     tt+=stop-start;
 1954                             }
 1955 
 1956                             int tmp=tt;
 1957 
 1958                             nbroken+=tmp;
 1959 
 1960                             if(tt>0 && conflictsString!=NULL){
 1961                                 QString s=tr("Time constraint activity tags not overlapping broken: activity with id=%1 (%2) overlaps with activity with id=%3 (%4) on a number of %5 periods",
 1962                                  "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
 1963                                  .arg(r.internalActivitiesList[i].id)
 1964                                  .arg(getActivityDetailedDescription(r, r.internalActivitiesList[i].id))
 1965                                  .arg(r.internalActivitiesList[j].id)
 1966                                  .arg(getActivityDetailedDescription(r, r.internalActivitiesList[j].id))
 1967                                  .arg(tt);
 1968                                 s+=", ";
 1969                                 s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
 1970                                 
 1971                                 dl.append(s);
 1972                                 cl.append(tmp*weightPercentage/100);
 1973                                 
 1974                                 *conflictsString+= s+"\n";
 1975                             }
 1976                         }
 1977                     }
 1978                 }
 1979             }
 1980         }
 1981     }
 1982     
 1983     if(weightPercentage==100)
 1984         assert(nbroken==0);
 1985     return weightPercentage/100 * nbroken;
 1986 }
 1987 
 1988 bool ConstraintActivityTagsNotOverlapping::isRelatedToActivity(Rules& r, Activity* a)
 1989 {
 1990     Q_UNUSED(r);
 1991     
 1992 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
 1993     QSet<QString> ats(activityTagsNames.begin(), activityTagsNames.end());
 1994     QSet<QString> aats(a->activityTagsNames.begin(), a->activityTagsNames.end());
 1995 #else
 1996     QSet<QString> ats=activityTagsNames.toSet();
 1997     QSet<QString> aats=a->activityTagsNames.toSet();
 1998 #endif
 1999     ats.intersect(aats);
 2000 
 2001     if(ats.count()>0)
 2002         return true;
 2003 
 2004     return false;
 2005 }
 2006 
 2007 bool ConstraintActivityTagsNotOverlapping::isRelatedToTeacher(Teacher* t)
 2008 {
 2009     Q_UNUSED(t);
 2010 
 2011     return false;
 2012 }
 2013 
 2014 bool ConstraintActivityTagsNotOverlapping::isRelatedToSubject(Subject* s)
 2015 {
 2016     Q_UNUSED(s);
 2017 
 2018     return false;
 2019 }
 2020 
 2021 bool ConstraintActivityTagsNotOverlapping::isRelatedToActivityTag(ActivityTag* s)
 2022 {
 2023 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
 2024     QSet<QString> ats(activityTagsNames.begin(), activityTagsNames.end());
 2025 #else
 2026     QSet<QString> ats=activityTagsNames.toSet();
 2027 #endif
 2028     if(ats.contains(s->name))
 2029         return true;
 2030 
 2031     return false;
 2032 }
 2033 
 2034 bool ConstraintActivityTagsNotOverlapping::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 2035 {
 2036     Q_UNUSED(r);
 2037     Q_UNUSED(s);
 2038 
 2039     return false;
 2040 }
 2041 
 2042 bool ConstraintActivityTagsNotOverlapping::hasWrongDayOrHour(Rules& r)
 2043 {
 2044     Q_UNUSED(r);
 2045     return false;
 2046 }
 2047 
 2048 bool ConstraintActivityTagsNotOverlapping::canRepairWrongDayOrHour(Rules& r)
 2049 {
 2050     Q_UNUSED(r);
 2051     assert(0);
 2052     
 2053     return true;
 2054 }
 2055 
 2056 bool ConstraintActivityTagsNotOverlapping::repairWrongDayOrHour(Rules& r)
 2057 {
 2058     Q_UNUSED(r);
 2059     assert(0); //should check hasWrongDayOrHour, firstly
 2060 
 2061     return true;
 2062 }
 2063 
 2064 ////////////////////////////////////////////////////////////////////////////////////////////
 2065 ////////////////////////////////////////////////////////////////////////////////////////////
 2066 
 2067 ConstraintMinDaysBetweenActivities::ConstraintMinDaysBetweenActivities()
 2068     : TimeConstraint()
 2069 {
 2070     type=CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES;
 2071 }
 2072 
 2073 ConstraintMinDaysBetweenActivities::ConstraintMinDaysBetweenActivities(double wp, bool cisd, int nact, const QList<int>& act, int n)
 2074  : TimeConstraint(wp)
 2075  {
 2076     this->consecutiveIfSameDay=cisd;
 2077 
 2078     assert(nact>=2);
 2079     assert(act.count()==nact);
 2080     this->n_activities=nact;
 2081     this->activitiesId.clear();
 2082     for(int i=0; i<nact; i++)
 2083         this->activitiesId.append(act.at(i));
 2084 
 2085     assert(n>0);
 2086     this->minDays=n;
 2087 
 2088     this->type=CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES;
 2089 }
 2090 
 2091 bool ConstraintMinDaysBetweenActivities::operator==(ConstraintMinDaysBetweenActivities& c){
 2092     assert(this->n_activities==this->activitiesId.count());
 2093     assert(c.n_activities==c.activitiesId.count());
 2094 
 2095     if(this->n_activities!=c.n_activities)
 2096         return false;
 2097     for(int i=0; i<this->n_activities; i++)
 2098         if(this->activitiesId[i]!=c.activitiesId[i])
 2099             return false;
 2100     if(this->minDays!=c.minDays)
 2101         return false;
 2102     if(this->weightPercentage!=c.weightPercentage)
 2103         return false;
 2104     if(this->consecutiveIfSameDay!=c.consecutiveIfSameDay)
 2105         return false;
 2106     return true;
 2107 }
 2108 
 2109 bool ConstraintMinDaysBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
 2110 {
 2111     //compute the indices of the activities,
 2112     //based on their unique ID
 2113 
 2114     assert(this->n_activities==this->activitiesId.count());
 2115 
 2116     this->_activities.clear();
 2117     for(int i=0; i<this->n_activities; i++){
 2118         int j=r.activitiesHash.value(activitiesId.at(i), -1);
 2119         //assert(j>=0);
 2120         if(j>=0)
 2121             _activities.append(j);
 2122         /*Activity* act;
 2123         for(j=0; j<r.nInternalActivities; j++){
 2124             act=&r.internalActivitiesList[j];
 2125             if(act->id==this->activitiesId[i]){
 2126                 this->_activities.append(j);
 2127                 break;
 2128             }
 2129         }*/
 2130     }
 2131     this->_n_activities=this->_activities.count();
 2132     
 2133     if(this->_n_activities<=1){
 2134         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
 2135             tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
 2136         //assert(0);
 2137         return false;
 2138     }
 2139 
 2140     return true;
 2141 }
 2142 
 2143 void ConstraintMinDaysBetweenActivities::removeUseless(Rules& r)
 2144 {
 2145     //remove the activitiesId which no longer exist (used after the deletion of an activity)
 2146     
 2147     assert(this->n_activities==this->activitiesId.count());
 2148 
 2149     QList<int> tmpList;
 2150 
 2151     for(int i=0; i<this->n_activities; i++){
 2152         Activity* act=r.activitiesPointerHash.value(activitiesId[i], NULL);
 2153         if(act!=NULL)
 2154             tmpList.append(act->id);
 2155         /*for(int k=0; k<r.activitiesList.size(); k++){
 2156             Activity* act=r.activitiesList[k];
 2157             if(act->id==this->activitiesId[i]){
 2158                 tmpList.append(act->id);
 2159                 break;
 2160             }
 2161         }*/
 2162     }
 2163     
 2164     this->activitiesId=tmpList;
 2165     this->n_activities=this->activitiesId.count();
 2166 
 2167     r.internalStructureComputed=false;
 2168 }
 2169 
 2170 bool ConstraintMinDaysBetweenActivities::hasInactiveActivities(Rules& r)
 2171 {
 2172     int count=0;
 2173 
 2174     for(int i=0; i<this->n_activities; i++)
 2175         if(r.inactiveActivities.contains(this->activitiesId[i]))
 2176             count++;
 2177 
 2178     if(this->n_activities-count<=1)
 2179         return true;
 2180     else
 2181         return false;
 2182 }
 2183 
 2184 QString ConstraintMinDaysBetweenActivities::getXmlDescription(Rules& r){
 2185     Q_UNUSED(r);
 2186 
 2187     QString s="<ConstraintMinDaysBetweenActivities>\n";
 2188     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 2189     s+="    <Consecutive_If_Same_Day>";s+=trueFalse(this->consecutiveIfSameDay);s+="</Consecutive_If_Same_Day>\n";
 2190     s+="    <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
 2191     for(int i=0; i<this->n_activities; i++)
 2192         s+="    <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
 2193     s+="    <MinDays>"+CustomFETString::number(this->minDays)+"</MinDays>\n";
 2194     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 2195     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 2196     s+="</ConstraintMinDaysBetweenActivities>\n";
 2197     return s;
 2198 }
 2199 
 2200 QString ConstraintMinDaysBetweenActivities::getDescription(Rules& r){
 2201     Q_UNUSED(r);
 2202 
 2203     QString begin=QString("");
 2204     if(!active)
 2205         begin="X - ";
 2206         
 2207     QString end=QString("");
 2208     if(!comments.isEmpty())
 2209         end=", "+tr("C: %1", "Comments").arg(comments);
 2210         
 2211     QString s;
 2212     s+=tr("Min days between activities");s+=", ";
 2213     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 2214     s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
 2215     for(int i=0; i<this->n_activities; i++){
 2216         s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
 2217     }
 2218     s+=tr("mD:%1", "Min days").arg(this->minDays);s+=", ";
 2219     s+=tr("CSD:%1", "Consecutive if same day").arg(yesNoTranslated(this->consecutiveIfSameDay));
 2220 
 2221     return begin+s+end;
 2222 }
 2223 
 2224 QString ConstraintMinDaysBetweenActivities::getDetailedDescription(Rules& r){
 2225     QString s=tr("Time constraint");s+="\n";
 2226     s+=tr("Minimum number of days between activities");s+="\n";
 2227     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 2228     s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
 2229     for(int i=0; i<this->n_activities; i++){
 2230         s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
 2231             .arg(this->activitiesId[i])
 2232             .arg(getActivityDetailedDescription(r, this->activitiesId[i]));
 2233         s+="\n";
 2234     }
 2235     s+=tr("Minimum number of days=%1").arg(this->minDays);s+="\n";
 2236     s+=tr("Consecutive if same day=%1").arg(yesNoTranslated(this->consecutiveIfSameDay));s+="\n";
 2237 
 2238     if(!active){
 2239         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 2240         s+="\n";
 2241     }
 2242     if(!comments.isEmpty()){
 2243         s+=tr("Comments=%1").arg(comments);
 2244         s+="\n";
 2245     }
 2246 
 2247     return s;
 2248 }
 2249 
 2250 double ConstraintMinDaysBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 2251 {
 2252     assert(r.internalStructureComputed);
 2253 
 2254     int nbroken;
 2255 
 2256     //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
 2257 
 2258     //sum the overlapping hours for all pairs of activities.
 2259     //without logging
 2260     if(conflictsString==NULL){
 2261         nbroken=0;
 2262         for(int i=1; i<this->_n_activities; i++){
 2263             int t1=c.times[this->_activities[i]];
 2264             if(t1!=UNALLOCATED_TIME){
 2265                 int day1=t1%r.nDaysPerWeek;
 2266                 int hour1=t1/r.nDaysPerWeek;
 2267                 int duration1=r.internalActivitiesList[this->_activities[i]].duration;
 2268 
 2269                 for(int j=0; j<i; j++){
 2270                     int t2=c.times[this->_activities[j]];
 2271                     if(t2!=UNALLOCATED_TIME){
 2272                         int day2=t2%r.nDaysPerWeek;
 2273                         int hour2=t2/r.nDaysPerWeek;
 2274                         int duration2=r.internalActivitiesList[this->_activities[j]].duration;
 2275                     
 2276                         int tmp;
 2277                         int tt=0;
 2278                         int dist=abs(day1-day2);
 2279                         if(dist<minDays){
 2280                             tt=minDays-dist;
 2281                             
 2282                             if(this->consecutiveIfSameDay && day1==day2)
 2283                                 assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
 2284                         }
 2285                         
 2286                         tmp=tt;
 2287     
 2288                         nbroken+=tmp;
 2289                     }
 2290                 }
 2291             }
 2292         }
 2293     }
 2294     //with logging
 2295     else{
 2296         nbroken=0;
 2297         for(int i=1; i<this->_n_activities; i++){
 2298             int t1=c.times[this->_activities[i]];
 2299             if(t1!=UNALLOCATED_TIME){
 2300                 int day1=t1%r.nDaysPerWeek;
 2301                 int hour1=t1/r.nDaysPerWeek;
 2302                 int duration1=r.internalActivitiesList[this->_activities[i]].duration;
 2303 
 2304                 for(int j=0; j<i; j++){
 2305                     int t2=c.times[this->_activities[j]];
 2306                     if(t2!=UNALLOCATED_TIME){
 2307                         int day2=t2%r.nDaysPerWeek;
 2308                         int hour2=t2/r.nDaysPerWeek;
 2309                         int duration2=r.internalActivitiesList[this->_activities[j]].duration;
 2310                     
 2311                         int tmp;
 2312                         int tt=0;
 2313                         int dist=abs(day1-day2);
 2314 
 2315                         if(dist<minDays){
 2316                             tt=minDays-dist;
 2317                             
 2318                             if(this->consecutiveIfSameDay && day1==day2)
 2319                                 assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
 2320                         }
 2321 
 2322                         tmp=tt;
 2323     
 2324                         nbroken+=tmp;
 2325 
 2326                         if(tt>0 && conflictsString!=NULL){
 2327                             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",
 2328                              "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr. Close here means near")
 2329                              .arg(this->activitiesId[i])
 2330                              .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
 2331                              .arg(this->activitiesId[j])
 2332                              .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
 2333                              .arg(tt)
 2334                              .arg(r.daysOfTheWeek[day1])
 2335                              .arg(r.daysOfTheWeek[day2]);
 2336                              ;
 2337 
 2338                             s+=", ";
 2339                             s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
 2340                             s+=".";
 2341                             
 2342                             if(this->consecutiveIfSameDay && day1==day2){
 2343                                 s+=" ";
 2344                                 s+=tr("The activities are placed consecutively in the timetable, because you selected this option"
 2345                                  " in case the activities are in the same day");
 2346                             }
 2347                             
 2348                             dl.append(s);
 2349                             cl.append(tmp*weightPercentage/100);
 2350                             
 2351                             *conflictsString+= s+"\n";
 2352                         }
 2353                     }
 2354                 }
 2355             }
 2356         }
 2357     }
 2358 
 2359     if(weightPercentage==100)
 2360         assert(nbroken==0);
 2361     return weightPercentage/100 * nbroken;
 2362 }
 2363 
 2364 bool ConstraintMinDaysBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
 2365 {
 2366     Q_UNUSED(r);
 2367 
 2368     for(int i=0; i<this->n_activities; i++)
 2369         if(this->activitiesId[i]==a->id)
 2370             return true;
 2371     return false;
 2372 }
 2373 
 2374 bool ConstraintMinDaysBetweenActivities::isRelatedToTeacher(Teacher* t)
 2375 {
 2376     Q_UNUSED(t);
 2377 
 2378     return false;
 2379 }
 2380 
 2381 bool ConstraintMinDaysBetweenActivities::isRelatedToSubject(Subject* s)
 2382 {
 2383     Q_UNUSED(s);
 2384 
 2385     return false;
 2386 }
 2387 
 2388 bool ConstraintMinDaysBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
 2389 {
 2390     Q_UNUSED(s);
 2391 
 2392     return false;
 2393 }
 2394 
 2395 bool ConstraintMinDaysBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 2396 {
 2397     Q_UNUSED(r);
 2398     Q_UNUSED(s);
 2399 
 2400     return false;
 2401 }
 2402 
 2403 bool ConstraintMinDaysBetweenActivities::hasWrongDayOrHour(Rules& r)
 2404 {
 2405     if(minDays>=r.nDaysPerWeek)
 2406         return true;
 2407         
 2408     return false;
 2409 }
 2410 
 2411 bool ConstraintMinDaysBetweenActivities::canRepairWrongDayOrHour(Rules& r)
 2412 {
 2413     assert(hasWrongDayOrHour(r));
 2414     
 2415     return true;
 2416 }
 2417 
 2418 bool ConstraintMinDaysBetweenActivities::repairWrongDayOrHour(Rules& r)
 2419 {
 2420     assert(hasWrongDayOrHour(r));
 2421     
 2422     if(minDays>=r.nDaysPerWeek)
 2423         minDays=r.nDaysPerWeek-1;
 2424 
 2425     return true;
 2426 }
 2427 
 2428 ////////////////////////////////////////////////////////////////////////////////////////////
 2429 ////////////////////////////////////////////////////////////////////////////////////////////
 2430 
 2431 ConstraintMaxDaysBetweenActivities::ConstraintMaxDaysBetweenActivities()
 2432     : TimeConstraint()
 2433 {
 2434     type=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES;
 2435 }
 2436 
 2437 ConstraintMaxDaysBetweenActivities::ConstraintMaxDaysBetweenActivities(double wp, int nact, const QList<int>& act, int n)
 2438  : TimeConstraint(wp)
 2439  {
 2440     assert(nact>=2);
 2441     assert(act.count()==nact);
 2442     this->n_activities=nact;
 2443     this->activitiesId.clear();
 2444     for(int i=0; i<nact; i++)
 2445         this->activitiesId.append(act.at(i));
 2446 
 2447     assert(n>=0);
 2448     this->maxDays=n;
 2449 
 2450     this->type=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES;
 2451 }
 2452 
 2453 bool ConstraintMaxDaysBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
 2454 {
 2455     //compute the indices of the activities,
 2456     //based on their unique ID
 2457 
 2458     assert(this->n_activities==this->activitiesId.count());
 2459 
 2460     this->_activities.clear();
 2461     for(int i=0; i<this->n_activities; i++){
 2462         int j=r.activitiesHash.value(activitiesId.at(i), -1);
 2463         //assert(j>=0);
 2464         if(j>=0)
 2465             _activities.append(j);
 2466         /*int j;
 2467         Activity* act;
 2468         for(j=0; j<r.nInternalActivities; j++){
 2469             act=&r.internalActivitiesList[j];
 2470             if(act->id==this->activitiesId[i]){
 2471                 this->_activities.append(j);
 2472                 break;
 2473             }
 2474         }*/
 2475     }
 2476     this->_n_activities=this->_activities.count();
 2477     
 2478     if(this->_n_activities<=1){
 2479         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
 2480             tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
 2481         //assert(0);
 2482         return false;
 2483     }
 2484 
 2485     return true;
 2486 }
 2487 
 2488 void ConstraintMaxDaysBetweenActivities::removeUseless(Rules& r)
 2489 {
 2490     //remove the activitiesId which no longer exist (used after the deletion of an activity)
 2491     
 2492     assert(this->n_activities==this->activitiesId.count());
 2493 
 2494     QList<int> tmpList;
 2495 
 2496     for(int i=0; i<this->n_activities; i++){
 2497         Activity* act=r.activitiesPointerHash.value(activitiesId[i], NULL);
 2498         if(act!=NULL)
 2499             tmpList.append(act->id);
 2500         /*for(int k=0; k<r.activitiesList.size(); k++){
 2501             Activity* act=r.activitiesList[k];
 2502             if(act->id==this->activitiesId[i]){
 2503                 tmpList.append(act->id);
 2504                 break;
 2505             }
 2506         }*/
 2507     }
 2508     
 2509     this->activitiesId=tmpList;
 2510     this->n_activities=this->activitiesId.count();
 2511 
 2512     r.internalStructureComputed=false;
 2513 }
 2514 
 2515 bool ConstraintMaxDaysBetweenActivities::hasInactiveActivities(Rules& r)
 2516 {
 2517     int count=0;
 2518 
 2519     for(int i=0; i<this->n_activities; i++)
 2520         if(r.inactiveActivities.contains(this->activitiesId[i]))
 2521             count++;
 2522 
 2523     if(this->n_activities-count<=1)
 2524         return true;
 2525     else
 2526         return false;
 2527 }
 2528 
 2529 QString ConstraintMaxDaysBetweenActivities::getXmlDescription(Rules& r){
 2530     Q_UNUSED(r);
 2531 
 2532     QString s="<ConstraintMaxDaysBetweenActivities>\n";
 2533     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 2534     s+="    <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
 2535     for(int i=0; i<this->n_activities; i++)
 2536         s+="    <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
 2537     s+="    <MaxDays>"+CustomFETString::number(this->maxDays)+"</MaxDays>\n";
 2538     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 2539     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 2540     s+="</ConstraintMaxDaysBetweenActivities>\n";
 2541     return s;
 2542 }
 2543 
 2544 QString ConstraintMaxDaysBetweenActivities::getDescription(Rules& r){
 2545     Q_UNUSED(r);
 2546 
 2547     QString begin=QString("");
 2548     if(!active)
 2549         begin="X - ";
 2550         
 2551     QString end=QString("");
 2552     if(!comments.isEmpty())
 2553         end=", "+tr("C: %1", "Comments").arg(comments);
 2554         
 2555     QString s;
 2556     s+=tr("Max days between activities");s+=", ";
 2557     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 2558     s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
 2559     for(int i=0; i<this->n_activities; i++){
 2560         s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
 2561     }
 2562     s+=tr("MD:%1", "Abbreviation for maximum days").arg(this->maxDays);
 2563 
 2564     return begin+s+end;
 2565 }
 2566 
 2567 QString ConstraintMaxDaysBetweenActivities::getDetailedDescription(Rules& r){
 2568     QString s=tr("Time constraint");s+="\n";
 2569     s+=tr("Maximum number of days between activities");s+="\n";
 2570     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 2571     s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
 2572     for(int i=0; i<this->n_activities; i++){
 2573         s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
 2574             .arg(this->activitiesId[i])
 2575             .arg(getActivityDetailedDescription(r, this->activitiesId[i]));
 2576         s+="\n";
 2577     }
 2578     s+=tr("Maximum number of days=%1").arg(this->maxDays);s+="\n";
 2579 
 2580     if(!active){
 2581         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 2582         s+="\n";
 2583     }
 2584     if(!comments.isEmpty()){
 2585         s+=tr("Comments=%1").arg(comments);
 2586         s+="\n";
 2587     }
 2588 
 2589     return s;
 2590 }
 2591 
 2592 double ConstraintMaxDaysBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 2593 {
 2594     assert(r.internalStructureComputed);
 2595 
 2596     int nbroken;
 2597 
 2598     //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
 2599 
 2600     //sum the overlapping hours for all pairs of activities.
 2601     //without logging
 2602     if(conflictsString==NULL){
 2603         nbroken=0;
 2604         for(int i=1; i<this->_n_activities; i++){
 2605             int t1=c.times[this->_activities[i]];
 2606             if(t1!=UNALLOCATED_TIME){
 2607                 int day1=t1%r.nDaysPerWeek;
 2608                 //int hour1=t1/r.nDaysPerWeek;
 2609                 //int duration1=r.internalActivitiesList[this->_activities[i]].duration;
 2610 
 2611                 for(int j=0; j<i; j++){
 2612                     int t2=c.times[this->_activities[j]];
 2613                     if(t2!=UNALLOCATED_TIME){
 2614                         int day2=t2%r.nDaysPerWeek;
 2615                         //int hour2=t2/r.nDaysPerWeek;
 2616                         //int duration2=r.internalActivitiesList[this->_activities[j]].duration;
 2617                     
 2618                         int tmp;
 2619                         int tt=0;
 2620                         int dist=abs(day1-day2);
 2621                         if(dist>maxDays){
 2622                             tt=dist-maxDays;
 2623                             
 2624                             //if(this->consecutiveIfSameDay && day1==day2)
 2625                             //  assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
 2626                         }
 2627                         
 2628                         tmp=tt;
 2629     
 2630                         nbroken+=tmp;
 2631                     }
 2632                 }
 2633             }
 2634         }
 2635     }
 2636     //with logging
 2637     else{
 2638         nbroken=0;
 2639         for(int i=1; i<this->_n_activities; i++){
 2640             int t1=c.times[this->_activities[i]];
 2641             if(t1!=UNALLOCATED_TIME){
 2642                 int day1=t1%r.nDaysPerWeek;
 2643                 //int hour1=t1/r.nDaysPerWeek;
 2644                 //int duration1=r.internalActivitiesList[this->_activities[i]].duration;
 2645 
 2646                 for(int j=0; j<i; j++){
 2647                     int t2=c.times[this->_activities[j]];
 2648                     if(t2!=UNALLOCATED_TIME){
 2649                         int day2=t2%r.nDaysPerWeek;
 2650                         //int hour2=t2/r.nDaysPerWeek;
 2651                         //int duration2=r.internalActivitiesList[this->_activities[j]].duration;
 2652                     
 2653                         int tmp;
 2654                         int tt=0;
 2655                         int dist=abs(day1-day2);
 2656 
 2657                         if(dist>maxDays){
 2658                             tt=dist-maxDays;
 2659                             
 2660                             //if(this->consecutiveIfSameDay && day1==day2)
 2661                             //  assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
 2662                         }
 2663 
 2664                         tmp=tt;
 2665     
 2666                         nbroken+=tmp;
 2667 
 2668                         if(tt>0 && conflictsString!=NULL){
 2669                             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"
 2670                              ", on days %6 and %7", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
 2671                              .arg(this->activitiesId[i])
 2672                              .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
 2673                              .arg(this->activitiesId[j])
 2674                              .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
 2675                              .arg(tt)
 2676                              .arg(r.daysOfTheWeek[day1])
 2677                              .arg(r.daysOfTheWeek[day2]);
 2678                             
 2679                             s+=", ";
 2680                             s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
 2681                             s+=".";
 2682                             
 2683                             dl.append(s);
 2684                             cl.append(tmp*weightPercentage/100);
 2685                             
 2686                             *conflictsString+= s+"\n";
 2687                         }
 2688                     }
 2689                 }
 2690             }
 2691         }
 2692     }
 2693 
 2694     if(weightPercentage==100)
 2695         assert(nbroken==0);
 2696     return weightPercentage/100 * nbroken;
 2697 }
 2698 
 2699 bool ConstraintMaxDaysBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
 2700 {
 2701     Q_UNUSED(r);
 2702 
 2703     for(int i=0; i<this->n_activities; i++)
 2704         if(this->activitiesId[i]==a->id)
 2705             return true;
 2706     return false;
 2707 }
 2708 
 2709 bool ConstraintMaxDaysBetweenActivities::isRelatedToTeacher(Teacher* t)
 2710 {
 2711     Q_UNUSED(t);
 2712 
 2713     return false;
 2714 }
 2715 
 2716 bool ConstraintMaxDaysBetweenActivities::isRelatedToSubject(Subject* s)
 2717 {
 2718     Q_UNUSED(s);
 2719 
 2720     return false;
 2721 }
 2722 
 2723 bool ConstraintMaxDaysBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
 2724 {
 2725     Q_UNUSED(s);
 2726 
 2727     return false;
 2728 }
 2729 
 2730 bool ConstraintMaxDaysBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 2731 {
 2732     Q_UNUSED(r);
 2733     Q_UNUSED(s);
 2734 
 2735     return false;
 2736 }
 2737 
 2738 bool ConstraintMaxDaysBetweenActivities::hasWrongDayOrHour(Rules& r)
 2739 {
 2740     if(maxDays>=r.nDaysPerWeek)
 2741         return true;
 2742         
 2743     return false;
 2744 }
 2745 
 2746 bool ConstraintMaxDaysBetweenActivities::canRepairWrongDayOrHour(Rules& r)
 2747 {
 2748     assert(hasWrongDayOrHour(r));
 2749     
 2750     return true;
 2751 }
 2752 
 2753 bool ConstraintMaxDaysBetweenActivities::repairWrongDayOrHour(Rules& r)
 2754 {
 2755     assert(hasWrongDayOrHour(r));
 2756     
 2757     if(maxDays>=r.nDaysPerWeek)
 2758         maxDays=r.nDaysPerWeek-1;
 2759 
 2760     return true;
 2761 }
 2762 
 2763 ////////////////////////////////////////////////////////////////////////////////////////////
 2764 ////////////////////////////////////////////////////////////////////////////////////////////
 2765 
 2766 ConstraintMinGapsBetweenActivities::ConstraintMinGapsBetweenActivities()
 2767     : TimeConstraint()
 2768 {
 2769     type=CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES;
 2770 }
 2771 
 2772 ConstraintMinGapsBetweenActivities::ConstraintMinGapsBetweenActivities(double wp, int nact, const QList<int>& actList, int ngaps)
 2773  : TimeConstraint(wp)
 2774  {
 2775     this->n_activities=nact;
 2776     assert(nact==actList.count());
 2777     this->activitiesId.clear();
 2778     for(int i=0; i<nact; i++)
 2779         this->activitiesId.append(actList.at(i));
 2780 
 2781     assert(ngaps>0);
 2782     this->minGaps=ngaps;
 2783 
 2784     this->type=CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES;
 2785 }
 2786 
 2787 bool ConstraintMinGapsBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
 2788 {
 2789     //compute the indices of the activities,
 2790     //based on their unique ID
 2791 
 2792     assert(this->n_activities==this->activitiesId.count());
 2793 
 2794     this->_activities.clear();
 2795     for(int i=0; i<this->n_activities; i++){
 2796         int j=r.activitiesHash.value(activitiesId.at(i), -1);
 2797         //assert(j>=0);
 2798         if(j>=0)
 2799             _activities.append(j);
 2800         /*int j;
 2801         Activity* act;
 2802         for(j=0; j<r.nInternalActivities; j++){
 2803             act=&r.internalActivitiesList[j];
 2804             if(act->id==this->activitiesId[i]){
 2805                 this->_activities.append(j);
 2806                 break;
 2807             }
 2808         }*/
 2809     }
 2810     this->_n_activities=this->_activities.count();
 2811     
 2812     if(this->_n_activities<=1){
 2813         TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
 2814             tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
 2815         //assert(0);
 2816         return false;
 2817     }
 2818 
 2819     return true;
 2820 }
 2821 
 2822 void ConstraintMinGapsBetweenActivities::removeUseless(Rules& r)
 2823 {
 2824     //remove the activitiesId which no longer exist (used after the deletion of an activity)
 2825     
 2826     assert(this->n_activities==this->activitiesId.count());
 2827 
 2828     QList<int> tmpList;
 2829 
 2830     for(int i=0; i<this->n_activities; i++){
 2831         Activity* act=r.activitiesPointerHash.value(activitiesId[i], NULL);
 2832         if(act!=NULL)
 2833             tmpList.append(act->id);
 2834         /*for(int k=0; k<r.activitiesList.size(); k++){
 2835             Activity* act=r.activitiesList[k];
 2836             if(act->id==this->activitiesId[i]){
 2837                 tmpList.append(act->id);
 2838                 break;
 2839             }
 2840         }*/
 2841     }
 2842     
 2843     this->activitiesId=tmpList;
 2844     this->n_activities=this->activitiesId.count();
 2845 
 2846     r.internalStructureComputed=false;
 2847 }
 2848 
 2849 bool ConstraintMinGapsBetweenActivities::hasInactiveActivities(Rules& r)
 2850 {
 2851     int count=0;
 2852 
 2853     for(int i=0; i<this->n_activities; i++)
 2854         if(r.inactiveActivities.contains(this->activitiesId[i]))
 2855             count++;
 2856 
 2857     if(this->n_activities-count<=1)
 2858         return true;
 2859     else
 2860         return false;
 2861 }
 2862 
 2863 QString ConstraintMinGapsBetweenActivities::getXmlDescription(Rules& r){
 2864     Q_UNUSED(r);
 2865 
 2866     QString s="<ConstraintMinGapsBetweenActivities>\n";
 2867     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 2868     s+="    <Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
 2869     for(int i=0; i<this->n_activities; i++)
 2870         s+="    <Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
 2871     s+="    <MinGaps>"+CustomFETString::number(this->minGaps)+"</MinGaps>\n";
 2872     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 2873     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 2874     s+="</ConstraintMinGapsBetweenActivities>\n";
 2875     return s;
 2876 }
 2877 
 2878 QString ConstraintMinGapsBetweenActivities::getDescription(Rules& r){
 2879     Q_UNUSED(r);
 2880 
 2881     QString begin=QString("");
 2882     if(!active)
 2883         begin="X - ";
 2884         
 2885     QString end=QString("");
 2886     if(!comments.isEmpty())
 2887         end=", "+tr("C: %1", "Comments").arg(comments);
 2888         
 2889     QString s;
 2890     s+=tr("Min gaps between activities");s+=", ";
 2891     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 2892     s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
 2893     for(int i=0; i<this->n_activities; i++){
 2894         s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
 2895     }
 2896     s+=tr("mG:%1", "Minimum number of gaps").arg(this->minGaps);
 2897 
 2898     return begin+s+end;
 2899 }
 2900 
 2901 QString ConstraintMinGapsBetweenActivities::getDetailedDescription(Rules& r){
 2902     QString s=tr("Time constraint");s+="\n";
 2903     s+=tr("Minimum gaps between activities (if activities on the same day)");s+="\n";
 2904     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 2905     s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
 2906     for(int i=0; i<this->n_activities; i++){
 2907         s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
 2908             .arg(this->activitiesId[i])
 2909             .arg(getActivityDetailedDescription(r, this->activitiesId[i]));
 2910         s+="\n";
 2911     }
 2912     s+=tr("Minimum number of gaps=%1").arg(this->minGaps);s+="\n";
 2913 
 2914     if(!active){
 2915         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 2916         s+="\n";
 2917     }
 2918     if(!comments.isEmpty()){
 2919         s+=tr("Comments=%1").arg(comments);
 2920         s+="\n";
 2921     }
 2922 
 2923     return s;
 2924 }
 2925 
 2926 double ConstraintMinGapsBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 2927 {
 2928     assert(r.internalStructureComputed);
 2929 
 2930     int nbroken;
 2931 
 2932     //We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
 2933 
 2934     nbroken=0;
 2935     for(int i=1; i<this->_n_activities; i++){
 2936         int t1=c.times[this->_activities[i]];
 2937         if(t1!=UNALLOCATED_TIME){
 2938             int day1=t1%r.nDaysPerWeek;
 2939             int hour1=t1/r.nDaysPerWeek;
 2940             int duration1=r.internalActivitiesList[this->_activities[i]].duration;
 2941 
 2942             for(int j=0; j<i; j++){
 2943                 int t2=c.times[this->_activities[j]];
 2944                 if(t2!=UNALLOCATED_TIME){
 2945                     int day2=t2%r.nDaysPerWeek;
 2946                     int hour2=t2/r.nDaysPerWeek;
 2947                     int duration2=r.internalActivitiesList[this->_activities[j]].duration;
 2948                 
 2949                     int tmp;
 2950                     int tt=0;
 2951                     int dist=abs(day1-day2);
 2952                     
 2953                     if(dist==0){ //same day
 2954                         assert(day1==day2);
 2955                         if(hour2>=hour1){
 2956                             //assert(hour1+duration1<=hour2); not true for activities which are not incompatible
 2957                             if(hour1+duration1+minGaps > hour2)
 2958                                 tt = (hour1+duration1+minGaps) - hour2;
 2959                         }
 2960                         else{
 2961                             //assert(hour2+duration2<=hour1); not true for activities which are not incompatible
 2962                             if(hour2+duration2+minGaps > hour1)
 2963                                 tt = (hour2+duration2+minGaps) - hour1;
 2964                         }
 2965                     }
 2966 
 2967                     tmp=tt;
 2968     
 2969                     nbroken+=tmp;
 2970 
 2971                     if(tt>0 && conflictsString!=NULL){
 2972                         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 more needed hours between them",
 2973                             "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
 2974                          .arg(this->activitiesId[i])
 2975                          .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
 2976                          .arg(this->activitiesId[j])
 2977                          .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
 2978                          .arg(r.daysOfTheWeek[day1])
 2979                          .arg(tt);
 2980 
 2981                         s+=", ";
 2982                         s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
 2983                         s+=".";
 2984                             
 2985                         dl.append(s);
 2986                         cl.append(tmp*weightPercentage/100);
 2987                             
 2988                         *conflictsString+= s+"\n";
 2989                     }
 2990                 }
 2991             }
 2992         }
 2993     }
 2994 
 2995     if(weightPercentage==100)
 2996         assert(nbroken==0);
 2997     return weightPercentage/100 * nbroken;
 2998 }
 2999 
 3000 bool ConstraintMinGapsBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
 3001 {
 3002     Q_UNUSED(r);
 3003 
 3004     for(int i=0; i<this->n_activities; i++)
 3005         if(this->activitiesId[i]==a->id)
 3006             return true;
 3007     return false;
 3008 }
 3009 
 3010 bool ConstraintMinGapsBetweenActivities::isRelatedToTeacher(Teacher* t)
 3011 {
 3012     Q_UNUSED(t);
 3013 
 3014     return false;
 3015 }
 3016 
 3017 bool ConstraintMinGapsBetweenActivities::isRelatedToSubject(Subject* s)
 3018 {
 3019     Q_UNUSED(s);
 3020 
 3021     return false;
 3022 }
 3023 
 3024 bool ConstraintMinGapsBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
 3025 {
 3026     Q_UNUSED(s);
 3027 
 3028     return false;
 3029 }
 3030 
 3031 bool ConstraintMinGapsBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 3032 {
 3033     Q_UNUSED(r);
 3034     Q_UNUSED(s);
 3035 
 3036     return false;
 3037 }
 3038 
 3039 bool ConstraintMinGapsBetweenActivities::hasWrongDayOrHour(Rules& r)
 3040 {
 3041     if(minGaps>r.nHoursPerDay)
 3042         return true;
 3043         
 3044     return false;
 3045 }
 3046 
 3047 bool ConstraintMinGapsBetweenActivities::canRepairWrongDayOrHour(Rules& r)
 3048 {
 3049     assert(hasWrongDayOrHour(r));
 3050     
 3051     return true;
 3052 }
 3053 
 3054 bool ConstraintMinGapsBetweenActivities::repairWrongDayOrHour(Rules& r)
 3055 {
 3056     assert(hasWrongDayOrHour(r));
 3057     
 3058     if(minGaps>r.nHoursPerDay)
 3059         minGaps=r.nHoursPerDay;
 3060 
 3061     return true;
 3062 }
 3063 
 3064 ///////////////////////////////////////////////////////////////////////////////////////////
 3065 ///////////////////////////////////////////////////////////////////////////////////////////
 3066 
 3067 ConstraintTeachersMaxHoursDaily::ConstraintTeachersMaxHoursDaily()
 3068     : TimeConstraint()
 3069 {
 3070     this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY;
 3071 }
 3072 
 3073 ConstraintTeachersMaxHoursDaily::ConstraintTeachersMaxHoursDaily(double wp, int maxhours)
 3074  : TimeConstraint(wp)
 3075  {
 3076     assert(maxhours>0);
 3077     this->maxHoursDaily=maxhours;
 3078 
 3079     this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY;
 3080 }
 3081 
 3082 bool ConstraintTeachersMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
 3083 {
 3084     Q_UNUSED(parent);
 3085     Q_UNUSED(r);
 3086     
 3087     return true;
 3088 }
 3089 
 3090 bool ConstraintTeachersMaxHoursDaily::hasInactiveActivities(Rules& r)
 3091 {
 3092     Q_UNUSED(r);
 3093     return false;
 3094 }
 3095 
 3096 QString ConstraintTeachersMaxHoursDaily::getXmlDescription(Rules& r){
 3097     Q_UNUSED(r);
 3098 
 3099     QString s="<ConstraintTeachersMaxHoursDaily>\n";
 3100     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 3101     s+="    <Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
 3102     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 3103     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 3104     s+="</ConstraintTeachersMaxHoursDaily>\n";
 3105     return s;
 3106 }
 3107 
 3108 QString ConstraintTeachersMaxHoursDaily::getDescription(Rules& r){
 3109     Q_UNUSED(r);
 3110 
 3111     QString begin=QString("");
 3112     if(!active)
 3113         begin="X - ";
 3114         
 3115     QString end=QString("");
 3116     if(!comments.isEmpty())
 3117         end=", "+tr("C: %1", "Comments").arg(comments);
 3118         
 3119     QString s;
 3120     s+=tr("Teachers max hours daily"), s+=", ";
 3121     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 3122     s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
 3123 
 3124     return begin+s+end;
 3125 }
 3126 
 3127 QString ConstraintTeachersMaxHoursDaily::getDetailedDescription(Rules& r){
 3128     Q_UNUSED(r);
 3129 
 3130     QString s=tr("Time constraint");s+="\n";
 3131     s+=tr("All teachers must respect the maximum number of hours daily");s+="\n";
 3132     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 3133     s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
 3134 
 3135     if(!active){
 3136         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 3137         s+="\n";
 3138     }
 3139     if(!comments.isEmpty()){
 3140         s+=tr("Comments=%1").arg(comments);
 3141         s+="\n";
 3142     }
 3143 
 3144     return s;
 3145 }
 3146 
 3147 double ConstraintTeachersMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 3148 {
 3149     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 3150     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 3151         c.teachersMatrixReady=true;
 3152         c.subgroupsMatrixReady=true;
 3153         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 3154         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 3155 
 3156         c.changedForMatrixCalculation=false;
 3157     }
 3158 
 3159     int nbroken;
 3160 
 3161     //without logging
 3162     if(conflictsString==NULL){
 3163         nbroken=0;
 3164         for(int i=0; i<r.nInternalTeachers; i++){
 3165             for(int d=0; d<r.nDaysPerWeek; d++){
 3166                 int n_hours_daily=0;
 3167                 for(int h=0; h<r.nHoursPerDay; h++)
 3168                     if(teachersMatrix[i][d][h]>0)
 3169                         n_hours_daily++;
 3170 
 3171                 if(n_hours_daily>this->maxHoursDaily)
 3172                     nbroken++;
 3173             }
 3174         }
 3175     }
 3176     //with logging
 3177     else{
 3178         nbroken=0;
 3179         for(int i=0; i<r.nInternalTeachers; i++){
 3180             for(int d=0; d<r.nDaysPerWeek; d++){
 3181                 int n_hours_daily=0;
 3182                 for(int h=0; h<r.nHoursPerDay; h++)
 3183                     if(teachersMatrix[i][d][h]>0)
 3184                         n_hours_daily++;
 3185 
 3186                 if(n_hours_daily>this->maxHoursDaily){
 3187                     nbroken++;
 3188 
 3189                     if(conflictsString!=NULL){
 3190                         QString s=(tr(
 3191                          "Time constraint teachers max %1 hours daily broken for teacher %2, on day %3, length=%4.")
 3192                          .arg(CustomFETString::number(this->maxHoursDaily))
 3193                          .arg(r.internalTeachersList[i]->name)
 3194                          .arg(r.daysOfTheWeek[d])
 3195                          .arg(n_hours_daily)
 3196                          )
 3197                          +
 3198                          " "
 3199                          +
 3200                          (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 3201                         
 3202                         dl.append(s);
 3203                         cl.append(weightPercentage/100);
 3204                     
 3205                         *conflictsString+= s+"\n";
 3206                     }
 3207                 }
 3208             }
 3209         }
 3210     }
 3211 
 3212     if(weightPercentage==100)
 3213         assert(nbroken==0);
 3214     return weightPercentage/100 * nbroken;
 3215 }
 3216 
 3217 bool ConstraintTeachersMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
 3218 {
 3219     Q_UNUSED(r);
 3220     Q_UNUSED(a);
 3221 
 3222     return false;
 3223 }
 3224 
 3225 bool ConstraintTeachersMaxHoursDaily::isRelatedToTeacher(Teacher* t)
 3226 {
 3227     Q_UNUSED(t);
 3228 
 3229     return true;
 3230 }
 3231 
 3232 bool ConstraintTeachersMaxHoursDaily::isRelatedToSubject(Subject* s)
 3233 {
 3234     Q_UNUSED(s);
 3235 
 3236     return false;
 3237 }
 3238 
 3239 bool ConstraintTeachersMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
 3240 {
 3241     Q_UNUSED(s);
 3242 
 3243     return false;
 3244 }
 3245 
 3246 bool ConstraintTeachersMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 3247 {
 3248     Q_UNUSED(r);
 3249     Q_UNUSED(s);
 3250 
 3251     return false;
 3252 }
 3253 
 3254 bool ConstraintTeachersMaxHoursDaily::hasWrongDayOrHour(Rules& r)
 3255 {
 3256     if(maxHoursDaily>r.nHoursPerDay)
 3257         return true;
 3258         
 3259     return false;
 3260 }
 3261 
 3262 bool ConstraintTeachersMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
 3263 {
 3264     assert(hasWrongDayOrHour(r));
 3265     
 3266     return true;
 3267 }
 3268 
 3269 bool ConstraintTeachersMaxHoursDaily::repairWrongDayOrHour(Rules& r)
 3270 {
 3271     assert(hasWrongDayOrHour(r));
 3272     
 3273     if(maxHoursDaily>r.nHoursPerDay)
 3274         maxHoursDaily=r.nHoursPerDay;
 3275 
 3276     return true;
 3277 }
 3278 
 3279 ///////////////////////////////////////////////////////////////////////////////////////////
 3280 ///////////////////////////////////////////////////////////////////////////////////////////
 3281 
 3282 ConstraintTeacherMaxHoursDaily::ConstraintTeacherMaxHoursDaily()
 3283     : TimeConstraint()
 3284 {
 3285     this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY;
 3286 }
 3287 
 3288 ConstraintTeacherMaxHoursDaily::ConstraintTeacherMaxHoursDaily(double wp, int maxhours, const QString& teacher)
 3289  : TimeConstraint(wp)
 3290  {
 3291     assert(maxhours>0);
 3292     this->maxHoursDaily=maxhours;
 3293     this->teacherName=teacher;
 3294 
 3295     this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY;
 3296 }
 3297 
 3298 bool ConstraintTeacherMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
 3299 {
 3300     Q_UNUSED(parent);
 3301 
 3302     //this->teacher_ID=r.searchTeacher(this->teacherName);
 3303     teacher_ID=r.teachersHash.value(teacherName, -1);
 3304     assert(this->teacher_ID>=0);
 3305     return true;
 3306 }
 3307 
 3308 bool ConstraintTeacherMaxHoursDaily::hasInactiveActivities(Rules& r)
 3309 {
 3310     Q_UNUSED(r);
 3311     return false;
 3312 }
 3313 
 3314 QString ConstraintTeacherMaxHoursDaily::getXmlDescription(Rules& r){
 3315     Q_UNUSED(r);
 3316 
 3317     QString s="<ConstraintTeacherMaxHoursDaily>\n";
 3318     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 3319     s+="    <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
 3320     s+="    <Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
 3321     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 3322     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 3323     s+="</ConstraintTeacherMaxHoursDaily>\n";
 3324     return s;
 3325 }
 3326 
 3327 QString ConstraintTeacherMaxHoursDaily::getDescription(Rules& r){
 3328     Q_UNUSED(r);
 3329 
 3330     QString begin=QString("");
 3331     if(!active)
 3332         begin="X - ";
 3333         
 3334     QString end=QString("");
 3335     if(!comments.isEmpty())
 3336         end=", "+tr("C: %1", "Comments").arg(comments);
 3337         
 3338     QString s;
 3339     s+=tr("Teacher max hours daily");s+=", ";
 3340     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 3341     s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
 3342     s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
 3343 
 3344     return begin+s+end;
 3345 }
 3346 
 3347 QString ConstraintTeacherMaxHoursDaily::getDetailedDescription(Rules& r){
 3348     Q_UNUSED(r);
 3349 
 3350     QString s=tr("Time constraint");s+="\n";
 3351     s+=tr("A teacher must respect the maximum number of hours daily");s+="\n";
 3352     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 3353     s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
 3354     s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
 3355 
 3356     if(!active){
 3357         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 3358         s+="\n";
 3359     }
 3360     if(!comments.isEmpty()){
 3361         s+=tr("Comments=%1").arg(comments);
 3362         s+="\n";
 3363     }
 3364 
 3365     return s;
 3366 }
 3367 
 3368 double ConstraintTeacherMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 3369 {
 3370     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 3371     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 3372         c.teachersMatrixReady=true;
 3373         c.subgroupsMatrixReady=true;
 3374         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 3375         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 3376 
 3377         c.changedForMatrixCalculation=false;
 3378     }
 3379 
 3380     int nbroken;
 3381 
 3382     //without logging
 3383     if(conflictsString==NULL){
 3384         nbroken=0;
 3385         int i=this->teacher_ID;
 3386         for(int d=0; d<r.nDaysPerWeek; d++){
 3387             int n_hours_daily=0;
 3388             for(int h=0; h<r.nHoursPerDay; h++)
 3389                 if(teachersMatrix[i][d][h]>0)
 3390                     n_hours_daily++;
 3391 
 3392             if(n_hours_daily>this->maxHoursDaily){
 3393                 nbroken++;
 3394             }
 3395         }
 3396     }
 3397     //with logging
 3398     else{
 3399         nbroken=0;
 3400         int i=this->teacher_ID;
 3401         for(int d=0; d<r.nDaysPerWeek; d++){
 3402             int n_hours_daily=0;
 3403             for(int h=0; h<r.nHoursPerDay; h++)
 3404                 if(teachersMatrix[i][d][h]>0)
 3405                     n_hours_daily++;
 3406 
 3407             if(n_hours_daily>this->maxHoursDaily){
 3408                 nbroken++;
 3409 
 3410                 if(conflictsString!=NULL){
 3411                     QString s=(tr(
 3412                      "Time constraint teacher max %1 hours daily broken for teacher %2, on day %3, length=%4.")
 3413                      .arg(CustomFETString::number(this->maxHoursDaily))
 3414                      .arg(r.internalTeachersList[i]->name)
 3415                      .arg(r.daysOfTheWeek[d])
 3416                      .arg(n_hours_daily)
 3417                      )
 3418                      +" "
 3419                      +
 3420                      (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 3421                         
 3422                     dl.append(s);
 3423                     cl.append(weightPercentage/100);
 3424                 
 3425                     *conflictsString+= s+"\n";
 3426                 }
 3427             }
 3428         }
 3429     }
 3430 
 3431     if(weightPercentage==100)
 3432         assert(nbroken==0);
 3433     return weightPercentage/100 * nbroken;
 3434 }
 3435 
 3436 bool ConstraintTeacherMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
 3437 {
 3438     Q_UNUSED(r);
 3439     Q_UNUSED(a);
 3440 
 3441     return false;
 3442 }
 3443 
 3444 bool ConstraintTeacherMaxHoursDaily::isRelatedToTeacher(Teacher* t)
 3445 {
 3446     if(this->teacherName==t->name)
 3447         return true;
 3448     return false;
 3449 }
 3450 
 3451 bool ConstraintTeacherMaxHoursDaily::isRelatedToSubject(Subject* s)
 3452 {
 3453     Q_UNUSED(s);
 3454 
 3455     return false;
 3456 }
 3457 
 3458 bool ConstraintTeacherMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
 3459 {
 3460     Q_UNUSED(s);
 3461 
 3462     return false;
 3463 }
 3464 
 3465 bool ConstraintTeacherMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 3466 {
 3467     Q_UNUSED(r);
 3468     Q_UNUSED(s);
 3469 
 3470     return false;
 3471 }
 3472 
 3473 bool ConstraintTeacherMaxHoursDaily::hasWrongDayOrHour(Rules& r)
 3474 {
 3475     if(maxHoursDaily>r.nHoursPerDay)
 3476         return true;
 3477         
 3478     return false;
 3479 }
 3480 
 3481 bool ConstraintTeacherMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
 3482 {
 3483     assert(hasWrongDayOrHour(r));
 3484     
 3485     return true;
 3486 }
 3487 
 3488 bool ConstraintTeacherMaxHoursDaily::repairWrongDayOrHour(Rules& r)
 3489 {
 3490     assert(hasWrongDayOrHour(r));
 3491     
 3492     if(maxHoursDaily>r.nHoursPerDay)
 3493         maxHoursDaily=r.nHoursPerDay;
 3494 
 3495     return true;
 3496 }
 3497 
 3498 ///////////////////////////////////////////////////////////////////////////////////////////
 3499 ///////////////////////////////////////////////////////////////////////////////////////////
 3500 
 3501 ConstraintTeachersMaxHoursContinuously::ConstraintTeachersMaxHoursContinuously()
 3502     : TimeConstraint()
 3503 {
 3504     this->type=CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY;
 3505 }
 3506 
 3507 ConstraintTeachersMaxHoursContinuously::ConstraintTeachersMaxHoursContinuously(double wp, int maxhours)
 3508  : TimeConstraint(wp)
 3509  {
 3510     assert(maxhours>0);
 3511     this->maxHoursContinuously=maxhours;
 3512 
 3513     this->type=CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY;
 3514 }
 3515 
 3516 bool ConstraintTeachersMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
 3517 {
 3518     Q_UNUSED(parent);
 3519     Q_UNUSED(r);
 3520 
 3521     return true;
 3522 }
 3523 
 3524 bool ConstraintTeachersMaxHoursContinuously::hasInactiveActivities(Rules& r)
 3525 {
 3526     Q_UNUSED(r);
 3527     return false;
 3528 }
 3529 
 3530 QString ConstraintTeachersMaxHoursContinuously::getXmlDescription(Rules& r){
 3531     Q_UNUSED(r);
 3532 
 3533     QString s="<ConstraintTeachersMaxHoursContinuously>\n";
 3534     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 3535     s+="    <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
 3536     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 3537     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 3538     s+="</ConstraintTeachersMaxHoursContinuously>\n";
 3539     return s;
 3540 }
 3541 
 3542 QString ConstraintTeachersMaxHoursContinuously::getDescription(Rules& r){
 3543     Q_UNUSED(r);
 3544 
 3545     QString begin=QString("");
 3546     if(!active)
 3547         begin="X - ";
 3548         
 3549     QString end=QString("");
 3550     if(!comments.isEmpty())
 3551         end=", "+tr("C: %1", "Comments").arg(comments);
 3552         
 3553     QString s;
 3554     s+=tr("Teachers max hours continuously");s+=", ";
 3555     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 3556     s+=tr("MH:%1", "Maximum hours (continuously)").arg(this->maxHoursContinuously);
 3557 
 3558     return begin+s+end;
 3559 }
 3560 
 3561 QString ConstraintTeachersMaxHoursContinuously::getDetailedDescription(Rules& r){
 3562     Q_UNUSED(r);
 3563 
 3564     QString s=tr("Time constraint");s+="\n";
 3565     s+=tr("All teachers must respect the maximum number of hours continuously");s+="\n";
 3566     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 3567     s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
 3568 
 3569     if(!active){
 3570         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 3571         s+="\n";
 3572     }
 3573     if(!comments.isEmpty()){
 3574         s+=tr("Comments=%1").arg(comments);
 3575         s+="\n";
 3576     }
 3577 
 3578     return s;
 3579 }
 3580 
 3581 double ConstraintTeachersMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 3582 {
 3583     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 3584     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 3585         c.teachersMatrixReady=true;
 3586         c.subgroupsMatrixReady=true;
 3587         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 3588         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 3589 
 3590         c.changedForMatrixCalculation=false;
 3591     }
 3592 
 3593     int nbroken;
 3594 
 3595     nbroken=0;
 3596     for(int i=0; i<r.nInternalTeachers; i++){
 3597         for(int d=0; d<r.nDaysPerWeek; d++){
 3598             int nc=0;
 3599             for(int h=0; h<r.nHoursPerDay; h++){
 3600                 if(teachersMatrix[i][d][h]>0)
 3601                     nc++;
 3602                 else{
 3603                     if(nc>this->maxHoursContinuously){
 3604                         nbroken++;
 3605 
 3606                         if(conflictsString!=NULL){
 3607                             QString s=(tr(
 3608                              "Time constraint teachers max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
 3609                              .arg(CustomFETString::number(this->maxHoursContinuously))
 3610                              .arg(r.internalTeachersList[i]->name)
 3611                              .arg(r.daysOfTheWeek[d])
 3612                              .arg(nc)
 3613                              )
 3614                              +
 3615                              " "
 3616                              +
 3617                              (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 3618                             
 3619                             dl.append(s);
 3620                             cl.append(weightPercentage/100);
 3621                 
 3622                             *conflictsString+= s+"\n";
 3623                         }
 3624                     }
 3625                 
 3626                     nc=0;
 3627                 }
 3628             }
 3629 
 3630             if(nc>this->maxHoursContinuously){
 3631                 nbroken++;
 3632 
 3633                 if(conflictsString!=NULL){
 3634                     QString s=(tr(
 3635                      "Time constraint teachers max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
 3636                      .arg(CustomFETString::number(this->maxHoursContinuously))
 3637                      .arg(r.internalTeachersList[i]->name)
 3638                      .arg(r.daysOfTheWeek[d])
 3639                      .arg(nc)
 3640                      )
 3641                      +
 3642                      " "
 3643                      +
 3644                      (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 3645                             
 3646                     dl.append(s);
 3647                     cl.append(weightPercentage/100);
 3648                 
 3649                     *conflictsString+= s+"\n";
 3650                 }
 3651             }
 3652         }
 3653     }
 3654 
 3655     if(weightPercentage==100)   
 3656         assert(nbroken==0);
 3657     return weightPercentage/100 * nbroken;
 3658 }
 3659 
 3660 bool ConstraintTeachersMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
 3661 {
 3662     Q_UNUSED(r);
 3663     Q_UNUSED(a);
 3664 
 3665     return false;
 3666 }
 3667 
 3668 bool ConstraintTeachersMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
 3669 {
 3670     Q_UNUSED(t);
 3671 
 3672     return true;
 3673 }
 3674 
 3675 bool ConstraintTeachersMaxHoursContinuously::isRelatedToSubject(Subject* s)
 3676 {
 3677     Q_UNUSED(s);
 3678 
 3679     return false;
 3680 }
 3681 
 3682 bool ConstraintTeachersMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
 3683 {
 3684     Q_UNUSED(s);
 3685 
 3686     return false;
 3687 }
 3688 
 3689 bool ConstraintTeachersMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 3690 {
 3691     Q_UNUSED(r);
 3692     Q_UNUSED(s);
 3693 
 3694     return false;
 3695 }
 3696 
 3697 bool ConstraintTeachersMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
 3698 {
 3699     if(maxHoursContinuously>r.nHoursPerDay)
 3700         return true;
 3701     
 3702     return false;
 3703 }
 3704 
 3705 bool ConstraintTeachersMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
 3706 {
 3707     assert(hasWrongDayOrHour(r));
 3708     
 3709     return true;
 3710 }
 3711 
 3712 bool ConstraintTeachersMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
 3713 {
 3714     assert(hasWrongDayOrHour(r));
 3715     
 3716     if(maxHoursContinuously>r.nHoursPerDay)
 3717         maxHoursContinuously=r.nHoursPerDay;
 3718 
 3719     return true;
 3720 }
 3721 
 3722 ///////////////////////////////////////////////////////////////////////////////////////////
 3723 ///////////////////////////////////////////////////////////////////////////////////////////
 3724 
 3725 ConstraintTeacherMaxHoursContinuously::ConstraintTeacherMaxHoursContinuously()
 3726     : TimeConstraint()
 3727 {
 3728     this->type=CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY;
 3729 }
 3730 
 3731 ConstraintTeacherMaxHoursContinuously::ConstraintTeacherMaxHoursContinuously(double wp, int maxhours, const QString& teacher)
 3732  : TimeConstraint(wp)
 3733  {
 3734     assert(maxhours>0);
 3735     this->maxHoursContinuously=maxhours;
 3736     this->teacherName=teacher;
 3737 
 3738     this->type=CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY;
 3739 }
 3740 
 3741 bool ConstraintTeacherMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
 3742 {
 3743     Q_UNUSED(parent);
 3744 
 3745     //this->teacher_ID=r.searchTeacher(this->teacherName);
 3746     teacher_ID=r.teachersHash.value(teacherName, -1);
 3747     assert(this->teacher_ID>=0);
 3748     return true;
 3749 }
 3750 
 3751 bool ConstraintTeacherMaxHoursContinuously::hasInactiveActivities(Rules& r)
 3752 {
 3753     Q_UNUSED(r);
 3754     return false;
 3755 }
 3756 
 3757 QString ConstraintTeacherMaxHoursContinuously::getXmlDescription(Rules& r){
 3758     Q_UNUSED(r);
 3759 
 3760     QString s="<ConstraintTeacherMaxHoursContinuously>\n";
 3761     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 3762     s+="    <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
 3763     s+="    <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
 3764     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 3765     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 3766     s+="</ConstraintTeacherMaxHoursContinuously>\n";
 3767     return s;
 3768 }
 3769 
 3770 QString ConstraintTeacherMaxHoursContinuously::getDescription(Rules& r){
 3771     Q_UNUSED(r);
 3772 
 3773     QString begin=QString("");
 3774     if(!active)
 3775         begin="X - ";
 3776         
 3777     QString end=QString("");
 3778     if(!comments.isEmpty())
 3779         end=", "+tr("C: %1", "Comments").arg(comments);
 3780         
 3781     QString s;
 3782     s+=tr("Teacher max hours continuously");s+=", ";
 3783     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 3784     s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
 3785     s+=tr("MH:%1", "Maximum hours continuously").arg(this->maxHoursContinuously);
 3786 
 3787     return begin+s+end;
 3788 }
 3789 
 3790 QString ConstraintTeacherMaxHoursContinuously::getDetailedDescription(Rules& r){
 3791     Q_UNUSED(r);
 3792 
 3793     QString s=tr("Time constraint");s+="\n";
 3794     s+=tr("A teacher must respect the maximum number of hours continuously");s+="\n";
 3795     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 3796     s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
 3797     s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
 3798 
 3799     if(!active){
 3800         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 3801         s+="\n";
 3802     }
 3803     if(!comments.isEmpty()){
 3804         s+=tr("Comments=%1").arg(comments);
 3805         s+="\n";
 3806     }
 3807 
 3808     return s;
 3809 }
 3810 
 3811 double ConstraintTeacherMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 3812 {
 3813     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 3814     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 3815         c.teachersMatrixReady=true;
 3816         c.subgroupsMatrixReady=true;
 3817         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 3818         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 3819 
 3820         c.changedForMatrixCalculation=false;
 3821     }
 3822 
 3823     int nbroken;
 3824 
 3825     nbroken=0;
 3826     int i=this->teacher_ID;
 3827     for(int d=0; d<r.nDaysPerWeek; d++){
 3828         int nc=0;
 3829         for(int h=0; h<r.nHoursPerDay; h++){
 3830             if(teachersMatrix[i][d][h]>0)
 3831                 nc++;
 3832             else{
 3833                 if(nc>this->maxHoursContinuously){
 3834                     nbroken++;
 3835 
 3836                     if(conflictsString!=NULL){
 3837                         QString s=(tr(
 3838                          "Time constraint teacher max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
 3839                          .arg(CustomFETString::number(this->maxHoursContinuously))
 3840                          .arg(r.internalTeachersList[i]->name)
 3841                          .arg(r.daysOfTheWeek[d])
 3842                          .arg(nc)
 3843                          )
 3844                          +
 3845                          " "
 3846                          +
 3847                          (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 3848                         
 3849                         dl.append(s);
 3850                         cl.append(weightPercentage/100);
 3851             
 3852                         *conflictsString+= s+"\n";
 3853                     }
 3854                 }
 3855             
 3856                 nc=0;
 3857             }
 3858         }
 3859 
 3860         if(nc>this->maxHoursContinuously){
 3861             nbroken++;
 3862 
 3863             if(conflictsString!=NULL){
 3864                 QString s=(tr(
 3865                  "Time constraint teacher max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
 3866                  .arg(CustomFETString::number(this->maxHoursContinuously))
 3867                  .arg(r.internalTeachersList[i]->name)
 3868                  .arg(r.daysOfTheWeek[d])
 3869                  .arg(nc)
 3870                  )
 3871                  +
 3872                  " "
 3873                  +
 3874                  (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 3875                         
 3876                 dl.append(s);
 3877                 cl.append(weightPercentage/100);
 3878             
 3879                 *conflictsString+= s+"\n";
 3880             }
 3881         }
 3882     }
 3883 
 3884     if(weightPercentage==100)
 3885         assert(nbroken==0);
 3886     return weightPercentage/100 * nbroken;
 3887 }
 3888 
 3889 bool ConstraintTeacherMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
 3890 {
 3891     Q_UNUSED(r);
 3892     Q_UNUSED(a);
 3893 
 3894     return false;
 3895 }
 3896 
 3897 bool ConstraintTeacherMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
 3898 {
 3899     if(this->teacherName==t->name)
 3900         return true;
 3901     return false;
 3902 }
 3903 
 3904 bool ConstraintTeacherMaxHoursContinuously::isRelatedToSubject(Subject* s)
 3905 {
 3906     Q_UNUSED(s);
 3907 
 3908     return false;
 3909 }
 3910 
 3911 bool ConstraintTeacherMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
 3912 {
 3913     Q_UNUSED(s);
 3914 
 3915     return false;
 3916 }
 3917 
 3918 bool ConstraintTeacherMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 3919 {
 3920     Q_UNUSED(r);
 3921     Q_UNUSED(s);
 3922 
 3923     return false;
 3924 }
 3925 
 3926 bool ConstraintTeacherMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
 3927 {
 3928     if(maxHoursContinuously>r.nHoursPerDay)
 3929         return true;
 3930     
 3931     return false;
 3932 }
 3933 
 3934 bool ConstraintTeacherMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
 3935 {
 3936     assert(hasWrongDayOrHour(r));
 3937     
 3938     return true;
 3939 }
 3940 
 3941 bool ConstraintTeacherMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
 3942 {
 3943     assert(hasWrongDayOrHour(r));
 3944     
 3945     if(maxHoursContinuously>r.nHoursPerDay)
 3946         maxHoursContinuously=r.nHoursPerDay;
 3947 
 3948     return true;
 3949 }
 3950 
 3951 ///////////////////////////////////////////////////////////////////////////////////////////
 3952 ///////////////////////////////////////////////////////////////////////////////////////////
 3953 
 3954 ConstraintTeachersActivityTagMaxHoursContinuously::ConstraintTeachersActivityTagMaxHoursContinuously()
 3955     : TimeConstraint()
 3956 {
 3957     this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
 3958 }
 3959 
 3960 ConstraintTeachersActivityTagMaxHoursContinuously::ConstraintTeachersActivityTagMaxHoursContinuously(double wp, int maxhours, const QString& activityTag)
 3961  : TimeConstraint(wp)
 3962  {
 3963     assert(maxhours>0);
 3964     this->maxHoursContinuously=maxhours;
 3965     this->activityTagName=activityTag;
 3966 
 3967     this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
 3968 }
 3969 
 3970 bool ConstraintTeachersActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
 3971 {
 3972     Q_UNUSED(parent);
 3973 
 3974     //this->activityTagIndex=r.searchActivityTag(this->activityTagName);
 3975     activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
 3976     assert(this->activityTagIndex>=0);
 3977     
 3978     this->canonicalTeachersList.clear();
 3979     for(int i=0; i<r.nInternalTeachers; i++){
 3980         bool found=false;
 3981     
 3982         Teacher* tch=r.internalTeachersList[i];
 3983         for(int actIndex : qAsConst(tch->activitiesForTeacher)){
 3984             if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
 3985                 found=true;
 3986                 break;
 3987             }
 3988         }
 3989         
 3990         if(found)
 3991             this->canonicalTeachersList.append(i);
 3992     }
 3993 
 3994     return true;
 3995 }
 3996 
 3997 bool ConstraintTeachersActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
 3998 {
 3999     Q_UNUSED(r);
 4000     return false;
 4001 }
 4002 
 4003 QString ConstraintTeachersActivityTagMaxHoursContinuously::getXmlDescription(Rules& r){
 4004     Q_UNUSED(r);
 4005 
 4006     QString s="<ConstraintTeachersActivityTagMaxHoursContinuously>\n";
 4007     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 4008     s+="    <Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
 4009     s+="    <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
 4010     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 4011     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 4012     s+="</ConstraintTeachersActivityTagMaxHoursContinuously>\n";
 4013     return s;
 4014 }
 4015 
 4016 QString ConstraintTeachersActivityTagMaxHoursContinuously::getDescription(Rules& r){
 4017     Q_UNUSED(r);
 4018 
 4019     QString begin=QString("");
 4020     if(!active)
 4021         begin="X - ";
 4022         
 4023     QString end=QString("");
 4024     if(!comments.isEmpty())
 4025         end=", "+tr("C: %1", "Comments").arg(comments);
 4026         
 4027     QString s;
 4028     s+=tr("Teachers for activity tag %1 have max %2 hours continuously").arg(this->activityTagName).arg(this->maxHoursContinuously);s+=", ";
 4029     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
 4030 
 4031     return begin+s+end;
 4032 }
 4033 
 4034 QString ConstraintTeachersActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r){
 4035     Q_UNUSED(r);
 4036 
 4037     QString s=tr("Time constraint");s+="\n";
 4038     s+=tr("All teachers, for an activity tag, must respect the maximum number of hours continuously");s+="\n";
 4039     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 4040     s+=tr("Activity tag=%1").arg(this->activityTagName); s+="\n";
 4041     s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously); s+="\n";
 4042 
 4043     if(!active){
 4044         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 4045         s+="\n";
 4046     }
 4047     if(!comments.isEmpty()){
 4048         s+=tr("Comments=%1").arg(comments);
 4049         s+="\n";
 4050     }
 4051 
 4052     return s;
 4053 }
 4054 
 4055 double ConstraintTeachersActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 4056 {
 4057     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 4058     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 4059         c.teachersMatrixReady=true;
 4060         c.subgroupsMatrixReady=true;
 4061         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 4062         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 4063 
 4064         c.changedForMatrixCalculation=false;
 4065     }
 4066 
 4067     int nbroken;
 4068 
 4069     nbroken=0;
 4070     for(int i : qAsConst(this->canonicalTeachersList)){
 4071         Teacher* tch=r.internalTeachersList[i];
 4072         static int crtTeacherTimetableActivityTag[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
 4073         for(int d=0; d<r.nDaysPerWeek; d++)
 4074             for(int h=0; h<r.nHoursPerDay; h++)
 4075                 crtTeacherTimetableActivityTag[d][h]=-1;
 4076         for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
 4077             int d=c.times[ai]%r.nDaysPerWeek;
 4078             int h=c.times[ai]/r.nDaysPerWeek;
 4079             for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
 4080                 assert(h+dur<r.nHoursPerDay);
 4081                 assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
 4082                 if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
 4083                     crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
 4084             }
 4085         }
 4086     
 4087         for(int d=0; d<r.nDaysPerWeek; d++){
 4088             int nc=0;
 4089             for(int h=0; h<r.nHoursPerDay; h++){
 4090                 bool inc=false;
 4091                 if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
 4092                     inc=true;
 4093                 
 4094                 if(inc){
 4095                     nc++;
 4096                 }
 4097                 else{
 4098                     if(nc>this->maxHoursContinuously){
 4099                         nbroken++;
 4100 
 4101                         if(conflictsString!=NULL){
 4102                             QString s=(tr(
 4103                              "Time constraint teachers activity tag %1 max %2 hours continuously broken for teacher %3, on day %4, length=%5.")
 4104                              .arg(this->activityTagName)
 4105                              .arg(CustomFETString::number(this->maxHoursContinuously))
 4106                              .arg(r.internalTeachersList[i]->name)
 4107                              .arg(r.daysOfTheWeek[d])
 4108                              .arg(nc)
 4109                              )
 4110                              +
 4111                              " "
 4112                              +
 4113                              (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 4114                             
 4115                             dl.append(s);
 4116                             cl.append(weightPercentage/100);
 4117                 
 4118                             *conflictsString+= s+"\n";
 4119                         }
 4120                     }
 4121                 
 4122                     nc=0;
 4123                 }
 4124             }
 4125 
 4126             if(nc>this->maxHoursContinuously){
 4127                 nbroken++;
 4128 
 4129                 if(conflictsString!=NULL){
 4130                     QString s=(tr(
 4131                      "Time constraint teachers activity tag %1 max %2 hours continuously broken for teacher %3, on day %4, length=%5.")
 4132                      .arg(this->activityTagName)
 4133                      .arg(CustomFETString::number(this->maxHoursContinuously))
 4134                      .arg(r.internalTeachersList[i]->name)
 4135                      .arg(r.daysOfTheWeek[d])
 4136                      .arg(nc)
 4137                      )
 4138                      +
 4139                      " "
 4140                      +
 4141                      (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 4142                             
 4143                     dl.append(s);
 4144                     cl.append(weightPercentage/100);
 4145                 
 4146                     *conflictsString+= s+"\n";
 4147                 }
 4148             }
 4149         }
 4150     }
 4151 
 4152     if(weightPercentage==100)   
 4153         assert(nbroken==0);
 4154     return weightPercentage/100 * nbroken;
 4155 }
 4156 
 4157 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
 4158 {
 4159     Q_UNUSED(r);
 4160     Q_UNUSED(a);
 4161 
 4162     return false;
 4163 }
 4164 
 4165 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
 4166 {
 4167     Q_UNUSED(t);
 4168 
 4169     return true;
 4170 }
 4171 
 4172 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
 4173 {
 4174     Q_UNUSED(s);
 4175 
 4176     return false;
 4177 }
 4178 
 4179 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
 4180 {
 4181     return s->name==this->activityTagName;
 4182 }
 4183 
 4184 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 4185 {
 4186     Q_UNUSED(r);
 4187     Q_UNUSED(s);
 4188 
 4189     return false;
 4190 }
 4191 
 4192 bool ConstraintTeachersActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
 4193 {
 4194     if(maxHoursContinuously>r.nHoursPerDay)
 4195         return true;
 4196     
 4197     return false;
 4198 }
 4199 
 4200 bool ConstraintTeachersActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
 4201 {
 4202     assert(hasWrongDayOrHour(r));
 4203     
 4204     return true;
 4205 }
 4206 
 4207 bool ConstraintTeachersActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
 4208 {
 4209     assert(hasWrongDayOrHour(r));
 4210     
 4211     if(maxHoursContinuously>r.nHoursPerDay)
 4212         maxHoursContinuously=r.nHoursPerDay;
 4213 
 4214     return true;
 4215 }
 4216 
 4217 ///////////////////////////////////////////////////////////////////////////////////////////
 4218 ///////////////////////////////////////////////////////////////////////////////////////////
 4219 ConstraintTeacherActivityTagMaxHoursContinuously::ConstraintTeacherActivityTagMaxHoursContinuously()
 4220     : TimeConstraint()
 4221 {
 4222     this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
 4223 }
 4224 
 4225 ConstraintTeacherActivityTagMaxHoursContinuously::ConstraintTeacherActivityTagMaxHoursContinuously(double wp, int maxhours, const QString& teacher, const QString& activityTag)
 4226  : TimeConstraint(wp)
 4227  {
 4228     assert(maxhours>0);
 4229     this->maxHoursContinuously=maxhours;
 4230     this->teacherName=teacher;
 4231     this->activityTagName=activityTag;
 4232 
 4233     this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
 4234 }
 4235 
 4236 bool ConstraintTeacherActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
 4237 {
 4238     Q_UNUSED(parent);
 4239 
 4240     //this->teacher_ID=r.searchTeacher(this->teacherName);
 4241     teacher_ID=r.teachersHash.value(teacherName, -1);
 4242     assert(this->teacher_ID>=0);
 4243 
 4244     //this->activityTagIndex=r.searchActivityTag(this->activityTagName);
 4245     activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
 4246     assert(this->activityTagIndex>=0);
 4247 
 4248     this->canonicalTeachersList.clear();
 4249     int i=this->teacher_ID;
 4250     bool found=false;
 4251     
 4252     Teacher* tch=r.internalTeachersList[i];
 4253     for(int actIndex : qAsConst(tch->activitiesForTeacher)){
 4254         if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
 4255             found=true;
 4256             break;
 4257         }
 4258     }
 4259         
 4260     if(found)
 4261         this->canonicalTeachersList.append(i);
 4262 
 4263     return true;
 4264 }
 4265 
 4266 bool ConstraintTeacherActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
 4267 {
 4268     Q_UNUSED(r);
 4269     return false;
 4270 }
 4271 
 4272 QString ConstraintTeacherActivityTagMaxHoursContinuously::getXmlDescription(Rules& r){
 4273     Q_UNUSED(r);
 4274 
 4275     QString s="<ConstraintTeacherActivityTagMaxHoursContinuously>\n";
 4276     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 4277     s+="    <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
 4278     s+="    <Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
 4279     s+="    <Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
 4280     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 4281     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 4282     s+="</ConstraintTeacherActivityTagMaxHoursContinuously>\n";
 4283     return s;
 4284 }
 4285 
 4286 QString ConstraintTeacherActivityTagMaxHoursContinuously::getDescription(Rules& r){
 4287     Q_UNUSED(r);
 4288 
 4289     QString begin=QString("");
 4290     if(!active)
 4291         begin="X - ";
 4292         
 4293     QString end=QString("");
 4294     if(!comments.isEmpty())
 4295         end=", "+tr("C: %1", "Comments").arg(comments);
 4296         
 4297     QString s;
 4298     s+=tr("Teacher %1 for activity tag %2 has max %3 hours continuously").arg(this->teacherName).arg(this->activityTagName).arg(this->maxHoursContinuously);s+=", ";
 4299     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
 4300 
 4301     return begin+s+end;
 4302 }
 4303 
 4304 QString ConstraintTeacherActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r){
 4305     Q_UNUSED(r);
 4306 
 4307     QString s=tr("Time constraint");s+="\n";
 4308     s+=tr("A teacher for an activity tag must respect the maximum number of hours continuously");s+="\n";
 4309     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 4310     s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
 4311     s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
 4312     s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously); s+="\n";
 4313 
 4314     if(!active){
 4315         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 4316         s+="\n";
 4317     }
 4318     if(!comments.isEmpty()){
 4319         s+=tr("Comments=%1").arg(comments);
 4320         s+="\n";
 4321     }
 4322 
 4323     return s;
 4324 }
 4325 
 4326 double ConstraintTeacherActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 4327 {
 4328     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 4329     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 4330         c.teachersMatrixReady=true;
 4331         c.subgroupsMatrixReady=true;
 4332         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 4333         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 4334 
 4335         c.changedForMatrixCalculation=false;
 4336     }
 4337 
 4338     int nbroken;
 4339 
 4340     nbroken=0;
 4341     for(int i : qAsConst(this->canonicalTeachersList)){
 4342         Teacher* tch=r.internalTeachersList[i];
 4343         static int crtTeacherTimetableActivityTag[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
 4344         for(int d=0; d<r.nDaysPerWeek; d++)
 4345             for(int h=0; h<r.nHoursPerDay; h++)
 4346                 crtTeacherTimetableActivityTag[d][h]=-1;
 4347         for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
 4348             int d=c.times[ai]%r.nDaysPerWeek;
 4349             int h=c.times[ai]/r.nDaysPerWeek;
 4350             for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
 4351                 assert(h+dur<r.nHoursPerDay);
 4352                 assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
 4353                 if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
 4354                     crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
 4355             }
 4356         }
 4357 
 4358         for(int d=0; d<r.nDaysPerWeek; d++){
 4359             int nc=0;
 4360             for(int h=0; h<r.nHoursPerDay; h++){
 4361                 bool inc=false;
 4362 
 4363                 if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
 4364                     inc=true;
 4365                 
 4366                 if(inc)
 4367                     nc++;
 4368                 else{
 4369                     if(nc>this->maxHoursContinuously){
 4370                         nbroken++;
 4371 
 4372                         if(conflictsString!=NULL){
 4373                             QString s=(tr(
 4374                              "Time constraint teacher activity tag max %1 hours continuously broken for teacher %2, activity tag %3, on day %4, length=%5.")
 4375                              .arg(CustomFETString::number(this->maxHoursContinuously))
 4376                              .arg(r.internalTeachersList[i]->name)
 4377                              .arg(this->activityTagName)
 4378                              .arg(r.daysOfTheWeek[d])
 4379                              .arg(nc)
 4380                              )
 4381                              +
 4382                              " "
 4383                              +
 4384                              (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 4385                             
 4386                             dl.append(s);
 4387                             cl.append(weightPercentage/100);
 4388                 
 4389                             *conflictsString+= s+"\n";
 4390                         }
 4391                     }
 4392                 
 4393                     nc=0;
 4394                 }
 4395             }
 4396 
 4397             if(nc>this->maxHoursContinuously){
 4398                 nbroken++;
 4399 
 4400                 if(conflictsString!=NULL){
 4401                     QString s=(tr(
 4402                      "Time constraint teacher activity tag max %1 hours continuously broken for teacher %2, activity tag %3, on day %4, length=%5.")
 4403                      .arg(CustomFETString::number(this->maxHoursContinuously))
 4404                      .arg(r.internalTeachersList[i]->name)
 4405                      .arg(this->activityTagName)
 4406                      .arg(r.daysOfTheWeek[d])
 4407                      .arg(nc)
 4408                      )
 4409                      +
 4410                      " "
 4411                      +
 4412                      (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
 4413                             
 4414                     dl.append(s);
 4415                     cl.append(weightPercentage/100);
 4416                 
 4417                     *conflictsString+= s+"\n";
 4418                 }
 4419             }
 4420         }
 4421     }
 4422 
 4423     if(weightPercentage==100)   
 4424         assert(nbroken==0);
 4425     return weightPercentage/100 * nbroken;
 4426 }
 4427 
 4428 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
 4429 {
 4430     Q_UNUSED(r);
 4431     Q_UNUSED(a);
 4432 
 4433     return false;
 4434 }
 4435 
 4436 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
 4437 {
 4438     if(this->teacherName==t->name)
 4439         return true;
 4440     return false;
 4441 }
 4442 
 4443 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
 4444 {
 4445     Q_UNUSED(s);
 4446 
 4447     return false;
 4448 }
 4449 
 4450 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
 4451 {
 4452     return this->activityTagName==s->name;
 4453 }
 4454 
 4455 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
 4456 {
 4457     Q_UNUSED(r);
 4458     Q_UNUSED(s);
 4459 
 4460     return false;
 4461 }
 4462 
 4463 bool ConstraintTeacherActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
 4464 {
 4465     if(maxHoursContinuously>r.nHoursPerDay)
 4466         return true;
 4467     
 4468     return false;
 4469 }
 4470 
 4471 bool ConstraintTeacherActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
 4472 {
 4473     assert(hasWrongDayOrHour(r));
 4474     
 4475     return true;
 4476 }
 4477 
 4478 bool ConstraintTeacherActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
 4479 {
 4480     assert(hasWrongDayOrHour(r));
 4481     
 4482     if(maxHoursContinuously>r.nHoursPerDay)
 4483         maxHoursContinuously=r.nHoursPerDay;
 4484 
 4485     return true;
 4486 }
 4487 
 4488 ///////////////////////////////////////////////////////////////////////////////////////////
 4489 ///////////////////////////////////////////////////////////////////////////////////////////
 4490 
 4491 ConstraintTeacherMaxDaysPerWeek::ConstraintTeacherMaxDaysPerWeek()
 4492     : TimeConstraint()
 4493 {
 4494     this->type=CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK;
 4495 }
 4496 
 4497 ConstraintTeacherMaxDaysPerWeek::ConstraintTeacherMaxDaysPerWeek(double wp, int maxnd, const QString& tn)
 4498      : TimeConstraint(wp)
 4499 {
 4500     this->teacherName = tn;
 4501     this->maxDaysPerWeek=maxnd;
 4502     this->type=CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK;
 4503 }
 4504 
 4505 bool ConstraintTeacherMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
 4506 {
 4507     Q_UNUSED(parent);
 4508 
 4509     //this->teacher_ID=r.searchTeacher(this->teacherName);
 4510     teacher_ID=r.teachersHash.value(teacherName, -1);
 4511     assert(this->teacher_ID>=0);
 4512     return true;
 4513 }
 4514 
 4515 bool ConstraintTeacherMaxDaysPerWeek::hasInactiveActivities(Rules& r)
 4516 {
 4517     Q_UNUSED(r);
 4518     return false;
 4519 }
 4520 
 4521 QString ConstraintTeacherMaxDaysPerWeek::getXmlDescription(Rules& r)
 4522 {
 4523     Q_UNUSED(r);
 4524 
 4525     QString s="<ConstraintTeacherMaxDaysPerWeek>\n";
 4526     s+="    <Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
 4527     s+="    <Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
 4528     s+="    <Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
 4529     s+="    <Active>"+trueFalse(active)+"</Active>\n";
 4530     s+="    <Comments>"+protect(comments)+"</Comments>\n";
 4531     s+="</ConstraintTeacherMaxDaysPerWeek>\n";
 4532     return s;
 4533 }
 4534 
 4535 QString ConstraintTeacherMaxDaysPerWeek::getDescription(Rules& r){
 4536     Q_UNUSED(r);
 4537 
 4538     QString begin=QString("");
 4539     if(!active)
 4540         begin="X - ";
 4541         
 4542     QString end=QString("");
 4543     if(!comments.isEmpty())
 4544         end=", "+tr("C: %1", "Comments").arg(comments);
 4545         
 4546     QString s=tr("Teacher max days per week");s+=", ";
 4547     s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
 4548     s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
 4549     s+=tr("MD:%1", "Max days (per week)").arg(this->maxDaysPerWeek);
 4550 
 4551     return begin+s+end;
 4552 }
 4553 
 4554 QString ConstraintTeacherMaxDaysPerWeek::getDetailedDescription(Rules& r){
 4555     Q_UNUSED(r);
 4556 
 4557     QString s=tr("Time constraint");s+="\n";
 4558     s+=tr("A teacher must respect the maximum number of days per week");s+="\n";
 4559     s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
 4560     s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
 4561     s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
 4562 
 4563     if(!active){
 4564         s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
 4565         s+="\n";
 4566     }
 4567     if(!comments.isEmpty()){
 4568         s+=tr("Comments=%1").arg(comments);
 4569         s+="\n";
 4570     }
 4571 
 4572     return s;
 4573 }
 4574 
 4575 double ConstraintTeacherMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
 4576 {
 4577     //if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
 4578     if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
 4579         c.teachersMatrixReady=true;
 4580         c.subgroupsMatrixReady=true;
 4581         subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
 4582         teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
 4583 
 4584         c.changedForMatrixCalculation=false;
 4585     }
 4586 
 4587     int nbroken;
 4588 
 4589     //without logging
 4590     if(conflictsString==NULL){
 4591         nbroken=0;
 4592         //count sort
 4593         int t=this->teacher_ID;
 4594         int nd[MAX_HOURS_PER_DAY + 1];
 4595         for(int h=0; h<=r.nHoursPerDay; h++)
 4596             nd[h]=0;
 4597         for(int d=0; d<r.nDaysPerWeek; d++){
 4598             int nh=0;
 4599             for(int h=0; h<r.nHoursPerDay; h++)
 4600                 nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
 4601             nd[nh]++;
 4602         }
 4603         //return the minimum occupied days which do not respect this constraint
 4604         int i = r.nDaysPerWeek - this->maxDaysPerWeek;
 4605         for(int k=0; k<=r.nHoursPerDay; k++){
 4606             if(nd[k]>0){
 4607                 if(i>nd[k]){
 4608                     i-=nd[k];
 4609                     nbroken+=nd[k]*k;
 4610                 }
 4611                 else{
 4612                     nbroken+=i*k;
 4613